mirror of
https://github.com/scroll-tech/scroll.git
synced 2026-01-14 16:37:56 -05:00
254 lines
8.3 KiB
Solidity
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]);
|
|
}
|
|
}
|
|
}
|