Commit c43b33ec authored by Kevin Ho's avatar Kevin Ho Committed by GitHub

WETH deposit and withdraw on OVM_ETH (#1083)

* feat(contracts): add no-op WETH9 functionality to OVM_ETH

* working WETH deposit and withdraw + tests

* add changeset

* address PR feedback

* update WETH9 contract implementation

* add fallback to WETH9

* add fallback and revert withdraw test

* update nit comment
Co-authored-by: default avatarben <ben@pseudonym.party>
parent ef0df819
---
'@eth-optimism/integration-tests': patch
'@eth-optimism/contracts': patch
---
Add WETH9 compatible deposit and withdraw functions to OVM_ETH
...@@ -6,6 +6,7 @@ import { Direction } from './shared/watcher-utils' ...@@ -6,6 +6,7 @@ import { Direction } from './shared/watcher-utils'
import { import {
expectApprox, expectApprox,
fundUser,
PROXY_SEQUENCER_ENTRYPOINT_ADDRESS, PROXY_SEQUENCER_ENTRYPOINT_ADDRESS,
} from './shared/utils' } from './shared/utils'
import { OptimismEnv } from './shared/env' import { OptimismEnv } from './shared/env'
...@@ -302,4 +303,81 @@ describe('Native ETH Integration Tests', async () => { ...@@ -302,4 +303,81 @@ describe('Native ETH Integration Tests', async () => {
expect(l1BalanceAfter).to.deep.eq(l1BalanceBefore.add(withdrawnAmount)) expect(l1BalanceAfter).to.deep.eq(l1BalanceBefore.add(withdrawnAmount))
expect(l2BalanceAfter).to.deep.eq(amount.sub(withdrawnAmount).sub(fee)) expect(l2BalanceAfter).to.deep.eq(amount.sub(withdrawnAmount).sub(fee))
}) })
describe('WETH9 functionality', async () => {
let initialBalance: BigNumber
const value = 10
beforeEach(async () => {
await fundUser(env.watcher, env.l1Bridge, value, env.l2Wallet.address)
initialBalance = await env.l2Wallet.provider.getBalance(
env.l2Wallet.address
)
})
it('successfully deposits', async () => {
const depositTx = await env.ovmEth.deposit({ value, gasPrice: 0 })
const receipt = await depositTx.wait()
expect(
await env.l2Wallet.provider.getBalance(env.l2Wallet.address)
).to.equal(initialBalance)
expect(receipt.events.length).to.equal(4)
// The first transfer event is fee payment
const [
,
firstTransferEvent,
secondTransferEvent,
depositEvent,
] = receipt.events
expect(firstTransferEvent.event).to.equal('Transfer')
expect(firstTransferEvent.args.from).to.equal(env.l2Wallet.address)
expect(firstTransferEvent.args.to).to.equal(env.ovmEth.address)
expect(firstTransferEvent.args.value).to.equal(value)
expect(secondTransferEvent.event).to.equal('Transfer')
expect(secondTransferEvent.args.from).to.equal(env.ovmEth.address)
expect(secondTransferEvent.args.to).to.equal(env.l2Wallet.address)
expect(secondTransferEvent.args.value).to.equal(value)
expect(depositEvent.event).to.equal('Deposit')
expect(depositEvent.args.dst).to.equal(env.l2Wallet.address)
expect(depositEvent.args.wad).to.equal(value)
})
it('successfully deposits on fallback', async () => {
const fallbackTx = await env.l2Wallet.sendTransaction({
to: env.ovmEth.address,
value,
gasPrice: 0,
})
const receipt = await fallbackTx.wait()
expect(receipt.status).to.equal(1)
expect(
await env.l2Wallet.provider.getBalance(env.l2Wallet.address)
).to.equal(initialBalance)
})
it('successfully withdraws', async () => {
const withdrawTx = await env.ovmEth.withdraw(value, { gasPrice: 0 })
const receipt = await withdrawTx.wait()
expect(
await env.l2Wallet.provider.getBalance(env.l2Wallet.address)
).to.equal(initialBalance)
expect(receipt.events.length).to.equal(2)
// The first transfer event is fee payment
const depositEvent = receipt.events[1]
expect(depositEvent.event).to.equal('Withdrawal')
expect(depositEvent.args.src).to.equal(env.l2Wallet.address)
expect(depositEvent.args.wad).to.equal(value)
})
it('reverts on invalid withdraw', async () => {
await expect(env.ovmEth.withdraw(initialBalance.add(1), { gasPrice: 0 }))
.to.be.reverted
})
})
}) })
...@@ -6,6 +6,7 @@ import { Lib_PredeployAddresses } from "../../libraries/constants/Lib_PredeployA ...@@ -6,6 +6,7 @@ import { Lib_PredeployAddresses } from "../../libraries/constants/Lib_PredeployA
/* Contract Imports */ /* Contract Imports */
import { L2StandardERC20 } from "../../libraries/standards/L2StandardERC20.sol"; import { L2StandardERC20 } from "../../libraries/standards/L2StandardERC20.sol";
import { IWETH9 } from "../../libraries/standards/IWETH9.sol";
/** /**
* @title OVM_ETH * @title OVM_ETH
...@@ -15,7 +16,7 @@ import { L2StandardERC20 } from "../../libraries/standards/L2StandardERC20.sol"; ...@@ -15,7 +16,7 @@ import { L2StandardERC20 } from "../../libraries/standards/L2StandardERC20.sol";
* Compiler used: optimistic-solc * Compiler used: optimistic-solc
* Runtime target: OVM * Runtime target: OVM
*/ */
contract OVM_ETH is L2StandardERC20 { contract OVM_ETH is L2StandardERC20, IWETH9 {
/*************** /***************
* Constructor * * Constructor *
...@@ -29,4 +30,50 @@ contract OVM_ETH is L2StandardERC20 { ...@@ -29,4 +30,50 @@ contract OVM_ETH is L2StandardERC20 {
"ETH" "ETH"
) )
{} {}
/******************************
* Custom WETH9 Functionality *
******************************/
fallback() external payable {
deposit();
}
/**
* Implements the WETH9 deposit() function as a no-op.
* WARNING: this function does NOT have to do with cross-chain asset bridging. The
* relevant deposit and withdraw functions for that use case can be found at L2StandardBridge.sol.
* This function allows developers to treat OVM_ETH as WETH without any modifications to their code.
*/
function deposit()
public
payable
override
{
// Calling deposit() with nonzero value will send the ETH to this contract address. Once recieved here,
// We transfer it back by sending to the msg.sender.
_transfer(address(this), msg.sender, msg.value);
emit Deposit(msg.sender, msg.value);
}
/**
* Implements the WETH9 withdraw() function as a no-op.
* WARNING: this function does NOT have to do with cross-chain asset bridging. The
* relevant deposit and withdraw functions for that use case can be found at L2StandardBridge.sol.
* This function allows developers to treat OVM_ETH as WETH without any modifications to their code.
* @param _wad Amount being withdrawn
*/
function withdraw(
uint256 _wad
)
external
override
{
// Calling withdraw() with value exceeding the withdrawer's ovmBALANCE should revert, as in WETH9.
require(balanceOf(msg.sender) >= _wad);
// Other than emitting an event, OVM_ETH already is native ETH, so we don't need to do anything else.
emit Withdrawal(msg.sender, _wad);
}
} }
...@@ -10,7 +10,7 @@ library Lib_PredeployAddresses { ...@@ -10,7 +10,7 @@ library Lib_PredeployAddresses {
address internal constant DEPLOYER_WHITELIST = 0x4200000000000000000000000000000000000002; address internal constant DEPLOYER_WHITELIST = 0x4200000000000000000000000000000000000002;
address internal constant ECDSA_CONTRACT_ACCOUNT = 0x4200000000000000000000000000000000000003; address internal constant ECDSA_CONTRACT_ACCOUNT = 0x4200000000000000000000000000000000000003;
address internal constant SEQUENCER_ENTRYPOINT = 0x4200000000000000000000000000000000000005; address internal constant SEQUENCER_ENTRYPOINT = 0x4200000000000000000000000000000000000005;
address internal constant OVM_ETH = 0x4200000000000000000000000000000000000006; address payable internal constant OVM_ETH = 0x4200000000000000000000000000000000000006;
address internal constant L2_CROSS_DOMAIN_MESSENGER = 0x4200000000000000000000000000000000000007; address internal constant L2_CROSS_DOMAIN_MESSENGER = 0x4200000000000000000000000000000000000007;
address internal constant LIB_ADDRESS_MANAGER = 0x4200000000000000000000000000000000000008; address internal constant LIB_ADDRESS_MANAGER = 0x4200000000000000000000000000000000000008;
address internal constant PROXY_EOA = 0x4200000000000000000000000000000000000009; address internal constant PROXY_EOA = 0x4200000000000000000000000000000000000009;
......
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
/// @title Interface for WETH9. Also contains the non-ERC20 events normally present in the WETH9 implementation.
interface IWETH9 is IERC20 {
event Deposit(address indexed dst, uint256 wad);
event Withdrawal(address indexed src, uint256 wad);
/// @notice Deposit ether to get wrapped ether
function deposit() external payable;
/// @notice Withdraw wrapped ether to get ether
function withdraw(uint256) external;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment