// File: src/arbi-fed/ArbiFed.sol



pragma solidity ^0.8.13;

import "src/interfaces/IERC20.sol";
import "src/interfaces/velo/IDola.sol";
import "src/interfaces/velo/IL1ERC20Bridge.sol";
import "src/arbi-fed/ArbiGasManager.sol";
import {IL1GatewayRouter} from "arbitrum/tokenbridge/ethereum/gateway/IL1GatewayRouter.sol";

contract ArbiFed is ArbiGasManager{
    address public chair;
    uint public underlyingSupply;

    IDola public immutable DOLA = IDola(0x865377367054516e17014CcdED1e7d814EDC9ce4);
    IL1GatewayRouter public immutable gatewayRouter = IL1GatewayRouter(0x72Ce9c846789fdB6fC1f34aC4AD25Dd9ef7031ef); 
    IL1GatewayRouter  public immutable gateway = IL1GatewayRouter(0xb4299A1F5f26fF6a98B7BA35572290C359fde900);
    address public immutable l1ERC20Gateway = 0xa3A7B6F88361F48403514059F1F16C8E78d60EeC;

    address public auraFarmer; // On L2

    event Expansion(uint amount);
    event Contraction(uint amount);

    error OnlyChair();
    error OnlyGuardian();
    error CantBurnZeroDOLA();
    error DeltaAboveMax();
    error ZeroGasPriceBid();
    error InsufficientGasFunds();
    
    constructor(
            address _gov,
            address _auraFarmer,
            address _chair,
            address _gasClerk,
            address _l2RefundAddress
    ) ArbiGasManager(_gov, _gasClerk, _l2RefundAddress)
    {
        chair = _chair;
        gov = _gov;
        auraFarmer = _auraFarmer;

        DOLA.approve(address(l1ERC20Gateway), type(uint).max); 
    }

    modifier onlyChair {
        if (msg.sender != chair) revert OnlyChair();
        _;
    }

    /**
     * @notice Mints & deposits `amountToBridge` of DOLA into Arbitrum Gateway to the `auraFarmer` contract
     * @param amountToBridge Amount of underlying token to briged into Aura farmer on Arbitrum
     */
    function expansion(uint amountToBridge) external payable onlyChair {
        if (gasPrice == 0) revert ZeroGasPriceBid();
        if (msg.value < maxSubmissionCost + defaultGasLimit * gasPrice) revert InsufficientGasFunds();
        uint dolaBal = DOLA.balanceOf(address(this));
        if(dolaBal < amountToBridge){
            uint amountToMint = amountToBridge - dolaBal;
            underlyingSupply += amountToMint;
            DOLA.mint(address(this), amountToMint);
            emit Expansion(amountToMint);
        }
        bytes memory data = abi.encode(maxSubmissionCost, "");
        gatewayRouter.outboundTransferCustomRefund{value: msg.value}(
            address(DOLA),
            refundAddress,
            auraFarmer,
            amountToBridge,
            defaultGasLimit, 
            gasPrice, 
            data
        );

    }

    /**
     * @notice Burns `amountUnderlying` of DOLA held in this contract
     * @param amountUnderlying Amount of underlying DOLA to burn
     */
    function contraction(uint amountUnderlying) external onlyChair {

        _contraction(amountUnderlying);
    }

    /**
     * @notice Attempts to contract (burn) all DOLA held by this contract
     */
    function contractAll() external onlyChair {

        _contraction(DOLA.balanceOf(address(this)));
    }

    /**
     * @notice Attempts to contract (burn) `amount` of DOLA. Sends remainder to `gov` if `amount` > DOLA minted by this fed.
     * @param amount Amount to contract
     */
    function _contraction(uint amount) internal {
        if (amount == 0) revert CantBurnZeroDOLA();
        if(amount > underlyingSupply){
            DOLA.burn(underlyingSupply);
            DOLA.transfer(gov, amount - underlyingSupply);
            emit Contraction(underlyingSupply);
            underlyingSupply = 0;
        } else {
            DOLA.burn(amount);
            underlyingSupply -= amount;
            emit Contraction(amount);
        }
    }

    /**
     * @notice Method for current chair of the Arbi FED to resign
     */
    function resign() external onlyChair {
        chair = address(0);
    }

    /**
     * @notice Method for gov to change the chair
     */
    function changeChair(address newChair) external onlyGov {
        chair = newChair;
    }

    /**
     * @notice Method for gov to change the L2 auraFarmer address
     */
    function changeAuraFarmer(address newAuraFarmer) external onlyGov {
        auraFarmer = newAuraFarmer;
    }

    /**
     * @notice Method for gov to withdraw any ERC20 token from this contract
     */
    function emergecyWithdraw(address token, address to, uint256 amount) external onlyGov {
        IERC20(token).transfer(to, amount);
    }
    
}

// File: src/interfaces/IERC20.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**

     * @dev Returns the decimal points used by the token.
     */
    function decimals() external view returns (uint8);

    /**
     
    * @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 `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, 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 `from` to `to` 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 from,
        address to,
        uint256 amount
    ) external returns (bool);
    
    /**
     * @dev Burns `amount` of token, shringking total supply
     */
    function burn(uint amount) external;

    /**
     * @dev Mints `amount` of token to address `to` increasing total supply
     */
    function mint(address to, uint amount) external;

    //For testing
    function addMinter(address minter_) external;
}

// File: src/interfaces/velo/IDola.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IDola {
    function decimals() external view returns (uint8);
    /**
     * @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);

    function mint(address recipient, uint256 amount) external;
    function burn(uint256 amount) external;

    /**
     * @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);

    function addMinter(address minter) external;

    /**
     * @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: src/interfaces/velo/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: src/arbi-fed/ArbiGasManager.sol



pragma solidity ^0.8.13;
import "src/arbi-fed/Governable.sol";


contract ArbiGasManager is Governable{
    address public gasClerk;
    address public refundAddress;
    mapping(bytes32 => uint) public functionGasLimit;
    uint public defaultGasLimit;
    uint public maxSubmissionCostCeiling;
    uint public maxSubmissionCost;
    uint public gasPriceCeiling;
    uint public gasPrice;

    struct L2GasParams {
        uint256 _maxSubmissionCost;
        uint256 _maxGas;
        uint256 _gasPriceBid;
    }


    constructor(address _gov, address _gasClerk, address _l2RefundAddress) Governable(_gov){
        gasClerk = _gasClerk;
        refundAddress = _l2RefundAddress;
        defaultGasLimit = 10**6; //Same gas stipend as Optimism bridge
        maxSubmissionCost = 0.01 ether;
        gasPriceCeiling = 2 * 10**10; //20 gWEI
        gasPrice = 10**9; //1 gWEI
    }

    error OnlyGasClerk();
    error MaxSubmissionCostAboveCeiling();
    error GasPriceAboveCeiling();

    modifier onlyGasClerk(){
        if(msg.sender != gasClerk) revert OnlyGasClerk();
        _;
    }

    /**
     * @notice Sets the default gas limit.
     * @dev This function can only be called by the gas clerk.
     * @param newDefaultGasLimit The new default gas limit to set.
     */
    function setDefaultGasLimit(uint newDefaultGasLimit) external onlyGasClerk {
        defaultGasLimit = newDefaultGasLimit; 
    }

    /**
     * @notice Sets the gas limit for a specific function on a contract.
     * @dev This function can only be called by the gas clerk.
     * @param contractAddress The address of the contract.
     * @param functionSelector The selector of the function to set the gas limit for.
     * @param gasLimit The new gas limit to set.
     */
    function setFunctionGasLimit(address contractAddress, bytes4 functionSelector, uint gasLimit) external onlyGasClerk {
        bytes32 hash = keccak256(abi.encodePacked(functionSelector, contractAddress));
        functionGasLimit[hash] = gasLimit; 
    }

    /**
     * @notice Sets the max submission cost.
     * @dev Throws if new submission cost exceeds the ceiling. Can only be called by the gas clerk.
     * @param newMaxSubmissionCost The new max submission cost to set.
     */
    function setMaxSubmissionCost(uint newMaxSubmissionCost) external onlyGasClerk {
        if(newMaxSubmissionCost > maxSubmissionCostCeiling) revert MaxSubmissionCostAboveCeiling();
        maxSubmissionCost = newMaxSubmissionCost;
    }

    /**
     * @notice Sets the gas price.
     * @dev Throws if new gas price exceeds the ceiling. Can only be called by the gas clerk.
     * @param newGasPrice The new gas price to set.
     */
    function setGasPrice(uint newGasPrice) external onlyGasClerk {
        if(newGasPrice > gasPriceCeiling) revert GasPriceAboveCeiling();
        gasPrice = newGasPrice;
    }

    /**
     * @notice Fetches gas parameters for a specific function on a contract.
     * @param contractAddress The address of the contract.
     * @param functionSelector The selector of the function to get gas parameters for.
     * @return L2GasParams Returns a struct containing gas parameters.
     */
    function getGasParams(address contractAddress, bytes4 functionSelector) public view returns(L2GasParams memory){
        L2GasParams memory gasParams;
        gasParams._maxSubmissionCost = maxSubmissionCost;
        gasParams._gasPriceBid = gasPrice;
        bytes32 hash = keccak256(abi.encodePacked(functionSelector, contractAddress));
        uint gasLimit = functionGasLimit[hash]; 
        if(gasLimit == 0) gasLimit = defaultGasLimit;
        gasParams._maxGas = gasLimit;
        return gasParams;
    }

    /**
     * @notice Sets the L2 refund address.
     * @dev Can only be called by governance.
     * @param newRefundAddress The new refund address to set.
     */
    function setRefundAddress(address newRefundAddress) external onlyGov {
        refundAddress = newRefundAddress;
    }

    /**
     * @notice Sets the max submission cost ceiling.
     * @dev Can only be called by governance.
     * @param newMaxSubmissionCostCeiling The new max submission cost ceiling to set.
     */
    function setMaxSubmissionCostCeiling(uint newMaxSubmissionCostCeiling) external onlyGov {
       maxSubmissionCostCeiling = newMaxSubmissionCostCeiling; 
    }

    /**
     * @notice Sets the gas price ceiling.
     * @dev Can only be called by governance.
     * @param newGasPriceCeiling The new gas price ceiling to set.
     */
    function setGasPriceCeiling(uint newGasPriceCeiling) external onlyGov {
       gasPriceCeiling = newGasPriceCeiling; 
    }
    /**
     * @notice Sets the gas clerk address.
     * @dev Can only be called by governance.
     * @param newGasClerk The new address to be set as the gas clerk.
     */
    function setGasClerk(address newGasClerk) external onlyGov {
        gasClerk = newGasClerk;
    }

    /**
     * @notice Fetches the default minimum call value based on gas settings.
     * @return minval Returns the default minimum call value calculated as `gasPrice * gasDefaultLimit + maxSubmissionCost`.
     */
    function getDefaultMinimumCallValue() public view returns(uint minval){
        return gasPrice * defaultGasLimit + maxSubmissionCost;
    }

    /**
     * @notice Fetches the minimum call value for a specific function on a contract based on gas settings.
     * @dev This function computes the gas limit using the default if not explicitly set for the function.
     * @param contractAddress The address of the L2 contract to be called.
     * @param functionSelector The selector of the function to get the minimum call value for.
     * @return minval Returns the minimum call value calculated as `gasPrice * gasLimit + maxSubmissionCost`.
     */
    function getFunctionMinimumCallValue(address contractAddress, bytes4 functionSelector) public view returns (uint minval) {
        bytes32 hash = keccak256(abi.encodePacked(functionSelector, contractAddress));
        uint gasLimit = functionGasLimit[hash];
        if(gasLimit == 0) gasLimit = defaultGasLimit;
        return gasPrice * gasLimit + maxSubmissionCost;
    }

    /**
     * @notice Checks if the provided call value is sufficient based on default gas settings.
     * @dev This function checks if the provided call value is greater than or equal to the default minimum call value and the current contract balance.
     * @param callValue The call value to check for sufficiency.
     * @return Returns `true` if the call value is sufficient, otherwise `false`.
     */
    function isCallValueSufficient(uint callValue) public view returns (bool){
        return getDefaultMinimumCallValue() <= callValue + address(this).balance;
    }

    /**
     * @notice Checks if the provided call value is sufficient for a specific function on a contract based on gas settings.
     * @dev This function checks if the provided call value is greater than or equal to the minimum call value for the specified function and the current contract balance.
     * @param contractAddress The address of the contract.
     * @param functionSelector The selector of the function to check the call value for.
     * @param callValue The call value to check for sufficiency.
     * @return Returns `true` if the call value is sufficient for the specified function, otherwise `false`.
     */
    function isCallValueSufficient(address contractAddress, bytes4 functionSelector, uint callValue) public view returns (bool){
        return getFunctionMinimumCallValue(contractAddress, functionSelector) <= callValue + address(this).balance;
    }
}

// File: lib/arbitrum/contracts/tokenbridge/ethereum/gateway/IL1GatewayRouter.sol



// SPDX-License-Identifier: Apache-2.0

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

// solhint-disable-next-line compiler-version
pragma solidity >=0.6.9 <0.9.0;

import "../../libraries/gateway/ITokenGateway.sol";
import "../../libraries/IERC165.sol";

/**
 * @title Handles deposits from Erhereum into Arbitrum. Tokens are routered to their appropriate L1 gateway (Router itself also conforms to the Gateway itnerface).
 * @notice Router also serves as an L1-L2 token address oracle.
 */
interface IL1GatewayRouter is ITokenGateway, IERC165 {
    /**
     * @notice Deposit ERC20 token from Ethereum into Arbitrum using the registered or otherwise default gateway
     * @dev Some legacy gateway might not have the outboundTransferCustomRefund method and will revert, in such case use outboundTransfer instead
     *      L2 address alias will not be applied to the following types of addresses on L1:
     *      - 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
     * @param _token L1 address of ERC20
     * @param _refundTo Account, or its L2 alias if it have code in L1, to be credited with excess gas refund in L2
     * @param _to Account to be credited with the tokens in the L2 (can be the user's L2 account or a contract), not subject to L2 aliasing
                  This account, or its L2 alias if it have code in L1, will also be able to cancel the retryable ticket and receive callvalue refund
     * @param _amount Token Amount
     * @param _maxGas Max gas deducted from user's L2 balance to cover L2 execution
     * @param _gasPriceBid Gas price for L2 execution
     * @param _data encoded data from router and user
     * @return res abi encoded inbox sequence number
     */
    function outboundTransferCustomRefund(
        address _token,
        address _refundTo,
        address _to,
        uint256 _amount,
        uint256 _maxGas,
        uint256 _gasPriceBid,
        bytes calldata _data
    ) external payable returns (bytes memory);

    /**
     * @notice Allows L1 Token contract to trustlessly register its gateway.
     * @param _gateway l1 gateway address
     * @param _maxGas max gas for L2 retryable exrecution
     * @param _gasPriceBid gas price for L2 retryable ticket
     * @param  _maxSubmissionCost base submission cost  L2 retryable tick3et
     * @param _creditBackAddress address for crediting back overpayment of _maxSubmissionCost
     * @return Retryable ticket ID
     */
    function setGateway(
        address _gateway,
        uint256 _maxGas,
        uint256 _gasPriceBid,
        uint256 _maxSubmissionCost,
        address _creditBackAddress
    ) external payable returns (uint256);

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

    function owner() external view returns (address);

    function inbox() external view returns (address);
}

// File: src/arbi-fed/Governable.sol



pragma solidity ^0.8.13;

abstract contract Governable {

    address public gov;
    address public pendingGov;

    constructor(address _gov){
        gov = _gov;
    }

    error OnlyGov();
    error OnlyPendingGov();

    modifier onlyGov() {
        if(msg.sender != gov) revert OnlyGov();
        _;
    }

    modifier onlyPendingGov() {
        if(msg.sender != gov) revert OnlyPendingGov();
        _;
    }

    function setPendingGov(address newPendingGov) external onlyGov {
        pendingGov = newPendingGov;
    }

    function claimPendingGov() external onlyPendingGov{
        gov = pendingGov;
        pendingGov = address(0);
    }
}

// File: lib/arbitrum/contracts/tokenbridge/libraries/gateway/ITokenGateway.sol



// SPDX-License-Identifier: Apache-2.0

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

// solhint-disable-next-line compiler-version
pragma solidity >=0.6.9 <0.9.0;

interface ITokenGateway {
    /// @notice event deprecated in favor of DepositInitiated and WithdrawalInitiated
    // event OutboundTransferInitiated(
    //     address token,
    //     address indexed _from,
    //     address indexed _to,
    //     uint256 indexed _transferId,
    //     uint256 _amount,
    //     bytes _data
    // );

    /// @notice event deprecated in favor of DepositFinalized and WithdrawalFinalized
    // event InboundTransferFinalized(
    //     address token,
    //     address indexed _from,
    //     address indexed _to,
    //     uint256 indexed _transferId,
    //     uint256 _amount,
    //     bytes _data
    // );

    function outboundTransfer(
        address _token,
        address _to,
        uint256 _amount,
        uint256 _maxGas,
        uint256 _gasPriceBid,
        bytes calldata _data
    ) external payable returns (bytes memory);

    function finalizeInboundTransfer(
        address _token,
        address _from,
        address _to,
        uint256 _amount,
        bytes calldata _data
    ) external payable;

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

    function getOutboundCalldata(
        address _token,
        address _from,
        address _to,
        uint256 _amount,
        bytes memory _data
    ) external view returns (bytes memory);
}

// File: lib/arbitrum/contracts/tokenbridge/libraries/IERC165.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

// With pragma modification to allow interface compatibility with >=0.6.9 <0.9.0
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.6/contracts/utils/introspection/IERC165.sol

// solhint-disable-next-line compiler-version
pragma solidity >=0.6.9 <0.9.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}