Skip to content

Commit

Permalink
test(integration-tests): ovmCALL-types with value (compiler and wrapper)
Browse files Browse the repository at this point in the history
* fix ovmDELEGATECALL type, update tests

* add ovmSELFBALANCE

* fix ovmDELEGATECALL jumping to CALL

* chore: lint
  • Loading branch information
ben-chain committed Jun 1, 2021
1 parent 6bba125 commit ea5c006
Show file tree
Hide file tree
Showing 13 changed files with 377 additions and 28 deletions.
63 changes: 63 additions & 0 deletions integration-tests/contracts/ValueCallsWithCompiler.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: MIT
// @unsupported: evm
pragma solidity >=0.7.0;

contract ValueCallsWithCompiler {

receive() external payable { }

function getBalance(
address _address
) external payable returns(uint256) {
return _address.balance;
}

function simpleSend(
address _address,
uint _value
) external payable returns (bool, bytes memory) {
return sendWithData(_address, _value, hex"");
}

function sendWithData(
address _address,
uint _value,
bytes memory _calldata
) public returns (bool, bytes memory) {
return _address.call{value: _value}(_calldata);
}

function verifyCallValueAndRevert(
uint256 _expectedValue
) external payable {
bool correct = _checkCallValue(_expectedValue);
// do the opposite of expected if the value is wrong.
if (correct) {
revert("expected revert");
} else {
return;
}
}

function getCallValue() public payable returns(uint256) {
return msg.value;
}

function verifyCallValueAndReturn(
uint256 _expectedValue
) external payable {
bool correct = _checkCallValue(_expectedValue);
// do the opposite of expected if the value is wrong.
if (correct) {
return;
} else {
revert("unexpected revert");
}
}

function _checkCallValue(
uint256 _expectedValue
) internal returns(bool) {
return getCallValue() == _expectedValue;
}
}
65 changes: 65 additions & 0 deletions integration-tests/contracts/ValueCallsWithWrapper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: MIT
// @unsupported: evm
pragma solidity >=0.7.0;

import { Lib_ExecutionManagerWrapper } from "@eth-optimism/contracts/contracts/optimistic-ethereum/libraries/wrappers/Lib_ExecutionManagerWrapper.sol";

contract ValueCallsWithWrapper {

receive() external payable { }

function getBalance(
address _address
) external payable returns(uint256) {
return Lib_ExecutionManagerWrapper.ovmBALANCE(_address);
}

function simpleSend(
address _address,
uint _value
) external payable returns (bool, bytes memory) {
return sendWithData(_address, _value, hex"");
}

function sendWithData(
address _address,
uint _value,
bytes memory _calldata
) public returns (bool, bytes memory) {
return Lib_ExecutionManagerWrapper.ovmCALL(gasleft(), _address, _value, _calldata);
}

function verifyCallValueAndRevert(
uint256 _expectedValue
) external payable {
bool correct = _checkCallValue(_expectedValue);
// do the opposite of expected if the value is wrong.
if (correct) {
revert("expected revert");
} else {
return;
}
}

function getCallValue() public payable returns(uint256) {
return Lib_ExecutionManagerWrapper.ovmCALLVALUE();
}

function verifyCallValueAndReturn(
uint256 _expectedValue
) external payable {
bool correct = _checkCallValue(_expectedValue);
// do the opposite of expected if the value is wrong.
if (correct) {
return;
} else {
revert("unexpected revert");
}
}

function _checkCallValue(
uint256 _expectedValue
) internal returns(bool) {
return getCallValue() == _expectedValue;
}
}
2 changes: 1 addition & 1 deletion integration-tests/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const config: HardhatUserConfig = {
},
solidity: '0.7.6',
ovm: {
solcVersion: '0.7.6',
solcVersion: '0.7.6-experimental_callvalue',
},
gasReporter: {
enabled: enableGasReport,
Expand Down
113 changes: 113 additions & 0 deletions integration-tests/test/native-eth-ovm-calls.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { BigNumber, Contract, ContractFactory, Wallet } from 'ethers'
import { ethers } from 'hardhat'
import chai, { expect } from 'chai'
import { GWEI, fundUser, encodeSolidityRevertMessage } from './shared/utils'
import { OptimismEnv } from './shared/env'
import { solidity } from 'ethereum-waffle'

chai.use(solidity)

for (const sourceName of ['ValueCallsWithWrapper', 'ValueCallsWithCompiler']) {
describe(`OVM calls with native ETH value (${sourceName})`, async () => {
const initialBalance0 = 42000

let env: OptimismEnv
let wallet: Wallet
let other: Wallet
let Factory__ValueCalls: ContractFactory
let ValueCalls0: Contract
let ValueCalls1: Contract

const checkBalances = async (expectedBalances: number[]) => {
// query geth as one check
const balance0 = await wallet.provider.getBalance(ValueCalls0.address)
const balance1 = await wallet.provider.getBalance(ValueCalls1.address)
expect(balance0).to.deep.eq(BigNumber.from(expectedBalances[0]))
expect(balance1).to.deep.eq(BigNumber.from(expectedBalances[1]))
// also use ovmBALANCE() opcode via eth_call
const ovmBALANCE0 = await ValueCalls0.callStatic.getBalance(
ValueCalls0.address
)
const ovmBALANCE1 = await ValueCalls0.callStatic.getBalance(
ValueCalls1.address
)
expect(ovmBALANCE0).to.deep.eq(BigNumber.from(expectedBalances[0]))
expect(ovmBALANCE1).to.deep.eq(BigNumber.from(expectedBalances[1]))
}

before(async () => {
env = await OptimismEnv.new()
wallet = env.l2Wallet
other = Wallet.createRandom().connect(ethers.provider)
Factory__ValueCalls = await ethers.getContractFactory(sourceName, wallet)
})

beforeEach(async () => {
ValueCalls0 = await Factory__ValueCalls.deploy()
ValueCalls1 = await Factory__ValueCalls.deploy()
await fundUser(
env.watcher,
env.gateway,
initialBalance0,
ValueCalls0.address
)
// These tests ass assume ValueCalls0 starts with a balance, but ValueCalls1 does not.
await checkBalances([initialBalance0, 0])
})

it('should allow ETH to be sent', async () => {
const sendAmount = 15
const tx = await ValueCalls0.simpleSend(ValueCalls1.address, sendAmount, {
gasPrice: 0,
})
await tx.wait()
await checkBalances([initialBalance0 - sendAmount, sendAmount])
})

it('should allow ETH to be sent and have the correct ovmCALLVALUE', async () => {
const sendAmount = 15
const [success, returndata] = await ValueCalls0.callStatic.sendWithData(
ValueCalls1.address,
sendAmount,
ValueCalls1.interface.encodeFunctionData('getCallValue')
)

expect(success).to.be.true
expect(BigNumber.from(returndata)).to.deep.eq(BigNumber.from(sendAmount))
})

it('should have the correct callvalue but not persist the transfer if the target reverts', async () => {
const sendAmount = 15
const internalCalldata = ValueCalls1.interface.encodeFunctionData(
'verifyCallValueAndRevert',
[sendAmount]
)
const [success, returndata] = await ValueCalls0.callStatic.sendWithData(
ValueCalls1.address,
sendAmount,
internalCalldata
)

expect(success).to.be.false
expect(returndata).to.eq(encodeSolidityRevertMessage('expected revert'))

await checkBalances([initialBalance0, 0])
})

it('should look like the subcall reverts with no data if value exceeds balance', async () => {
const sendAmount = initialBalance0 + 1
const internalCalldata = ValueCalls1.interface.encodeFunctionData(
'verifyCallValueAndReturn',
[sendAmount] // this would be correct and return successfuly, IF it could get here
)
const [success, returndata] = await ValueCalls0.callStatic.sendWithData(
ValueCalls1.address,
sendAmount,
internalCalldata
)

expect(success).to.be.false
expect(returndata).to.eq('0x')
})
})
}
4 changes: 2 additions & 2 deletions integration-tests/test/native-eth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ describe('Native ETH Integration Tests', async () => {
const amount = utils.parseEther('0.5')
const addr = '0x' + '1234'.repeat(10)
const gas = await env.ovmEth.estimateGas.transfer(addr, amount)
expect(gas).to.be.deep.eq(BigNumber.from(0x23284d28fe6d))
expect(gas).to.be.deep.eq(BigNumber.from(0x23284d2907d4))
})

it('Should estimate gas for ETH withdraw', async () => {
const amount = utils.parseEther('0.5')
const gas = await env.ovmEth.estimateGas.withdraw(amount)
expect(gas).to.be.deep.eq(BigNumber.from(0x207ad91a77b4))
expect(gas).to.be.deep.eq(BigNumber.from(0x207ad91aeaad))
})
})

Expand Down
2 changes: 1 addition & 1 deletion integration-tests/test/rpc.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ describe('Basic RPC tests', () => {
to: DEFAULT_TRANSACTION.to,
value: 0,
})
expect(estimate).to.be.eq(33600000119751)
expect(estimate).to.be.eq(33600000122284)
})

it('should return a gas estimate that grows with the size of data', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -678,20 +678,19 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
function ovmDELEGATECALL(
uint256 _gasLimit,
address _address,
uint256 _value,
bytes memory _calldata
)
override
external
public
fixedGasDiscount(40000)
returns (
bool _success,
bytes memory _returndata
)
{
// DELEGATECALL does not change anything about the message context other than value.
// DELEGATECALL does not change anything about the message context other than value 0.
MessageContext memory nextMessageContext = messageContext;
nextMessageContext.ovmCALLVALUE = _value;
nextMessageContext.ovmCALLVALUE = 0;

return _callContract(
nextMessageContext,
Expand All @@ -702,14 +701,13 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
}

/**
* @notice Legacy ovmCALL function which did not support ETH value; maintains backwards compatibility.
* @notice Legacy ovmCALL function which did not support ETH value; this maintains backwards compatibility.
* @param _gasLimit Amount of gas to be passed into this call.
* @param _address Address of the contract to call.
* @param _calldata Data to send along with the call.
* @return _success Whether or not the call returned (rather than reverted).
* @return _returndata Data returned by the call.
*/
// TODO: replicate this for ovmDELEGATECALL
function ovmCALL(
uint256 _gasLimit,
address _address,
Expand Down Expand Up @@ -860,7 +858,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
address _contract
)
override
external
public
returns (
uint256 _BALANCE
)
Expand All @@ -887,6 +885,20 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
return abi.decode(returndata, (uint256));
}

/**
* @notice Overrides SELFBALANCE.
* @return _BALANCE OVM_ETH balance of the requesting contract.
*/
function ovmSELFBALANCE()
override
external
returns (
uint256 _BALANCE
)
{
return ovmBALANCE(ovmADDRESS());
}

/***************************************
* Public Functions: Execution Context *
***************************************/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ interface iOVM_ExecutionManager {

function ovmCALL(uint256 _gasLimit, address _address, uint _value, bytes memory _calldata) external returns (bool _success, bytes memory _returndata);
function ovmSTATICCALL(uint256 _gasLimit, address _address, bytes memory _calldata) external returns (bool _success, bytes memory _returndata);
function ovmDELEGATECALL(uint256 _gasLimit, address _address, uint _value, bytes memory _calldata) external returns (bool _success, bytes memory _returndata);
function ovmDELEGATECALL(uint256 _gasLimit, address _address, bytes memory _calldata) external returns (bool _success, bytes memory _returndata);


/****************************
Expand All @@ -149,6 +149,7 @@ interface iOVM_ExecutionManager {
function ovmEXTCODESIZE(address _contract) external returns (uint256 _size);
function ovmEXTCODEHASH(address _contract) external returns (bytes32 _hash);
function ovmBALANCE(address _contract) external returns (uint256 _balance); // TODO: where to put this one?
function ovmSELFBALANCE() external returns (uint256 _balance); // TODO: where to put this one?


/***************************************
Expand Down
Loading

0 comments on commit ea5c006

Please sign in to comment.