Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions integration-tests/contracts/ConstructorReverter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0;

import { Reverter } from './Reverter.sol';

contract ConstructorReverter is Reverter {
constructor() {
doRevert();
}
}
10 changes: 10 additions & 0 deletions integration-tests/contracts/Reverter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0;

contract Reverter {
string constant public revertMessage = "This is a simple reversion.";

function doRevert() public pure {
revert(revertMessage);
}
}
137 changes: 134 additions & 3 deletions integration-tests/test/rpc.spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
import { injectL2Context } from '@eth-optimism/core-utils'
import { Wallet, BigNumber, ethers } from 'ethers'
import { Wallet, BigNumber, Contract } from 'ethers'
import { ethers } from 'hardhat'
import chai, { expect } from 'chai'
import { sleep, l2Provider, GWEI } from './shared/utils'
import {
sleep,
l2Provider,
GWEI,
encodeSolidityRevertMessage,
} from './shared/utils'
import chaiAsPromised from 'chai-as-promised'
import { OptimismEnv } from './shared/env'
import {
TransactionReceipt,
TransactionRequest,
} from '@ethersproject/providers'
import { solidity } from 'ethereum-waffle'
chai.use(chaiAsPromised)
chai.use(solidity)

describe('Basic RPC tests', () => {
let env: OptimismEnv
let wallet: Wallet

const DEFAULT_TRANSACTION = {
to: '0x' + '1234'.repeat(10),
Expand All @@ -18,10 +31,33 @@ describe('Basic RPC tests', () => {
}

const provider = injectL2Context(l2Provider)
const wallet = Wallet.createRandom().connect(provider)

let Reverter: Contract
let revertMessage: string
let revertingTx: TransactionRequest
let revertingDeployTx: TransactionRequest

before(async () => {
env = await OptimismEnv.new()
wallet = env.l2Wallet
const Factory__Reverter = await ethers.getContractFactory(
'Reverter',
wallet
)
Reverter = await Factory__Reverter.connect(env.l2Wallet).deploy()
await Reverter.deployTransaction.wait()
revertMessage = await Reverter.revertMessage()
revertingTx = {
to: Reverter.address,
data: Reverter.interface.encodeFunctionData('doRevert'),
}
const Factory__ConstructorReverter = await ethers.getContractFactory(
'ConstructorReverter',
wallet
)
revertingDeployTx = {
data: Factory__ConstructorReverter.bytecode,
}
})

describe('eth_sendRawTransaction', () => {
Expand Down Expand Up @@ -92,6 +128,91 @@ describe('Basic RPC tests', () => {
})
})

describe('eth_call', () => {
let expectedReverterRevertData: string

before(async () => {
expectedReverterRevertData = encodeSolidityRevertMessage(revertMessage)
})

it('should correctly return solidity revert data from a call', async () => {
const revertData = await provider.call(revertingTx)
const expectedRevertData = encodeSolidityRevertMessage(revertMessage)
expect(revertData).to.eq(expectedRevertData)
})

it('should produce error when called from ethers', async () => {
await expect(Reverter.doRevert()).to.be.revertedWith(revertMessage)
})

it('should correctly return revert data from contract creation', async () => {
const revertData = await provider.call(revertingDeployTx)

expect(revertData).to.eq(expectedReverterRevertData)
})

it('should return the correct error message when attempting to deploy unsafe initcode', async () => {
// PUSH1 0x00 PUSH1 0x00 SSTORE
const unsafeCode = '0x6000600055'
const tx: TransactionRequest = {
data: unsafeCode,
}
const result = await provider.call(tx)
const expected = encodeSolidityRevertMessage(
'Contract creation code contains unsafe opcodes. Did you use the right compiler or pass an unsafe constructor argument?'
)
expect(result).to.eq(expected)
})
})

describe('eth_getTransactionReceipt', () => {
it('correctly exposes revert data for contract calls', async () => {
const req: TransactionRequest = {
...revertingTx,
gasLimit: 8_999_999, // override gas estimation
}

const tx = await wallet.sendTransaction(req)

let errored = false
try {
await tx.wait()
} catch (e) {
errored = true
}
expect(errored).to.be.true

const receipt: TransactionReceipt = await provider.getTransactionReceipt(
tx.hash
)

expect(receipt.status).to.eq(0)
})

it('correctly exposes revert data for contract creations', async () => {
const req: TransactionRequest = {
...revertingDeployTx,
gasLimit: 8_999_999, // override gas estimation
}

const tx = await wallet.sendTransaction(req)

let errored = false
try {
await tx.wait()
} catch (e) {
errored = true
}
expect(errored).to.be.true

const receipt: TransactionReceipt = await provider.getTransactionReceipt(
tx.hash
)

expect(receipt.status).to.eq(0)
})
})

describe('eth_getTransactionByHash', () => {
it('should be able to get all relevant l1/l2 transaction data', async () => {
const tx = DEFAULT_TRANSACTION
Expand Down Expand Up @@ -195,5 +316,15 @@ describe('Basic RPC tests', () => {
expect(estimate).to.be.deep.eq(expected)
}
})

it('should fail for a reverting call transaction', async () => {
await expect(provider.send('eth_estimateGas', [revertingTx])).to.be
.reverted
})

it('should fail for a reverting deploy transaction', async () => {
await expect(provider.send('eth_estimateGas', [revertingDeployTx])).to.be
.reverted
})
})
})
8 changes: 7 additions & 1 deletion integration-tests/test/shared/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import {
getContractFactory,
getContractInterface,
} from '@eth-optimism/contracts'
import { Watcher } from '@eth-optimism/core-utils'
import { remove0x, Watcher } from '@eth-optimism/core-utils'
import {
Contract,
Wallet,
constants,
providers,
BigNumberish,
BigNumber,
utils,
} from 'ethers'
import { cleanEnv, str, num } from 'envalid'

Expand Down Expand Up @@ -100,3 +101,8 @@ export const fundUser = async (
}

export const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))

const abiCoder = new utils.AbiCoder()
export const encodeSolidityRevertMessage = (_reason: string): string => {
return '0x08c379a0' + remove0x(abiCoder.encode(['string'], [_reason]))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the Error(string) function selector?

}