L2AtomicTokenBridgeFactory

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "contracts/tokenbridge/arbitrum/L2AtomicTokenBridgeFactory.sol": {
      "content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.4;

import {L2GatewayRouter} from "./gateway/L2GatewayRouter.sol";
import {L2ERC20Gateway} from "./gateway/L2ERC20Gateway.sol";
import {L2CustomGateway} from "./gateway/L2CustomGateway.sol";
import {L2WethGateway} from "./gateway/L2WethGateway.sol";
import {StandardArbERC20} from "./StandardArbERC20.sol";
import {IUpgradeExecutor} from "@offchainlabs/upgrade-executor/src/IUpgradeExecutor.sol";
import {CreationCodeHelper} from "../libraries/CreationCodeHelper.sol";
import {BeaconProxyFactory} from "../libraries/ClonableBeaconProxy.sol";
import {aeWETH} from "../libraries/aeWETH.sol";
import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
import {
    TransparentUpgradeableProxy,
    ITransparentUpgradeableProxy
} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";

/**
 * @title Layer2 token bridge creator
 * @notice This contract is used to deploy token bridge on L2 chain.
 * @dev L1AtomicTokenBridgeCreator shall call `deployL2Contracts` using retryable and that will result in deployment of canonical token bridge contracts.
 */
contract L2AtomicTokenBridgeFactory {
    error L2AtomicTokenBridgeFactory_AlreadyExists();

    // In order to avoid having uninitialized logic contracts, `initialize` function will be called
    // on all logic contracts which don't have initializers disabled. This dummy non-zero address
    // will be provided to those initializers, as values written to the logic contract's storage
    // are not used.
    address private constant ADDRESS_DEAD = address(0x000000000000000000000000000000000000dEaD);

    function deployL2Contracts(
        L2RuntimeCode calldata l2Code,
        address l1Router,
        address l1StandardGateway,
        address l1CustomGateway,
        address l1WethGateway,
        address l1Weth,
        address l2StandardGatewayCanonicalAddress,
        address rollupOwner,
        address aliasedL1UpgradeExecutor
    ) external {
        // Create proxyAdmin which will be used for all contracts. Revert if canonical deployment already exists
        {
            address proxyAdminAddress = Create2.computeAddress(
                _getL2Salt(OrbitSalts.L2_PROXY_ADMIN),
                keccak256(type(ProxyAdmin).creationCode),
                address(this)
            );
            if (proxyAdminAddress.code.length > 0) {
                revert L2AtomicTokenBridgeFactory_AlreadyExists();
            }
        }
        address proxyAdmin = address(new ProxyAdmin{salt: _getL2Salt(OrbitSalts.L2_PROXY_ADMIN)}());

        // deploy router/gateways/executor
        address upgradeExecutor = _deployUpgradeExecutor(
            l2Code.upgradeExecutor, rollupOwner, proxyAdmin, aliasedL1UpgradeExecutor
        );
        address router =
            _deployRouter(l2Code.router, l1Router, l2StandardGatewayCanonicalAddress, proxyAdmin);
        _deployStandardGateway(
            l2Code.standardGateway, l1StandardGateway, router, proxyAdmin, upgradeExecutor
        );
        _deployCustomGateway(l2Code.customGateway, l1CustomGateway, router, proxyAdmin);

        // fee token based creator will provide address(0) as WETH is not used in ERC20-based chains
        if (l1WethGateway != address(0)) {
            _deployWethGateway(
                l2Code.wethGateway, l2Code.aeWeth, l1WethGateway, l1Weth, router, proxyAdmin
            );
        }

        // deploy multicall
        Create2.deploy(
            0,
            _getL2Salt(OrbitSalts.L2_MULTICALL),
            CreationCodeHelper.getCreationCodeFor(l2Code.multicall)
        );

        // transfer ownership to L2 upgradeExecutor
        ProxyAdmin(proxyAdmin).transferOwnership(upgradeExecutor);
    }

    function _deployUpgradeExecutor(
        bytes calldata runtimeCode,
        address rollupOwner,
        address proxyAdmin,
        address aliasedL1UpgradeExecutor
    ) internal returns (address) {
        // canonical L2 upgrade executor with dummy logic
        address canonicalUpgradeExecutor = _deploySeedProxy(proxyAdmin, OrbitSalts.L2_EXECUTOR);

        // Create UpgradeExecutor logic and upgrade to it.
        address upExecutorLogic = Create2.deploy(
            0,
            _getL2Salt(OrbitSalts.L2_EXECUTOR),
            CreationCodeHelper.getCreationCodeFor(runtimeCode)
        );

        ProxyAdmin(proxyAdmin).upgrade(
            ITransparentUpgradeableProxy(canonicalUpgradeExecutor), upExecutorLogic
        );

        // init logic contract with dummy values
        address[] memory empty = new address[](0);
        IUpgradeExecutor(upExecutorLogic).initialize(ADDRESS_DEAD, empty);

        // init upgrade executor
        address[] memory executors = new address[](2);
        executors[0] = rollupOwner;
        executors[1] = aliasedL1UpgradeExecutor;
        IUpgradeExecutor(canonicalUpgradeExecutor).initialize(canonicalUpgradeExecutor, executors);

        return canonicalUpgradeExecutor;
    }

    function _deployRouter(
        bytes calldata runtimeCode,
        address l1Router,
        address l2StandardGatewayCanonicalAddress,
        address proxyAdmin
    ) internal returns (address) {
        // canonical L2 router with dummy logic
        address canonicalRouter = _deploySeedProxy(proxyAdmin, OrbitSalts.L2_ROUTER);

        // create L2 router logic and upgrade
        address routerLogic = Create2.deploy(
            0, _getL2Salt(OrbitSalts.L2_ROUTER), CreationCodeHelper.getCreationCodeFor(runtimeCode)
        );
        ProxyAdmin(proxyAdmin).upgrade(ITransparentUpgradeableProxy(canonicalRouter), routerLogic);

        // init logic contract with dummy values.
        L2GatewayRouter(routerLogic).initialize(ADDRESS_DEAD, ADDRESS_DEAD);

        // init
        L2GatewayRouter(canonicalRouter).initialize(l1Router, l2StandardGatewayCanonicalAddress);

        return canonicalRouter;
    }

    function _deployStandardGateway(
        bytes calldata runtimeCode,
        address l1StandardGateway,
        address router,
        address proxyAdmin,
        address upgradeExecutor
    ) internal {
        // canonical L2 standard gateway with dummy logic
        address canonicalStdGateway = _deploySeedProxy(proxyAdmin, OrbitSalts.L2_STANDARD_GATEWAY);

        // create L2 standard gateway logic and upgrade
        address stdGatewayLogic = Create2.deploy(
            0,
            _getL2Salt(OrbitSalts.L2_STANDARD_GATEWAY),
            CreationCodeHelper.getCreationCodeFor(runtimeCode)
        );
        ProxyAdmin(proxyAdmin).upgrade(
            ITransparentUpgradeableProxy(canonicalStdGateway), stdGatewayLogic
        );

        // init logic contract with dummy values
        L2ERC20Gateway(stdGatewayLogic).initialize(ADDRESS_DEAD, ADDRESS_DEAD, ADDRESS_DEAD);

        // create beacon
        StandardArbERC20 standardArbERC20 =
            new StandardArbERC20{salt: _getL2Salt(OrbitSalts.BEACON_PROXY_FACTORY)}();
        UpgradeableBeacon beacon = new UpgradeableBeacon{
            salt: _getL2Salt(OrbitSalts.BEACON_PROXY_FACTORY)
        }(address(standardArbERC20));
        BeaconProxyFactory beaconProxyFactory =
            new BeaconProxyFactory{salt: _getL2Salt(OrbitSalts.BEACON_PROXY_FACTORY)}();

        // init contracts
        beaconProxyFactory.initialize(address(beacon));
        L2ERC20Gateway(canonicalStdGateway).initialize(
            l1StandardGateway, router, address(beaconProxyFactory)
        );

        // make L2 executor the beacon owner
        beacon.transferOwnership(upgradeExecutor);
    }

    function _deployCustomGateway(
        bytes calldata runtimeCode,
        address l1CustomGateway,
        address router,
        address proxyAdmin
    ) internal {
        // canonical L2 custom gateway with dummy logic
        address canonicalCustomGateway = _deploySeedProxy(proxyAdmin, OrbitSalts.L2_CUSTOM_GATEWAY);

        // create L2 custom gateway logic and upgrade
        address customGatewayLogicAddress = Create2.deploy(
            0,
            _getL2Salt(OrbitSalts.L2_CUSTOM_GATEWAY),
            CreationCodeHelper.getCreationCodeFor(runtimeCode)
        );
        ProxyAdmin(proxyAdmin).upgrade(
            ITransparentUpgradeableProxy(canonicalCustomGateway), customGatewayLogicAddress
        );

        // init logic contract with dummy values
        L2CustomGateway(customGatewayLogicAddress).initialize(ADDRESS_DEAD, ADDRESS_DEAD);

        // init
        L2CustomGateway(canonicalCustomGateway).initialize(l1CustomGateway, router);
    }

    function _deployWethGateway(
        bytes calldata wethGatewayRuntimeCode,
        bytes calldata aeWethRuntimeCode,
        address l1WethGateway,
        address l1Weth,
        address router,
        address proxyAdmin
    ) internal {
        // canonical L2 WETH with dummy logic
        address canonicalL2Weth = _deploySeedProxy(proxyAdmin, OrbitSalts.L2_WETH);

        // Create L2WETH logic and upgrade
        address l2WethLogic = Create2.deploy(
            0,
            _getL2Salt(OrbitSalts.L2_WETH),
            CreationCodeHelper.getCreationCodeFor(aeWethRuntimeCode)
        );
        ProxyAdmin(proxyAdmin).upgrade(ITransparentUpgradeableProxy(canonicalL2Weth), l2WethLogic);

        // canonical L2 WETH gateway with dummy logic
        address canonicalL2WethGateway = _deploySeedProxy(proxyAdmin, OrbitSalts.L2_WETH_GATEWAY);

        // create L2WETH gateway logic and upgrade
        address l2WethGatewayLogic = Create2.deploy(
            0,
            _getL2Salt(OrbitSalts.L2_WETH_GATEWAY),
            CreationCodeHelper.getCreationCodeFor(wethGatewayRuntimeCode)
        );
        ProxyAdmin(proxyAdmin).upgrade(
            ITransparentUpgradeableProxy(canonicalL2WethGateway), l2WethGatewayLogic
        );

        // init logic contract with dummy values
        L2WethGateway(payable(l2WethGatewayLogic)).initialize(
            ADDRESS_DEAD, ADDRESS_DEAD, ADDRESS_DEAD, ADDRESS_DEAD
        );

        // init gateway
        L2WethGateway(payable(canonicalL2WethGateway)).initialize(
            l1WethGateway, router, l1Weth, address(canonicalL2Weth)
        );

        // init logic contract with dummy values
        aeWETH(payable(l2WethLogic)).initialize("", "", 0, ADDRESS_DEAD, ADDRESS_DEAD);

        // init L2Weth
        aeWETH(payable(canonicalL2Weth)).initialize(
            "WETH", "WETH", 18, canonicalL2WethGateway, l1Weth
        );
    }

    /**
     * In addition to hard-coded prefix, salt for L2 contracts depends on msg.sender and the chainId. Deploying L2 token bridge contracts is
     * permissionless. By making msg.sender part of the salt we know exactly which set of contracts is the "canonical" one for given chain,
     * deployed by L1TokenBridgeRetryableSender via retryable ticket.
     */
    function _getL2Salt(bytes memory prefix) internal view returns (bytes32) {
        return keccak256(abi.encodePacked(prefix, block.chainid, msg.sender));
    }

    /**
     * Deploys a proxy with address(this) as logic in order to get deterministic address
     * the proxy is salted using a salt derived from the prefix, the chainId and the sender
     */
    function _deploySeedProxy(address proxyAdmin, bytes memory prefix) internal returns (address) {
        return address(
            new TransparentUpgradeableProxy{salt: _getL2Salt(prefix)}(
                address(this), proxyAdmin, bytes("")
            )
        );
    }
}

/**
 * Placeholder for bytecode of token bridge contracts which is sent from L1 to L2 through retryable ticket.
 */
struct L2RuntimeCode {
    bytes router;
    bytes standardGateway;
    bytes customGateway;
    bytes wethGateway;
    bytes aeWeth;
    bytes upgradeExecutor;
    bytes multicall;
}

/**
 * Collection of salts used in CREATE2 deployment of L2 token bridge contracts.
 * Logic contracts are deployed using the same salt as the proxy, it's fine as they have different code
 */
library OrbitSalts {
    bytes internal constant L1_ROUTER = bytes("L1R");
    bytes internal constant L1_STANDARD_GATEWAY = bytes("L1SGW");
    bytes internal constant L1_CUSTOM_GATEWAY = bytes("L1CGW");
    bytes internal constant L1_WETH_GATEWAY = bytes("L1WGW");

    bytes internal constant L2_PROXY_ADMIN = bytes("L2PA");
    bytes internal constant L2_ROUTER = bytes("L2R");
    bytes internal constant L2_STANDARD_GATEWAY = bytes("L2SGW");
    bytes internal constant L2_CUSTOM_GATEWAY = bytes("L2CGW");
    bytes internal constant L2_WETH_GATEWAY = bytes("L2WGW");
    bytes internal constant L2_WETH = bytes("L2W");
    bytes internal constant BEACON_PROXY_FACTORY = bytes("L2BPF");
    bytes internal constant L2_EXECUTOR = bytes("L2E");
    bytes internal constant L2_MULTICALL = bytes("L2MC");
}
"
    },
    "contracts/tokenbridge/arbitrum/gateway/L2GatewayRouter.sol": {
      "content": "// SPDX-License-Identifier: Apache-2.0

/*
 * Copyright 2020, Offchain Labs, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pragma solidity ^0.8.0;

import "../../libraries/gateway/GatewayRouter.sol";
import "../../ethereum/gateway/L1GatewayRouter.sol";
import "../L2ArbitrumMessenger.sol";
import "../../libraries/AddressAliasHelper.sol";

/**
 * @title Handles withdrawals from Ethereum into Arbitrum. Tokens are routered to their appropriate L2 gateway (Router itself also conforms to the Gateway interface).
 * @notice Router also serves as an L2-L1 token address oracle.
 */
contract L2GatewayRouter is GatewayRouter, L2ArbitrumMessenger {
    modifier onlyCounterpartGateway() override {
        require(
            msg.sender == AddressAliasHelper.applyL1ToL2Alias(counterpartGateway),
            "ONLY_COUNTERPART_GATEWAY"
        );
        _;
    }

    function initialize(address _counterpartGateway, address _defaultGateway) public {
        GatewayRouter._initialize(_counterpartGateway, address(0), _defaultGateway);
    }

    function setGateway(address[] memory _l1Token, address[] memory _gateway)
        external
        onlyCounterpartGateway
    {
        // counterpart gateway (L1 router) should never allow wrong lengths
        assert(_l1Token.length == _gateway.length);

        for (uint256 i = 0; i < _l1Token.length; i++) {
            l1TokenToGateway[_l1Token[i]] = _gateway[i];
            emit GatewaySet(_l1Token[i], _gateway[i]);
        }
    }

    function outboundTransfer(
        address _l1Token,
        address _to,
        uint256 _amount,
        bytes calldata _data
    ) public payable returns (bytes memory) {
        return outboundTransfer(_l1Token, _to, _amount, 0, 0, _data);
    }

    function setDefaultGateway(address newL2DefaultGateway) external onlyCounterpartGateway {
        defaultGateway = newL2DefaultGateway;
        emit DefaultGatewayUpdated(newL2DefaultGateway);
    }
}
"
    },
    "contracts/tokenbridge/arbitrum/gateway/L2ERC20Gateway.sol": {
      "content": "// SPDX-License-Identifier: Apache-2.0

/*
 * Copyright 2020, Offchain Labs, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
import "@openzeppelin/contracts/utils/Create2.sol";
import "./L2ArbitrumGateway.sol";
import "../StandardArbERC20.sol";
import "../../libraries/ClonableBeaconProxy.sol";

contract L2ERC20Gateway is L2ArbitrumGateway {
    address public beaconProxyFactory;

    function initialize(
        address _l1Counterpart,
        address _router,
        address _beaconProxyFactory
    ) public {
        L2ArbitrumGateway._initialize(_l1Counterpart, _router);
        require(_beaconProxyFactory != address(0), "INVALID_BEACON");
        beaconProxyFactory = _beaconProxyFactory;
    }

    /**
     * @notice Calculate the address used when bridging an ERC20 token
     * @dev the L1 and L2 address oracles may not always be in sync.
     * For example, a custom token may have been registered but not deploy or the contract self destructed.
     * @param l1ERC20 address of L1 token
     * @return L2 address of a bridged ERC20 token
     */
    function calculateL2TokenAddress(address l1ERC20)
        public
        view
        virtual
        override
        returns (address)
    {
        // this method is marked virtual to be overriden in subclasses used in testing
        return
            BeaconProxyFactory(beaconProxyFactory).calculateExpectedAddress(
                address(this),
                getUserSalt(l1ERC20)
            );
    }

    function cloneableProxyHash() public view returns (bytes32) {
        return BeaconProxyFactory(beaconProxyFactory).cloneableProxyHash();
    }

    function getUserSalt(address l1ERC20) public pure returns (bytes32) {
        return keccak256(abi.encode(l1ERC20));
    }

    /**
     * @notice internal utility function used to deploy ERC20 tokens with the beacon proxy pattern.
     * @dev the transparent proxy implementation by OpenZeppelin can't be used if we want to be able to
     * upgrade the token logic.
     * @param l1ERC20 L1 address of ERC20
     * @param expectedL2Address L2 address of ERC20
     * @param deployData encoded symbol/name/decimal data for initial deploy
     */
    function handleNoContract(
        address l1ERC20,
        address expectedL2Address,
        address _from,
        address, /* _to */
        uint256 _amount,
        bytes memory deployData
    ) internal override returns (bool shouldHalt) {
        bytes32 userSalt = getUserSalt(l1ERC20);
        address createdContract = BeaconProxyFactory(beaconProxyFactory).createProxy(userSalt);

        StandardArbERC20(createdContract).bridgeInit(l1ERC20, deployData);

        if (createdContract == expectedL2Address) {
            return false;
        } else {
            // trigger withdrawal then halt
            // this codepath should only be hit if the system is setup incorrectly
            // this withdrawal is for error recovery, not composing with L2 dapps, so we ignore the return value
            triggerWithdrawal(l1ERC20, address(this), _from, _amount, "");
            return true;
        }
    }
}
"
    },
    "contracts/tokenbridge/arbitrum/gateway/L2CustomGateway.sol": {
      "content": "// SPDX-License-Identifier: Apache-2.0

/*
 * Copyright 2020, Offchain Labs, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pragma solidity ^0.8.0;

import "./L2ArbitrumGateway.sol";
import "../../libraries/gateway/ICustomGateway.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract L2CustomGateway is L2ArbitrumGateway, ICustomGateway {
    // stores addresses of L2 tokens to be used
    mapping(address => address) public override l1ToL2Token;

    function initialize(address _l1Counterpart, address _router) public {
        L2ArbitrumGateway._initialize(_l1Counterpart, _router);
    }

    /**
     * @notice internal utility function used to handle when no contract is deployed at expected address
     */
    function handleNoContract(
        address _l1Token,
        address, /* expectedL2Address */
        address _from,
        address, /* _to */
        uint256 _amount,
        bytes memory /* gatewayData */
    ) internal override returns (bool shouldHalt) {
        // it is assumed that the custom token is deployed in the L2 before deposits are made
        // trigger withdrawal
        // we don't need the return value from triggerWithdrawal since this is forcing a withdrawal back to the L1
        // instead of composing with a L2 dapp
        triggerWithdrawal(_l1Token, address(this), _from, _amount, "");
        return true;
    }

    /**
     * @notice Calculate the address used when bridging an ERC20 token
     * @dev the L1 and L2 address oracles may not always be in sync.
     * For example, a custom token may have been registered but not deploy or the contract self destructed.
     * @param l1ERC20 address of L1 token
     * @return L2 address of a bridged ERC20 token
     */
    function calculateL2TokenAddress(address l1ERC20) public view override returns (address) {
        return l1ToL2Token[l1ERC20];
    }

    function registerTokenFromL1(address[] calldata l1Address, address[] calldata l2Address)
        external
        onlyCounterpartGateway
    {
        // we assume both arrays are the same length, safe since its encoded by the L1
        for (uint256 i = 0; i < l1Address.length; i++) {
            // here we don't check if l2Address is a contract and instead deal with that behaviour
            // in `handleNoContract` this way we keep the l1 and l2 address oracles in sync
            l1ToL2Token[l1Address[i]] = l2Address[i];
            emit TokenSet(l1Address[i], l2Address[i]);
        }
    }
}
"
    },
    "contracts/tokenbridge/arbitrum/gateway/L2WethGateway.sol": {
      "content": "// SPDX-License-Identifier: Apache-2.0

/*
 * Copyright 2020, Offchain Labs, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pragma solidity ^0.8.0;

import "./L2ArbitrumGateway.sol";
import "../../libraries/IWETH9.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

contract L2WethGateway is L2ArbitrumGateway {
    using SafeERC20 for IERC20;

    address public l1Weth;
    address public l2Weth;

    function initialize(
        address _l1Counterpart,
        address _router,
        address _l1Weth,
        address _l2Weth
    ) public {
        L2ArbitrumGateway._initialize(_l1Counterpart, _router);
        require(_l1Weth != address(0), "INVALID_L1WETH");
        require(_l2Weth != address(0), "INVALID_L2WETH");
        l1Weth = _l1Weth;
        l2Weth = _l2Weth;
    }

    /**
     * @notice internal utility function used to handle when no contract is deployed at expected address
     * @param l1ERC20 L1 address of ERC20
     */
    function handleNoContract(
        address l1ERC20,
        address, /* expectedL2Address */
        address _from,
        address, /* _to */
        uint256 _amount,
        bytes memory /* deployData */
    ) internal override returns (bool shouldHalt) {
        // it is assumed that the custom token is deployed in the L2 before deposits are made
        // trigger withdrawal
        // this codepath should only be hit if the system is setup incorrectly
        // this withdrawal is for error recovery, not composing with L2 dapps, so we ignore the return value
        triggerWithdrawal(l1ERC20, address(this), _from, _amount, "");
        return true;
    }

    /**
     * @notice Calculate the address used when bridging an ERC20 token
     * @dev the L1 and L2 address oracles may not always be in sync.
     * For example, a custom token may have been registered but not deploy or the contract self destructed.
     * @param l1ERC20 address of L1 token
     * @return L2 address of a bridged ERC20 token
     */
    function calculateL2TokenAddress(address l1ERC20) public view override returns (address) {
        if (l1ERC20 != l1Weth) {
            // invalid L1 weth address
            return address(0);
        }
        return l2Weth;
    }

    function inboundEscrowTransfer(
        address _l2TokenAddress,
        address _dest,
        uint256 _amount
    ) internal override {
        IWETH9(_l2TokenAddress).deposit{ value: _amount }();
        IERC20(_l2TokenAddress).safeTransfer(_dest, _amount);
    }

    function createOutboundTx(
        address _from,
        uint256 _tokenAmount,
        bytes memory _outboundCalldata
    ) internal override returns (uint256) {
        // exitNum incremented after being included in _outboundCalldata
        exitNum++;
        return
            sendTxToL1(
                // we send the amount of weth withdrawn as callvalue to the L1 gateway
                _tokenAmount,
                _from,
                counterpartGateway,
                _outboundCalldata
            );
    }

    receive() external payable {}
}
"
    },
    "contracts/tokenbridge/arbitrum/StandardArbERC20.sol": {
      "content": "// SPDX-License-Identifier: Apache-2.0

/*
 * Copyright 2020, Offchain Labs, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pragma solidity ^0.8.0;

import "../libraries/Cloneable.sol";
import "../libraries/L2GatewayToken.sol";
import "../libraries/BytesParser.sol";
import "./IArbToken.sol";

/**
 * @title Standard (i.e., non-custom) contract deployed by L2Gateway.sol as L2 ERC20. Includes standard ERC20 interface plus additional methods for deposits/withdraws
 */
contract StandardArbERC20 is IArbToken, L2GatewayToken, Cloneable {
    struct ERC20Getters {
        bool ignoreDecimals;
        bool ignoreName;
        bool ignoreSymbol;
    }
    ERC20Getters private availableGetters;

    /**
     * @notice initialize the token
     * @dev the L2 bridge assumes this does not fail or revert
     * @param _l1Address L1 address of ERC20
     * @param _data encoded symbol/name/decimal data for initial deploy
     */
    function bridgeInit(address _l1Address, bytes memory _data) public virtual {
        (bytes memory name_, bytes memory symbol_, bytes memory decimals_) = abi.decode(
            _data,
            (bytes, bytes, bytes)
        );
        // what if decode reverts? shouldn't as this is encoded by L1 contract

        /*
         *  if parsing fails, the type's default value gets assigned
         *  the parsing can fail for different reasons:
         *      1. method not available in L1 (empty input)
         *      2. data type is encoded differently in the L1 (trying to abi decode the wrong data type)
         *  currently (1) returns a parser fails and (2) reverts as there is no `abi.tryDecode`
         *  https://github.com/ethereum/solidity/issues/10381
         */

        (bool parseNameSuccess, string memory parsedName) = BytesParser.toString(name_);
        (bool parseSymbolSuccess, string memory parsedSymbol) = BytesParser.toString(symbol_);
        (bool parseDecimalSuccess, uint8 parsedDecimals) = BytesParser.toUint8(decimals_);

        L2GatewayToken._initialize(
            parsedName,
            parsedSymbol,
            parsedDecimals,
            msg.sender, // _l2Gateway,
            _l1Address // _l1Counterpart
        );

        // here we assume that (2) would have reverted, so if the parser failed its because the getter isn't available in the L1.
        // instead of storing on a struct, we could instead set a magic number, at something like `type(uint8).max` or random string
        // to be more general we instead use an extra storage slot
        availableGetters = ERC20Getters({
            ignoreName: !parseNameSuccess,
            ignoreSymbol: !parseSymbolSuccess,
            ignoreDecimals: !parseDecimalSuccess
        });
    }

    function decimals() public view override returns (uint8) {
        // no revert message just as in the L1 if you called and the function is not implemented
        if (availableGetters.ignoreDecimals) revert();
        return super.decimals();
    }

    function name() public view override returns (string memory) {
        // no revert message just as in the L1 if you called and the function is not implemented
        if (availableGetters.ignoreName) revert();
        return super.name();
    }

    function symbol() public view override returns (string memory) {
        // no revert message just as in the L1 if you called and the function is not implemented
        if (availableGetters.ignoreSymbol) revert();
        return super.symbol();
    }
}
"
    },
    "node_modules/@offchainlabs/upgrade-executor/src/IUpgradeExecutor.sol": {
      "content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.6.2 <0.9.0;

interface IUpgradeExecutor {
    function initialize(address admin, address[] memory executors) external;
    function execute(address upgrade, bytes memory upgradeCallData) external payable;
    function executeCall(address target, bytes memory targetCallData) external payable;
}
"
    },
    "contracts/tokenbridge/libraries/CreationCodeHelper.sol": {
      "content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

library CreationCodeHelper {
    /**
     * @notice Generate a creation code that results with a contract with `code` as deployed code.
     *         Generated creation code shall match the one generated by Solidity compiler with an empty constructor.
     * @dev Prepended constructor bytecode consists of:
     *      - 608060405234801561001057600080fd5b50 - store free memory pointer, then check no callvalue is provided
     *      - 61xxxx - push 2 bytes of `code` length
     *      - 806100206000396000f3fe - copy deployed code to memory and return the location of it
     * @param runtimeCode Deployed bytecode to which constructor bytecode will be prepended
     * @return Creation code of a new contract
     */
    function getCreationCodeFor(bytes memory runtimeCode) internal pure returns (bytes memory) {
        return abi.encodePacked(
            hex"608060405234801561001057600080fd5b50",
            hex"61",
            uint16(runtimeCode.length),
            hex"806100206000396000f3fe",
            runtimeCode
        );
    }
}
"
    },
    "contracts/tokenbridge/libraries/ClonableBeaconProxy.sol": {
      "content": "// SPDX-License-Identifier: Apache-2.0
// solhint-disable-next-line compiler-version
pragma solidity >=0.6.0 <0.9.0;

import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
import "@openzeppelin/contracts/utils/Create2.sol";

interface ProxySetter {
    function beacon() external view returns (address);
}

contract ClonableBeaconProxy is BeaconProxy {
    constructor() BeaconProxy(ProxySetter(msg.sender).beacon(), "") {}
}

contract BeaconProxyFactory is ProxySetter {
    bytes32 public constant cloneableProxyHash = keccak256(type(ClonableBeaconProxy).creationCode);

    /**
     * @notice utility function used in ClonableBeaconProxy.
     * @dev this method makes it possible to use ClonableBeaconProxy.creationCode without encoding constructor parameters
     * @return the beacon to be used by the proxy contract.
     */
    address public override beacon;

    function initialize(address _beacon) external {
        require(_beacon != address(0), "INVALID_BEACON");
        require(beacon == address(0), "ALREADY_INIT");
        beacon = _beacon;
    }

    function getSalt(address user, bytes32 userSalt) public pure returns (bytes32) {
        return keccak256(abi.encode(user, userSalt));
    }

    function createProxy(bytes32 userSalt) external returns (address) {
        // deployment will fail and this function will revert if contract `salt` is not unique
        bytes32 salt = getSalt(msg.sender, userSalt);
        address createdContract = address(new ClonableBeaconProxy{ salt: salt }());
        return createdContract;
    }

    function calculateExpectedAddress(address user, bytes32 userSalt)
        public
        view
        returns (address)
    {
        bytes32 salt = getSalt(user, userSalt);
        return Create2.computeAddress(salt, cloneableProxyHash, address(this));
    }

    function calculateExpectedAddress(bytes32 salt) public view returns (address) {
        return Create2.computeAddress(salt, cloneableProxyHash, address(this));
    }
}
"
    },
    "contracts/tokenbridge/libraries/aeWETH.sol": {
      "content": "// SPDX-License-Identifier: Apache-2.0

/*
 * Copyright 2020, Offchain Labs, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pragma solidity ^0.8.0;

import "./L2GatewayToken.sol";
import "./IWETH9.sol";

/// @title Arbitrum extended WETH
contract aeWETH is L2GatewayToken, IWETH9 {
    function initialize(
        string memory name_,
        string memory symbol_,
        uint8 decimals_,
        address l2Gateway_,
        address l1Address_
    ) external {
        L2GatewayToken._initialize(name_, symbol_, decimals_, l2Gateway_, l1Address_);
    }

    function bridgeMint(
        address, /* account */
        uint256 /* amount */
    ) external virtual override {
        // we want weth to always be fully collaterized
        revert("NO_BRIDGE_MINT");
    }

    function bridgeBurn(address account, uint256 amount) external virtual override onlyGateway {
        _burn(account, amount);
        (bool success, ) = msg.sender.call{ value: amount }("");
        require(success, "FAIL_TRANSFER");
    }

    function deposit() external payable override {
        depositTo(msg.sender);
    }

    function withdraw(uint256 amount) external override {
        withdrawTo(msg.sender, amount);
    }

    function depositTo(address account) public payable {
        _mint(account, msg.value);
    }

    function withdrawTo(address account, uint256 amount) public {
        _burn(msg.sender, amount);
        (bool success, ) = account.call{ value: amount }("");
        require(success, "FAIL_TRANSFER");
    }

    receive() external payable {
        depositTo(msg.sender);
    }
}
"
    },
    "node_modules/@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/beacon/UpgradeableBeacon.sol)

pragma solidity ^0.8.0;

import "./IBeacon.sol";
import "../../access/Ownable.sol";
import "../../utils/Address.sol";

/**
 * @dev This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their
 * implementation contract, which is where they will delegate all function calls.
 *
 * An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon.
 */
contract UpgradeableBeacon is IBeacon, Ownable {
    address private _implementation;

    /**
     * @dev Emitted when the implementation returned by the beacon is changed.
     */
    event Upgraded(address indexed implementation);

    /**
     * @dev Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the
     * beacon.
     */
    constructor(address implementation_) {
        _setImplementation(implementation_);
    }

    /**
     * @dev Returns the current implementation address.
     */
    function implementation() public view virtual override returns (address) {
        return _implementation;
    }

    /**
     * @dev Upgrades the beacon to a new implementation.
     *
     * Emits an {Upgraded} event.
     *
     * Requirements:
     *
     * - msg.sender must be the owner of the contract.
     * - `newImplementation` must be a contract.
     */
    function upgradeTo(address newImplementation) public virtual onlyOwner {
        _setImplementation(newImplementation);
        emit Upgraded(newImplementation);
    }

    /**
     * @dev Sets the implementation contract address for this beacon
     *
     * Requirements:
     *
     * - `newImplementation` must be a contract.
     */
    function _setImplementation(address newImplementation) private {
        require(Address.isContract(newImplementation), "UpgradeableBeacon: implementation is not a contract");
        _implementation = newImplementation;
    }
}
"
    },
    "node_modules/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.3) (proxy/transparent/ProxyAdmin.sol)

pragma solidity ^0.8.0;

import "./TransparentUpgradeableProxy.sol";
import "../../access/Ownable.sol";

/**
 * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
 * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
 */
contract ProxyAdmin is Ownable {
    /**
     * @dev Returns the current implementation of `proxy`.
     *
     * Requirements:
     *
     * - This contract must be the admin of `proxy`.
     */
    function getProxyImplementation(ITransparentUpgradeableProxy proxy) public view virtual returns (address) {
        // We need to manually run the static call since the getter cannot be flagged as view
        // bytes4(keccak256("implementation()")) == 0x5c60da1b
        (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b");
        require(success);
        return abi.decode(returndata, (address));
    }

    /**
     * @dev Returns the current admin of `proxy`.
     *
     * Requirements:
     *
     * - This contract must be the admin of `proxy`.
     */
    function getProxyAdmin(ITransparentUpgradeableProxy proxy) public view virtual returns (address) {
        // We need to manually run the static call since the getter cannot be flagged as view
        // bytes4(keccak256("admin()")) == 0xf851a440
        (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
        require(success);
        return abi.decode(returndata, (address));
    }

    /**
     * @dev Changes the admin of `proxy` to `newAdmin`.
     *
     * Requirements:
     *
     * - This contract must be the current admin of `proxy`.
     */
    function changeProxyAdmin(ITransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner {
        proxy.changeAdmin(newAdmin);
    }

    /**
     * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.
     *
     * Requirements:
     *
     * - This contract must be the admin of `proxy`.
     */
    function upgrade(ITransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner {
        proxy.upgradeTo(implementation);
    }

    /**
     * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See
     * {TransparentUpgradeableProxy-upgradeToAndCall}.
     *
     * Requirements:
     *
     * - This contract must be the admin of `proxy`.
     */
    function upgradeAndCall(
        ITransparentUpgradeableProxy proxy,
        address implementation,
        bytes memory data
    ) public payable virtual onlyOwner {
        proxy.upgradeToAndCall{value: msg.value}(implementation, data);
    }
}
"
    },
    "node_modules/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.3) (proxy/transparent/TransparentUpgradeableProxy.sol)

pragma solidity ^0.8.0;

import "../ERC1967/ERC1967Proxy.sol";

/**
 * @dev Interface for {TransparentUpgradeableProxy}. In order to implement transparency, {TransparentUpgradeableProxy}
 * does not implement this interface directly, and some of its functions are implemented by an internal dispatch
 * mechanism. The compiler is unaware that these functions are implemented by {TransparentUpgradeableProxy} and will not
 * include them in the ABI so this interface must be used to interact with it.
 */
interface ITransparentUpgradeableProxy is IERC1967 {
    function admin() external view returns (address);

    function implementation() external view returns (address);

    function changeAdmin(address) external;

    function upgradeTo(address) external;

    function upgradeToAndCall(address, bytes memory) external payable;
}

/**
 * @dev This contract implements a proxy that is upgradeable by an admin.
 *
 * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
 * clashing], which can potentially be used in an attack, this contract uses the
 * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
 * things that go hand in hand:
 *
 * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
 * that call matches one of the admin functions exposed by the proxy itself.
 * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
 * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
 * "admin cannot fallback to proxy target".
 *
 * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
 * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
 * to sudden errors when trying to call a function from the proxy implementation.
 *
 * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
 * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
 *
 * NOTE: The real interface of this proxy is that defined in `ITransparentUpgradeableProxy`. This contract does not
 * inherit from that interface, and instead the admin functions are implicitly implemented using a custom dispatch
 * mechanism in `_fallback`. Consequently, the compiler will not produce an ABI for this contract. This is necessary to
 * fully implement transparency without decoding reverts caused by selector clashes between the proxy and the
 * implementation.
 *
 * WARNING: It is not recommended to extend this contract to add additional external functions. If you do so, the compiler
 * will not check that there are no selector conflicts, due to the note above. A selector clash between any new function
 * and the functions declared in {ITransparentUpgradeableProxy} will be resolved in favor of the new one. This could
 * render the admin operations inaccessible, which could prevent upgradeability. Transparency may also be compromised.
 */
contract TransparentUpgradeableProxy is ERC1967Proxy {
    /**
     * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
     * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
     */
    constructor(
        address _logic,
        address admin_,
        bytes memory _data
    ) payable ERC1967Proxy(_logic, _data) {
        _changeAdmin(admin_);
    }

    /**
     * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
     *
     * CAUTION: This modifier is deprecated, as it could cause issues if the modified function has arguments, and the
     * implementation provides a function with the same selector.
     */
    modifier ifAdmin() {
        if (msg.sender == _getAdmin()) {
            _;
        } else {
            _fallback();
        }
    }

    /**
     * @dev If caller is the admin process the call internally, otherwise transparently fallback to the proxy behavior
     */
    function _fallback() internal virtual override {
        if (msg.sender == _getAdmin()) {
            bytes memory ret;
            bytes4 selector = msg.sig;
            if (selector == ITransparentUpgradeableProxy.upgradeTo.selector) {
                ret = _dispatchUpgradeTo();
            } else if (selector == ITransparentUpgradeableProxy.upgradeToAndCall.selector) {
                ret = _dispatchUpgradeToAndCall();
            } else if (selector == ITransparentUpgradeableProxy.changeAdmin.selector) {
                ret = _dispatchChangeAdmin();
            } else if (selector == ITransparentUpgradeableProxy.admin.selector) {
                ret = _dispatchAdmin();
            } else if (selector == ITransparentUpgradeableProxy.implementation.selector) {
                ret = _dispatchImplementation();
            } else {
                revert("TransparentUpgradeableProxy: admin cannot fallback to proxy target");
            }
            assembly {
                return(add(ret, 0x20), mload(ret))
            }
        } else {
            super._fallback();
        }
    }

    /**
     * @dev Returns the current admin.
     *
     * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
     * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
     * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
     */
    function _dispatchAdmin() private returns (bytes memory) {
        _requireZeroValue();

        address admin = _getAdmin();
        return abi.encode(admin);
    }

    /**
     * @dev Returns the current implementation.
     *
     * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
     * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
     * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
     */
    function _dispatchImplementation() private returns (bytes memory) {
        _requireZeroValue();

        address implementation = _implementation();
        return abi.encode(implementation);
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {AdminChanged} event.
     */
    function _dispatchChangeAdmin() private returns (bytes memory) {
        _requireZeroValue();

        address newAdmin = abi.decode(msg.data[4:], (address));
        _changeAdmin(newAdmin);

        return "";
    }

    /**
     * @dev Upgrade the implementation of the proxy.
     */
    function _dispatchUpgradeTo() private returns (bytes memory) {
        _requireZeroValue();

        address newImplementation = abi.decode(msg.data[4:], (address));
        _upgradeToAndCall(newImplementation, bytes(""), false);

        return "";
    }

    /**
     * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
     * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
     * proxied contract.
     */
    function _dispatchUpgradeToAndCall() private returns (bytes memory) {
        (address newImplementation, bytes memory data) = abi.decode(msg.data[4:], (address, bytes));
        _upgradeToAndCall(newImplementation, data, true);

        return "";
    }

    /**
     * @dev Returns the current admin.
     */
    function _admin() internal view virtual returns (address) {
        return _getAdmin();
    }

    /**
     * @dev To keep this contract fully transparent, all `ifAdmin` functions must be payable. This helper is here to
     * emulate some proxy functions being non-payable while still allowing value to pass through.
     */
    function _requireZeroValue() private {
        require(msg.value == 0);
    }
}
"
    },
    "node_modules/@openzeppelin/contracts/utils/Create2.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Create2.sol)

pragma solidity ^0.8.0;

/**
 * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
 * `CREATE2` can be used to compute in advance the address where a smart
 * contract will be deployed, which allows for interesting new mechanisms known
 * as 'counterfactual interactions'.
 *
 * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
 * information.
 */
library Create2 {
    /**
     * @dev Deploys a contract using `CREATE2`. The address where the contract
     * will be deployed can be known in advance via {computeAddress}.
     *
     * The bytecode for a contract can be obtained from Solidity with
     * `type(contractName).creationCode`.
     *
     * Requirements:
     *
     * - `bytecode` must not be empty.
     * - `salt` must have not been used for `bytecode` already.
     * - the factory must have a balance of at least `amount`.
     * - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
     */
    function deploy(
        uint256 amount,
        bytes32 salt,
        bytes memory bytecode
    ) internal returns (address addr) {
        require(address(this).balance >= amount, "Create2: insufficient balance");
        require(bytecode.length != 0, "Create2: bytecode length is zero");
        /// @solidity memory-safe-assembly
        assembly {
            addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
        }
        require(addr != address(0), "Create2: Failed on deploy");
    }

    /**
     * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
     * `bytecodeHash` or `salt` will result in a new destination address.
     */
    function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
        return computeAddress(salt, bytecodeHash, address(this));
    }

    /**
     * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
     * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
     */
    function computeAddress(
        bytes32 salt,
        bytes32 bytecodeHash,
        address deployer
    ) internal pure returns (address addr) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40) // Get free memory pointer

            // |                   | ↓ ptr ...  ↓ ptr + 0x0B (start) ...  ↓ ptr + 0x20 ...  ↓ ptr + 0x40 ...   |
            // |-------------------|---------------------------------------------------------------------------|
            // | bytecodeHash      |                                                        CCCCCCCCCCCCC...CC |
            // | salt              |                                      BBBBBBBBBBBBB...BB                   |
            // | deployer          | 000000...0000AAAAAAAAAAAAAAAAAAA...AA                                     |
            // | 0xFF              |            FF                                                             |
            // |-------------------|---------------------------------------------------------------------------|
            // | memory            | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
            // | keccak(start, 85) |            ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |

            mstore(add(ptr, 0x40), bytecodeHash)
            mstore(add(ptr, 0x20), salt)
            mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes
            let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff
            mstore8(start, 0xff)
            addr := keccak256(start, 85)
        }
    }
}
"
    },
    "contracts/tokenbridge/libraries/gateway/GatewayRouter.sol": {
      "content": "// SPDX-License-Identifier: Apache-2.0

/*
 * Copyright 2020, Offchain Labs, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pragma solidity ^0.8.0;

import "../ProxyUtil.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "./TokenGateway.sol";
import "./GatewayMessageHandler.sol";
import "./IGatewayRouter.sol";

/**
 * @title Common interface for L1 and L2 Gateway Routers
 */
abstract contract GatewayRouter is TokenGateway, IGatewayRouter {
    using Address for address;

    address internal constant ZERO_ADDR = address(0);
    address internal constant DISABLED = address(1);

    mapping(address => address) public l1TokenToGateway;
    address public override defaultGateway;

    function postUpgradeInit() external {
        // it is assumed the L2 Arbitrum Gateway contract is behind a Proxy controlled by a proxy admin
        // this function can only be called by the proxy admin contract
        address proxyAdmin = ProxyUtil.getProxyAdmin();
        require(msg.sender == proxyAdmin, "NOT_FROM_ADMIN");
        // this has no other logic since the current upgrade doesn't require this logic
    }

    function _initialize(
        address _counterpartGateway,
        address _router,
        address _defaultGateway
    ) internal {
        // if you are a router, you can't have a router
        require(_router == address(0), "BAD_ROUTER");
        TokenGateway._initialize(_counterpartGateway, _router);
        // default gateway can have 0 address
        defaultGateway = _defaultGateway;
    }

    function finalizeInboundTransfer(
        address, /* _token */
        address, /* _from */
        address, /* _to */
        uint256, /* _amount */
        bytes calldata /* _data */
    ) external payable virtual override {
        revert("ONLY_OUTBOUND_ROUTER");
    }

    function outboundTransfer(
        address _token,
        address _to,
        uint256 _amount,
        uint256 _maxGas,
        uint256 _gasPriceBid,
        bytes calldata _data
    ) public payable virtual override returns (bytes memory) {
        // this function is kept instead of delegating to outboundTransferCustomRefund to allow
        // compatibility with older gateways that did not implement outboundTransferCustomRefund
        address gateway = getGateway(_token);
        bytes memory gatewayData = GatewayMessageHandler.encodeFromRouterToGateway(
            msg.sender,
            _data
        );

        emit TransferRouted(_token, msg.sender, _to, gateway);
        return
            ITokenGateway(gateway).outboundTransfer{ value: msg.value }(
                _token,
                _to,
                _amount,
                _maxGas,
                _gasPriceBid,
                gatewayData
            );
    }

    function getOutboundCalldata(
        address _token,
        address _from,
        address _to,
        uint256 _amount,
        bytes memory _data
    ) public view virtual override returns (bytes memory) {
        address gateway = getGateway(_token);
        return TokenGateway(gateway).getOutboundCalldata(_token, _from, _to, _amount, _data);
    }

    function getGateway(address _token) public view virtual override returns (address gateway) {
        gateway = l1TokenToGateway[_token];

        if (gateway == ZERO_ADDR) {
            // if no gateway value set, use default gateway
            gateway = defaultGateway;
        }

        if (gateway == DISABLED || !gateway.isContract()) {
            // not a valid gateway
            return ZERO_ADDR;
        }

        return gateway;
    }

    function calculateL2TokenAddress(address l1ERC20)
        public
        view
        virtual
        override(TokenGateway, ITokenGateway)
        returns (address)
    {
        address gateway = getGateway(l1ERC20);
        if (gateway == ZERO_ADDR) {
            return ZERO_ADDR;
        }
        return TokenGateway(gateway).calculateL2TokenAddress(l1ERC20);
    }
}
"
    },
    "contracts/tokenbridge/ethereum/gateway/L1GatewayRouter.sol": {
      "content": "// SPDX-License-Identifier: Apache-2.0

/*
 * Copyright 2020, Offchain Labs, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

pragma solidity ^0.8.0;

import "../../libraries/Whitelist.sol";

import { ArbitrumEnabledToken } from "../ICustomToken.sol";
import "../L1ArbitrumMessenger.sol";
import "../../libraries/gateway/GatewayRouter.sol";
import "../../arbitrum/gateway/L2GatewayRouter.sol";
import "../../libraries/ERC165.sol";
import "./IL1GatewayRouter.sol";
import "./IL1ArbitrumGateway.sol";

/**
 * @title Handles deposits from Erhereum into Arbitrum. Tokens are routered to their appropriate L1 gateway (Router itself also conforms to the Gateway itnerface).
 * @notice Router also serves as an L1-L2 token address oracle.
 */
contract L1GatewayRouter is
    WhitelistConsumer,
    L1ArbitrumMessenger,
    GatewayRouter,
    ERC165,
    IL1GatewayRouter
{
    using Address for address;

    address public override owner;
    address public override inbox;

    modifier onlyOwner() {
        require(msg.sender == owner, "ONLY_OWNER");
        _;
    }

    function initialize(
        address _owner,
        address _defaultGateway,
        address, // was _whitelist, now unused
        address _counterpartGateway,
        address _inbox
    ) public {
        GatewayRouter._initialize(_counterpartGateway, address(0), _defaultGateway);
        owner = _owner;
        WhitelistConsumer.whitelist = address(0);
        inbox = _inbox;
    }

    function setDefaultGateway(
        address newL1DefaultGateway,
        uint256 _maxGas,
        uint256 _gasPriceBid,
        uint256 _maxSubmissionCost
    ) external payable virtual onlyOwner returns (uint256) {
        return
            _setDefaultGateway(
                newL1DefaultGateway,
                _maxGas,
                _gasPriceBid,
                _maxSubmissionCost,
                msg.value
            );
    }

    function _setDefaultGateway(
        address newL1DefaultGateway,
        uint256 _maxGas,
        uint256 _gasPriceBid,
        uint256 _maxSubmissionCost,
        uint256 feeAmount
    ) internal returns (uint256) {
        defaultGateway = newL1DefaultGateway;

        emit DefaultGatewayUpdated(newL1DefaultGateway);

        address l2NewDefaultGateway;

        if (newL1DefaultGateway != address(0)) {
            l2NewDefaultGateway = TokenGateway(newL1DefaultGateway).counterpartGateway();
        }

        bytes memory data = abi.encodeWithSelector(
            L2GatewayRouter.setDefaultGateway.selector,
            l2NewDefaultGateway
        );

        return
            sendTxToL2(
                inbox,
                counterpartGateway,
                msg.sender,
                feeAmount,
                0,
                L2GasParams({
                    _maxSubmissionCost: _maxSubmissionCost,
                    _maxGas: _maxGas,
                    _gasPriceBid: _gasPriceBid
                }),
                data
            );
    }

    function setOwner(address newOwner) external onlyOwner {
        require(newOwner != address(0), "INVALID_OWNER");
        // set newOwner to address(1) to disable owner and keep `initialize` safe
        owner = newOwner;
    }

    /**
     * @notice Allows L1 Token contract to trustlessly register its gateway. (other setGateway method allows excess eth recovery from _maxSubmissionCost and is recommended)
     * @param _gateway l1 gateway address
     * @param _maxGas max gas for L2 retryable exrecution
     * @param _gasPriceBid gas price for L2 retryable ticket
     * @param  _maxSubmissionCost base submission cost  L2 retryable tick3et
     * @return Retryable ticket ID
     */
    function setGateway(
        address _gateway,
        uint256 _maxGas,
        uint256 _gasPriceBid,
        uint256 _maxSubmissionCost
    ) external payable virtual override returns (uint256) {
        return setGateway(_gateway, _maxGas, _gasPriceBid, _maxSubmissionCost, msg.sender);
    }

    /**
     * @notice Allows L1 Token contract to trustlessly register its gateway.
     * @param _gateway l1 gateway address
     * @param _maxGas max gas for L2 retryable exrecution
     * @param _gasPriceBid gas price for L2 retryable ticket
     * @param  _maxSubmissionCost base submission cost  L2 retryable tick3et
     * @param _creditBackAddress ad

Tags:
ERC20, ERC165, Multisig, Pausable, Voting, Upgradeable, Multi-Signature, Factory, Oracle|addr:0x56ce8d41585683f4038d7b646778bc1e00fb46e5|verified:true|block:23549142|tx:0x40c5923aa22aa39cc692ec1a1c7ac868d55d9e22a08d7ce704bb0f121870fc0c|first_check:1760124909

Submitted on: 2025-10-10 21:35:10

Comments

Log in to comment.

No comments yet.