Commit a25ad8ba authored by ben-chain's avatar ben-chain Committed by GitHub

Extensible deposit withdraw (#254)

* messenging --> messaging

* fix imports

* fix all messaging rename

* starting

* sub in L2 ERC20, unit tests passing

* progress, contracts building

* all tests passing, time to clean up

* start commenting

* revert ETH gateway

* clean up everything, all tests passing

* add more comments

* naming and comment cleanup

* final naming?

* fix var name

* final comments

* rename ovmEth

* fix accidental reversion

* make l1 token gateway public

* newl

* remove whitespace

* fix decimals

* fix decimal build

* get tests passing
parent fc63ccda
......@@ -3,7 +3,7 @@ pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iAbs_BaseCrossDomainMessenger } from "../../../iOVM/bridge/messenging/iAbs_BaseCrossDomainMessenger.sol";
import { iAbs_BaseCrossDomainMessenger } from "../../../iOVM/bridge/messaging/iAbs_BaseCrossDomainMessenger.sol";
/* Library Imports */
import { Lib_ReentrancyGuard } from "../../../libraries/utils/Lib_ReentrancyGuard.sol";
......
......@@ -10,7 +10,7 @@ import { Lib_SecureMerkleTrie } from "../../../libraries/trie/Lib_SecureMerkleTr
import { Lib_ReentrancyGuard } from "../../../libraries/utils/Lib_ReentrancyGuard.sol";
/* Interface Imports */
import { iOVM_L1CrossDomainMessenger } from "../../../iOVM/bridge/messenging/iOVM_L1CrossDomainMessenger.sol";
import { iOVM_L1CrossDomainMessenger } from "../../../iOVM/bridge/messaging/iOVM_L1CrossDomainMessenger.sol";
import { iOVM_CanonicalTransactionChain } from "../../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
import { iOVM_StateCommitmentChain } from "../../../iOVM/chain/iOVM_StateCommitmentChain.sol";
......
......@@ -3,8 +3,8 @@
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_L1CrossDomainMessenger } from "../../../iOVM/bridge/messenging/iOVM_L1CrossDomainMessenger.sol";
import { iOVM_L1MultiMessageRelayer } from "../../../iOVM/bridge/messenging/iOVM_L1MultiMessageRelayer.sol";
import { iOVM_L1CrossDomainMessenger } from "../../../iOVM/bridge/messaging/iOVM_L1CrossDomainMessenger.sol";
import { iOVM_L1MultiMessageRelayer } from "../../../iOVM/bridge/messaging/iOVM_L1MultiMessageRelayer.sol";
/* Contract Imports */
import { Lib_AddressResolver } from "../../../libraries/resolver/Lib_AddressResolver.sol";
......
......@@ -7,7 +7,7 @@ import { Lib_AddressResolver } from "../../../libraries/resolver/Lib_AddressReso
import { Lib_ReentrancyGuard } from "../../../libraries/utils/Lib_ReentrancyGuard.sol";
/* Interface Imports */
import { iOVM_L2CrossDomainMessenger } from "../../../iOVM/bridge/messenging/iOVM_L2CrossDomainMessenger.sol";
import { iOVM_L2CrossDomainMessenger } from "../../../iOVM/bridge/messaging/iOVM_L2CrossDomainMessenger.sol";
import { iOVM_L1MessageSender } from "../../../iOVM/precompiles/iOVM_L1MessageSender.sol";
import { iOVM_L2ToL1MessagePasser } from "../../../iOVM/precompiles/iOVM_L2ToL1MessagePasser.sol";
......
// SPDX-License-Identifier: MIT
// @unsupported: ovm
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_L1TokenGateway } from "../../../iOVM/bridge/tokens/iOVM_L1TokenGateway.sol";
import { iOVM_L2DepositedToken } from "../../../iOVM/bridge/tokens/iOVM_L2DepositedToken.sol";
/* Library Imports */
import { OVM_CrossDomainEnabled } from "../../../libraries/bridge/OVM_CrossDomainEnabled.sol";
/**
* @title Abs_L1TokenGateway
* @dev An L1 Token Gateway is a contract which stores deposited L1 funds that are in use on L2.
* It synchronizes a corresponding L2 representation of the "deposited token", informing it
* of new deposits and releasing L1 funds when there are newly finalized withdrawals.
*
* NOTE: This abstract contract gives all the core functionality of an L1 token gateway,
* but provides easy hooks in case developers need extensions in child contracts.
* In many cases, the default OVM_L1ERC20Gateway will suffice.
*
* Compiler used: solc
* Runtime target: EVM
*/
abstract contract Abs_L1TokenGateway is iOVM_L1TokenGateway, OVM_CrossDomainEnabled {
/********************************
* External Contract References *
********************************/
address public l2DepositedToken;
/***************
* Constructor *
***************/
/**
* @param _l2DepositedToken iOVM_L2DepositedToken-compatible address on the chain being deposited into.
* @param _l1messenger L1 Messenger address being used for cross-chain communications.
*/
constructor(
address _l2DepositedToken,
address _l1messenger
)
OVM_CrossDomainEnabled(_l1messenger)
{
l2DepositedToken = _l2DepositedToken;
}
/********************************
* Overridable Accounting logic *
********************************/
// Default gas value which can be overridden if more complex logic runs on L2.
uint32 public DEFAULT_FINALIZE_DEPOSIT_L2_GAS = 1200000;
/**
* @dev Core logic to be performed when a withdrawal is finalized on L1.
* In most cases, this will simply send locked funds to the withdrawer.
*
* @param _to Address being withdrawn to.
* @param _amount Amount being withdrawn.
*/
function _handleFinalizeWithdrawal(
address _to,
uint256 _amount
)
internal
virtual
{
revert("Implement me in child contracts");
}
/**
* @dev Core logic to be performed when a deposit is initiated on L1.
* In most cases, this will simply send locked funds to the withdrawer.
*
* @param _from Address being deposited from on L1.
* @param _to Address being deposited into on L2.
* @param _amount Amount being deposited.
*/
function _handleInitiateDeposit(
address _from,
address _to,
uint256 _amount
)
internal
virtual
{
revert("Implement me in child contracts");
}
/**
* @dev Overridable getter for the L2 gas limit, in the case it may be
* dynamic, and the above public constant does not suffice.
*
*/
function getFinalizeDepositL2Gas()
public
view
override
returns(
uint32
)
{
return DEFAULT_FINALIZE_DEPOSIT_L2_GAS;
}
/**************
* Depositing *
**************/
/**
* @dev deposit an amount of the ERC20 to the caller's balance on L2
* @param _amount Amount of the ERC20 to deposit
*/
function deposit(
uint _amount
)
public
override
{
_initiateDeposit(msg.sender, msg.sender, _amount);
}
/**
* @dev deposit an amount of ERC20 to a recipients's balance on L2
* @param _to L2 address to credit the withdrawal to
* @param _amount Amount of the ERC20 to deposit
*/
function depositTo(
address _to,
uint _amount
)
public
override
{
_initiateDeposit(msg.sender, _to, _amount);
}
/**
* @dev Performs the logic for deposits by informing the L2 Deposited Token
* contract of the deposit and calling a handler to lock the L1 funds. (e.g. transferFrom)
*
* @param _from Account to pull the deposit from on L1
* @param _to Account to give the deposit to on L2
* @param _amount Amount of the ERC20 to deposit.
*/
function _initiateDeposit(
address _from,
address _to,
uint _amount
)
internal
{
// Call our deposit accounting handler implemented by child contracts.
_handleInitiateDeposit(
_from,
_to,
_amount
);
// Construct calldata for l2DepositedToken.finalizeDeposit(_to, _amount)
bytes memory data = abi.encodeWithSelector(
iOVM_L2DepositedToken.finalizeDeposit.selector,
_to,
_amount
);
// Send calldata into L2
sendCrossDomainMessage(
l2DepositedToken,
data,
getFinalizeDepositL2Gas()
);
emit DepositInitiated(_from, _to, _amount);
}
/*************************
* Cross-chain Functions *
*************************/
/**
* @dev Complete a withdrawal from L2 to L1, and credit funds to the recipient's balance of the
* L1 ERC20 token.
* This call will fail if the initialized withdrawal from L2 has not been finalized.
*
* @param _to L1 address to credit the withdrawal to
* @param _amount Amount of the ERC20 to withdraw
*/
function finalizeWithdrawal(
address _to,
uint _amount
)
external
override
onlyFromCrossDomainAccount(l2DepositedToken)
{
// Call our withdrawal accounting handler implemented by child contracts.
_handleFinalizeWithdrawal(
_to,
_amount
);
emit WithdrawalFinalized(_to, _amount);
}
}
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_L2DepositedToken } from "../../../iOVM/bridge/tokens/iOVM_L2DepositedToken.sol";
import { iOVM_L1TokenGateway } from "../../../iOVM/bridge/tokens/iOVM_L1TokenGateway.sol";
/* Library Imports */
import { OVM_CrossDomainEnabled } from "../../../libraries/bridge/OVM_CrossDomainEnabled.sol";
/**
* @title Abs_L2DepositedToken
* @dev An L2 Deposited Token is an L2 representation of funds which were deposited from L1.
* Usually contract mints new tokens when it hears about deposits into the L1 ERC20 gateway.
* This contract also burns the tokens intended for withdrawal, informing the L1 gateway to release L1 funds.
*
* NOTE: This abstract contract gives all the core functionality of a deposited token implementation except for the
* token's internal accounting itself. This gives developers an easy way to implement children with their own token code.
*
* Compiler used: optimistic-solc
* Runtime target: OVM
*/
abstract contract Abs_L2DepositedToken is iOVM_L2DepositedToken, OVM_CrossDomainEnabled {
/*******************
* Contract Events *
*******************/
event Initialized(iOVM_L1TokenGateway _l1TokenGateway);
/********************************
* External Contract References *
********************************/
iOVM_L1TokenGateway public l1TokenGateway;
/********************************
* Constructor & Initialization *
********************************/
/**
* @param _l2CrossDomainMessenger L1 Messenger address being used for cross-chain communications.
*/
constructor(
address _l2CrossDomainMessenger
)
OVM_CrossDomainEnabled(_l2CrossDomainMessenger)
{}
/**
* @dev Initialize this contract with the L1 token gateway address.
* The flow: 1) this contract gets deployed on L2, 2) the L1
* gateway is dpeloyed with addr from (1), 3) L1 gatweway address passed here.
*
* @param _l1TokenGateway Address of the corresponding L1 gateway deployed to the main chain
*/
function init(
iOVM_L1TokenGateway _l1TokenGateway
)
public
{
require(address(l1TokenGateway) == address(0), "Contract has already been initialized");
l1TokenGateway = _l1TokenGateway;
emit Initialized(l1TokenGateway);
}
/**********************
* Function Modifiers *
**********************/
modifier onlyInitialized() {
require(address(l1TokenGateway) != address(0), "Contract has not yet been initialized");
_;
}
/********************************
* Overridable Accounting logic *
********************************/
// Default gas value which can be overridden if more complex logic runs on L2.
uint32 constant DEFAULT_FINALIZE_WITHDRAWAL_L1_GAS = 100000;
/**
* @dev Core logic to be performed when a withdrawal from L2 is initialized.
* In most cases, this will simply burn the withdrawn L2 funds.
*
* @param _to Address being withdrawn to
* @param _amount Amount being withdrawn
*/
function _handleInitiateWithdrawal(
address _to,
uint _amount
)
internal
virtual
{
revert("Accounting must be implemented by child contract.");
}
/**
* @dev Core logic to be performed when a deposit from L2 is finalized on L2.
* In most cases, this will simply _mint() to credit L2 funds to the recipient.
*
* @param _to Address being deposited to on L2
* @param _amount Amount which was deposited on L1
*/
function _handleFinalizeDeposit(
address _to,
uint _amount
)
internal
virtual
{
revert("Accounting must be implemented by child contract.");
}
/**
* @dev Overridable getter for the *L1* gas limit of settling the withdrawal, in the case it may be
* dynamic, and the above public constant does not suffice.
*
*/
function getFinalizeWithdrawalL1Gas()
public
view
virtual
override
returns(
uint32
)
{
return DEFAULT_FINALIZE_WITHDRAWAL_L1_GAS;
}
/***************
* Withdrawing *
***************/
/**
* @dev initiate a withdraw of some tokens to the caller's account on L1
* @param _amount Amount of the token to withdraw
*/
function withdraw(
uint _amount
)
external
override
onlyInitialized()
{
_initiateWithdrawal(msg.sender, _amount);
}
/**
* @dev initiate a withdraw of some token to a recipient's account on L1
* @param _to L1 adress to credit the withdrawal to
* @param _amount Amount of the token to withdraw
*/
function withdrawTo(address _to, uint _amount) external override onlyInitialized() {
_initiateWithdrawal(_to, _amount);
}
/**
* @dev Performs the logic for deposits by storing the token and informing the L2 token Gateway of the deposit.
*
* @param _to Account to give the withdrawal to on L1
* @param _amount Amount of the token to withdraw
*/
function _initiateWithdrawal(address _to, uint _amount) internal {
// Call our withdrawal accounting handler implemented by child contracts (usually a _burn)
_handleInitiateWithdrawal(_to, _amount);
// Construct calldata for l1TokenGateway.finalizeWithdrawal(_to, _amount)
bytes memory data = abi.encodeWithSelector(
iOVM_L1TokenGateway.finalizeWithdrawal.selector,
_to,
_amount
);
// Send message up to L1 gateway
sendCrossDomainMessage(
address(l1TokenGateway),
data,
getFinalizeWithdrawalL1Gas()
);
emit WithdrawalInitiated(msg.sender, _to, _amount);
}
/************************************
* Cross-chain Function: Depositing *
************************************/
/**
* @dev Complete a deposit from L1 to L2, and credits funds to the recipient's balance of this
* L2 token.
* This call will fail if it did not originate from a corresponding deposit in OVM_l1TokenGateway.
*
* @param _to Address to receive the withdrawal at
* @param _amount Amount of the token to withdraw
*/
function finalizeDeposit(address _to, uint _amount) external override onlyInitialized()
onlyFromCrossDomainAccount(address(l1TokenGateway))
{
_handleFinalizeDeposit(_to, _amount);
emit DepositFinalized(_to, _amount);
}
}
......@@ -4,30 +4,31 @@ pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_L1ERC20Gateway } from "../../../iOVM/bridge/tokens/iOVM_L1ERC20Gateway.sol";
import { iOVM_L2DepositedERC20 } from "../../../iOVM/bridge/tokens/iOVM_L2DepositedERC20.sol";
import { iOVM_L1TokenGateway } from "../../../iOVM/bridge/tokens/iOVM_L1TokenGateway.sol";
import { Abs_L1TokenGateway } from "./Abs_L1TokenGateway.sol";
import { iOVM_ERC20 } from "../../../iOVM/precompiles/iOVM_ERC20.sol";
/* Library Imports */
import { OVM_CrossDomainEnabled } from "../../../libraries/bridge/OVM_CrossDomainEnabled.sol";
/**
* @title OVM_L1ERC20Gateway
* @dev The L1 ERC20 Gateway is a contract which stores deposited L1 funds that are in use on L2.
* It synchronizes a corresponding L2 ERC20 Gateway, informing it of deposits, and listening to it
* It synchronizes a corresponding L2 ERC20 Gateway, informing it of deposits, and listening to it
* for newly finalized withdrawals.
*
* NOTE: This contract extends Abs_L1TokenGateway, which is where we
* takes care of most of the initialization and the cross-chain logic.
* If you are looking to implement your own deposit/withdrawal contracts, you
* may also want to extend the abstract contract in a similar manner.
*
* Compiler used: solc
* Runtime target: EVM
*/
contract OVM_L1ERC20Gateway is iOVM_L1ERC20Gateway, OVM_CrossDomainEnabled {
contract OVM_L1ERC20Gateway is Abs_L1TokenGateway {
/********************************
* External Contract References *
********************************/
iOVM_ERC20 public l1ERC20;
address public l2DepositedERC20;
/***************
* Constructor *
......@@ -36,111 +37,64 @@ contract OVM_L1ERC20Gateway is iOVM_L1ERC20Gateway, OVM_CrossDomainEnabled {
/**
* @param _l1ERC20 L1 ERC20 address this contract stores deposits for
* @param _l2DepositedERC20 L2 Gateway address on the chain being deposited into
* @param _l1messenger L1 Messenger address being used for cross-chain communications.
*/
constructor(
iOVM_ERC20 _l1ERC20,
address _l2DepositedERC20,
address _l1messenger
)
OVM_CrossDomainEnabled(_l1messenger)
Abs_L1TokenGateway(
_l2DepositedERC20,
_l1messenger
)
{
l1ERC20 = _l1ERC20;
l2DepositedERC20 = _l2DepositedERC20;
}
/**************
* Depositing *
* Accounting *
**************/
/**
* @dev deposit an amount of the ERC20 to the caller's balance on L2
* @param _amount Amount of the ERC20 to deposit
*/
function deposit(
uint _amount
)
external
override
{
_initiateDeposit(msg.sender, msg.sender, _amount);
}
/**
* @dev deposit an amount of ERC20 to a recipients's balance on L2
* @param _to L2 address to credit the withdrawal to
* @param _amount Amount of the ERC20 to deposit
*/
function depositTo(
address _to,
uint _amount
)
external
override
{
_initiateDeposit(msg.sender, _to, _amount);
}
/**
* @dev Performs the logic for deposits by storing the ERC20 and informing the L2 Deposited ERC20 contract of the deposit.
* @dev When a deposit is initiated on L1, the L1 Gateway
* transfers the funds to itself for future withdrawals
*
* @param _from Account to pull the deposit from on L1
* @param _to Account to give the deposit to on L2
* @param _amount Amount of the ERC20 to deposit.
* @param _from L1 address ETH is being deposited from
* @param _to L2 address that the ETH is being deposited to
* @param _amount Amount of ERC20 to send
*/
function _initiateDeposit(
function _handleInitiateDeposit(
address _from,
address _to,
uint _amount
uint256 _amount
)
internal
override
{
// Hold on to the newly deposited funds
// Hold on to the newly deposited funds
l1ERC20.transferFrom(
_from,
address(this),
_amount
);
// Construct calldata for l2DepositedERC20.finalizeDeposit(_to, _amount)
bytes memory data = abi.encodeWithSelector(
iOVM_L2DepositedERC20.finalizeDeposit.selector,
_to,
_amount
);
// Send calldata into L2
sendCrossDomainMessage(
l2DepositedERC20,
data,
DEFAULT_FINALIZE_DEPOSIT_L2_GAS
);
emit DepositInitiated(_from, _to, _amount);
}
/*************************************
* Cross-chain Function: Withdrawing *
*************************************/
/**
* @dev Complete a withdrawal from L2 to L1, and credit funds to the recipient's balance of the
* L1 ERC20 token.
* This call will fail if the initialized withdrawal from L2 has not been finalized.
* @dev When a withdrawal is finalized on L1, the L1 Gateway
* transfers the funds to the withdraer.
*
* @param _to L1 address to credit the withdrawal to
* @param _amount Amount of the ERC20 to withdraw
* @param _to L1 address that the ERC20 is being withdrawn to
* @param _amount Amount of ERC20 to send
*/
function finalizeWithdrawal(
function _handleFinalizeWithdrawal(
address _to,
uint _amount
)
external
override
onlyFromCrossDomainAccount(l2DepositedERC20)
internal
override
{
// Transfer withdrawn funds out to withdrawer
l1ERC20.transfer(_to, _amount);
emit WithdrawalFinalized(_to, _amount);
}
}
......@@ -5,7 +5,7 @@ pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_L1ETHGateway } from "../../../iOVM/bridge/tokens/iOVM_L1ETHGateway.sol";
import { iOVM_L2DepositedERC20 } from "../../../iOVM/bridge/tokens/iOVM_L2DepositedERC20.sol";
import { iOVM_L2DepositedToken } from "../../../iOVM/bridge/tokens/iOVM_L2DepositedToken.sol";
import { iOVM_ERC20 } from "../../../iOVM/precompiles/iOVM_ERC20.sol";
/* Library Imports */
......@@ -20,11 +20,18 @@ import { Lib_AddressResolver } from "../../../libraries/resolver/Lib_AddressReso
* Runtime target: EVM
*/
contract OVM_L1ETHGateway is iOVM_L1ETHGateway, OVM_CrossDomainEnabled, Lib_AddressResolver {
/********************
* Public Constants *
********************/
uint32 public constant override getFinalizeDepositL2Gas = 1200000;
/********************************
* External Contract References *
********************************/
address public l2ERC20Gateway;
address public ovmEth;
/***************
* Constructor *
......@@ -32,15 +39,16 @@ contract OVM_L1ETHGateway is iOVM_L1ETHGateway, OVM_CrossDomainEnabled, Lib_Addr
/**
* @param _libAddressManager Address manager for this OE deployment
* @param _ovmEth L2 OVM_ETH implementation of iOVM_DepositedToken
*/
constructor(
address _libAddressManager,
address _l2ERC20Gateway
address _ovmEth
)
OVM_CrossDomainEnabled(address(0)) // overridden in constructor code
Lib_AddressResolver(_libAddressManager)
{
l2ERC20Gateway = _l2ERC20Gateway;
ovmEth = _ovmEth;
messenger = resolve("Proxy__OVM_L1CrossDomainMessenger"); // overrides OVM_CrossDomainEnabled constructor setting because resolve() is not yet accessible
}
......@@ -88,16 +96,16 @@ contract OVM_L1ETHGateway is iOVM_L1ETHGateway, OVM_CrossDomainEnabled, Lib_Addr
// Construct calldata for l2ERC20Gateway.finalizeDeposit(_to, _amount)
bytes memory data =
abi.encodeWithSelector(
iOVM_L2DepositedERC20.finalizeDeposit.selector,
iOVM_L2DepositedToken.finalizeDeposit.selector,
_to,
msg.value
);
// Send calldata into L2
sendCrossDomainMessage(
l2ERC20Gateway,
ovmEth,
data,
DEFAULT_FINALIZE_DEPOSIT_L2_GAS
getFinalizeDepositL2Gas
);
emit DepositInitiated(_from, _to, msg.value);
......@@ -121,7 +129,7 @@ contract OVM_L1ETHGateway is iOVM_L1ETHGateway, OVM_CrossDomainEnabled, Lib_Addr
)
external
override
onlyFromCrossDomainAccount(l2ERC20Gateway)
onlyFromCrossDomainAccount(ovmEth)
{
_safeTransferETH(_to, _amount);
......
......@@ -3,14 +3,13 @@ pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_L2DepositedERC20 } from "../../../iOVM/bridge/tokens/iOVM_L2DepositedERC20.sol";
import { iOVM_L1ERC20Gateway } from "../../../iOVM/bridge/tokens/iOVM_L1ERC20Gateway.sol";
import { iOVM_L1TokenGateway } from "../../../iOVM/bridge/tokens/iOVM_L1TokenGateway.sol";
/* Contract Imports */
import { UniswapV2ERC20 } from "../../../libraries/standards/UniswapV2ERC20.sol";
/* Library Imports */
import { OVM_CrossDomainEnabled } from "../../../libraries/bridge/OVM_CrossDomainEnabled.sol";
import { Abs_L2DepositedToken } from "./Abs_L2DepositedToken.sol";
/**
* @title OVM_L2DepositedERC20
......@@ -18,142 +17,52 @@ import { OVM_CrossDomainEnabled } from "../../../libraries/bridge/OVM_CrossDomai
* This contract mints new tokens when it hears about deposits into the L1 ERC20 gateway.
* This contract also burns the tokens intended for withdrawal, informing the L1 gateway to release L1 funds.
*
* NOTE: This contract implements the Abs_L2DepositedToken contract using Uniswap's ERC20 as the implementation.
* Alternative implementations can be used in this similar manner.
*
* Compiler used: optimistic-solc
* Runtime target: OVM
*/
contract OVM_L2DepositedERC20 is iOVM_L2DepositedERC20, UniswapV2ERC20, OVM_CrossDomainEnabled {
/*******************
* Contract Events *
*******************/
event Initialized(iOVM_L1ERC20Gateway _l1ERC20Gateway);
/********************************
* External Contract References *
********************************/
contract OVM_L2DepositedERC20 is Abs_L2DepositedToken, UniswapV2ERC20 {
iOVM_L1ERC20Gateway l1ERC20Gateway;
/********************************
* Constructor & Initialization *
********************************/
/***************
* Constructor *
***************/
/**
* @param _l2CrossDomainMessenger L1 Messenger address being used for cross-chain communications.
* @param _decimals L2 ERC20 decimals
* @param _name L2 ERC20 name
* @param _symbol L2 ERC20 symbol
* @param _l2CrossDomainMessenger Cross-domain messenger used by this contract.
* @param _name ERC20 name
* @param _symbol ERC20 symbol
*/
constructor(
address _l2CrossDomainMessenger,
uint8 _decimals,
string memory _name,
string memory _symbol
)
public
OVM_CrossDomainEnabled(_l2CrossDomainMessenger)
UniswapV2ERC20(_decimals, _name, _symbol)
Abs_L2DepositedToken(_l2CrossDomainMessenger)
UniswapV2ERC20(_name, _symbol)
{}
/**
* @dev Initialize this gateway with the L1 gateway address
* The assumed flow is that this contract is deployed on L2, then the L1
* gateway is deployed, and its address passed here to init.
*
* @param _l1ERC20Gateway Address of the corresponding L1 gateway deployed to the main chain
*/
function init(
iOVM_L1ERC20Gateway _l1ERC20Gateway
)
public
{
require(address(l1ERC20Gateway) == address(0), "Contract has already been initialized");
l1ERC20Gateway = _l1ERC20Gateway;
emit Initialized(l1ERC20Gateway);
}
/**********************
* Function Modifiers *
**********************/
modifier onlyInitialized() {
require(address(l1ERC20Gateway) != address(0), "Contract has not yet been initialized");
_;
}
/***************
* Withdrawing *
***************/
/**
* @dev initiate a withdraw of some ERC20 to the caller's account on L1
* @param _amount Amount of the ERC20 to withdraw
*/
function withdraw(
// When a withdrawal is initiated, we burn the withdrawer's funds to prevent subsequent L2 usage.
function _handleInitiateWithdrawal(
address _to,
uint _amount
)
external
internal
override
onlyInitialized()
{
_initiateWithdrawal(msg.sender, _amount);
}
/**
* @dev initiate a withdraw of some ERC20 to a recipient's account on L1
* @param _to L1 adress to credit the withdrawal to
* @param _amount Amount of the ERC20 to withdraw
*/
function withdrawTo(address _to, uint _amount) external override onlyInitialized() {
_initiateWithdrawal(_to, _amount);
}
/**
* @dev Performs the logic for deposits by storing the ERC20 and informing the L2 ERC20 Gateway of the deposit.
*
* @param _to Account to give the withdrawal to on L1
* @param _amount Amount of the ERC20 to withdraw
*/
function _initiateWithdrawal(address _to, uint _amount) internal {
// burn L2 funds so they can't be used more on L2
_burn(msg.sender, _amount);
// Construct calldata for l1ERC20Gateway.finalizeWithdrawal(_to, _amount)
bytes memory data = abi.encodeWithSelector(
iOVM_L1ERC20Gateway.finalizeWithdrawal.selector,
_to,
_amount
);
// Send message up to L1 gateway
sendCrossDomainMessage(
address(l1ERC20Gateway),
data,
DEFAULT_FINALIZE_WITHDRAWAL_L1_GAS
);
emit WithdrawalInitiated(msg.sender, _to, _amount);
}
/************************************
* Cross-chain Function: Depositing *
************************************/
/**
* @dev Complete a deposit from L1 to L2, and credits funds to the recipient's balance of this
* L2 ERC20 token.
* This call will fail if it did not originate from a corresponding deposit in OVM_L1ERC20Gateway.
*
* @param _to Address to receive the withdrawal at
* @param _amount Amount of the ERC20 to withdraw
*/
function finalizeDeposit(address _to, uint _amount) external override onlyInitialized()
onlyFromCrossDomainAccount(address(l1ERC20Gateway))
// When a deposit is finalized, we credit the account on L2 with the same amount of tokens.
function _handleFinalizeDeposit(
address _to,
uint _amount
)
internal
override
{
_mint(_to, _amount);
emit DepositFinalized(_to, _amount);
}
}
......@@ -5,7 +5,7 @@ pragma solidity >0.5.0 <0.8.0;
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
/* Interface Imports */
import { iOVM_L1ERC20Gateway } from "../../iOVM/bridge/tokens/iOVM_L1ERC20Gateway.sol";
import { iOVM_L1TokenGateway } from "../../iOVM/bridge/tokens/iOVM_L1TokenGateway.sol";
/* Contract Imports */
import { OVM_L2DepositedERC20 } from "../bridge/tokens/OVM_L2DepositedERC20.sol";
......@@ -25,12 +25,11 @@ contract OVM_ETH is OVM_L2DepositedERC20 {
)
OVM_L2DepositedERC20(
_l2CrossDomainMessenger,
18, // WETH decimals
"ovmWETH",
"oWETH"
)
public
{
init(iOVM_L1ERC20Gateway(_l1ETHGateway));
init(iOVM_L1TokenGateway(_l1ETHGateway));
}
}
......@@ -3,7 +3,7 @@ pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_L1CrossDomainMessenger } from "../../../iOVM/bridge/messenging/iOVM_L1CrossDomainMessenger.sol";
import { iOVM_L1CrossDomainMessenger } from "../../../iOVM/bridge/messaging/iOVM_L1CrossDomainMessenger.sol";
interface iOVM_L1MultiMessageRelayer {
struct L2ToL1Message {
......
......@@ -22,7 +22,6 @@ interface iOVM_L1ETHGateway {
uint256 _amount
);
/********************
* Public Functions *
********************/
......@@ -46,4 +45,11 @@ interface iOVM_L1ETHGateway {
uint _amount
)
external;
function getFinalizeDepositL2Gas()
external
view
returns(
uint32
);
}
......@@ -3,9 +3,9 @@ pragma solidity >0.5.0;
pragma experimental ABIEncoderV2;
/**
* @title iOVM_L1ERC20Gateway
* @title iOVM_L1TokenGateway
*/
interface iOVM_L1ERC20Gateway {
interface iOVM_L1TokenGateway {
/**********
* Events *
......@@ -48,4 +48,11 @@ interface iOVM_L1ERC20Gateway {
uint _amount
)
external;
function getFinalizeDepositL2Gas()
external
view
returns(
uint32
);
}
......@@ -3,9 +3,9 @@ pragma solidity >0.5.0;
pragma experimental ABIEncoderV2;
/**
* @title iOVM_L2DepositedERC20
* @title iOVM_L2DepositedToken
*/
interface iOVM_L2DepositedERC20 {
interface iOVM_L2DepositedToken {
/**********
* Events *
......@@ -48,4 +48,11 @@ interface iOVM_L2DepositedERC20 {
uint _amount
)
external;
function getFinalizeWithdrawalL1Gas()
external
view
returns(
uint32
);
}
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
/* Interface Imports */
import { iAbs_BaseCrossDomainMessenger } from "../../iOVM/bridge/messenging/iAbs_BaseCrossDomainMessenger.sol";
import { iAbs_BaseCrossDomainMessenger } from "../../iOVM/bridge/messaging/iAbs_BaseCrossDomainMessenger.sol";
/**
* @title OVM_CrossDomainEnabled
......@@ -14,9 +14,6 @@ contract OVM_CrossDomainEnabled {
// Messenger contract used to send and recieve messages from the other domain.
address public messenger;
uint32 public constant DEFAULT_FINALIZE_DEPOSIT_L2_GAS = 1200000;
uint32 public constant DEFAULT_FINALIZE_WITHDRAWAL_L1_GAS = 100000;
/***************
* Constructor *
***************/
......
......@@ -8,7 +8,7 @@ contract UniswapV2ERC20 is IUniswapV2ERC20 {
string public override name;
string public override symbol;
uint8 public override immutable decimals;
uint8 public constant override decimals = 18;
uint public override totalSupply;
mapping(address => uint) public override balanceOf;
mapping(address => mapping(address => uint)) public override allowance;
......@@ -19,11 +19,9 @@ contract UniswapV2ERC20 is IUniswapV2ERC20 {
mapping(address => uint) public override nonces;
constructor(
uint8 _decimals,
string memory _name,
string memory _name,
string memory _symbol
) public {
decimals = _decimals;
name = _name;
symbol = _symbol;
......
......@@ -3,7 +3,7 @@ pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Contract Imports */
import { iAbs_BaseCrossDomainMessenger } from "../../iOVM/bridge/messenging/iAbs_BaseCrossDomainMessenger.sol";
import { iAbs_BaseCrossDomainMessenger } from "../../iOVM/bridge/messaging/iAbs_BaseCrossDomainMessenger.sol";
/**
* @title mockOVM_CrossDomainMessenger
......
......@@ -39,7 +39,7 @@ describe('OVM_L1ERC20Gateway', () => {
// deploy an ERC20 contract on L1
Factory__L1ERC20 = await smoddit('UniswapV2ERC20')
L1ERC20 = await Factory__L1ERC20.deploy(18, 'L1ERC20', 'ERC')
L1ERC20 = await Factory__L1ERC20.deploy('L1ERC20', 'ERC')
const aliceAddress = await alice.getAddress()
L1ERC20.smodify.put({
......@@ -128,13 +128,11 @@ describe('OVM_L1ERC20Gateway', () => {
await OVM_L1ERC20Gateway.provider.getTransactionReceipt(res.hash)
).gasUsed
await expect(
gasUsed.gt(
((await OVM_L1ERC20Gateway.DEFAULT_FINALIZE_WITHDRAWAL_L1_GAS()) *
11) /
10
)
)
const OVM_L2DepositedERC20 = await (
await ethers.getContractFactory('OVM_L2DepositedERC20')
).deploy(ZERO_ADDRESS, '', '')
const defaultFinalizeWithdrawalGas = await OVM_L2DepositedERC20.getFinalizeWithdrawalL1Gas()
await expect(gasUsed.gt((defaultFinalizeWithdrawalGas * 11) / 10))
})
it.skip('finalizeWithdrawalAndCall(): should should credit funds to the withdrawer, and forward from and data', async () => {
......@@ -151,7 +149,7 @@ describe('OVM_L1ERC20Gateway', () => {
beforeEach(async () => {
// Deploy the L1 ERC20 token, Alice will receive the full initialSupply
L1ERC20 = await Factory__L1ERC20.deploy(18, 'L1ERC20', 'ERC')
L1ERC20 = await Factory__L1ERC20.deploy('L1ERC20', 'ERC')
// get a new mock L1 messenger
Mock__OVM_L1CrossDomainMessenger = await smockit(
......
......@@ -59,7 +59,7 @@ describe('OVM_L1ETHGateway', () => {
await ethers.getContractFactory('OVM_L1ETHGateway')
).deploy(AddressManager.address, Mock__OVM_L2DepositedERC20.address)
finalizeDepositGasLimit = await OVM_L1ETHGateway.DEFAULT_FINALIZE_DEPOSIT_L2_GAS()
finalizeDepositGasLimit = await OVM_L1ETHGateway.getFinalizeDepositL2Gas()
})
describe('finalizeWithdrawal', () => {
......@@ -120,10 +120,14 @@ describe('OVM_L1ETHGateway', () => {
await OVM_L1ETHGateway.provider.getTransactionReceipt(res.hash)
).gasUsed
// Deploy this just for the getter
const OVM_L2DepositedERC20 = await (
await ethers.getContractFactory('OVM_L2DepositedERC20')
).deploy(ZERO_ADDRESS, '', '')
await expect(
gasUsed.gt(
((await OVM_L1ETHGateway.DEFAULT_FINALIZE_WITHDRAWAL_L1_GAS()) * 11) /
10
((await OVM_L2DepositedERC20.getFinalizeWithdrawalL1Gas()) * 11) / 10
)
)
})
......
......@@ -52,7 +52,6 @@ describe('OVM_L2DepositedERC20', () => {
await ethers.getContractFactory('OVM_L2DepositedERC20')
).deploy(
Mock__OVM_L2CrossDomainMessenger.address,
decimals,
'ovmWETH',
'oWETH'
)
......@@ -60,7 +59,7 @@ describe('OVM_L2DepositedERC20', () => {
// initialize the L2 Gateway with the L1G ateway addrss
await OVM_L2DepositedERC20.init(MOCK_L1GATEWAY_ADDRESS)
finalizeWithdrawalGasLimit = await OVM_L2DepositedERC20.DEFAULT_FINALIZE_WITHDRAWAL_L1_GAS()
finalizeWithdrawalGasLimit = await OVM_L2DepositedERC20.getFinalizeWithdrawalL1Gas()
})
// test the transfer flow of moving a token from L2 to L1
......@@ -69,7 +68,7 @@ describe('OVM_L2DepositedERC20', () => {
// Deploy new gateway, initialize with random messenger
OVM_L2DepositedERC20 = await (
await ethers.getContractFactory('OVM_L2DepositedERC20')
).deploy(NON_ZERO_ADDRESS, decimals, 'ovmWETH', 'oWETH')
).deploy(NON_ZERO_ADDRESS, 'ovmWETH', 'oWETH')
await OVM_L2DepositedERC20.init(NON_ZERO_ADDRESS)
await expect(
......@@ -119,7 +118,6 @@ describe('OVM_L2DepositedERC20', () => {
await smoddit('OVM_L2DepositedERC20', alice)
).deploy(
Mock__OVM_L2CrossDomainMessenger.address,
decimals,
'ovmWETH',
'oWETH'
)
......
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