Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add reverse gateway and update l2 gateway router #335

Merged
merged 4 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion bindings/bin/l2gatewayrouter_deployed.hex

Large diffs are not rendered by default.

56 changes: 54 additions & 2 deletions bindings/bindings/l2gatewayrouter.go

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions bindings/bindings/l2gatewayrouter_more.go

Large diffs are not rendered by default.

147 changes: 147 additions & 0 deletions contracts/contracts/l1/gateways/L1ReverseCustomGateway.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// SPDX-License-Identifier: MIT

pragma solidity =0.8.24;

import {IL2ERC20Gateway} from "../../l2/gateways/IL2ERC20Gateway.sol";
import {IL1CrossDomainMessenger} from "../IL1CrossDomainMessenger.sol";
import {IL1ERC20Gateway} from "./IL1ERC20Gateway.sol";
import {IMorphERC20Upgradeable} from "../../libraries/token/IMorphERC20Upgradeable.sol";

import {GatewayBase} from "../../libraries/gateway/GatewayBase.sol";
import {L1ERC20Gateway} from "./L1ERC20Gateway.sol";

contract L1ReverseCustomGateway is L1ERC20Gateway {
/**********
* Events *
**********/

/// @notice Emitted when token mapping for ERC20 token is updated.
/// @param l1Token The address of ERC20 token in layer 1.
/// @param oldL2Token The address of the old corresponding ERC20 token in layer 2.
/// @param newL2Token The address of the new corresponding ERC20 token in layer 2.
event UpdateTokenMapping(address indexed l1Token, address indexed oldL2Token, address indexed newL2Token);

/*************
* Variables *
*************/

/// @notice Mapping from l1 token address to l2 token address for ERC20 token.
mapping(address => address) public tokenMapping;

/***************
* Constructor *
***************/

constructor() {
_disableInitializers();
}

/// @notice Initialize the storage of L1CustomERC20Gateway.
/// @param _counterpart The address of L2CustomERC20Gateway in L2.
/// @param _router The address of L1GatewayRouter.
/// @param _messenger The address of L1CrossDomainMessenger.
function initialize(address _counterpart, address _router, address _messenger) external initializer {
require(_router != address(0), "zero router address");

GatewayBase._initialize(_counterpart, _router, _messenger);
}

/*************************
* Public View Functions *
*************************/

/// @inheritdoc IL1ERC20Gateway
function getL2ERC20Address(address _l1Token) public view override returns (address) {
return tokenMapping[_l1Token];
}

/************************
* Restricted Functions *
************************/

/// @notice Update layer 1 to layer 2 token mapping.
/// @param _l1Token The address of ERC20 token on layer 1.
/// @param _l2Token The address of corresponding ERC20 token on layer 2.
function updateTokenMapping(address _l1Token, address _l2Token) external onlyOwner {
require(_l2Token != address(0), "token address cannot be 0");

address _oldL2Token = tokenMapping[_l1Token];
tokenMapping[_l1Token] = _l2Token;

emit UpdateTokenMapping(_l1Token, _oldL2Token, _l2Token);
}

/**********************
* Internal Functions *
**********************/

/// @inheritdoc L1ERC20Gateway
function _beforeFinalizeWithdrawERC20(
address _l1Token,
address _l2Token,
address,
address,
uint256,
bytes calldata
) internal virtual override {
require(msg.value == 0, "nonzero msg.value");
require(_l2Token != address(0), "token address cannot be 0");
require(_l2Token == tokenMapping[_l1Token], "l2 token mismatch");
}

/// @inheritdoc L1ERC20Gateway
function _beforeDropMessage(address, address, uint256) internal virtual override {
require(msg.value == 0, "nonzero msg.value");
}

/// @inheritdoc L1ERC20Gateway
function finalizeWithdrawERC20(
address _l1Token,
address _l2Token,
address _from,
address _to,
uint256 _amount,
bytes calldata _data
) external payable virtual override onlyCallByCounterpart nonReentrant {
_beforeFinalizeWithdrawERC20(_l1Token, _l2Token, _from, _to, _amount, _data);

IMorphERC20Upgradeable(_l1Token).mint(_to, _amount);

_doCallback(_to, _data);

emit FinalizeWithdrawERC20(_l1Token, _l2Token, _from, _to, _amount, _data);
}

/// @inheritdoc L1ERC20Gateway
function _deposit(
address _token,
address _to,
uint256 _amount,
bytes memory _data,
uint256 _gasLimit
) internal virtual override nonReentrant {
address _l2Token = tokenMapping[_token];
require(_l2Token != address(0), "no corresponding l2 token");

// 1. Transfer token into this contract.
address _from = _msgSender();
if (router == _from) {
(_from, _data) = abi.decode(_data, (address, bytes));
}

// 2. Burn token.
IMorphERC20Upgradeable(_token).burn(_from, _amount);

// 2. Generate message passed to L2ReverseCustomGateway.
bytes memory _message = abi.encodeCall(
IL2ERC20Gateway.finalizeDepositERC20,
(_token, _l2Token, _from, _to, _amount, _data)
);

uint256 nonce = IL1CrossDomainMessenger(messenger).messageNonce();
// 3. Send message to L1CrossDomainMessenger.
IL1CrossDomainMessenger(messenger).sendMessage{value: msg.value}(counterpart, 0, _message, _gasLimit, _from);

emit DepositERC20(_token, _l2Token, _from, _to, _amount, _data, nonce);
}
}
18 changes: 18 additions & 0 deletions contracts/contracts/l2/gateways/IL2GatewayRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,22 @@ interface IL2GatewayRouter is IL2ETHGateway, IL2ERC20Gateway {
/// @param oldGateway The corresponding address of the old gateway.
/// @param newGateway The corresponding address of the new gateway.
event SetERC20Gateway(address indexed token, address indexed oldGateway, address indexed newGateway);

/*************************
* Public View Functions *
*************************/

/// @notice Return the corresponding gateway address for given token address.
/// @param _token The address of token to query.
function getERC20Gateway(address _token) external view returns (address);

/*****************************
* Public Mutating Functions *
*****************************/

/// @notice Request ERC20 token transfer from users to gateways.
/// @param sender The address of sender to request fund.
/// @param token The address of token to request.
/// @param amount The amount of token to request.
function requestERC20(address sender, address token, uint256 amount) external returns (uint256);
}
43 changes: 41 additions & 2 deletions contracts/contracts/l2/gateways/L2GatewayRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
pragma solidity =0.8.24;

import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {SafeERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";

import {IL2GatewayRouter} from "./IL2GatewayRouter.sol";
import {IL2ETHGateway} from "./IL2ETHGateway.sol";
Expand All @@ -14,6 +16,8 @@ import {IL2ERC20Gateway} from "./IL2ERC20Gateway.sol";
/// @dev One can also use this contract to query L1/L2 token address mapping.
/// In the future, ERC-721 and ERC-1155 tokens will be added to the router too.
contract L2GatewayRouter is OwnableUpgradeable, IL2GatewayRouter {
using SafeERC20Upgradeable for IERC20Upgradeable;

/*************
* Variables *
*************/
Expand All @@ -28,6 +32,23 @@ contract L2GatewayRouter is OwnableUpgradeable, IL2GatewayRouter {
// solhint-disable-next-line var-name-mixedcase
mapping(address => address) public ERC20Gateway;

/// @notice The address of gateway in current execution context.
address public gatewayInContext;

/**********************
* Function Modifiers *
**********************/

modifier onlyNotInContext() {
require(gatewayInContext == address(0), "Only not in context");
_;
}

modifier onlyInContext() {
require(_msgSender() == gatewayInContext, "Only in deposit context");
_;
}

/***************
* Constructor *
***************/
Expand Down Expand Up @@ -85,6 +106,16 @@ contract L2GatewayRouter is OwnableUpgradeable, IL2GatewayRouter {
* Public Mutating Functions *
*****************************/

/// @inheritdoc IL2GatewayRouter
/// @dev All the gateways should have reentrancy guard to prevent potential attack though this function.
function requestERC20(address _sender, address _token, uint256 _amount) external onlyInContext returns (uint256) {
address _caller = _msgSender();
uint256 _balance = IERC20Upgradeable(_token).balanceOf(_caller);
IERC20Upgradeable(_token).safeTransferFrom(_sender, _caller, _amount);
_amount = IERC20Upgradeable(_token).balanceOf(_caller) - _balance;
return _amount;
}

/// @inheritdoc IL2ERC20Gateway
function withdrawERC20(address _token, uint256 _amount, uint256 _gasLimit) external payable override {
withdrawERC20AndCall(_token, _msgSender(), _amount, new bytes(0), _gasLimit);
Expand All @@ -102,14 +133,18 @@ contract L2GatewayRouter is OwnableUpgradeable, IL2GatewayRouter {
uint256 _amount,
bytes memory _data,
uint256 _gasLimit
) public payable override {
) public payable override onlyNotInContext{
address _gateway = getERC20Gateway(_token);
require(_gateway != address(0), "no gateway available");

// enter deposit context
gatewayInContext = _gateway;
// encode msg.sender with _data
bytes memory _routerData = abi.encode(_msgSender(), _data);

IL2ERC20Gateway(_gateway).withdrawERC20AndCall{value: msg.value}(_token, _to, _amount, _routerData, _gasLimit);
// leave deposit context
gatewayInContext = address(0);
}

/// @inheritdoc IL2ETHGateway
Expand All @@ -128,14 +163,18 @@ contract L2GatewayRouter is OwnableUpgradeable, IL2GatewayRouter {
uint256 _amount,
bytes memory _data,
uint256 _gasLimit
) public payable override {
) public payable override onlyNotInContext{
address _gateway = ethGateway;
require(_gateway != address(0), "eth gateway available");

// enter deposit context
gatewayInContext = _gateway;
// encode msg.sender with _data
bytes memory _routerData = abi.encode(_msgSender(), _data);

IL2ETHGateway(_gateway).withdrawETHAndCall{value: msg.value}(_to, _amount, _routerData, _gasLimit);
// leave deposit context
gatewayInContext = address(0);
}

/// @inheritdoc IL2ETHGateway
Expand Down
Loading
Loading