Files
scroll/contracts/src/L1/gateways/L1GatewayRouter.sol
2024-03-22 18:02:02 +08:00

254 lines
8.3 KiB
Solidity

// SPDX-License-Identifier: MIT
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 {IL1ETHGateway} from "./IL1ETHGateway.sol";
import {IL1ERC20Gateway} from "./IL1ERC20Gateway.sol";
import {IL1GatewayRouter} from "./IL1GatewayRouter.sol";
/// @title L1GatewayRouter
/// @notice The `L1GatewayRouter` is the main entry for depositing Ether and ERC20 tokens.
/// All deposited tokens are routed to corresponding gateways.
/// @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 L1GatewayRouter is OwnableUpgradeable, IL1GatewayRouter {
using SafeERC20Upgradeable for IERC20Upgradeable;
/*************
* Variables *
*************/
/// @notice The address of L1ETHGateway.
address public ethGateway;
/// @notice The addess of default ERC20 gateway, normally the L1StandardERC20Gateway contract.
address public defaultERC20Gateway;
/// @notice Mapping from ERC20 token address to corresponding L1ERC20Gateway.
// 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 *
***************/
constructor() {
_disableInitializers();
}
/// @notice Initialize the storage of L1GatewayRouter.
/// @param _ethGateway The address of L1ETHGateway contract.
/// @param _defaultERC20Gateway The address of default ERC20 Gateway contract.
function initialize(address _ethGateway, address _defaultERC20Gateway) external initializer {
OwnableUpgradeable.__Ownable_init();
// it can be zero during initialization
if (_defaultERC20Gateway != address(0)) {
defaultERC20Gateway = _defaultERC20Gateway;
emit SetDefaultERC20Gateway(address(0), _defaultERC20Gateway);
}
// it can be zero during initialization
if (_ethGateway != address(0)) {
ethGateway = _ethGateway;
emit SetETHGateway(address(0), _ethGateway);
}
}
/*************************
* Public View Functions *
*************************/
/// @inheritdoc IL1ERC20Gateway
function getL2ERC20Address(address _l1Address) external view override returns (address) {
address _gateway = getERC20Gateway(_l1Address);
if (_gateway == address(0)) {
return address(0);
}
return IL1ERC20Gateway(_gateway).getL2ERC20Address(_l1Address);
}
/// @inheritdoc IL1GatewayRouter
function getERC20Gateway(address _token) public view returns (address) {
address _gateway = ERC20Gateway[_token];
if (_gateway == address(0)) {
_gateway = defaultERC20Gateway;
}
return _gateway;
}
/*****************************
* Public Mutating Functions *
*****************************/
/// @inheritdoc IL1GatewayRouter
/// @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;
}
/*************************************************
* Public Mutating Functions from L1ERC20Gateway *
*************************************************/
/// @inheritdoc IL1ERC20Gateway
function depositERC20(
address _token,
uint256 _amount,
uint256 _gasLimit
) external payable override {
depositERC20AndCall(_token, _msgSender(), _amount, new bytes(0), _gasLimit);
}
/// @inheritdoc IL1ERC20Gateway
function depositERC20(
address _token,
address _to,
uint256 _amount,
uint256 _gasLimit
) external payable override {
depositERC20AndCall(_token, _to, _amount, new bytes(0), _gasLimit);
}
/// @inheritdoc IL1ERC20Gateway
function depositERC20AndCall(
address _token,
address _to,
uint256 _amount,
bytes memory _data,
uint256 _gasLimit
) 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);
IL1ERC20Gateway(_gateway).depositERC20AndCall{value: msg.value}(_token, _to, _amount, _routerData, _gasLimit);
// leave deposit context
gatewayInContext = address(0);
}
/// @inheritdoc IL1ERC20Gateway
function finalizeWithdrawERC20(
address,
address,
address,
address,
uint256,
bytes calldata
) external payable virtual override {
revert("should never be called");
}
/***********************************************
* Public Mutating Functions from L1ETHGateway *
***********************************************/
/// @inheritdoc IL1ETHGateway
function depositETH(uint256 _amount, uint256 _gasLimit) external payable override {
depositETHAndCall(_msgSender(), _amount, new bytes(0), _gasLimit);
}
/// @inheritdoc IL1ETHGateway
function depositETH(
address _to,
uint256 _amount,
uint256 _gasLimit
) external payable override {
depositETHAndCall(_to, _amount, new bytes(0), _gasLimit);
}
/// @inheritdoc IL1ETHGateway
function depositETHAndCall(
address _to,
uint256 _amount,
bytes memory _data,
uint256 _gasLimit
) public payable override onlyNotInContext {
address _gateway = ethGateway;
require(_gateway != address(0), "eth gateway available");
// encode msg.sender with _data
bytes memory _routerData = abi.encode(_msgSender(), _data);
IL1ETHGateway(_gateway).depositETHAndCall{value: msg.value}(_to, _amount, _routerData, _gasLimit);
}
/// @inheritdoc IL1ETHGateway
function finalizeWithdrawETH(
address,
address,
uint256,
bytes calldata
) external payable virtual override {
revert("should never be called");
}
/************************
* Restricted Functions *
************************/
/// @inheritdoc IL1GatewayRouter
function setETHGateway(address _newEthGateway) external onlyOwner {
address _oldETHGateway = ethGateway;
ethGateway = _newEthGateway;
emit SetETHGateway(_oldETHGateway, _newEthGateway);
}
/// @inheritdoc IL1GatewayRouter
function setDefaultERC20Gateway(address _newDefaultERC20Gateway) external onlyOwner {
address _oldDefaultERC20Gateway = defaultERC20Gateway;
defaultERC20Gateway = _newDefaultERC20Gateway;
emit SetDefaultERC20Gateway(_oldDefaultERC20Gateway, _newDefaultERC20Gateway);
}
/// @inheritdoc IL1GatewayRouter
function setERC20Gateway(address[] memory _tokens, address[] memory _gateways) external onlyOwner {
require(_tokens.length == _gateways.length, "length mismatch");
for (uint256 i = 0; i < _tokens.length; i++) {
address _oldGateway = ERC20Gateway[_tokens[i]];
ERC20Gateway[_tokens[i]] = _gateways[i];
emit SetERC20Gateway(_tokens[i], _oldGateway, _gateways[i]);
}
}
}