GLMR Price: $0.51 (+0.00%)
Gas: 153 GWei

Contract

0x1c98d96cfcDc4599D5FbB61Aa30301d94fcA8002

Overview

GLMR Balance

Moonbeam Chain LogoMoonbeam Chain LogoMoonbeam Chain Logo0 GLMR

GLMR Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
Initialize33738812023-04-17 17:49:42346 days ago1681753782IN
0x1c98d96c...94fcA8002
0 GLMR0.02541584101
0x6080604017078722022-08-22 18:11:54584 days ago1661191914IN
 Create: L1LiquidityPool
0 GLMR0.40048964102.5

Latest 2 internal transactions

Advanced mode:
Parent Txn Hash Block From To Value
35393932023-05-11 13:53:42322 days ago1683813222
0x1c98d96c...94fcA8002
158.372 GLMR
23280632022-11-19 2:26:00495 days ago1668824760
0x1c98d96c...94fcA8002
3 GLMR
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
L1LiquidityPool

Compiler Version
v0.8.9+commit.e5eed63a

Optimization Enabled:
Yes with 10000 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 18 : L1LiquidityPool.sol
// SPDX-License-Identifier: MIT
pragma solidity >0.7.5;
pragma experimental ABIEncoderV2;

import "./interfaces/iL2LiquidityPool.sol";
import "../libraries/CrossDomainEnabledFast.sol";

/* External Imports */
import '@openzeppelin/contracts/utils/math/SafeMath.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";

/* External Imports */
import "@eth-optimism/contracts/contracts/L1/messaging/L1StandardBridge.sol";

/**
 * @dev An L1 LiquidityPool implementation
 */
contract L1LiquidityPool is CrossDomainEnabledFast, ReentrancyGuardUpgradeable, PausableUpgradeable {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    /**************
     *   Struct   *
     **************/
    // Info of each user.
    struct UserInfo {
        uint256 amount; // How many LP tokens the user has provided.
        uint256 rewardDebt; // Reward debt. See explanation below.
        uint256 pendingReward; // Pending reward
        //
        // We do some fancy math here. Basically, any point in time, the amount of rewards
        // entitled to a user but is pending to be distributed is:
        //
        //   Update Reward Per Share:
        //   accUserRewardPerShare = accUserRewardPerShare + (accUserReward - lastAccUserReward) / userDepositAmount
        //
        //  LP Provider:
        //      Deposit:
        //          Case 1 (new user):
        //              Update Reward Per Share();
        //              Calculate user.rewardDebt = amount * accUserRewardPerShare;
        //          Case 2 (user who has already deposited add more funds):
        //              Update Reward Per Share();
        //              Calculate user.pendingReward = amount * accUserRewardPerShare - user.rewardDebt;
        //              Calculate user.rewardDebt = (amount + new_amount) * accUserRewardPerShare;
        //
        //      Withdraw
        //          Update Reward Per Share();
        //          Calculate user.pendingReward = amount * accUserRewardPerShare - user.rewardDebt;
        //          Calculate user.rewardDebt = (amount - withdraw_amount) * accUserRewardPerShare;
    }
    // Info of each pool.
    struct PoolInfo {
        address l1TokenAddress; // Address of token contract.
        address l2TokenAddress; // Address of toekn contract.

        // balance
        uint256 userDepositAmount; // user deposit amount;

        // user rewards
        uint256 lastAccUserReward; // Last accumulated user reward
        uint256 accUserReward; // Accumulated user reward.
        uint256 accUserRewardPerShare; // Accumulated user rewards per share, times 1e12. See below.

        // owner rewards
        uint256 accOwnerReward; // Accumulated owner reward.

        // start time -- used to calculate APR
        uint256 startTime;
    }
    // Token batch structure
    struct ClientDepositToken {
        address l1TokenAddress;
        uint256 amount;
    }
    struct ClientPayToken {
        address to;
        address l2TokenAddress;
        uint256 amount;
    }

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

    // mapping L1 and L2 token address to poolInfo
    mapping(address => PoolInfo) public poolInfo;
    // Info of each user that stakes tokens.
    mapping(address => mapping(address => UserInfo)) public userInfo;

    address public owner;
    address public L2LiquidityPoolAddress;
    uint256 public userRewardMinFeeRate;
    uint256 public ownerRewardFeeRate;
    // Default gas value which can be overridden if more complex logic runs on L2.
    uint32 public SETTLEMENT_L2_GAS;
    uint256 public SAFE_GAS_STIPEND;
    // cdm address
    address public l1CrossDomainMessenger;
    // L1StandardBridge address
    address payable public L1StandardBridgeAddress;
    uint256 public userRewardMaxFeeRate;

    bytes32 public priorDepositInfoHash;
    bytes32 public currentDepositInfoHash;
    uint256 public lastHashUpdateBlock;

    /********************
     *       Events     *
     ********************/

    event AddLiquidity(
        address sender,
        uint256 amount,
        address tokenAddress
    );

    event OwnerRecoverFee(
        address sender,
        address receiver,
        uint256 amount,
        address tokenAddress
    );

    event ClientDepositL1(
        address sender,
        uint256 receivedAmount,
        address tokenAddress
    );

    event ClientDepositL1Batch(
        ClientPayToken[] _tokens
    );

    event ClientPayL1(
        address sender,
        uint256 amount,
        uint256 userRewardFee,
        uint256 ownerRewardFee,
        uint256 totalFee,
        address tokenAddress
    );

    event ClientPayL1Settlement(
        address sender,
        uint256 amount,
        uint256 userRewardFee,
        uint256 ownerRewardFee,
        uint256 totalFee,
        address tokenAddress
    );

    event WithdrawLiquidity(
        address sender,
        address receiver,
        uint256 amount,
        address tokenAddress
    );

    event WithdrawReward(
        address sender,
        address receiver,
        uint256 amount,
        address tokenAddress
    );

    event RebalanceLP(
        uint256 amount,
        address tokenAddress
    );

    event OwnershipTransferred(
        address newOwner
    );

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

    constructor()
        CrossDomainEnabledFast(address(0), address(0))
    {}

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

    modifier onlyOwner() {
        require(msg.sender == owner || owner == address(0), 'Caller is not the owner');
        _;
    }

    modifier onlyNotInitialized() {
        require(address(L2LiquidityPoolAddress) == address(0), "Contract has been initialized");
        _;
    }

    modifier onlyInitialized() {
        require(address(L2LiquidityPoolAddress) != address(0), "Contract has not yet been initialized");
        _;
    }

    modifier onlyL1StandardBridge() {
        require(address(L1StandardBridgeAddress) == msg.sender, "Can't receive ETH");
        _;
    }

    /********************
     * Fall back Functions *
     ********************/
    receive()
        external
        payable
        onlyL1StandardBridge()
    {}

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

    /**
     * @dev transfer ownership
     *
     * @param _newOwner new owner of this contract
     */
    function transferOwnership(
        address _newOwner
    )
        public
        onlyOwner()
    {
        require(_newOwner != address(0), 'New owner cannot be the zero address');
        owner = _newOwner;
        emit OwnershipTransferred(_newOwner);
    }

    /**
     * @dev Initialize this contract.
     *
     * @param _l1CrossDomainMessenger L1 Messenger address being used for sending the cross-chain message.
     * @param _l1CrossDomainMessengerFast L1 Messenger address being used for relaying cross-chain messages quickly.
     * @param _L2LiquidityPoolAddress Address of the corresponding L2 LP deployed to the L2 chain
     * @param _L1StandardBridgeAddress Address of L1 StandardBridge
     */
    function initialize(
        address _l1CrossDomainMessenger,
        address _l1CrossDomainMessengerFast,
        address _L2LiquidityPoolAddress,
        address payable _L1StandardBridgeAddress
    )
        public
        onlyOwner()
        onlyNotInitialized()
        initializer()
    {
        require(_l1CrossDomainMessenger != address(0) && _l1CrossDomainMessengerFast != address(0) && _L2LiquidityPoolAddress != address(0), "zero address not allowed");
        senderMessenger = _l1CrossDomainMessenger;
        relayerMessenger = _l1CrossDomainMessengerFast;
        L2LiquidityPoolAddress = _L2LiquidityPoolAddress;
        L1StandardBridgeAddress = _L1StandardBridgeAddress;
        owner = msg.sender;
        // translates to fee rates 0.5%, 5% and 0% respectively
        _configureFee(5, 50, 0);
        configureGas(700000, 2300);

        __Context_init_unchained();
        __Pausable_init_unchained();
        __ReentrancyGuard_init_unchained();

    }

    function _configureFee(
        uint256 _userRewardMinFeeRate,
        uint256 _userRewardMaxFeeRate,
        uint256 _ownerRewardFeeRate
    )
        internal
    {
        userRewardMinFeeRate = _userRewardMinFeeRate;
        userRewardMaxFeeRate = _userRewardMaxFeeRate;
        ownerRewardFeeRate = _ownerRewardFeeRate;
    }

    /**
     * @dev Configure gas.
     *
     * @param _l2GasFee default finalized deposit L2 Gas
     * @param _safeGas safe gas stipened
     */
    function configureGas(
        uint32 _l2GasFee,
        uint256 _safeGas
    )
        public
        onlyOwner()
        onlyInitialized()
    {
        SETTLEMENT_L2_GAS = _l2GasFee;
        SAFE_GAS_STIPEND = _safeGas;
    }

    /**
     * @dev Return user reward fee rate.
     *
     * @param _l1TokenAddress L1 token address
     */
    function getUserRewardFeeRate(
        address _l1TokenAddress
    )
        public
        view
        onlyInitialized()
        returns (uint256 userRewardFeeRate)
    {
        PoolInfo storage pool = poolInfo[_l1TokenAddress];
        uint256 poolLiquidity = pool.userDepositAmount;
        uint256 poolBalance;
        if (_l1TokenAddress == address(0)) {
            poolBalance = address(this).balance;
        } else {
            poolBalance = IERC20(_l1TokenAddress).balanceOf(address(this));
        }
        if (poolBalance == 0) {
            return userRewardMaxFeeRate;
        } else {
            uint256 poolRewardRate = userRewardMinFeeRate * poolLiquidity / poolBalance;
            if (userRewardMinFeeRate > poolRewardRate) {
                return userRewardMinFeeRate;
            } else if (userRewardMaxFeeRate < poolRewardRate) {
                return userRewardMaxFeeRate;
            }
            return poolRewardRate;
        }
    }

    /***
     * @dev Add the new token pair to the pool
     * DO NOT add the same LP token more than once. Rewards will be messed up if you do.
     *
     * @param _l1TokenAddress
     * @param _l2TokenAddress
     *
     */
    function registerPool(
        address _l1TokenAddress,
        address _l2TokenAddress
    )
        public
        onlyOwner()
    {
        require(_l1TokenAddress != _l2TokenAddress, "l1 and l2 token addresses cannot be same");
        require(_l2TokenAddress != address(0), "l2 token address cannot be zero address");
        // use with caution, can register only once
        PoolInfo storage pool = poolInfo[_l1TokenAddress];
        // l2 token address equal to zero, then pair is not registered.
        require(pool.l2TokenAddress == address(0), "Token Address Already Registered");
        poolInfo[_l1TokenAddress] =
            PoolInfo({
                l1TokenAddress: _l1TokenAddress,
                l2TokenAddress: _l2TokenAddress,
                userDepositAmount: 0,
                lastAccUserReward: 0,
                accUserReward: 0,
                accUserRewardPerShare: 0,
                accOwnerReward: 0,
                startTime: block.timestamp
            });
    }

    /**
     * Update the user reward per share
     * @param _tokenAddress Address of the target token.
     */
    function updateUserRewardPerShare(
        address _tokenAddress
    )
        public
    {
        PoolInfo storage pool = poolInfo[_tokenAddress];
        if (pool.lastAccUserReward < pool.accUserReward) {
            uint256 accUserRewardDiff = (pool.accUserReward.sub(pool.lastAccUserReward));
            if (pool.userDepositAmount != 0) {
                pool.accUserRewardPerShare = pool.accUserRewardPerShare.add(
                    accUserRewardDiff.mul(1e12).div(pool.userDepositAmount)
                );
            }
            pool.lastAccUserReward = pool.accUserReward;
        }
    }

    /**
     * Liquididity providers add liquidity
     * @param _amount liquidity amount that users want to deposit.
     * @param _tokenAddress address of the liquidity token.
     */
     function addLiquidity(
        uint256 _amount,
        address _tokenAddress
    )
        external
        payable
        nonReentrant
        whenNotPaused
    {
        require(msg.value != 0 || _tokenAddress != address(0), "Either Amount Incorrect or Token Address Incorrect");
        // combine to make logical XOR to avoid user error
        require(!(msg.value != 0 && _tokenAddress != address(0)), "Either Amount Incorrect or Token Address Incorrect");
        // check whether user sends ETH or ERC20
        if (msg.value != 0) {
            // override the _amount and token address
            _amount = msg.value;
            _tokenAddress = address(0);
        }

        PoolInfo storage pool = poolInfo[_tokenAddress];
        UserInfo storage user = userInfo[_tokenAddress][msg.sender];

        require(pool.l2TokenAddress != address(0), "Token Address Not Registered");

        // Update accUserRewardPerShare
        updateUserRewardPerShare(_tokenAddress);

        // if the user has already deposited token, we move the rewards to
        // pendingReward and update the reward debet.
        if (user.amount > 0) {
            user.pendingReward = user.pendingReward.add(
                user.amount.mul(pool.accUserRewardPerShare).div(1e12).sub(user.rewardDebt)
            );
            user.rewardDebt = (user.amount.add(_amount)).mul(pool.accUserRewardPerShare).div(1e12);
        } else {
            user.rewardDebt = _amount.mul(pool.accUserRewardPerShare).div(1e12);
        }

        // update amounts
        user.amount = user.amount.add(_amount);
        pool.userDepositAmount = pool.userDepositAmount.add(_amount);

        emit AddLiquidity(
            msg.sender,
            _amount,
            _tokenAddress
        );

        // transfer funds if users deposit ERC20
        if (_tokenAddress != address(0)) {
            IERC20(_tokenAddress).safeTransferFrom(msg.sender, address(this), _amount);
        }
    }

    /**
     * Client deposit ERC20 from their account to this contract, which then releases funds on the L2 side
     * @param _amount amount that client wants to transfer.
     * @param _tokenAddress L2 token address
     */
    function clientDepositL1(
        uint256 _amount,
        address _tokenAddress
    )
        external
        payable
        whenNotPaused
    {
        require(msg.value != 0 || _tokenAddress != address(0), "Either Amount Incorrect or Token Address Incorrect");
        // combine to make logical XOR to avoid user error
        require(!(msg.value != 0 && _tokenAddress != address(0)), "Either Amount Incorrect or Token Address Incorrect");
        // check whether user sends ETH or ERC20
        if (msg.value != 0) {
            // override the _amount and token address
            _amount = msg.value;
            _tokenAddress = address(0);
        }

        PoolInfo storage pool = poolInfo[_tokenAddress];

        require(pool.l2TokenAddress != address(0), "Token Address Not Registered");

        _updateDepositHash(_tokenAddress, msg.sender, _amount);

        emit ClientDepositL1(
            msg.sender,
            _amount,
            _tokenAddress
        );

        // transfer funds if users deposit ERC20
        if (_tokenAddress != address(0)) {
            IERC20(_tokenAddress).safeTransferFrom(msg.sender, address(this), _amount);
        }

        // Construct calldata for L1LiquidityPool.clientPayL2(_to, receivedAmount, l2TokenAddress)
        bytes memory data = abi.encodeWithSelector(
            iL2LiquidityPool.clientPayL2.selector,
            msg.sender,
            _amount,
            pool.l2TokenAddress
        );

        // Send calldata into L2
        sendCrossDomainMessage(
            address(L2LiquidityPoolAddress),
            // extra gas for complex l2 logic
            SETTLEMENT_L2_GAS,
            data
        );
    }

    function clientDepositL1Batch(
        ClientDepositToken[] calldata _tokens
    )
        external
        payable
        whenNotPaused
    {
        // The l2 gas amount is SETTLEMENT_L2_GAS * 2
        // We shouldn't send too many tokens in one batch
        require(_tokens.length < 5, "Too Many Tokens");

        // The total ETH amount in ClientDepositToken[]
        uint256 ETHAmount;
        // Create a payload that is sent to L2
        ClientPayToken[] memory payload = new ClientPayToken[](_tokens.length);

        // Check all tokens
        for (uint256 i = 0; i < _tokens.length; i++) {
            ClientDepositToken memory token = _tokens[i];
            require(token.amount != 0, "Invalid Amount");

            PoolInfo storage pool = poolInfo[token.l1TokenAddress];
            require(pool.l2TokenAddress != address(0), "Invaild Token");

            if (token.l1TokenAddress != address(0)) {
                IERC20(token.l1TokenAddress).safeTransferFrom(msg.sender, address(this), token.amount);
            } else {
                ETHAmount = ETHAmount + token.amount;
            }
            payload[i] = ClientPayToken(msg.sender, pool.l2TokenAddress, token.amount);
        }

        // verify that the total ETH amount is eqaul to msg.value
        require(ETHAmount == msg.value, "Invalid ETH Amount");

        // Construct calldata for L1LiquidityPool.clientPayL2Batch(ClientPayToken)
        bytes memory data = abi.encodeWithSelector(
            iL2LiquidityPool.clientPayL2Batch.selector,
            payload
        );

        // Send calldata into L1
        sendCrossDomainMessage(
            address(L2LiquidityPoolAddress),
            // extra gas for complex l2 logic
            SETTLEMENT_L2_GAS * 2,
            data
        );

        emit ClientDepositL1Batch(
            payload
        );
    }

    /**
     * Users withdraw token from LP
     * @param _amount amount to withdraw
     * @param _tokenAddress L1 token address
     * @param _to receiver to get the funds
     */
    function withdrawLiquidity(
        uint256 _amount,
        address _tokenAddress,
        address payable _to
    )
        external
        whenNotPaused
    {
        PoolInfo storage pool = poolInfo[_tokenAddress];
        UserInfo storage user = userInfo[_tokenAddress][msg.sender];

        require(pool.l2TokenAddress != address(0), "Token Address Not Registered");
        require(user.amount >= _amount, "Withdraw Error");

        // Update accUserRewardPerShare
        updateUserRewardPerShare(_tokenAddress);

        // calculate all the rewards and set it as pending rewards
        user.pendingReward = user.pendingReward.add(
            user.amount.mul(pool.accUserRewardPerShare).div(1e12).sub(user.rewardDebt)
        );
        // Update the user data
        user.amount = user.amount.sub(_amount);
        // update reward debt
        user.rewardDebt = user.amount.mul(pool.accUserRewardPerShare).div(1e12);
        // update total user deposit amount
        pool.userDepositAmount = pool.userDepositAmount.sub(_amount);

        emit WithdrawLiquidity(
            msg.sender,
            _to,
            _amount,
            _tokenAddress
        );

        if (_tokenAddress != address(0)) {
            IERC20(_tokenAddress).safeTransfer(_to, _amount);
        } else {
            (bool sent,) = _to.call{gas: SAFE_GAS_STIPEND, value: _amount}("");
            require(sent, "Failed to send ETH");
        }
    }

    /**
     * owner recovers fee from ERC20
     * @param _amount amount that owner wants to recover.
     * @param _tokenAddress L1 token address
     * @param _to receiver to get the fee.
     */
    function ownerRecoverFee(
        uint256 _amount,
        address _tokenAddress,
        address _to
    )
        external
        onlyOwner()
    {
        PoolInfo storage pool = poolInfo[_tokenAddress];

        require(pool.l2TokenAddress != address(0), "Token Address Not Registered");
        require(pool.accOwnerReward >= _amount, "Owner Reward Withdraw Error");

        pool.accOwnerReward = pool.accOwnerReward.sub(_amount);

        emit OwnerRecoverFee(
            msg.sender,
            _to,
            _amount,
            _tokenAddress
        );

        if (_tokenAddress != address(0)) {
            IERC20(_tokenAddress).safeTransfer(_to, _amount);
        } else {
            (bool sent,) = _to.call{gas: SAFE_GAS_STIPEND, value: _amount}("");
            require(sent, "Failed to send Ether");
        }
    }

    /**
     * withdraw reward from ERC20
     * @param _amount reward amount that liquidity providers want to withdraw
     * @param _tokenAddress L1 token address
     * @param _to receiver to get the reward
     */
    function withdrawReward(
        uint256 _amount,
        address _tokenAddress,
        address _to
    )
        external
        whenNotPaused
    {
        PoolInfo storage pool = poolInfo[_tokenAddress];
        UserInfo storage user = userInfo[_tokenAddress][msg.sender];

        require(pool.l2TokenAddress != address(0), "Token Address Not Registered");

        uint256 pendingReward = user.pendingReward.add(
            user.amount.mul(pool.accUserRewardPerShare).div(1e12).sub(user.rewardDebt)
        );

        require(pendingReward >= _amount, "Withdraw Reward Error");

        user.pendingReward = pendingReward.sub(_amount);
        user.rewardDebt = user.amount.mul(pool.accUserRewardPerShare).div(1e12);

        emit WithdrawReward(
            msg.sender,
            _to,
            _amount,
            _tokenAddress
        );

        if (_tokenAddress != address(0)) {
            IERC20(_tokenAddress).safeTransfer(_to, _amount);
        } else {
            (bool sent,) = _to.call{gas: SAFE_GAS_STIPEND, value: _amount}("");
            require(sent, "Failed to send Ether");
        }
    }

    /*
     * Rebalance LPs
     * @param _amount token amount that we want to move from L1 to L2
     * @param _tokenAddress L1 token address
     */
    function rebalanceLP(
        uint256 _amount,
        address _tokenAddress
    )
        external
        onlyOwner()
        whenNotPaused()
    {
        require(_amount != 0, "Amount Incorrect");

        PoolInfo storage pool = poolInfo[_tokenAddress];

        require(L2LiquidityPoolAddress != address(0), "L2 Liquidity Pool Not Registered");
        require(pool.l2TokenAddress != address(0), "Token Address Not Registered");

        emit RebalanceLP(
            _amount,
            _tokenAddress
        );

        if (_tokenAddress == address(0)) {
            require(_amount <= address(this).balance, "Failed to Rebalance LP");
            L1StandardBridge(L1StandardBridgeAddress).depositNativeTokenTo{value: _amount}(
                L2LiquidityPoolAddress,
                SETTLEMENT_L2_GAS,
                ""
            );
        } else {
            require(_amount <= IERC20(_tokenAddress).balanceOf(address(this)), "Failed to Rebalance LP");
            IERC20(_tokenAddress).safeIncreaseAllowance(L1StandardBridgeAddress, _amount);
            L1StandardBridge(L1StandardBridgeAddress).depositERC20To(
                _tokenAddress,
                pool.l2TokenAddress,
                L2LiquidityPoolAddress,
                _amount,
                SETTLEMENT_L2_GAS,
                ""
            );
        }
    }

    /**
     * Pause contract
     */
    function pause() external onlyOwner() {
        _pause();
    }

    /**
     * UnPause contract
     */
    function unpause() external onlyOwner() {
        _unpause();
    }

    function _updateDepositHash(address _tokenAddress, address _account, uint256 _amount) internal {
        // if block number is different only then update prior
        if (block.number > lastHashUpdateBlock) {
            priorDepositInfoHash = currentDepositInfoHash;
        }
        currentDepositInfoHash = keccak256(abi.encode(
            currentDepositInfoHash,
            _tokenAddress,
            _account,
            _amount
        ));

        lastHashUpdateBlock = block.number;
    }

    /*************************
     * Cross-chain Functions *
     *************************/

    /**
     * Move funds from L2 to L1, and pay out from the right liquidity pool
     * part of the contract pause, if only this method needs pausing use pause on CDM_Fast
     * @param _to receiver to get the funds
     * @param _amount amount to to be transferred.
     * @param _tokenAddress L1 token address
     */
    function clientPayL1(
        address payable _to,
        uint256 _amount,
        address _tokenAddress
    )
        external
        onlyFromCrossDomainAccount(address(L2LiquidityPoolAddress))
        whenNotPaused
    {
        // replyNeeded helps store the status if a message needs to be sent back to the other layer
        // in case there is not enough funds to give away
        bool replyNeeded = false;
        PoolInfo storage pool = poolInfo[_tokenAddress];
        uint256 userRewardFeeRate = getUserRewardFeeRate(_tokenAddress);
        uint256 userRewardFee = (_amount.mul(userRewardFeeRate)).div(1000);
        uint256 ownerRewardFee = (_amount.mul(ownerRewardFeeRate)).div(1000);
        uint256 totalFee = userRewardFee.add(ownerRewardFee);
        uint256 receivedAmount = _amount.sub(totalFee);

        if (_tokenAddress != address(0)) {
            if (receivedAmount > IERC20(_tokenAddress).balanceOf(address(this))) {
                replyNeeded = true;
            } else {
                pool.accUserReward = pool.accUserReward.add(userRewardFee);
                pool.accOwnerReward = pool.accOwnerReward.add(ownerRewardFee);
                IERC20(_tokenAddress).safeTransfer(_to, receivedAmount);
            }
        } else {
            // //this is ETH
            if (receivedAmount > address(this).balance) {
                 replyNeeded = true;
             } else {
                pool.accUserReward = pool.accUserReward.add(userRewardFee);
                pool.accOwnerReward = pool.accOwnerReward.add(ownerRewardFee);
                 //this is ETH
                 (bool sent,) = _to.call{gas: SAFE_GAS_STIPEND, value: receivedAmount}("");
                 require(sent, "Failed to send ETH");
             }
         }

         if (replyNeeded) {
             // send cross domain message
             bytes memory data = abi.encodeWithSelector(
             iL2LiquidityPool.clientPayL2Settlement.selector,
             _to,
             _amount,
             pool.l2TokenAddress
             );

             sendCrossDomainMessage(
                 address(L2LiquidityPoolAddress),
                 SETTLEMENT_L2_GAS,
                 data
             );
         } else {
             emit ClientPayL1(
             _to,
             receivedAmount,
             userRewardFee,
             ownerRewardFee,
             totalFee,
             _tokenAddress
             );
         }
    }

    /**
     * Settlement pay when there's not enough funds on the other side
     * part of the contract pause, if only this method needs pausing use pause on CDM_Fast
     * @param _to receiver to get the funds
     * @param _amount amount to to be transferred.
     * @param _tokenAddress L1 token address
     */
    function clientPayL1Settlement(
        address payable _to,
        uint256 _amount,
        address _tokenAddress
    )
        external
        onlyFromCrossDomainAccount(address(L2LiquidityPoolAddress))
        whenNotPaused
    {
        PoolInfo storage pool = poolInfo[_tokenAddress];
        uint256 userRewardFeeRate = getUserRewardFeeRate(_tokenAddress);
        uint256 userRewardFee = (_amount.mul(userRewardFeeRate)).div(1000);
        uint256 ownerRewardFee = (_amount.mul(ownerRewardFeeRate)).div(1000);
        uint256 totalFee = userRewardFee.add(ownerRewardFee);
        uint256 receivedAmount = _amount.sub(totalFee);

        pool.accUserReward = pool.accUserReward.add(userRewardFee);
        pool.accOwnerReward = pool.accOwnerReward.add(ownerRewardFee);

        emit ClientPayL1Settlement(
        _to,
        receivedAmount,
        userRewardFee,
        ownerRewardFee,
        totalFee,
        _tokenAddress
        );

        if (_tokenAddress != address(0)) {
            IERC20(_tokenAddress).safeTransfer(_to, receivedAmount);
        } else {
            //this is ETH
            (bool sent,) = _to.call{gas: SAFE_GAS_STIPEND, value: receivedAmount}("");
            require(sent, "Failed to send Ether");
        }
    }

    /**
     * @dev Configure fee of this contract. called from L2
     * @dev Each fee rate is scaled by 10^3 for precision, eg- a fee rate of 50 would mean 5%
     * @param _userRewardMinFeeRate minimum fee rate that users get
     * @param _userRewardMaxFeeRate maximum fee rate that users get
     * @param _ownerRewardFeeRate fee rate that contract owner gets
     */
    function configureFee(
        uint256 _userRewardMinFeeRate,
        uint256 _userRewardMaxFeeRate,
        uint256 _ownerRewardFeeRate
    )
        external
        onlyOwner()
        onlyInitialized()
    {
        require(_userRewardMinFeeRate <= _userRewardMaxFeeRate, "Invalud user reward fee");
        _configureFee(_userRewardMinFeeRate, _userRewardMaxFeeRate,_ownerRewardFeeRate);
    }
}

File 2 of 18 : iL2LiquidityPool.sol
// SPDX-License-Identifier: MIT
pragma solidity >0.7.5;
pragma experimental ABIEncoderV2;

/**
 * @title iL2LiquidityPool
 */
interface iL2LiquidityPool {

    struct ClientPayToken {
        address payable to;
        address l2TokenAddress;
        uint256 amount;
    }

    /********************
     *       Events     *
     ********************/

    event AddLiquidity(
        address sender,
        uint256 amount,
        address tokenAddress
    );

    event OwnerRecoverFee(
        address sender,
        address receiver,
        uint256 amount,
        address tokenAddress
    );

    event ClientDepositL2(
        address sender,
        uint256 receivedAmount,
        address tokenAddress
    );

    event ClientPayL2(
        address sender,
        uint256 amount,
        uint256 userRewardFee,
        uint256 ownerRewardFee,
        uint256 totalFee,
        address tokenAddress
    );

    event ClientPayL2Settlement(
        address sender,
        uint256 amount,
        uint256 userRewardFee,
        uint256 ownerRewardFee,
        uint256 totalFee,
        address tokenAddress
    );

    event WithdrawLiquidity(
        address sender,
        address receiver,
        uint256 amount,
        address tokenAddress
    );

    event WithdrawReward(
        address sender,
        address receiver,
        uint256 amount,
        address tokenAddress
    );

    /*************************
     * Cross-chain Functions *
     *************************/

    function clientPayL2(
        address payable _to,
        uint256 _amount,
        address _tokenAddress
    )
        external;

    function clientPayL2Batch(
        ClientPayToken [] calldata _tokens
    )
        external;

    function clientPayL2Settlement(
        address payable _to,
        uint256 _amount,
        address _tokenAddress
    )
        external;
}

File 3 of 18 : CrossDomainEnabledFast.sol
// SPDX-License-Identifier: MIT
pragma solidity >0.7.5;
/* Interface Imports */
import { ICrossDomainMessenger } from "@eth-optimism/contracts/contracts/libraries/bridge/ICrossDomainMessenger.sol";

/**
 * @title CrossDomainEnabledFast
 * @dev Helper contract for contracts performing cross-domain communications
 *
 * Compiler used: defined by inheriting contract
 * Runtime target: defined by inheriting contract
 */
contract CrossDomainEnabledFast {

    // Messenger contract used to send and receive messages from the other domain.
    address public senderMessenger;
    address public relayerMessenger;

    /***************
     * Constructor *
     ***************/
    constructor(
        address _senderMessenger,
        address _relayerMessenger
    ) {
        senderMessenger = _senderMessenger;
        relayerMessenger = _relayerMessenger;
    }

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

    /**
     * @notice Enforces that the modified function is only callable by a specific cross-domain account.
     * @param _sourceDomainAccount The only account on the originating domain which is authenticated to call this function.
     */
    modifier onlyFromCrossDomainAccount(
        address _sourceDomainAccount
    ) {
        require(
            msg.sender == address(getCrossDomainRelayerMessenger()),
            "XCHAIN: messenger contract unauthenticated"
        );

        require(
            getCrossDomainRelayerMessenger().xDomainMessageSender() == _sourceDomainAccount,
            "XCHAIN: wrong sender of cross-domain message"
        );

        _;
    }

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

    /**
     * @notice Gets the messenger, usually from storage.  This function is exposed in case a child contract needs to override.
     * @return The address of the cross-domain messenger contract which should be used.
     */
    function getCrossDomainSenderMessenger()
        internal
        virtual
        returns(
            ICrossDomainMessenger
        )
    {
        return ICrossDomainMessenger(senderMessenger);
    }

    /**
     * @notice Gets the messenger, usually from storage.  This function is exposed in case a child contract needs to override.
     * @return The address of the cross-domain messenger contract which should be used.
     */
    function getCrossDomainRelayerMessenger()
        internal
        virtual
        returns(
            ICrossDomainMessenger
        )
    {
        return ICrossDomainMessenger(relayerMessenger);
    }

    /**
     * @notice Sends a message to an account on another domain
     * @param _crossDomainTarget The intended recipient on the destination domain
     * @param _data The data to send to the target (usually calldata to a function with `onlyFromCrossDomainAccount()`)
     * @param _gasLimit The gasLimit for the receipt of the message on the target domain.
     */
    function sendCrossDomainMessage(
        address _crossDomainTarget,
        uint32 _gasLimit,
        bytes memory _data
    ) internal {
        getCrossDomainSenderMessenger().sendMessage(_crossDomainTarget, _data, _gasLimit);
    }
}

File 4 of 18 : SafeMath.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.

/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SafeMath` is no longer needed starting with Solidity 0.8. The compiler
 * now has built in overflow checking.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        return a + b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        return a * b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator.
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b <= a, errorMessage);
            return a - b;
        }
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a / b;
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a % b;
        }
    }
}

File 5 of 18 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 6 of 18 : ReentrancyGuardUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuardUpgradeable is Initializable {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    function __ReentrancyGuard_init() internal initializer {
        __ReentrancyGuard_init_unchained();
    }

    function __ReentrancyGuard_init_unchained() internal initializer {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
    uint256[49] private __gap;
}

File 7 of 18 : PausableUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    function __Pausable_init() internal initializer {
        __Context_init_unchained();
        __Pausable_init_unchained();
    }

    function __Pausable_init_unchained() internal initializer {
        _paused = false;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        require(!paused(), "Pausable: paused");
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        require(paused(), "Pausable: not paused");
        _;
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
    uint256[49] private __gap;
}

File 8 of 18 : L1StandardBridge.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

/* Interface Imports */
import { IL1StandardBridge } from "./IL1StandardBridge.sol";
import { IL1ERC20Bridge } from "./IL1ERC20Bridge.sol";
import { IL2ERC20Bridge } from "../../L2/messaging/IL2ERC20Bridge.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/* Library Imports */
import { CrossDomainEnabled } from "../../libraries/bridge/CrossDomainEnabled.sol";
import { Lib_PredeployAddresses } from "../../libraries/constants/Lib_PredeployAddresses.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/**
 * @title L1StandardBridge
 * @dev The L1 native token and ERC20 Bridge is a contract which stores deposited L1 funds and standard
 * tokens that are in use on L2. It synchronizes a corresponding L2 Bridge, informing it of deposits
 * and listening to it for newly finalized withdrawals.
 *
 * Runtime target: EVM
 */
contract L1StandardBridge is IL1StandardBridge, CrossDomainEnabled {
    using SafeERC20 for IERC20;

    /********************************
     * External Contract References *
     ********************************/

    address public l2TokenBridge;

    // Maps L1 token to L2 token to balance of the L1 token deposited
    mapping(address => mapping(address => uint256)) public deposits;

    bytes32 public priorDepositInfoHash;
    bytes32 public currentDepositInfoHash;
    uint256 public lastHashUpdateBlock;

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

    // This contract lives behind a proxy, so the constructor parameters will go unused.
    constructor() CrossDomainEnabled(address(0)) {}

    /******************
     * Initialization *
     ******************/

    /**
     * @param _l1messenger L1 Messenger address being used for cross-chain communications.
     * @param _l2TokenBridge L2 standard bridge address.
     */
    function initialize(address _l1messenger, address _l2TokenBridge) public {
        require(messenger == address(0), "Contract has already been initialized.");
        messenger = _l1messenger;
        l2TokenBridge = _l2TokenBridge;
    }

    /**************
     * Depositing *
     **************/

    /** @dev Modifier requiring sender to be EOA.  This check could be bypassed by a malicious
     *  contract via initcode, but it takes care of the user error we want to avoid.
     */
    modifier onlyEOA() {
        // Used to stop deposits from contracts (avoid accidentally lost tokens)
        require(!Address.isContract(msg.sender), "Account not EOA");
        _;
    }

    /**
     * @dev This function can be called with no data
     * to deposit an amount of native token to the caller's balance on L2.
     * Since the receive function doesn't take data, a conservative
     * default amount is forwarded to L2.
     */
    receive() external payable onlyEOA {
        _initiateNativeTokenDeposit(msg.sender, msg.sender, 1_300_000, bytes(""));
    }

    /**
     * @inheritdoc IL1StandardBridge
     */
    function depositNativeToken(uint32 _l2Gas, bytes calldata _data) external payable onlyEOA {
        _initiateNativeTokenDeposit(msg.sender, msg.sender, _l2Gas, _data);
    }

    /**
     * @inheritdoc IL1StandardBridge
     */
    function depositNativeTokenTo(
        address _to,
        uint32 _l2Gas,
        bytes calldata _data
    ) external payable {
        _initiateNativeTokenDeposit(msg.sender, _to, _l2Gas, _data);
    }

    /**
     * @dev Performs the logic for deposits by storing the nativa token and informing the L2 Gateway of
     * the deposit.
     * @param _from Account to pull the deposit from on L1.
     * @param _to Account to give the deposit to on L2.
     * @param _l2Gas Gas limit required to complete the deposit on L2.
     * @param _data Optional data to forward to L2. This data is provided
     *        solely as a convenience for external contracts. Aside from enforcing a maximum
     *        length, these contracts provide no guarantees about its content.
     */
    function _initiateNativeTokenDeposit(
        address _from,
        address _to,
        uint32 _l2Gas,
        bytes memory _data
    ) internal {
        // Construct calldata for finalizeDeposit call
        bytes memory message = abi.encodeWithSelector(
            IL2ERC20Bridge.finalizeDeposit.selector,
            address(0),
            Lib_PredeployAddresses.L1_NATIVE_TOKEN_L2_ADDRESS,
            _from,
            _to,
            msg.value,
            _data
        );

        // Send calldata into L2
        sendCrossDomainMessage(l2TokenBridge, _l2Gas, message);

        // compute and update deposit hash
        _updateDepositHash(
            address(0),
            Lib_PredeployAddresses.L1_NATIVE_TOKEN_L2_ADDRESS,
            _from,
            _to,
            msg.value
        );

        emit NativeTokenDepositInitiated(_from, _to, msg.value, _data);
    }

    /**
     * @inheritdoc IL1ERC20Bridge
     */
    function depositERC20(
        address _l1Token,
        address _l2Token,
        uint256 _amount,
        uint32 _l2Gas,
        bytes calldata _data
    ) external virtual onlyEOA {
        _initiateERC20Deposit(_l1Token, _l2Token, msg.sender, msg.sender, _amount, _l2Gas, _data);
    }

    /**
     * @inheritdoc IL1ERC20Bridge
     */
    function depositERC20To(
        address _l1Token,
        address _l2Token,
        address _to,
        uint256 _amount,
        uint32 _l2Gas,
        bytes calldata _data
    ) external virtual {
        _initiateERC20Deposit(_l1Token, _l2Token, msg.sender, _to, _amount, _l2Gas, _data);
    }

    /**
     * @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 _l1Token Address of the L1 ERC20 we are depositing
     * @param _l2Token Address of the L1 respective L2 ERC20
     * @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 _l2Gas Gas limit required to complete the deposit on L2.
     * @param _data Optional data to forward to L2. This data is provided
     *        solely as a convenience for external contracts. Aside from enforcing a maximum
     *        length, these contracts provide no guarantees about its content.
     */
    function _initiateERC20Deposit(
        address _l1Token,
        address _l2Token,
        address _from,
        address _to,
        uint256 _amount,
        uint32 _l2Gas,
        bytes calldata _data
    ) internal {
        // When a deposit is initiated on L1, the L1 Bridge transfers the funds to itself for future
        // withdrawals. safeTransferFrom also checks if the contract has code, so this will fail if
        // _from is an EOA or address(0).
        IERC20(_l1Token).safeTransferFrom(_from, address(this), _amount);

        // Construct calldata for _l2Token.finalizeDeposit(_to, _amount)
        bytes memory message = abi.encodeWithSelector(
            IL2ERC20Bridge.finalizeDeposit.selector,
            _l1Token,
            _l2Token,
            _from,
            _to,
            _amount,
            _data
        );

        // Send calldata into L2
        sendCrossDomainMessage(l2TokenBridge, _l2Gas, message);

        deposits[_l1Token][_l2Token] = deposits[_l1Token][_l2Token] + _amount;

        _updateDepositHash(_l1Token, _l2Token, _from, _to, _amount);

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

    function _updateDepositHash(
        address _l1Token,
        address _l2Token,
        address _from,
        address _to,
        uint256 _amount
    ) internal {
        // if block number is different only then update prior
        if (block.number > lastHashUpdateBlock) {
            priorDepositInfoHash = currentDepositInfoHash;
        }
        currentDepositInfoHash = keccak256(
            abi.encode(currentDepositInfoHash, _l1Token, _l2Token, _from, _to, _amount)
        );

        lastHashUpdateBlock = block.number;
    }

    /*************************
     * Cross-chain Functions *
     *************************/

    /**
     * @inheritdoc IL1StandardBridge
     */
    function finalizeNativeTokenWithdrawal(
        address _from,
        address _to,
        uint256 _amount,
        bytes calldata _data
    ) external onlyFromCrossDomainAccount(l2TokenBridge) {
        (bool success, ) = _to.call{ value: _amount }(new bytes(0));
        require(success, "TransferHelper::safeTransferNativeToken: NativeToken transfer failed");

        emit NativeTokenWithdrawalFinalized(_from, _to, _amount, _data);
    }

    /**
     * @inheritdoc IL1ERC20Bridge
     */
    function finalizeERC20Withdrawal(
        address _l1Token,
        address _l2Token,
        address _from,
        address _to,
        uint256 _amount,
        bytes calldata _data
    ) external onlyFromCrossDomainAccount(l2TokenBridge) {
        deposits[_l1Token][_l2Token] = deposits[_l1Token][_l2Token] - _amount;

        // When a withdrawal is finalized on L1, the L1 Bridge transfers the funds to the withdrawer
        IERC20(_l1Token).safeTransfer(_to, _amount);

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

    /**************************************
     * Temporary - Migrating Native Token *
     **************************************/

    /**
     * @dev Adds native token balance to the account. This is meant to allow for native token
     * to be migrated from an old gateway to a new gateway.
     * NOTE: This is left for one upgrade only so we are able to receive the migrated
     * native token from the old contract
     */
    function donateNativeToken() external payable {}
}

File 9 of 18 : ICrossDomainMessenger.sol
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.9.0;

/**
 * @title ICrossDomainMessenger
 */
interface ICrossDomainMessenger {
    /**********
     * Events *
     **********/

    event SentMessage(
        address indexed target,
        address sender,
        bytes message,
        uint256 messageNonce,
        uint256 gasLimit
    );
    event RelayedMessage(bytes32 indexed msgHash);
    event FailedRelayedMessage(bytes32 indexed msgHash);

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

    function xDomainMessageSender() external view returns (address);

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

    /**
     * Sends a cross domain message to the target messenger.
     * @param _target Target contract address.
     * @param _message Message to send to the target.
     * @param _gasLimit Gas limit for the provided message.
     */
    function sendMessage(
        address _target,
        bytes calldata _message,
        uint32 _gasLimit
    ) external;
}

File 10 of 18 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

File 11 of 18 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 12 of 18 : Initializable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     */
    bool private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Modifier to protect an initializer function from being invoked twice.
     */
    modifier initializer() {
        require(_initializing || !_initialized, "Initializable: contract is already initialized");

        bool isTopLevelCall = !_initializing;
        if (isTopLevelCall) {
            _initializing = true;
            _initialized = true;
        }

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }
}

File 13 of 18 : ContextUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract ContextUpgradeable is Initializable {
    function __Context_init() internal initializer {
        __Context_init_unchained();
    }

    function __Context_init_unchained() internal initializer {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
    uint256[50] private __gap;
}

File 14 of 18 : IL1StandardBridge.sol
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.9.0;

import "./IL1ERC20Bridge.sol";

/**
 * @title IL1StandardBridge
 */
interface IL1StandardBridge is IL1ERC20Bridge {
    /**********
     * Events *
     **********/
    event NativeTokenDepositInitiated(
        address indexed _from,
        address indexed _to,
        uint256 _amount,
        bytes _data
    );

    event NativeTokenWithdrawalFinalized(
        address indexed _from,
        address indexed _to,
        uint256 _amount,
        bytes _data
    );

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

    /**
     * @dev Deposit an amount of the native token to the caller's balance on L2.
     * @param _l2Gas Gas limit required to complete the deposit on L2.
     * @param _data Optional data to forward to L2. This data is provided
     *        solely as a convenience for external contracts. Aside from enforcing a maximum
     *        length, these contracts provide no guarantees about its content.
     */
    function depositNativeToken(uint32 _l2Gas, bytes calldata _data) external payable;

    /**
     * @dev Deposit an amount of native token to a recipient's balance on L2.
     * @param _to L2 address to credit the withdrawal to.
     * @param _l2Gas Gas limit required to complete the deposit on L2.
     * @param _data Optional data to forward to L2. This data is provided
     *        solely as a convenience for external contracts. Aside from enforcing a maximum
     *        length, these contracts provide no guarantees about its content.
     */
    function depositNativeTokenTo(
        address _to,
        uint32 _l2Gas,
        bytes calldata _data
    ) external payable;

    /*************************
     * Cross-chain Functions *
     *************************/

    /**
     * @dev Complete a withdrawal from L2 to L1, and credit funds to the recipient's balance of the
     * L1 ETH token. Since only the xDomainMessenger can call this function, it will never be called
     * before the withdrawal is finalized.
     * @param _from L2 address initiating the transfer.
     * @param _to L1 address to credit the withdrawal to.
     * @param _amount Amount of the ERC20 to deposit.
     * @param _data Optional data to forward to L2. This data is provided
     *        solely as a convenience for external contracts. Aside from enforcing a maximum
     *        length, these contracts provide no guarantees about its content.
     */
    function finalizeNativeTokenWithdrawal(
        address _from,
        address _to,
        uint256 _amount,
        bytes calldata _data
    ) external;
}

File 15 of 18 : IL1ERC20Bridge.sol
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.9.0;

/**
 * @title IL1ERC20Bridge
 */
interface IL1ERC20Bridge {
    /**********
     * Events *
     **********/

    event ERC20DepositInitiated(
        address indexed _l1Token,
        address indexed _l2Token,
        address indexed _from,
        address _to,
        uint256 _amount,
        bytes _data
    );

    event ERC20WithdrawalFinalized(
        address indexed _l1Token,
        address indexed _l2Token,
        address indexed _from,
        address _to,
        uint256 _amount,
        bytes _data
    );

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

    /**
     * @dev get the address of the corresponding L2 bridge contract.
     * @return Address of the corresponding L2 bridge contract.
     */
    function l2TokenBridge() external returns (address);

    /**
     * @dev deposit an amount of the ERC20 to the caller's balance on L2.
     * @param _l1Token Address of the L1 ERC20 we are depositing
     * @param _l2Token Address of the L1 respective L2 ERC20
     * @param _amount Amount of the ERC20 to deposit
     * @param _l2Gas Gas limit required to complete the deposit on L2.
     * @param _data Optional data to forward to L2. This data is provided
     *        solely as a convenience for external contracts. Aside from enforcing a maximum
     *        length, these contracts provide no guarantees about its content.
     */
    function depositERC20(
        address _l1Token,
        address _l2Token,
        uint256 _amount,
        uint32 _l2Gas,
        bytes calldata _data
    ) external;

    /**
     * @dev deposit an amount of ERC20 to a recipient's balance on L2.
     * @param _l1Token Address of the L1 ERC20 we are depositing
     * @param _l2Token Address of the L1 respective L2 ERC20
     * @param _to L2 address to credit the withdrawal to.
     * @param _amount Amount of the ERC20 to deposit.
     * @param _l2Gas Gas limit required to complete the deposit on L2.
     * @param _data Optional data to forward to L2. This data is provided
     *        solely as a convenience for external contracts. Aside from enforcing a maximum
     *        length, these contracts provide no guarantees about its content.
     */
    function depositERC20To(
        address _l1Token,
        address _l2Token,
        address _to,
        uint256 _amount,
        uint32 _l2Gas,
        bytes calldata _data
    ) external;

    /*************************
     * 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 _l1Token Address of L1 token to finalizeWithdrawal for.
     * @param _l2Token Address of L2 token where withdrawal was initiated.
     * @param _from L2 address initiating the transfer.
     * @param _to L1 address to credit the withdrawal to.
     * @param _amount Amount of the ERC20 to deposit.
     * @param _data Data provided by the sender on L2. This data is provided
     *   solely as a convenience for external contracts. Aside from enforcing a maximum
     *   length, these contracts provide no guarantees about its content.
     */
    function finalizeERC20Withdrawal(
        address _l1Token,
        address _l2Token,
        address _from,
        address _to,
        uint256 _amount,
        bytes calldata _data
    ) external;
}

File 16 of 18 : IL2ERC20Bridge.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

/**
 * @title IL2ERC20Bridge
 */
interface IL2ERC20Bridge {
    /**********
     * Events *
     **********/

    event WithdrawalInitiated(
        address indexed _l1Token,
        address indexed _l2Token,
        address indexed _from,
        address _to,
        uint256 _amount,
        bytes _data
    );

    event DepositFinalized(
        address indexed _l1Token,
        address indexed _l2Token,
        address indexed _from,
        address _to,
        uint256 _amount,
        bytes _data
    );

    event DepositFailed(
        address indexed _l1Token,
        address indexed _l2Token,
        address indexed _from,
        address _to,
        uint256 _amount,
        bytes _data
    );

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

    /**
     * @dev get the address of the corresponding L1 bridge contract.
     * @return Address of the corresponding L1 bridge contract.
     */
    function l1TokenBridge() external returns (address);

    /**
     * @dev initiate a withdraw of some tokens to the caller's account on L1
     * @param _l2Token Address of L2 token where withdrawal was initiated.
     * @param _amount Amount of the token to withdraw.
     * param _l1Gas Unused, but included for potential forward compatibility considerations.
     * @param _data Optional data to forward to L1. This data is provided
     *        solely as a convenience for external contracts. Aside from enforcing a maximum
     *        length, these contracts provide no guarantees about its content.
     */
    function withdraw(
        address _l2Token,
        uint256 _amount,
        uint32 _l1Gas,
        bytes calldata _data
    ) external;

    /**
     * @dev initiate a withdraw of some token to a recipient's account on L1.
     * @param _l2Token Address of L2 token where withdrawal is initiated.
     * @param _to L1 adress to credit the withdrawal to.
     * @param _amount Amount of the token to withdraw.
     * param _l1Gas Unused, but included for potential forward compatibility considerations.
     * @param _data Optional data to forward to L1. This data is provided
     *        solely as a convenience for external contracts. Aside from enforcing a maximum
     *        length, these contracts provide no guarantees about its content.
     */
    function withdrawTo(
        address _l2Token,
        address _to,
        uint256 _amount,
        uint32 _l1Gas,
        bytes calldata _data
    ) external;

    /*************************
     * Cross-chain Functions *
     *************************/

    /**
     * @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
     * L1StandardTokenBridge.
     * @param _l1Token Address for the l1 token this is called with
     * @param _l2Token Address for the l2 token this is called with
     * @param _from Account to pull the deposit from on L2.
     * @param _to Address to receive the withdrawal at
     * @param _amount Amount of the token to withdraw
     * @param _data Data provider by the sender on L1. This data is provided
     *        solely as a convenience for external contracts. Aside from enforcing a maximum
     *        length, these contracts provide no guarantees about its content.
     */
    function finalizeDeposit(
        address _l1Token,
        address _l2Token,
        address _from,
        address _to,
        uint256 _amount,
        bytes calldata _data
    ) external;
}

File 17 of 18 : CrossDomainEnabled.sol
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.9.0;

/* Interface Imports */
import { ICrossDomainMessenger } from "./ICrossDomainMessenger.sol";

/**
 * @title CrossDomainEnabled
 * @dev Helper contract for contracts performing cross-domain communications
 *
 * Compiler used: defined by inheriting contract
 * Runtime target: defined by inheriting contract
 */
contract CrossDomainEnabled {
    /*************
     * Variables *
     *************/

    // Messenger contract used to send and recieve messages from the other domain.
    address public messenger;

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

    /**
     * @param _messenger Address of the CrossDomainMessenger on the current layer.
     */
    constructor(address _messenger) {
        messenger = _messenger;
    }

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

    /**
     * Enforces that the modified function is only callable by a specific cross-domain account.
     * @param _sourceDomainAccount The only account on the originating domain which is
     *  authenticated to call this function.
     */
    modifier onlyFromCrossDomainAccount(address _sourceDomainAccount) {
        require(
            msg.sender == address(getCrossDomainMessenger()),
            "OVM_XCHAIN: messenger contract unauthenticated"
        );

        require(
            getCrossDomainMessenger().xDomainMessageSender() == _sourceDomainAccount,
            "OVM_XCHAIN: wrong sender of cross-domain message"
        );

        _;
    }

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

    /**
     * Gets the messenger, usually from storage. This function is exposed in case a child contract
     * needs to override.
     * @return The address of the cross-domain messenger contract which should be used.
     */
    function getCrossDomainMessenger() internal virtual returns (ICrossDomainMessenger) {
        return ICrossDomainMessenger(messenger);
    }

    /**q
     * Sends a message to an account on another domain
     * @param _crossDomainTarget The intended recipient on the destination domain
     * @param _message The data to send to the target (usually calldata to a function with
     *  `onlyFromCrossDomainAccount()`)
     * @param _gasLimit The gasLimit for the receipt of the message on the target domain.
     */
    function sendCrossDomainMessage(
        address _crossDomainTarget,
        uint32 _gasLimit,
        bytes memory _message
    ) internal {
        getCrossDomainMessenger().sendMessage(_crossDomainTarget, _message, _gasLimit);
    }
}

File 18 of 18 : Lib_PredeployAddresses.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

/**
 * @title Lib_PredeployAddresses
 */
library Lib_PredeployAddresses {
    // solhint-disable max-line-length
    address internal constant L2_TO_L1_MESSAGE_PASSER = 0x4200000000000000000000000000000000000000;
    address internal constant L1_MESSAGE_SENDER = 0x4200000000000000000000000000000000000001;
    address internal constant DEPLOYER_WHITELIST = 0x4200000000000000000000000000000000000002;

    // BOBA is the L2 native token
    address payable internal constant L2_BOBA = payable(0x4200000000000000000000000000000000000006);
    // L1 native token is a ERC20 token on L2
    address internal constant L1_NATIVE_TOKEN_L2_ADDRESS =
        0x4200000000000000000000000000000000000023;

    // solhint-disable-next-line max-line-length
    address internal constant L2_CROSS_DOMAIN_MESSENGER =
        0x4200000000000000000000000000000000000007;
    address internal constant LIB_ADDRESS_MANAGER = 0x4200000000000000000000000000000000000008;
    address internal constant PROXY_EOA = 0x4200000000000000000000000000000000000009;
    address internal constant L2_STANDARD_BRIDGE = 0x4200000000000000000000000000000000000010;
    address internal constant SEQUENCER_FEE_WALLET = 0x4200000000000000000000000000000000000011;
    address internal constant L2_STANDARD_TOKEN_FACTORY =
        0x4200000000000000000000000000000000000012;
    address internal constant L1_BLOCK_NUMBER = 0x4200000000000000000000000000000000000013;
    address internal constant OVM_GAS_PRICE_ORACLE = 0x420000000000000000000000000000000000000F;
    address internal constant PROXY__BOBA_TURING_PREPAY =
        0x4200000000000000000000000000000000000020;
    address internal constant BOBA_TURING_PREPAY = 0x4200000000000000000000000000000000000021;
    address internal constant BOBA_TURING_HELPER = 0x4200000000000000000000000000000000000022;
    address internal constant PROXY__BOBA_GAS_PRICE_ORACLE =
        0x4200000000000000000000000000000000000024;
    address internal constant BOBA_GAS_PRICE_ORACLE = 0x4200000000000000000000000000000000000025;
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "metadata": {
    "bytecodeHash": "none",
    "useLiteralContent": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"}],"name":"AddLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"}],"name":"ClientDepositL1","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"l2TokenAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"indexed":false,"internalType":"struct L1LiquidityPool.ClientPayToken[]","name":"_tokens","type":"tuple[]"}],"name":"ClientDepositL1Batch","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"userRewardFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ownerRewardFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalFee","type":"uint256"},{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"}],"name":"ClientPayL1","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"userRewardFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ownerRewardFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalFee","type":"uint256"},{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"}],"name":"ClientPayL1Settlement","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"}],"name":"OwnerRecoverFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"}],"name":"RebalanceLP","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"}],"name":"WithdrawLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"}],"name":"WithdrawReward","type":"event"},{"inputs":[],"name":"L1StandardBridgeAddress","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"L2LiquidityPoolAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SAFE_GAS_STIPEND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SETTLEMENT_L2_GAS","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_tokenAddress","type":"address"}],"name":"addLiquidity","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_tokenAddress","type":"address"}],"name":"clientDepositL1","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"l1TokenAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct L1LiquidityPool.ClientDepositToken[]","name":"_tokens","type":"tuple[]"}],"name":"clientDepositL1Batch","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address payable","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_tokenAddress","type":"address"}],"name":"clientPayL1","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_tokenAddress","type":"address"}],"name":"clientPayL1Settlement","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_userRewardMinFeeRate","type":"uint256"},{"internalType":"uint256","name":"_userRewardMaxFeeRate","type":"uint256"},{"internalType":"uint256","name":"_ownerRewardFeeRate","type":"uint256"}],"name":"configureFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_l2GasFee","type":"uint32"},{"internalType":"uint256","name":"_safeGas","type":"uint256"}],"name":"configureGas","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"currentDepositInfoHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_l1TokenAddress","type":"address"}],"name":"getUserRewardFeeRate","outputs":[{"internalType":"uint256","name":"userRewardFeeRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_l1CrossDomainMessenger","type":"address"},{"internalType":"address","name":"_l1CrossDomainMessengerFast","type":"address"},{"internalType":"address","name":"_L2LiquidityPoolAddress","type":"address"},{"internalType":"address payable","name":"_L1StandardBridgeAddress","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"l1CrossDomainMessenger","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastHashUpdateBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"address","name":"_to","type":"address"}],"name":"ownerRecoverFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"ownerRewardFeeRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"poolInfo","outputs":[{"internalType":"address","name":"l1TokenAddress","type":"address"},{"internalType":"address","name":"l2TokenAddress","type":"address"},{"internalType":"uint256","name":"userDepositAmount","type":"uint256"},{"internalType":"uint256","name":"lastAccUserReward","type":"uint256"},{"internalType":"uint256","name":"accUserReward","type":"uint256"},{"internalType":"uint256","name":"accUserRewardPerShare","type":"uint256"},{"internalType":"uint256","name":"accOwnerReward","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priorDepositInfoHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_tokenAddress","type":"address"}],"name":"rebalanceLP","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_l1TokenAddress","type":"address"},{"internalType":"address","name":"_l2TokenAddress","type":"address"}],"name":"registerPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"relayerMessenger","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"senderMessenger","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"}],"name":"updateUserRewardPerShare","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"userInfo","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"rewardDebt","type":"uint256"},{"internalType":"uint256","name":"pendingReward","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"userRewardMaxFeeRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"userRewardMinFeeRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"address payable","name":"_to","type":"address"}],"name":"withdrawLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"address","name":"_to","type":"address"}],"name":"withdrawReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

608060405234801561001057600080fd5b50600080546001600160a01b03199081169091556001805490911690556145fc8061003c6000396000f3fe6080604052600436106102385760003560e01c806381e6bdac11610138578063c95f9d0e116100b0578063f2fde38b1161007f578063f72e5aa011610064578063f72e5aa01461073d578063f8c8765e1461075d578063f8fe09a31461077d57600080fd5b8063f2fde38b1461070a578063f64b5f441461072a57600080fd5b8063c95f9d0e14610685578063cf26fb1b14610698578063d04a966a146106b8578063ecb12db0146106ea57600080fd5b80639a7b5f1111610107578063a5ab6190116100ec578063a5ab61901461062f578063a711986914610645578063c58827ea1461066557600080fd5b80639a7b5f111461056b578063a44c80e31461061c57600080fd5b806381e6bdac146104f65780638456cb59146105165780638cac78931461052b5780638da5cb5b1461054b57600080fd5b8063513f3132116101cb57806368be42ca1161019a578063744a5aa21161017f578063744a5aa2146104aa57806377b594a2146104c0578063818c021e146104d657600080fd5b806368be42ca1461046a5780637286e5e51461048a57600080fd5b8063513f3132146103fb57806353174cc11461041157806359326e6f146104315780635c975abb1461044757600080fd5b80633f4ba83a116102075780633f4ba83a1461036e5780633f89e9521461038357806349561dc4146103a3578063508e01c4146103c357600080fd5b80630f208beb146102a357806312f54c1a1461030a578063358fc07e1461032a578063369fd1021461034e57600080fd5b3661029e5760a1546001600160a01b0316331461029c5760405162461bcd60e51b815260206004820152601160248201527f43616e277420726563656976652045544800000000000000000000000000000060448201526064015b60405180910390fd5b005b600080fd5b3480156102af57600080fd5b506102ea6102be366004614017565b609960209081526000928352604080842090915290825290208054600182015460029092015490919083565b604080519384526020840192909252908201526060015b60405180910390f35b34801561031657600080fd5b5061029c610325366004614050565b610793565b34801561033657600080fd5b50610340609d5481565b604051908152602001610301565b34801561035a57600080fd5b5061029c61036936600461406d565b610829565b34801561037a57600080fd5b5061029c610950565b34801561038f57600080fd5b5061029c61039e3660046140a2565b6109c8565b3480156103af57600080fd5b5061029c6103be3660046140c7565b610e8b565b3480156103cf57600080fd5b506000546103e3906001600160a01b031681565b6040516001600160a01b039091168152602001610301565b34801561040757600080fd5b50610340609f5481565b34801561041d57600080fd5b5061029c61042c366004614109565b61114a565b34801561043d57600080fd5b5061034060a55481565b34801561045357600080fd5b5060665460ff166040519015158152602001610301565b34801561047657600080fd5b5061029c6104853660046140c7565b611521565b34801561049657600080fd5b5061029c6104a5366004614017565b6117f7565b3480156104b657600080fd5b5061034060a25481565b3480156104cc57600080fd5b50610340609c5481565b3480156104e257600080fd5b506001546103e3906001600160a01b031681565b34801561050257600080fd5b5061029c6105113660046140c7565b611aa3565b34801561052257600080fd5b5061029c611d0b565b34801561053757600080fd5b5060a1546103e3906001600160a01b031681565b34801561055757600080fd5b50609a546103e3906001600160a01b031681565b34801561057757600080fd5b506105d6610586366004614050565b609860205260009081526040902080546001820154600283015460038401546004850154600586015460068701546007909701546001600160a01b03968716979590961695939492939192909188565b604080516001600160a01b03998a168152989097166020890152958701949094526060860192909252608085015260a084015260c083015260e082015261010001610301565b61029c61062a366004614140565b611d81565b34801561063b57600080fd5b5061034060a45481565b34801561065157600080fd5b5060a0546103e3906001600160a01b031681565b34801561067157600080fd5b5061029c6106803660046141b5565b6121a6565b61029c6106933660046140a2565b6122f6565b3480156106a457600080fd5b5061029c6106b3366004614109565b612674565b3480156106c457600080fd5b50609e546106d59063ffffffff1681565b60405163ffffffff9091168152602001610301565b3480156106f657600080fd5b50610340610705366004614050565b612bcf565b34801561071657600080fd5b5061029c610725366004614050565b612d7c565b61029c6107383660046140a2565b612ed1565b34801561074957600080fd5b50609b546103e3906001600160a01b031681565b34801561076957600080fd5b5061029c6107783660046141e1565b6131c6565b34801561078957600080fd5b5061034060a35481565b6001600160a01b03811660009081526098602052604090206004810154600382015410156108255760006107d8826003015483600401546134ff90919063ffffffff16565b9050816002015460001461081957600282015461081390610808906108028464e8d4a51000613512565b9061351e565b60058401549061352a565b60058301555b50600481015460038201555b5050565b609a546001600160a01b031633148061084b5750609a546001600160a01b0316155b6108975760405162461bcd60e51b815260206004820152601760248201527f43616c6c6572206973206e6f7420746865206f776e65720000000000000000006044820152606401610293565b609b546001600160a01b03166109155760405162461bcd60e51b815260206004820152602560248201527f436f6e747261637420686173206e6f7420796574206265656e20696e6974696160448201527f6c697a65640000000000000000000000000000000000000000000000000000006064820152608401610293565b609e80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff9390931692909217909155609f55565b609a546001600160a01b03163314806109725750609a546001600160a01b0316155b6109be5760405162461bcd60e51b815260206004820152601760248201527f43616c6c6572206973206e6f7420746865206f776e65720000000000000000006044820152606401610293565b6109c6613536565b565b609a546001600160a01b03163314806109ea5750609a546001600160a01b0316155b610a365760405162461bcd60e51b815260206004820152601760248201527f43616c6c6572206973206e6f7420746865206f776e65720000000000000000006044820152606401610293565b60665460ff1615610a895760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610293565b81610ad65760405162461bcd60e51b815260206004820152601060248201527f416d6f756e7420496e636f7272656374000000000000000000000000000000006044820152606401610293565b6001600160a01b038082166000908152609860205260409020609b54909116610b415760405162461bcd60e51b815260206004820181905260248201527f4c32204c697175696469747920506f6f6c204e6f7420526567697374657265646044820152606401610293565b60018101546001600160a01b0316610b9b5760405162461bcd60e51b815260206004820152601c60248201527f546f6b656e2041646472657373204e6f742052656769737465726564000000006044820152606401610293565b604080518481526001600160a01b03841660208201527f40637a7e139eeb28b936b8decebe78604164b2ade81ce7f4c70deb132e7614c2910160405180910390a16001600160a01b038216610cd95747831115610c3a5760405162461bcd60e51b815260206004820152601660248201527f4661696c656420746f20526562616c616e6365204c50000000000000000000006044820152606401610293565b60a154609b54609e546040517f50cc72d20000000000000000000000000000000000000000000000000000000081526001600160a01b03928316600482015263ffffffff909116602482015260606044820152600060648201529116906350cc72d29085906084016000604051808303818588803b158015610cbb57600080fd5b505af1158015610ccf573d6000803e3d6000fd5b5050505050505050565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038316906370a082319060240160206040518083038186803b158015610d3157600080fd5b505afa158015610d45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d69919061423d565b831115610db85760405162461bcd60e51b815260206004820152601660248201527f4661696c656420746f20526562616c616e6365204c50000000000000000000006044820152606401610293565b60a154610dd2906001600160a01b038481169116856135f0565b60a1546001820154609b54609e546040517f838b25200000000000000000000000000000000000000000000000000000000081526001600160a01b038781166004830152938416602482015291831660448301526064820187905263ffffffff16608482015260c060a4820152600060c482015291169063838b25209060e4015b600060405180830381600087803b158015610e6d57600080fd5b505af1158015610e81573d6000803e3d6000fd5b505050505b505050565b60665460ff1615610ede5760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610293565b6001600160a01b0380831660009081526098602090815260408083206099835281842033855290925290912060018201549192909116610f605760405162461bcd60e51b815260206004820152601c60248201527f546f6b656e2041646472657373204e6f742052656769737465726564000000006044820152606401610293565b6000610fa2610f978360010154610f9164e8d4a510006108028860050154886000015461351290919063ffffffff16565b906134ff565b60028401549061352a565b905085811015610ff45760405162461bcd60e51b815260206004820152601560248201527f576974686472617720526577617264204572726f7200000000000000000000006044820152606401610293565b610ffe81876134ff565b60028301556005830154825461101e9164e8d4a510009161080291613512565b6001830155604080513381526001600160a01b0386811660208301528183018990528716606082015290517f3cb7cb475a33eda02ee6e719b6c2fc0c899157cfc6f098daf545354dbbce41ec9181900360800190a16001600160a01b0385161561109b576110966001600160a01b0386168588613761565b611142565b6000846001600160a01b0316609f5488604051600060405180830381858888f193505050503d80600081146110ec576040519150601f19603f3d011682016040523d82523d6000602084013e6110f1565b606091505b5050905080610e815760405162461bcd60e51b815260206004820152601460248201527f4661696c656420746f2073656e642045746865720000000000000000000000006044820152606401610293565b505050505050565b609b546001600160a01b03166111686001546001600160a01b031690565b6001600160a01b0316336001600160a01b0316146111ee5760405162461bcd60e51b815260206004820152602a60248201527f58434841494e3a206d657373656e67657220636f6e747261637420756e61757460448201527f68656e74696361746564000000000000000000000000000000000000000000006064820152608401610293565b806001600160a01b031661120a6001546001600160a01b031690565b6001600160a01b0316636e296e456040518163ffffffff1660e01b815260040160206040518083038186803b15801561124257600080fd5b505afa158015611256573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061127a9190614256565b6001600160a01b0316146112f65760405162461bcd60e51b815260206004820152602c60248201527f58434841494e3a2077726f6e672073656e646572206f662063726f73732d646f60448201527f6d61696e206d65737361676500000000000000000000000000000000000000006064820152608401610293565b60665460ff16156113495760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610293565b6001600160a01b03821660009081526098602052604081209061136b84612bcf565b9050600061137f6103e86108028885613512565b9050600061139e6103e8610802609d548a61351290919063ffffffff16565b905060006113ac838361352a565b905060006113ba89836134ff565b60048701549091506113cc908561352a565b600487015560068601546113e0908461352a565b6006870155604080516001600160a01b038c811682526020820184905281830187905260608201869052608082018590528a1660a082015290517fe42a885e97ef72aed483b57141e41f9d64499b43411f58b60a0e07fcdb6e824a9181900360c00190a16001600160a01b0388161561146c576114676001600160a01b0389168b83613761565b611515565b60008a6001600160a01b0316609f5483604051600060405180830381858888f193505050503d80600081146114bd576040519150601f19603f3d011682016040523d82523d6000602084013e6114c2565b606091505b50509050806115135760405162461bcd60e51b815260206004820152601460248201527f4661696c656420746f2073656e642045746865720000000000000000000000006044820152606401610293565b505b50505050505050505050565b60665460ff16156115745760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610293565b6001600160a01b03808316600090815260986020908152604080832060998352818420338552909252909120600182015491929091166115f65760405162461bcd60e51b815260206004820152601c60248201527f546f6b656e2041646472657373204e6f742052656769737465726564000000006044820152606401610293565b80548511156116475760405162461bcd60e51b815260206004820152600e60248201527f5769746864726177204572726f720000000000000000000000000000000000006044820152606401610293565b61165084610793565b61168a61167f8260010154610f9164e8d4a510006108028760050154876000015461351290919063ffffffff16565b60028301549061352a565b6002820155805461169b90866134ff565b80825560058301546116b89164e8d4a51000916108029190613512565b600182015560028201546116cc90866134ff565b6002830155604080513381526001600160a01b0385811660208301528183018890528616606082015290517ffa2e8fcf14fd6ea11b6ebe7caf7de210198b8fe1eaf0e06d19f8d87c73860c469181900360800190a16001600160a01b03841615611749576117446001600160a01b0385168487613761565b6117f0565b6000836001600160a01b0316609f5487604051600060405180830381858888f193505050503d806000811461179a576040519150601f19603f3d011682016040523d82523d6000602084013e61179f565b606091505b50509050806111425760405162461bcd60e51b815260206004820152601260248201527f4661696c656420746f2073656e642045544800000000000000000000000000006044820152606401610293565b5050505050565b609a546001600160a01b03163314806118195750609a546001600160a01b0316155b6118655760405162461bcd60e51b815260206004820152601760248201527f43616c6c6572206973206e6f7420746865206f776e65720000000000000000006044820152606401610293565b806001600160a01b0316826001600160a01b031614156118ed5760405162461bcd60e51b815260206004820152602860248201527f6c3120616e64206c3220746f6b656e206164647265737365732063616e6e6f7460448201527f2062652073616d650000000000000000000000000000000000000000000000006064820152608401610293565b6001600160a01b0381166119695760405162461bcd60e51b815260206004820152602760248201527f6c3220746f6b656e20616464726573732063616e6e6f74206265207a65726f2060448201527f61646472657373000000000000000000000000000000000000000000000000006064820152608401610293565b6001600160a01b0380831660009081526098602052604090206001810154909116156119d75760405162461bcd60e51b815260206004820181905260248201527f546f6b656e204164647265737320416c726561647920526567697374657265646044820152606401610293565b5060408051610100810182526001600160a01b0393841680825292841660208083019182526000838501818152606085018281526080860183815260a0870184815260c088018581524260e08a019081529a865260989096529790932095518654908a167fffffffffffffffffffffffff0000000000000000000000000000000000000000918216178755945160018701805491909a16951694909417909755955160028401559051600383015593516004820155905160058201559151600683015551600790910155565b609a546001600160a01b0316331480611ac55750609a546001600160a01b0316155b611b115760405162461bcd60e51b815260206004820152601760248201527f43616c6c6572206973206e6f7420746865206f776e65720000000000000000006044820152606401610293565b6001600160a01b0380831660009081526098602052604090206001810154909116611b7e5760405162461bcd60e51b815260206004820152601c60248201527f546f6b656e2041646472657373204e6f742052656769737465726564000000006044820152606401610293565b8381600601541015611bd25760405162461bcd60e51b815260206004820152601b60248201527f4f776e657220526577617264205769746864726177204572726f7200000000006044820152606401610293565b6006810154611be190856134ff565b6006820155604080513381526001600160a01b0384811660208301528183018790528516606082015290517f3cb71b9a1fb601579f96812b9f86ab5e914fc3e54c98d5f84d95581b2b9884f39181900360800190a16001600160a01b03831615611c5e57611c596001600160a01b0384168386613761565b611d05565b6000826001600160a01b0316609f5486604051600060405180830381858888f193505050503d8060008114611caf576040519150601f19603f3d011682016040523d82523d6000602084013e611cb4565b606091505b50509050806117f05760405162461bcd60e51b815260206004820152601460248201527f4661696c656420746f2073656e642045746865720000000000000000000000006044820152606401610293565b50505050565b609a546001600160a01b0316331480611d2d5750609a546001600160a01b0316155b611d795760405162461bcd60e51b815260206004820152601760248201527f43616c6c6572206973206e6f7420746865206f776e65720000000000000000006044820152606401610293565b6109c66137aa565b60665460ff1615611dd45760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610293565b60058110611e245760405162461bcd60e51b815260206004820152600f60248201527f546f6f204d616e7920546f6b656e7300000000000000000000000000000000006044820152606401610293565b6000808267ffffffffffffffff811115611e4057611e40614273565b604051908082528060200260200182016040528015611ea957816020015b60408051606081018252600080825260208083018290529282015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909201910181611e5e5790505b50905060005b83811015612050576000858583818110611ecb57611ecb6142a2565b905060400201803603810190611ee191906142d1565b9050806020015160001415611f385760405162461bcd60e51b815260206004820152600e60248201527f496e76616c696420416d6f756e740000000000000000000000000000000000006044820152606401610293565b80516001600160a01b0390811660009081526098602052604090206001810154909116611fa75760405162461bcd60e51b815260206004820152600d60248201527f496e7661696c6420546f6b656e000000000000000000000000000000000000006044820152606401610293565b81516001600160a01b031615611fdc5760208201518251611fd7916001600160a01b039091169033903090613850565b611fee565b6020820151611feb908661437f565b94505b6040805160608101825233815260018301546001600160a01b0316602080830191909152840151918101919091528451859085908110612030576120306142a2565b60200260200101819052505050808061204890614397565b915050611eaf565b503482146120a05760405162461bcd60e51b815260206004820152601260248201527f496e76616c69642045544820416d6f756e7400000000000000000000000000006044820152606401610293565b600063b4eeb98860e01b826040516024016120bb91906143d0565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152609b54609e54919250612168916001600160a01b03909116906121629063ffffffff166002614435565b836138a1565b7f1a79d691d0cd9e7c4f70b52af899cfc71146947ba0fa6fc236e2649c07a8e9128260405161219791906143d0565b60405180910390a15050505050565b609a546001600160a01b03163314806121c85750609a546001600160a01b0316155b6122145760405162461bcd60e51b815260206004820152601760248201527f43616c6c6572206973206e6f7420746865206f776e65720000000000000000006044820152606401610293565b609b546001600160a01b03166122925760405162461bcd60e51b815260206004820152602560248201527f436f6e747261637420686173206e6f7420796574206265656e20696e6974696160448201527f6c697a65640000000000000000000000000000000000000000000000000000006064820152608401610293565b818311156122e25760405162461bcd60e51b815260206004820152601760248201527f496e76616c7564207573657220726577617264206665650000000000000000006044820152606401610293565b610e86838383609c9290925560a255609d55565b6002805414156123485760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610293565b6002805560665460ff161561239f5760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610293565b341515806123b557506001600160a01b03811615155b6124275760405162461bcd60e51b815260206004820152603260248201527f45697468657220416d6f756e7420496e636f7272656374206f7220546f6b656e60448201527f204164647265737320496e636f727265637400000000000000000000000000006064820152608401610293565b341580159061243e57506001600160a01b03811615155b156124b15760405162461bcd60e51b815260206004820152603260248201527f45697468657220416d6f756e7420496e636f7272656374206f7220546f6b656e60448201527f204164647265737320496e636f727265637400000000000000000000000000006064820152608401610293565b34156124be575034905060005b6001600160a01b03808216600090815260986020908152604080832060998352818420338552909252909120600182015491929091166125405760405162461bcd60e51b815260206004820152601c60248201527f546f6b656e2041646472657373204e6f742052656769737465726564000000006044820152606401610293565b61254983610793565b8054156125b55761257f61167f8260010154610f9164e8d4a510006108028760050154876000015461351290919063ffffffff16565b6002820155600582015481546125ab9164e8d4a510009161080291906125a5908961352a565b90613512565b60018201556125db565b6125d564e8d4a5100061080284600501548761351290919063ffffffff16565b60018201555b80546125e7908561352a565b815560028201546125f8908561352a565b600283015560408051338152602081018690526001600160a01b0385168183015290517f5852d1d46e583f7e92c2a572221de0e681d82ef71f489847e056b9445c0147369181900360600190a16001600160a01b03831615612669576126696001600160a01b038416333087613850565b505060016002555050565b609b546001600160a01b03166126926001546001600160a01b031690565b6001600160a01b0316336001600160a01b0316146127185760405162461bcd60e51b815260206004820152602a60248201527f58434841494e3a206d657373656e67657220636f6e747261637420756e61757460448201527f68656e74696361746564000000000000000000000000000000000000000000006064820152608401610293565b806001600160a01b03166127346001546001600160a01b031690565b6001600160a01b0316636e296e456040518163ffffffff1660e01b815260040160206040518083038186803b15801561276c57600080fd5b505afa158015612780573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127a49190614256565b6001600160a01b0316146128205760405162461bcd60e51b815260206004820152602c60248201527f58434841494e3a2077726f6e672073656e646572206f662063726f73732d646f60448201527f6d61696e206d65737361676500000000000000000000000000000000000000006064820152608401610293565b60665460ff16156128735760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610293565b6001600160a01b03821660009081526098602052604081208161289585612bcf565b905060006128a96103e86108028985613512565b905060006128c86103e8610802609d548b61351290919063ffffffff16565b905060006128d6838361352a565b905060006128e48a836134ff565b90506001600160a01b038916156129d6576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038a16906370a082319060240160206040518083038186803b15801561294d57600080fd5b505afa158015612961573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612985919061423d565b8111156129955760019650612ab6565b60048601546129a4908561352a565b600487015560068601546129b8908461352a565b60068701556129d16001600160a01b038a168c83613761565b612ab6565b478111156129e75760019650612ab6565b60048601546129f6908561352a565b60048701556006860154612a0a908461352a565b6006870155609f546040516000916001600160a01b038e1691849084818181858888f193505050503d8060008114612a5e576040519150601f19603f3d011682016040523d82523d6000602084013e612a63565b606091505b5050905080612ab45760405162461bcd60e51b815260206004820152601260248201527f4661696c656420746f2073656e642045544800000000000000000000000000006044820152606401610293565b505b8615612b63576001860154604080516001600160a01b038e81166024830152604482018e90529283166064808301919091528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f650a767b00000000000000000000000000000000000000000000000000000000179052609b54609e549192612b5d9291169063ffffffff16836138a1565b50611513565b604080516001600160a01b038d811682526020820184905281830187905260608201869052608082018590528b1660a082015290517ffe1f8646fcb202e48551fbfd19085116c64b291c60cede7dce3665a57c05e3819181900360c00190a15050505050505050505050565b609b546000906001600160a01b0316612c505760405162461bcd60e51b815260206004820152602560248201527f436f6e747261637420686173206e6f7420796574206265656e20696e6974696160448201527f6c697a65640000000000000000000000000000000000000000000000000000006064820152608401610293565b6001600160a01b038216600081815260986020526040812060028101549092909190612c7d575047612d10565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038616906370a082319060240160206040518083038186803b158015612cd557600080fd5b505afa158015612ce9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d0d919061423d565b90505b80612d2157505060a2549392505050565b60008183609c54612d329190614461565b612d3c919061449e565b905080609c541115612d55575050609c54949350505050565b8060a2541015612d6c57505060a254949350505050565b9350612d7792505050565b919050565b609a546001600160a01b0316331480612d9e5750609a546001600160a01b0316155b612dea5760405162461bcd60e51b815260206004820152601760248201527f43616c6c6572206973206e6f7420746865206f776e65720000000000000000006044820152606401610293565b6001600160a01b038116612e655760405162461bcd60e51b8152602060048201526024808201527f4e6577206f776e65722063616e6e6f7420626520746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152608401610293565b609a80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f04dba622d284ed0014ee4b9a6a68386be1a4c08a4913ae272de89199cc6861639060200160405180910390a150565b60665460ff1615612f245760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610293565b34151580612f3a57506001600160a01b03811615155b612fac5760405162461bcd60e51b815260206004820152603260248201527f45697468657220416d6f756e7420496e636f7272656374206f7220546f6b656e60448201527f204164647265737320496e636f727265637400000000000000000000000000006064820152608401610293565b3415801590612fc357506001600160a01b03811615155b156130365760405162461bcd60e51b815260206004820152603260248201527f45697468657220416d6f756e7420496e636f7272656374206f7220546f6b656e60448201527f204164647265737320496e636f727265637400000000000000000000000000006064820152608401610293565b3415613043575034905060005b6001600160a01b03808216600090815260986020526040902060018101549091166130b05760405162461bcd60e51b815260206004820152601c60248201527f546f6b656e2041646472657373204e6f742052656769737465726564000000006044820152606401610293565b6130bb8233856138ee565b60408051338152602081018590526001600160a01b0384168183015290517f860e25c3d48fb81f0b272550dd125a5a6ab20ee7511dd3e250d04964bd37a7a99181900360600190a16001600160a01b03821615613127576131276001600160a01b038316333086613850565b600181015460408051336024820152604481018690526001600160a01b039283166064808301919091528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fbd2d1cab00000000000000000000000000000000000000000000000000000000179052609b54609e549192611d059291169063ffffffff16836138a1565b609a546001600160a01b03163314806131e85750609a546001600160a01b0316155b6132345760405162461bcd60e51b815260206004820152601760248201527f43616c6c6572206973206e6f7420746865206f776e65720000000000000000006044820152606401610293565b609b546001600160a01b03161561328d5760405162461bcd60e51b815260206004820152601d60248201527f436f6e747261637420686173206265656e20696e697469616c697a65640000006044820152606401610293565b6001547501000000000000000000000000000000000000000000900460ff16806132d2575060015474010000000000000000000000000000000000000000900460ff16155b6133445760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610293565b6001547501000000000000000000000000000000000000000000900460ff161580156133ab57600180547fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff1675010100000000000000000000000000000000000000001790555b6001600160a01b038516158015906133cb57506001600160a01b03841615155b80156133df57506001600160a01b03831615155b61342b5760405162461bcd60e51b815260206004820152601860248201527f7a65726f2061646472657373206e6f7420616c6c6f77656400000000000000006044820152606401610293565b600080546001600160a01b038781167fffffffffffffffffffffffff000000000000000000000000000000000000000092831617835560018054888316908416179055609b805487831690841617905560a18054918616918316919091179055609a8054909116331790556005609c55603260a255609d556134b2620aae606108fc610829565b6134ba613973565b6134c2613ac3565b6134ca613c3a565b80156117f057600180547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff1690555050505050565b600061350b82846144d9565b9392505050565b600061350b8284614461565b600061350b828461449e565b600061350b828461437f565b60665460ff166135885760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610293565b606680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e9060440160206040518083038186803b15801561365557600080fd5b505afa158015613669573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061368d919061423d565b613697919061437f565b6040516001600160a01b038516602482015260448101829052909150611d059085907f095ea7b300000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152613d8e565b6040516001600160a01b038316602482015260448101829052610e869084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064016136df565b60665460ff16156137fd5760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610293565b606680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586135d33390565b6040516001600160a01b0380851660248301528316604482015260648101829052611d059085907f23b872dd00000000000000000000000000000000000000000000000000000000906084016136df565b6000546040517f3dbb202b0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911690633dbb202b90610e5390869085908790600401614566565b60a5544311156138ff5760a45460a3555b60a4546040805160208101929092526001600160a01b0380861691830191909152831660608201526080810182905260a001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012060a45550504360a55550565b6001547501000000000000000000000000000000000000000000900460ff16806139b8575060015474010000000000000000000000000000000000000000900460ff16155b613a2a5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610293565b6001547501000000000000000000000000000000000000000000900460ff16158015613a9157600180547fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff1675010100000000000000000000000000000000000000001790555b8015613ac057600180547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff1690555b50565b6001547501000000000000000000000000000000000000000000900460ff1680613b08575060015474010000000000000000000000000000000000000000900460ff16155b613b7a5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610293565b6001547501000000000000000000000000000000000000000000900460ff16158015613be157600180547fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff1675010100000000000000000000000000000000000000001790555b606680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690558015613ac057600180547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff16905550565b6001547501000000000000000000000000000000000000000000900460ff1680613c7f575060015474010000000000000000000000000000000000000000900460ff16155b613cf15760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610293565b6001547501000000000000000000000000000000000000000000900460ff16158015613d5857600180547fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff1675010100000000000000000000000000000000000000001790555b60016002558015613ac057600180547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff16905550565b6000613de3826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613e739092919063ffffffff16565b805190915015610e865780806020019051810190613e01919061459e565b610e865760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610293565b6060613e828484600085613e8a565b949350505050565b606082471015613f025760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610293565b843b613f505760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610293565b600080866001600160a01b03168587604051613f6c91906145c0565b60006040518083038185875af1925050503d8060008114613fa9576040519150601f19603f3d011682016040523d82523d6000602084013e613fae565b606091505b5091509150613fbe828286613fc9565b979650505050505050565b60608315613fd857508161350b565b825115613fe85782518084602001fd5b8160405162461bcd60e51b815260040161029391906145dc565b6001600160a01b0381168114613ac057600080fd5b6000806040838503121561402a57600080fd5b823561403581614002565b9150602083013561404581614002565b809150509250929050565b60006020828403121561406257600080fd5b813561350b81614002565b6000806040838503121561408057600080fd5b823563ffffffff8116811461409457600080fd5b946020939093013593505050565b600080604083850312156140b557600080fd5b82359150602083013561404581614002565b6000806000606084860312156140dc57600080fd5b8335925060208401356140ee81614002565b915060408401356140fe81614002565b809150509250925092565b60008060006060848603121561411e57600080fd5b833561412981614002565b92506020840135915060408401356140fe81614002565b6000806020838503121561415357600080fd5b823567ffffffffffffffff8082111561416b57600080fd5b818501915085601f83011261417f57600080fd5b81358181111561418e57600080fd5b8660208260061b85010111156141a357600080fd5b60209290920196919550909350505050565b6000806000606084860312156141ca57600080fd5b505081359360208301359350604090920135919050565b600080600080608085870312156141f757600080fd5b843561420281614002565b9350602085013561421281614002565b9250604085013561422281614002565b9150606085013561423281614002565b939692955090935050565b60006020828403121561424f57600080fd5b5051919050565b60006020828403121561426857600080fd5b815161350b81614002565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000604082840312156142e357600080fd5b6040516040810181811067ffffffffffffffff8211171561432d577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604052823561433b81614002565b81526020928301359281019290925250919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000821982111561439257614392614350565b500190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156143c9576143c9614350565b5060010190565b602080825282518282018190526000919060409081850190868401855b8281101561442857815180516001600160a01b03908116865287820151168786015285015185850152606090930192908501906001016143ed565b5091979650505050505050565b600063ffffffff8083168185168183048111821515161561445857614458614350565b02949350505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561449957614499614350565b500290565b6000826144d4577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b6000828210156144eb576144eb614350565b500390565b60005b8381101561450b5781810151838201526020016144f3565b83811115611d055750506000910152565b600081518084526145348160208601602086016144f0565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6001600160a01b0384168152606060208201526000614588606083018561451c565b905063ffffffff83166040830152949350505050565b6000602082840312156145b057600080fd5b8151801515811461350b57600080fd5b600082516145d28184602087016144f0565b9190910192915050565b60208152600061350b602083018461451c56fea164736f6c6343000809000a

Deployed Bytecode

0x6080604052600436106102385760003560e01c806381e6bdac11610138578063c95f9d0e116100b0578063f2fde38b1161007f578063f72e5aa011610064578063f72e5aa01461073d578063f8c8765e1461075d578063f8fe09a31461077d57600080fd5b8063f2fde38b1461070a578063f64b5f441461072a57600080fd5b8063c95f9d0e14610685578063cf26fb1b14610698578063d04a966a146106b8578063ecb12db0146106ea57600080fd5b80639a7b5f1111610107578063a5ab6190116100ec578063a5ab61901461062f578063a711986914610645578063c58827ea1461066557600080fd5b80639a7b5f111461056b578063a44c80e31461061c57600080fd5b806381e6bdac146104f65780638456cb59146105165780638cac78931461052b5780638da5cb5b1461054b57600080fd5b8063513f3132116101cb57806368be42ca1161019a578063744a5aa21161017f578063744a5aa2146104aa57806377b594a2146104c0578063818c021e146104d657600080fd5b806368be42ca1461046a5780637286e5e51461048a57600080fd5b8063513f3132146103fb57806353174cc11461041157806359326e6f146104315780635c975abb1461044757600080fd5b80633f4ba83a116102075780633f4ba83a1461036e5780633f89e9521461038357806349561dc4146103a3578063508e01c4146103c357600080fd5b80630f208beb146102a357806312f54c1a1461030a578063358fc07e1461032a578063369fd1021461034e57600080fd5b3661029e5760a1546001600160a01b0316331461029c5760405162461bcd60e51b815260206004820152601160248201527f43616e277420726563656976652045544800000000000000000000000000000060448201526064015b60405180910390fd5b005b600080fd5b3480156102af57600080fd5b506102ea6102be366004614017565b609960209081526000928352604080842090915290825290208054600182015460029092015490919083565b604080519384526020840192909252908201526060015b60405180910390f35b34801561031657600080fd5b5061029c610325366004614050565b610793565b34801561033657600080fd5b50610340609d5481565b604051908152602001610301565b34801561035a57600080fd5b5061029c61036936600461406d565b610829565b34801561037a57600080fd5b5061029c610950565b34801561038f57600080fd5b5061029c61039e3660046140a2565b6109c8565b3480156103af57600080fd5b5061029c6103be3660046140c7565b610e8b565b3480156103cf57600080fd5b506000546103e3906001600160a01b031681565b6040516001600160a01b039091168152602001610301565b34801561040757600080fd5b50610340609f5481565b34801561041d57600080fd5b5061029c61042c366004614109565b61114a565b34801561043d57600080fd5b5061034060a55481565b34801561045357600080fd5b5060665460ff166040519015158152602001610301565b34801561047657600080fd5b5061029c6104853660046140c7565b611521565b34801561049657600080fd5b5061029c6104a5366004614017565b6117f7565b3480156104b657600080fd5b5061034060a25481565b3480156104cc57600080fd5b50610340609c5481565b3480156104e257600080fd5b506001546103e3906001600160a01b031681565b34801561050257600080fd5b5061029c6105113660046140c7565b611aa3565b34801561052257600080fd5b5061029c611d0b565b34801561053757600080fd5b5060a1546103e3906001600160a01b031681565b34801561055757600080fd5b50609a546103e3906001600160a01b031681565b34801561057757600080fd5b506105d6610586366004614050565b609860205260009081526040902080546001820154600283015460038401546004850154600586015460068701546007909701546001600160a01b03968716979590961695939492939192909188565b604080516001600160a01b03998a168152989097166020890152958701949094526060860192909252608085015260a084015260c083015260e082015261010001610301565b61029c61062a366004614140565b611d81565b34801561063b57600080fd5b5061034060a45481565b34801561065157600080fd5b5060a0546103e3906001600160a01b031681565b34801561067157600080fd5b5061029c6106803660046141b5565b6121a6565b61029c6106933660046140a2565b6122f6565b3480156106a457600080fd5b5061029c6106b3366004614109565b612674565b3480156106c457600080fd5b50609e546106d59063ffffffff1681565b60405163ffffffff9091168152602001610301565b3480156106f657600080fd5b50610340610705366004614050565b612bcf565b34801561071657600080fd5b5061029c610725366004614050565b612d7c565b61029c6107383660046140a2565b612ed1565b34801561074957600080fd5b50609b546103e3906001600160a01b031681565b34801561076957600080fd5b5061029c6107783660046141e1565b6131c6565b34801561078957600080fd5b5061034060a35481565b6001600160a01b03811660009081526098602052604090206004810154600382015410156108255760006107d8826003015483600401546134ff90919063ffffffff16565b9050816002015460001461081957600282015461081390610808906108028464e8d4a51000613512565b9061351e565b60058401549061352a565b60058301555b50600481015460038201555b5050565b609a546001600160a01b031633148061084b5750609a546001600160a01b0316155b6108975760405162461bcd60e51b815260206004820152601760248201527f43616c6c6572206973206e6f7420746865206f776e65720000000000000000006044820152606401610293565b609b546001600160a01b03166109155760405162461bcd60e51b815260206004820152602560248201527f436f6e747261637420686173206e6f7420796574206265656e20696e6974696160448201527f6c697a65640000000000000000000000000000000000000000000000000000006064820152608401610293565b609e80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff9390931692909217909155609f55565b609a546001600160a01b03163314806109725750609a546001600160a01b0316155b6109be5760405162461bcd60e51b815260206004820152601760248201527f43616c6c6572206973206e6f7420746865206f776e65720000000000000000006044820152606401610293565b6109c6613536565b565b609a546001600160a01b03163314806109ea5750609a546001600160a01b0316155b610a365760405162461bcd60e51b815260206004820152601760248201527f43616c6c6572206973206e6f7420746865206f776e65720000000000000000006044820152606401610293565b60665460ff1615610a895760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610293565b81610ad65760405162461bcd60e51b815260206004820152601060248201527f416d6f756e7420496e636f7272656374000000000000000000000000000000006044820152606401610293565b6001600160a01b038082166000908152609860205260409020609b54909116610b415760405162461bcd60e51b815260206004820181905260248201527f4c32204c697175696469747920506f6f6c204e6f7420526567697374657265646044820152606401610293565b60018101546001600160a01b0316610b9b5760405162461bcd60e51b815260206004820152601c60248201527f546f6b656e2041646472657373204e6f742052656769737465726564000000006044820152606401610293565b604080518481526001600160a01b03841660208201527f40637a7e139eeb28b936b8decebe78604164b2ade81ce7f4c70deb132e7614c2910160405180910390a16001600160a01b038216610cd95747831115610c3a5760405162461bcd60e51b815260206004820152601660248201527f4661696c656420746f20526562616c616e6365204c50000000000000000000006044820152606401610293565b60a154609b54609e546040517f50cc72d20000000000000000000000000000000000000000000000000000000081526001600160a01b03928316600482015263ffffffff909116602482015260606044820152600060648201529116906350cc72d29085906084016000604051808303818588803b158015610cbb57600080fd5b505af1158015610ccf573d6000803e3d6000fd5b5050505050505050565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038316906370a082319060240160206040518083038186803b158015610d3157600080fd5b505afa158015610d45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d69919061423d565b831115610db85760405162461bcd60e51b815260206004820152601660248201527f4661696c656420746f20526562616c616e6365204c50000000000000000000006044820152606401610293565b60a154610dd2906001600160a01b038481169116856135f0565b60a1546001820154609b54609e546040517f838b25200000000000000000000000000000000000000000000000000000000081526001600160a01b038781166004830152938416602482015291831660448301526064820187905263ffffffff16608482015260c060a4820152600060c482015291169063838b25209060e4015b600060405180830381600087803b158015610e6d57600080fd5b505af1158015610e81573d6000803e3d6000fd5b505050505b505050565b60665460ff1615610ede5760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610293565b6001600160a01b0380831660009081526098602090815260408083206099835281842033855290925290912060018201549192909116610f605760405162461bcd60e51b815260206004820152601c60248201527f546f6b656e2041646472657373204e6f742052656769737465726564000000006044820152606401610293565b6000610fa2610f978360010154610f9164e8d4a510006108028860050154886000015461351290919063ffffffff16565b906134ff565b60028401549061352a565b905085811015610ff45760405162461bcd60e51b815260206004820152601560248201527f576974686472617720526577617264204572726f7200000000000000000000006044820152606401610293565b610ffe81876134ff565b60028301556005830154825461101e9164e8d4a510009161080291613512565b6001830155604080513381526001600160a01b0386811660208301528183018990528716606082015290517f3cb7cb475a33eda02ee6e719b6c2fc0c899157cfc6f098daf545354dbbce41ec9181900360800190a16001600160a01b0385161561109b576110966001600160a01b0386168588613761565b611142565b6000846001600160a01b0316609f5488604051600060405180830381858888f193505050503d80600081146110ec576040519150601f19603f3d011682016040523d82523d6000602084013e6110f1565b606091505b5050905080610e815760405162461bcd60e51b815260206004820152601460248201527f4661696c656420746f2073656e642045746865720000000000000000000000006044820152606401610293565b505050505050565b609b546001600160a01b03166111686001546001600160a01b031690565b6001600160a01b0316336001600160a01b0316146111ee5760405162461bcd60e51b815260206004820152602a60248201527f58434841494e3a206d657373656e67657220636f6e747261637420756e61757460448201527f68656e74696361746564000000000000000000000000000000000000000000006064820152608401610293565b806001600160a01b031661120a6001546001600160a01b031690565b6001600160a01b0316636e296e456040518163ffffffff1660e01b815260040160206040518083038186803b15801561124257600080fd5b505afa158015611256573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061127a9190614256565b6001600160a01b0316146112f65760405162461bcd60e51b815260206004820152602c60248201527f58434841494e3a2077726f6e672073656e646572206f662063726f73732d646f60448201527f6d61696e206d65737361676500000000000000000000000000000000000000006064820152608401610293565b60665460ff16156113495760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610293565b6001600160a01b03821660009081526098602052604081209061136b84612bcf565b9050600061137f6103e86108028885613512565b9050600061139e6103e8610802609d548a61351290919063ffffffff16565b905060006113ac838361352a565b905060006113ba89836134ff565b60048701549091506113cc908561352a565b600487015560068601546113e0908461352a565b6006870155604080516001600160a01b038c811682526020820184905281830187905260608201869052608082018590528a1660a082015290517fe42a885e97ef72aed483b57141e41f9d64499b43411f58b60a0e07fcdb6e824a9181900360c00190a16001600160a01b0388161561146c576114676001600160a01b0389168b83613761565b611515565b60008a6001600160a01b0316609f5483604051600060405180830381858888f193505050503d80600081146114bd576040519150601f19603f3d011682016040523d82523d6000602084013e6114c2565b606091505b50509050806115135760405162461bcd60e51b815260206004820152601460248201527f4661696c656420746f2073656e642045746865720000000000000000000000006044820152606401610293565b505b50505050505050505050565b60665460ff16156115745760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610293565b6001600160a01b03808316600090815260986020908152604080832060998352818420338552909252909120600182015491929091166115f65760405162461bcd60e51b815260206004820152601c60248201527f546f6b656e2041646472657373204e6f742052656769737465726564000000006044820152606401610293565b80548511156116475760405162461bcd60e51b815260206004820152600e60248201527f5769746864726177204572726f720000000000000000000000000000000000006044820152606401610293565b61165084610793565b61168a61167f8260010154610f9164e8d4a510006108028760050154876000015461351290919063ffffffff16565b60028301549061352a565b6002820155805461169b90866134ff565b80825560058301546116b89164e8d4a51000916108029190613512565b600182015560028201546116cc90866134ff565b6002830155604080513381526001600160a01b0385811660208301528183018890528616606082015290517ffa2e8fcf14fd6ea11b6ebe7caf7de210198b8fe1eaf0e06d19f8d87c73860c469181900360800190a16001600160a01b03841615611749576117446001600160a01b0385168487613761565b6117f0565b6000836001600160a01b0316609f5487604051600060405180830381858888f193505050503d806000811461179a576040519150601f19603f3d011682016040523d82523d6000602084013e61179f565b606091505b50509050806111425760405162461bcd60e51b815260206004820152601260248201527f4661696c656420746f2073656e642045544800000000000000000000000000006044820152606401610293565b5050505050565b609a546001600160a01b03163314806118195750609a546001600160a01b0316155b6118655760405162461bcd60e51b815260206004820152601760248201527f43616c6c6572206973206e6f7420746865206f776e65720000000000000000006044820152606401610293565b806001600160a01b0316826001600160a01b031614156118ed5760405162461bcd60e51b815260206004820152602860248201527f6c3120616e64206c3220746f6b656e206164647265737365732063616e6e6f7460448201527f2062652073616d650000000000000000000000000000000000000000000000006064820152608401610293565b6001600160a01b0381166119695760405162461bcd60e51b815260206004820152602760248201527f6c3220746f6b656e20616464726573732063616e6e6f74206265207a65726f2060448201527f61646472657373000000000000000000000000000000000000000000000000006064820152608401610293565b6001600160a01b0380831660009081526098602052604090206001810154909116156119d75760405162461bcd60e51b815260206004820181905260248201527f546f6b656e204164647265737320416c726561647920526567697374657265646044820152606401610293565b5060408051610100810182526001600160a01b0393841680825292841660208083019182526000838501818152606085018281526080860183815260a0870184815260c088018581524260e08a019081529a865260989096529790932095518654908a167fffffffffffffffffffffffff0000000000000000000000000000000000000000918216178755945160018701805491909a16951694909417909755955160028401559051600383015593516004820155905160058201559151600683015551600790910155565b609a546001600160a01b0316331480611ac55750609a546001600160a01b0316155b611b115760405162461bcd60e51b815260206004820152601760248201527f43616c6c6572206973206e6f7420746865206f776e65720000000000000000006044820152606401610293565b6001600160a01b0380831660009081526098602052604090206001810154909116611b7e5760405162461bcd60e51b815260206004820152601c60248201527f546f6b656e2041646472657373204e6f742052656769737465726564000000006044820152606401610293565b8381600601541015611bd25760405162461bcd60e51b815260206004820152601b60248201527f4f776e657220526577617264205769746864726177204572726f7200000000006044820152606401610293565b6006810154611be190856134ff565b6006820155604080513381526001600160a01b0384811660208301528183018790528516606082015290517f3cb71b9a1fb601579f96812b9f86ab5e914fc3e54c98d5f84d95581b2b9884f39181900360800190a16001600160a01b03831615611c5e57611c596001600160a01b0384168386613761565b611d05565b6000826001600160a01b0316609f5486604051600060405180830381858888f193505050503d8060008114611caf576040519150601f19603f3d011682016040523d82523d6000602084013e611cb4565b606091505b50509050806117f05760405162461bcd60e51b815260206004820152601460248201527f4661696c656420746f2073656e642045746865720000000000000000000000006044820152606401610293565b50505050565b609a546001600160a01b0316331480611d2d5750609a546001600160a01b0316155b611d795760405162461bcd60e51b815260206004820152601760248201527f43616c6c6572206973206e6f7420746865206f776e65720000000000000000006044820152606401610293565b6109c66137aa565b60665460ff1615611dd45760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610293565b60058110611e245760405162461bcd60e51b815260206004820152600f60248201527f546f6f204d616e7920546f6b656e7300000000000000000000000000000000006044820152606401610293565b6000808267ffffffffffffffff811115611e4057611e40614273565b604051908082528060200260200182016040528015611ea957816020015b60408051606081018252600080825260208083018290529282015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909201910181611e5e5790505b50905060005b83811015612050576000858583818110611ecb57611ecb6142a2565b905060400201803603810190611ee191906142d1565b9050806020015160001415611f385760405162461bcd60e51b815260206004820152600e60248201527f496e76616c696420416d6f756e740000000000000000000000000000000000006044820152606401610293565b80516001600160a01b0390811660009081526098602052604090206001810154909116611fa75760405162461bcd60e51b815260206004820152600d60248201527f496e7661696c6420546f6b656e000000000000000000000000000000000000006044820152606401610293565b81516001600160a01b031615611fdc5760208201518251611fd7916001600160a01b039091169033903090613850565b611fee565b6020820151611feb908661437f565b94505b6040805160608101825233815260018301546001600160a01b0316602080830191909152840151918101919091528451859085908110612030576120306142a2565b60200260200101819052505050808061204890614397565b915050611eaf565b503482146120a05760405162461bcd60e51b815260206004820152601260248201527f496e76616c69642045544820416d6f756e7400000000000000000000000000006044820152606401610293565b600063b4eeb98860e01b826040516024016120bb91906143d0565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152609b54609e54919250612168916001600160a01b03909116906121629063ffffffff166002614435565b836138a1565b7f1a79d691d0cd9e7c4f70b52af899cfc71146947ba0fa6fc236e2649c07a8e9128260405161219791906143d0565b60405180910390a15050505050565b609a546001600160a01b03163314806121c85750609a546001600160a01b0316155b6122145760405162461bcd60e51b815260206004820152601760248201527f43616c6c6572206973206e6f7420746865206f776e65720000000000000000006044820152606401610293565b609b546001600160a01b03166122925760405162461bcd60e51b815260206004820152602560248201527f436f6e747261637420686173206e6f7420796574206265656e20696e6974696160448201527f6c697a65640000000000000000000000000000000000000000000000000000006064820152608401610293565b818311156122e25760405162461bcd60e51b815260206004820152601760248201527f496e76616c7564207573657220726577617264206665650000000000000000006044820152606401610293565b610e86838383609c9290925560a255609d55565b6002805414156123485760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610293565b6002805560665460ff161561239f5760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610293565b341515806123b557506001600160a01b03811615155b6124275760405162461bcd60e51b815260206004820152603260248201527f45697468657220416d6f756e7420496e636f7272656374206f7220546f6b656e60448201527f204164647265737320496e636f727265637400000000000000000000000000006064820152608401610293565b341580159061243e57506001600160a01b03811615155b156124b15760405162461bcd60e51b815260206004820152603260248201527f45697468657220416d6f756e7420496e636f7272656374206f7220546f6b656e60448201527f204164647265737320496e636f727265637400000000000000000000000000006064820152608401610293565b34156124be575034905060005b6001600160a01b03808216600090815260986020908152604080832060998352818420338552909252909120600182015491929091166125405760405162461bcd60e51b815260206004820152601c60248201527f546f6b656e2041646472657373204e6f742052656769737465726564000000006044820152606401610293565b61254983610793565b8054156125b55761257f61167f8260010154610f9164e8d4a510006108028760050154876000015461351290919063ffffffff16565b6002820155600582015481546125ab9164e8d4a510009161080291906125a5908961352a565b90613512565b60018201556125db565b6125d564e8d4a5100061080284600501548761351290919063ffffffff16565b60018201555b80546125e7908561352a565b815560028201546125f8908561352a565b600283015560408051338152602081018690526001600160a01b0385168183015290517f5852d1d46e583f7e92c2a572221de0e681d82ef71f489847e056b9445c0147369181900360600190a16001600160a01b03831615612669576126696001600160a01b038416333087613850565b505060016002555050565b609b546001600160a01b03166126926001546001600160a01b031690565b6001600160a01b0316336001600160a01b0316146127185760405162461bcd60e51b815260206004820152602a60248201527f58434841494e3a206d657373656e67657220636f6e747261637420756e61757460448201527f68656e74696361746564000000000000000000000000000000000000000000006064820152608401610293565b806001600160a01b03166127346001546001600160a01b031690565b6001600160a01b0316636e296e456040518163ffffffff1660e01b815260040160206040518083038186803b15801561276c57600080fd5b505afa158015612780573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127a49190614256565b6001600160a01b0316146128205760405162461bcd60e51b815260206004820152602c60248201527f58434841494e3a2077726f6e672073656e646572206f662063726f73732d646f60448201527f6d61696e206d65737361676500000000000000000000000000000000000000006064820152608401610293565b60665460ff16156128735760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610293565b6001600160a01b03821660009081526098602052604081208161289585612bcf565b905060006128a96103e86108028985613512565b905060006128c86103e8610802609d548b61351290919063ffffffff16565b905060006128d6838361352a565b905060006128e48a836134ff565b90506001600160a01b038916156129d6576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038a16906370a082319060240160206040518083038186803b15801561294d57600080fd5b505afa158015612961573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612985919061423d565b8111156129955760019650612ab6565b60048601546129a4908561352a565b600487015560068601546129b8908461352a565b60068701556129d16001600160a01b038a168c83613761565b612ab6565b478111156129e75760019650612ab6565b60048601546129f6908561352a565b60048701556006860154612a0a908461352a565b6006870155609f546040516000916001600160a01b038e1691849084818181858888f193505050503d8060008114612a5e576040519150601f19603f3d011682016040523d82523d6000602084013e612a63565b606091505b5050905080612ab45760405162461bcd60e51b815260206004820152601260248201527f4661696c656420746f2073656e642045544800000000000000000000000000006044820152606401610293565b505b8615612b63576001860154604080516001600160a01b038e81166024830152604482018e90529283166064808301919091528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f650a767b00000000000000000000000000000000000000000000000000000000179052609b54609e549192612b5d9291169063ffffffff16836138a1565b50611513565b604080516001600160a01b038d811682526020820184905281830187905260608201869052608082018590528b1660a082015290517ffe1f8646fcb202e48551fbfd19085116c64b291c60cede7dce3665a57c05e3819181900360c00190a15050505050505050505050565b609b546000906001600160a01b0316612c505760405162461bcd60e51b815260206004820152602560248201527f436f6e747261637420686173206e6f7420796574206265656e20696e6974696160448201527f6c697a65640000000000000000000000000000000000000000000000000000006064820152608401610293565b6001600160a01b038216600081815260986020526040812060028101549092909190612c7d575047612d10565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038616906370a082319060240160206040518083038186803b158015612cd557600080fd5b505afa158015612ce9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d0d919061423d565b90505b80612d2157505060a2549392505050565b60008183609c54612d329190614461565b612d3c919061449e565b905080609c541115612d55575050609c54949350505050565b8060a2541015612d6c57505060a254949350505050565b9350612d7792505050565b919050565b609a546001600160a01b0316331480612d9e5750609a546001600160a01b0316155b612dea5760405162461bcd60e51b815260206004820152601760248201527f43616c6c6572206973206e6f7420746865206f776e65720000000000000000006044820152606401610293565b6001600160a01b038116612e655760405162461bcd60e51b8152602060048201526024808201527f4e6577206f776e65722063616e6e6f7420626520746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152608401610293565b609a80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f04dba622d284ed0014ee4b9a6a68386be1a4c08a4913ae272de89199cc6861639060200160405180910390a150565b60665460ff1615612f245760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610293565b34151580612f3a57506001600160a01b03811615155b612fac5760405162461bcd60e51b815260206004820152603260248201527f45697468657220416d6f756e7420496e636f7272656374206f7220546f6b656e60448201527f204164647265737320496e636f727265637400000000000000000000000000006064820152608401610293565b3415801590612fc357506001600160a01b03811615155b156130365760405162461bcd60e51b815260206004820152603260248201527f45697468657220416d6f756e7420496e636f7272656374206f7220546f6b656e60448201527f204164647265737320496e636f727265637400000000000000000000000000006064820152608401610293565b3415613043575034905060005b6001600160a01b03808216600090815260986020526040902060018101549091166130b05760405162461bcd60e51b815260206004820152601c60248201527f546f6b656e2041646472657373204e6f742052656769737465726564000000006044820152606401610293565b6130bb8233856138ee565b60408051338152602081018590526001600160a01b0384168183015290517f860e25c3d48fb81f0b272550dd125a5a6ab20ee7511dd3e250d04964bd37a7a99181900360600190a16001600160a01b03821615613127576131276001600160a01b038316333086613850565b600181015460408051336024820152604481018690526001600160a01b039283166064808301919091528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fbd2d1cab00000000000000000000000000000000000000000000000000000000179052609b54609e549192611d059291169063ffffffff16836138a1565b609a546001600160a01b03163314806131e85750609a546001600160a01b0316155b6132345760405162461bcd60e51b815260206004820152601760248201527f43616c6c6572206973206e6f7420746865206f776e65720000000000000000006044820152606401610293565b609b546001600160a01b03161561328d5760405162461bcd60e51b815260206004820152601d60248201527f436f6e747261637420686173206265656e20696e697469616c697a65640000006044820152606401610293565b6001547501000000000000000000000000000000000000000000900460ff16806132d2575060015474010000000000000000000000000000000000000000900460ff16155b6133445760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610293565b6001547501000000000000000000000000000000000000000000900460ff161580156133ab57600180547fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff1675010100000000000000000000000000000000000000001790555b6001600160a01b038516158015906133cb57506001600160a01b03841615155b80156133df57506001600160a01b03831615155b61342b5760405162461bcd60e51b815260206004820152601860248201527f7a65726f2061646472657373206e6f7420616c6c6f77656400000000000000006044820152606401610293565b600080546001600160a01b038781167fffffffffffffffffffffffff000000000000000000000000000000000000000092831617835560018054888316908416179055609b805487831690841617905560a18054918616918316919091179055609a8054909116331790556005609c55603260a255609d556134b2620aae606108fc610829565b6134ba613973565b6134c2613ac3565b6134ca613c3a565b80156117f057600180547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff1690555050505050565b600061350b82846144d9565b9392505050565b600061350b8284614461565b600061350b828461449e565b600061350b828461437f565b60665460ff166135885760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610293565b606680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e9060440160206040518083038186803b15801561365557600080fd5b505afa158015613669573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061368d919061423d565b613697919061437f565b6040516001600160a01b038516602482015260448101829052909150611d059085907f095ea7b300000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152613d8e565b6040516001600160a01b038316602482015260448101829052610e869084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064016136df565b60665460ff16156137fd5760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610293565b606680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586135d33390565b6040516001600160a01b0380851660248301528316604482015260648101829052611d059085907f23b872dd00000000000000000000000000000000000000000000000000000000906084016136df565b6000546040517f3dbb202b0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911690633dbb202b90610e5390869085908790600401614566565b60a5544311156138ff5760a45460a3555b60a4546040805160208101929092526001600160a01b0380861691830191909152831660608201526080810182905260a001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012060a45550504360a55550565b6001547501000000000000000000000000000000000000000000900460ff16806139b8575060015474010000000000000000000000000000000000000000900460ff16155b613a2a5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610293565b6001547501000000000000000000000000000000000000000000900460ff16158015613a9157600180547fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff1675010100000000000000000000000000000000000000001790555b8015613ac057600180547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff1690555b50565b6001547501000000000000000000000000000000000000000000900460ff1680613b08575060015474010000000000000000000000000000000000000000900460ff16155b613b7a5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610293565b6001547501000000000000000000000000000000000000000000900460ff16158015613be157600180547fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff1675010100000000000000000000000000000000000000001790555b606680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690558015613ac057600180547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff16905550565b6001547501000000000000000000000000000000000000000000900460ff1680613c7f575060015474010000000000000000000000000000000000000000900460ff16155b613cf15760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610293565b6001547501000000000000000000000000000000000000000000900460ff16158015613d5857600180547fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff1675010100000000000000000000000000000000000000001790555b60016002558015613ac057600180547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff16905550565b6000613de3826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613e739092919063ffffffff16565b805190915015610e865780806020019051810190613e01919061459e565b610e865760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610293565b6060613e828484600085613e8a565b949350505050565b606082471015613f025760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610293565b843b613f505760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610293565b600080866001600160a01b03168587604051613f6c91906145c0565b60006040518083038185875af1925050503d8060008114613fa9576040519150601f19603f3d011682016040523d82523d6000602084013e613fae565b606091505b5091509150613fbe828286613fc9565b979650505050505050565b60608315613fd857508161350b565b825115613fe85782518084602001fd5b8160405162461bcd60e51b815260040161029391906145dc565b6001600160a01b0381168114613ac057600080fd5b6000806040838503121561402a57600080fd5b823561403581614002565b9150602083013561404581614002565b809150509250929050565b60006020828403121561406257600080fd5b813561350b81614002565b6000806040838503121561408057600080fd5b823563ffffffff8116811461409457600080fd5b946020939093013593505050565b600080604083850312156140b557600080fd5b82359150602083013561404581614002565b6000806000606084860312156140dc57600080fd5b8335925060208401356140ee81614002565b915060408401356140fe81614002565b809150509250925092565b60008060006060848603121561411e57600080fd5b833561412981614002565b92506020840135915060408401356140fe81614002565b6000806020838503121561415357600080fd5b823567ffffffffffffffff8082111561416b57600080fd5b818501915085601f83011261417f57600080fd5b81358181111561418e57600080fd5b8660208260061b85010111156141a357600080fd5b60209290920196919550909350505050565b6000806000606084860312156141ca57600080fd5b505081359360208301359350604090920135919050565b600080600080608085870312156141f757600080fd5b843561420281614002565b9350602085013561421281614002565b9250604085013561422281614002565b9150606085013561423281614002565b939692955090935050565b60006020828403121561424f57600080fd5b5051919050565b60006020828403121561426857600080fd5b815161350b81614002565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000604082840312156142e357600080fd5b6040516040810181811067ffffffffffffffff8211171561432d577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604052823561433b81614002565b81526020928301359281019290925250919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000821982111561439257614392614350565b500190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156143c9576143c9614350565b5060010190565b602080825282518282018190526000919060409081850190868401855b8281101561442857815180516001600160a01b03908116865287820151168786015285015185850152606090930192908501906001016143ed565b5091979650505050505050565b600063ffffffff8083168185168183048111821515161561445857614458614350565b02949350505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561449957614499614350565b500290565b6000826144d4577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b6000828210156144eb576144eb614350565b500390565b60005b8381101561450b5781810151838201526020016144f3565b83811115611d055750506000910152565b600081518084526145348160208601602086016144f0565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6001600160a01b0384168152606060208201526000614588606083018561451c565b905063ffffffff83166040830152949350505050565b6000602082840312156145b057600080fd5b8151801515811461350b57600080fd5b600082516145d28184602087016144f0565b9190910192915050565b60208152600061350b602083018461451c56fea164736f6c6343000809000a

Block Transaction Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Txn Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.