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



pragma solidity ^0.8.13;

import {IL1GatewayRouter} from "arbitrum/tokenbridge/ethereum/gateway/IL1GatewayRouter.sol";
import {IInbox} from "arbitrum-nitro/contracts/src/bridge/IInbox.sol";
import "src/arbi-fed/ArbiGasManager.sol";

contract ArbiGovMessengerL1 is ArbiGasManager{
    IL1GatewayRouter public immutable gatewayRouter = IL1GatewayRouter(0x72Ce9c846789fdB6fC1f34aC4AD25Dd9ef7031ef); 
    
    IInbox public inbox;
    mapping(address => bool) public allowList;

    event MessageSent(address to, bytes data);

    constructor (address _gov, address _inbox, address _gasClerk, address _l2RefundAddress)
        ArbiGasManager(gov, _gasClerk, _l2RefundAddress)
    {
        gov = _gov;
        inbox = IInbox(_inbox);
    }

    error OnlyAllowed();

    modifier onlyAllowed {
        if (msg.sender != gov && !allowList[msg.sender]) revert OnlyAllowed();
        _;
    }
    /**
     * @notice Generalized function for sending messages to Arbitrum
     * @dev Call value of the function should be the funds necessary for gasLimit * gasPrice
     * @param _to The destination L2 address
     * @param _refundTo The L2 address to which the excess fee is credited (l1CallValue - (autoredeem ? ticket execution cost : submission cost) - l2CallValue)
     * @param _callValueRefundAddress The L2 address to which the l2CallValue is credited if the ticket times out or gets cancelled (this is also called the `beneficiary`, who's got a critical permission to cancel the ticket)
     * @param _l2CallValue The callvalue for retryable L2 message that is supplied within the deposit (l1CallValue)
     * @param _l2GasParams A struct consisting of three variables:
        - `uint256 maxSubmissionCost`: The maximum amount of ETH to be paid for submitting the ticket. This amount is (1) supplied within the deposit (l1CallValue) to be later deducted from sender's L2 balance and is (2) directly proportional to the size of the retryable’s data and L1 basefee
        - `uint256 gasLimit`: Maximum amount of gas used to cover L2 execution of the ticket
        - `uint256 maxFeePerGas`: The gas price bid for L2 execution of the ticket that is supplied within the deposit (l1CallValue)
     * @param _data The calldata to the destination L2 address
     */    
    function sendMessage(
        address _to,
        address _refundTo,
        address _callValueRefundAddress,
        uint256 _l1CallValue,
        uint256 _l2CallValue,
        L2GasParams memory _l2GasParams,
        bytes memory _data
    ) external payable onlyAllowed() returns (uint256) {
        
        emit MessageSent(_to, _data);

        return inbox.createRetryableTicket{ value: _l1CallValue }(
            _to,
            _l2CallValue,
            _l2GasParams._maxSubmissionCost,
            _refundTo, // only refund excess fee to the custom address
            _callValueRefundAddress, // user can cancel the retryable and receive call value refund
            _l2GasParams._maxGas,
            _l2GasParams._gasPriceBid,
            _data
        );
    }

    /**
     * @notice Send message to L2 address
     * @dev This function automatically handles gas estimation. Default gas limit may be too low to execute.
     * @param _to L2 address to send message to
     * @param _functionSelector Function selector of the function to call at the L2 address
     * @param _data The calldata to the L2 address
     */
    function sendMessage(
        address _to,
        bytes4 _functionSelector,
        bytes calldata _data
    ) external payable onlyAllowed() returns (uint256) {
        emit MessageSent(_to, _data);
        bytes32 functionId = keccak256(abi.encodePacked(_functionSelector, _to)); //Hash of address + functionSelector should be collision resistant
        uint gasLimit = functionGasLimit[functionId];
        if(gasLimit == 0) gasLimit = defaultGasLimit;

        return inbox.createRetryableTicket{ value: gasLimit * gasPrice + maxSubmissionCost}(
            _to,
            0,
            maxSubmissionCost,
            refundAddress,
            refundAddress, // refundAddress can cancel the retryable ticket and receive call value refund
            gasLimit,
            gasPrice,
            _data
        );
    }

    /**
     * @notice Sets the arbitrum bridge inbox
     * @param _newInbox Address of the new inbox
     */
    function setInbox(address _newInbox) external onlyGov {
        inbox = IInbox(_newInbox);
    }

    /**
     * @notice Sets the status of an address that is allowed to send messages on behalf of gov on L2
     * @dev Allows for the creation of governance contracts for specific contracts, simplifying proposal making. Should never allow an EOA, MSIG or upgradeable contract.
     * @param allowee Address of the contract to set allowed status for
     * @param isAllowed true for allowing a contract, false for disallowing
     */
    function setAllowed(address allowee, bool isAllowed) external onlyGov {
        allowList[allowee] = isAllowed;
    }

    /**
     * @notice Sweep Eth to gov
     */
    function sweepEth() external onlyGov {
        uint amount = address(this).balance;
        payable(gov).transfer(amount);
    }

    receive() external payable{}
}

// 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: lib/arbitrum-nitro/contracts/src/bridge/IInbox.sol



// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/nitro/blob/master/LICENSE
// SPDX-License-Identifier: BUSL-1.1

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

import "./IBridge.sol";
import "./IDelayedMessageProvider.sol";
import "./ISequencerInbox.sol";

interface IInbox is IDelayedMessageProvider {
    function bridge() external view returns (IBridge);

    function sequencerInbox() external view returns (ISequencerInbox);

    /**
     * @notice Send a generic L2 message to the chain
     * @dev This method is an optimization to avoid having to emit the entirety of the messageData in a log. Instead validators are expected to be able to parse the data from the transaction's input
     *      This method will be disabled upon L1 fork to prevent replay attacks on L2
     * @param messageData Data of the message being sent
     */
    function sendL2MessageFromOrigin(bytes calldata messageData) external returns (uint256);

    /**
     * @notice Send a generic L2 message to the chain
     * @dev This method can be used to send any type of message that doesn't require L1 validation
     *      This method will be disabled upon L1 fork to prevent replay attacks on L2
     * @param messageData Data of the message being sent
     */
    function sendL2Message(bytes calldata messageData) external returns (uint256);

    function sendL1FundedUnsignedTransaction(
        uint256 gasLimit,
        uint256 maxFeePerGas,
        uint256 nonce,
        address to,
        bytes calldata data
    ) external payable returns (uint256);

    function sendL1FundedContractTransaction(
        uint256 gasLimit,
        uint256 maxFeePerGas,
        address to,
        bytes calldata data
    ) external payable returns (uint256);

    function sendUnsignedTransaction(
        uint256 gasLimit,
        uint256 maxFeePerGas,
        uint256 nonce,
        address to,
        uint256 value,
        bytes calldata data
    ) external returns (uint256);

    function sendContractTransaction(
        uint256 gasLimit,
        uint256 maxFeePerGas,
        address to,
        uint256 value,
        bytes calldata data
    ) external returns (uint256);

    /**
     * @dev This method can only be called upon L1 fork and will not alias the caller
     *      This method will revert if not called from origin
     */
    function sendL1FundedUnsignedTransactionToFork(
        uint256 gasLimit,
        uint256 maxFeePerGas,
        uint256 nonce,
        address to,
        bytes calldata data
    ) external payable returns (uint256);

    /**
     * @dev This method can only be called upon L1 fork and will not alias the caller
     *      This method will revert if not called from origin
     */
    function sendUnsignedTransactionToFork(
        uint256 gasLimit,
        uint256 maxFeePerGas,
        uint256 nonce,
        address to,
        uint256 value,
        bytes calldata data
    ) external returns (uint256);

    /**
     * @notice Send a message to initiate L2 withdrawal
     * @dev This method can only be called upon L1 fork and will not alias the caller
     *      This method will revert if not called from origin
     */
    function sendWithdrawEthToFork(
        uint256 gasLimit,
        uint256 maxFeePerGas,
        uint256 nonce,
        uint256 value,
        address withdrawTo
    ) external returns (uint256);

    /**
     * @notice Get the L1 fee for submitting a retryable
     * @dev This fee can be paid by funds already in the L2 aliased address or by the current message value
     * @dev This formula may change in the future, to future proof your code query this method instead of inlining!!
     * @param dataLength The length of the retryable's calldata, in bytes
     * @param baseFee The block basefee when the retryable is included in the chain, if 0 current block.basefee will be used
     */
    function calculateRetryableSubmissionFee(uint256 dataLength, uint256 baseFee)
        external
        view
        returns (uint256);

    /**
     * @notice Deposit eth from L1 to L2 to address of the sender if sender is an EOA, and to its aliased address if the sender is a contract
     * @dev This does not trigger the fallback function when receiving in the L2 side.
     *      Look into retryable tickets if you are interested in this functionality.
     * @dev This function should not be called inside contract constructors
     */
    function depositEth() external payable returns (uint256);

    /**
     * @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts
     * @dev all msg.value will deposited to callValueRefundAddress on L2
     * @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error
     * @param to destination L2 contract address
     * @param l2CallValue call value for retryable L2 message
     * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee
     * @param excessFeeRefundAddress gasLimit x maxFeePerGas - execution cost gets credited here on L2 balance
     * @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled
     * @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
     * @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
     * @param data ABI encoded data of L2 message
     * @return unique message number of the retryable transaction
     */
    function createRetryableTicket(
        address to,
        uint256 l2CallValue,
        uint256 maxSubmissionCost,
        address excessFeeRefundAddress,
        address callValueRefundAddress,
        uint256 gasLimit,
        uint256 maxFeePerGas,
        bytes calldata data
    ) external payable returns (uint256);

    /**
     * @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts
     * @dev Same as createRetryableTicket, but does not guarantee that submission will succeed by requiring the needed funds
     * come from the deposit alone, rather than falling back on the user's L2 balance
     * @dev Advanced usage only (does not rewrite aliases for excessFeeRefundAddress and callValueRefundAddress).
     * createRetryableTicket method is the recommended standard.
     * @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error
     * @param to destination L2 contract address
     * @param l2CallValue call value for retryable L2 message
     * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee
     * @param excessFeeRefundAddress gasLimit x maxFeePerGas - execution cost gets credited here on L2 balance
     * @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled
     * @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
     * @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
     * @param data ABI encoded data of L2 message
     * @return unique message number of the retryable transaction
     */
    function unsafeCreateRetryableTicket(
        address to,
        uint256 l2CallValue,
        uint256 maxSubmissionCost,
        address excessFeeRefundAddress,
        address callValueRefundAddress,
        uint256 gasLimit,
        uint256 maxFeePerGas,
        bytes calldata data
    ) external payable returns (uint256);

    // ---------- onlyRollupOrOwner functions ----------

    /// @notice pauses all inbox functionality
    function pause() external;

    /// @notice unpauses all inbox functionality
    function unpause() external;

    // ---------- initializer ----------

    /**
     * @dev function to be called one time during the inbox upgrade process
     *      this is used to fix the storage slots
     */
    function postUpgradeInit(IBridge _bridge) external;

    function initialize(IBridge _bridge, ISequencerInbox _sequencerInbox) 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/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);
}

// File: lib/arbitrum-nitro/contracts/src/bridge/IBridge.sol



// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/nitro/blob/master/LICENSE
// SPDX-License-Identifier: BUSL-1.1

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

import "./IOwnable.sol";

interface IBridge {
    event MessageDelivered(
        uint256 indexed messageIndex,
        bytes32 indexed beforeInboxAcc,
        address inbox,
        uint8 kind,
        address sender,
        bytes32 messageDataHash,
        uint256 baseFeeL1,
        uint64 timestamp
    );

    event BridgeCallTriggered(
        address indexed outbox,
        address indexed to,
        uint256 value,
        bytes data
    );

    event InboxToggle(address indexed inbox, bool enabled);

    event OutboxToggle(address indexed outbox, bool enabled);

    event SequencerInboxUpdated(address newSequencerInbox);

    function allowedDelayedInboxList(uint256) external returns (address);

    function allowedOutboxList(uint256) external returns (address);

    /// @dev Accumulator for delayed inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message.
    function delayedInboxAccs(uint256) external view returns (bytes32);

    /// @dev Accumulator for sequencer inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message.
    function sequencerInboxAccs(uint256) external view returns (bytes32);

    function rollup() external view returns (IOwnable);

    function sequencerInbox() external view returns (address);

    function activeOutbox() external view returns (address);

    function allowedDelayedInboxes(address inbox) external view returns (bool);

    function allowedOutboxes(address outbox) external view returns (bool);

    function sequencerReportedSubMessageCount() external view returns (uint256);

    /**
     * @dev Enqueue a message in the delayed inbox accumulator.
     *      These messages are later sequenced in the SequencerInbox, either
     *      by the sequencer as part of a normal batch, or by force inclusion.
     */
    function enqueueDelayedMessage(
        uint8 kind,
        address sender,
        bytes32 messageDataHash
    ) external payable returns (uint256);

    function executeCall(
        address to,
        uint256 value,
        bytes calldata data
    ) external returns (bool success, bytes memory returnData);

    function delayedMessageCount() external view returns (uint256);

    function sequencerMessageCount() external view returns (uint256);

    // ---------- onlySequencerInbox functions ----------

    function enqueueSequencerMessage(
        bytes32 dataHash,
        uint256 afterDelayedMessagesRead,
        uint256 prevMessageCount,
        uint256 newMessageCount
    )
        external
        returns (
            uint256 seqMessageIndex,
            bytes32 beforeAcc,
            bytes32 delayedAcc,
            bytes32 acc
        );

    /**
     * @dev Allows the sequencer inbox to submit a delayed message of the batchPostingReport type
     *      This is done through a separate function entrypoint instead of allowing the sequencer inbox
     *      to call `enqueueDelayedMessage` to avoid the gas overhead of an extra SLOAD in either
     *      every delayed inbox or every sequencer inbox call.
     */
    function submitBatchSpendingReport(address batchPoster, bytes32 dataHash)
        external
        returns (uint256 msgNum);

    // ---------- onlyRollupOrOwner functions ----------

    function setSequencerInbox(address _sequencerInbox) external;

    function setDelayedInbox(address inbox, bool enabled) external;

    function setOutbox(address inbox, bool enabled) external;

    // ---------- initializer ----------

    function initialize(IOwnable rollup_) external;
}

// File: lib/arbitrum-nitro/contracts/src/bridge/IDelayedMessageProvider.sol



// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/nitro/blob/master/LICENSE
// SPDX-License-Identifier: BUSL-1.1

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

interface IDelayedMessageProvider {
    /// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator
    event InboxMessageDelivered(uint256 indexed messageNum, bytes data);

    /// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator
    /// same as InboxMessageDelivered but the batch data is available in tx.input
    event InboxMessageDeliveredFromOrigin(uint256 indexed messageNum);
}

// File: lib/arbitrum-nitro/contracts/src/bridge/ISequencerInbox.sol



// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/nitro/blob/master/LICENSE
// SPDX-License-Identifier: BUSL-1.1

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

import "../libraries/IGasRefunder.sol";
import "./IDelayedMessageProvider.sol";
import "./IBridge.sol";

interface ISequencerInbox is IDelayedMessageProvider {
    struct MaxTimeVariation {
        uint256 delayBlocks;
        uint256 futureBlocks;
        uint256 delaySeconds;
        uint256 futureSeconds;
    }

    struct TimeBounds {
        uint64 minTimestamp;
        uint64 maxTimestamp;
        uint64 minBlockNumber;
        uint64 maxBlockNumber;
    }

    enum BatchDataLocation {
        TxInput,
        SeparateBatchEvent,
        NoData
    }

    event SequencerBatchDelivered(
        uint256 indexed batchSequenceNumber,
        bytes32 indexed beforeAcc,
        bytes32 indexed afterAcc,
        bytes32 delayedAcc,
        uint256 afterDelayedMessagesRead,
        TimeBounds timeBounds,
        BatchDataLocation dataLocation
    );

    event OwnerFunctionCalled(uint256 indexed id);

    /// @dev a separate event that emits batch data when this isn't easily accessible in the tx.input
    event SequencerBatchData(uint256 indexed batchSequenceNumber, bytes data);

    /// @dev a valid keyset was added
    event SetValidKeyset(bytes32 indexed keysetHash, bytes keysetBytes);

    /// @dev a keyset was invalidated
    event InvalidateKeyset(bytes32 indexed keysetHash);

    function totalDelayedMessagesRead() external view returns (uint256);

    function bridge() external view returns (IBridge);

    /// @dev The size of the batch header
    // solhint-disable-next-line func-name-mixedcase
    function HEADER_LENGTH() external view returns (uint256);

    /// @dev If the first batch data byte after the header has this bit set,
    ///      the sequencer inbox has authenticated the data. Currently not used.
    // solhint-disable-next-line func-name-mixedcase
    function DATA_AUTHENTICATED_FLAG() external view returns (bytes1);

    function rollup() external view returns (IOwnable);

    function isBatchPoster(address) external view returns (bool);

    struct DasKeySetInfo {
        bool isValidKeyset;
        uint64 creationBlock;
    }

    // https://github.com/ethereum/solidity/issues/11826
    // function maxTimeVariation() external view returns (MaxTimeVariation calldata);
    // function dasKeySetInfo(bytes32) external view returns (DasKeySetInfo calldata);

    /// @notice Remove force inclusion delay after a L1 chainId fork
    function removeDelayAfterFork() external;

    /// @notice Force messages from the delayed inbox to be included in the chain
    ///         Callable by any address, but message can only be force-included after maxTimeVariation.delayBlocks and
    ///         maxTimeVariation.delaySeconds has elapsed. As part of normal behaviour the sequencer will include these
    ///         messages so it's only necessary to call this if the sequencer is down, or not including any delayed messages.
    /// @param _totalDelayedMessagesRead The total number of messages to read up to
    /// @param kind The kind of the last message to be included
    /// @param l1BlockAndTime The l1 block and the l1 timestamp of the last message to be included
    /// @param baseFeeL1 The l1 gas price of the last message to be included
    /// @param sender The sender of the last message to be included
    /// @param messageDataHash The messageDataHash of the last message to be included
    function forceInclusion(
        uint256 _totalDelayedMessagesRead,
        uint8 kind,
        uint64[2] calldata l1BlockAndTime,
        uint256 baseFeeL1,
        address sender,
        bytes32 messageDataHash
    ) external;

    function inboxAccs(uint256 index) external view returns (bytes32);

    function batchCount() external view returns (uint256);

    function isValidKeysetHash(bytes32 ksHash) external view returns (bool);

    /// @notice the creation block is intended to still be available after a keyset is deleted
    function getKeysetCreationBlock(bytes32 ksHash) external view returns (uint256);

    // ---------- BatchPoster functions ----------

    function addSequencerL2BatchFromOrigin(
        uint256 sequenceNumber,
        bytes calldata data,
        uint256 afterDelayedMessagesRead,
        IGasRefunder gasRefunder
    ) external;

    function addSequencerL2Batch(
        uint256 sequenceNumber,
        bytes calldata data,
        uint256 afterDelayedMessagesRead,
        IGasRefunder gasRefunder,
        uint256 prevMessageCount,
        uint256 newMessageCount
    ) external;

    // ---------- onlyRollupOrOwner functions ----------

    /**
     * @notice Set max delay for sequencer inbox
     * @param maxTimeVariation_ the maximum time variation parameters
     */
    function setMaxTimeVariation(MaxTimeVariation memory maxTimeVariation_) external;

    /**
     * @notice Updates whether an address is authorized to be a batch poster at the sequencer inbox
     * @param addr the address
     * @param isBatchPoster_ if the specified address should be authorized as a batch poster
     */
    function setIsBatchPoster(address addr, bool isBatchPoster_) external;

    /**
     * @notice Makes Data Availability Service keyset valid
     * @param keysetBytes bytes of the serialized keyset
     */
    function setValidKeyset(bytes calldata keysetBytes) external;

    /**
     * @notice Invalidates a Data Availability Service keyset
     * @param ksHash hash of the keyset
     */
    function invalidateKeysetHash(bytes32 ksHash) external;

    // ---------- initializer ----------

    function initialize(IBridge bridge_, MaxTimeVariation calldata maxTimeVariation_) external;
}

// 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-nitro/contracts/src/bridge/IOwnable.sol



// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/nitro/blob/master/LICENSE
// SPDX-License-Identifier: BUSL-1.1

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

interface IOwnable {
    function owner() external view returns (address);
}

// File: lib/arbitrum-nitro/contracts/src/libraries/IGasRefunder.sol



// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/nitro/blob/master/LICENSE
// SPDX-License-Identifier: BUSL-1.1

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

interface IGasRefunder {
    function onGasSpent(
        address payable spender,
        uint256 gasUsed,
        uint256 calldataSize
    ) external returns (bool success);
}

abstract contract GasRefundEnabled {
    /// @dev this refunds the sender for execution costs of the tx
    /// calldata costs are only refunded if `msg.sender == tx.origin` to guarantee the value refunded relates to charging
    /// for the `tx.input`. this avoids a possible attack where you generate large calldata from a contract and get over-refunded
    modifier refundsGas(IGasRefunder gasRefunder) {
        uint256 startGasLeft = gasleft();
        _;
        if (address(gasRefunder) != address(0)) {
            uint256 calldataSize;
            assembly {
                calldataSize := calldatasize()
            }
            uint256 calldataWords = (calldataSize + 31) / 32;
            // account for the CALLDATACOPY cost of the proxy contract, including the memory expansion cost
            startGasLeft += calldataWords * 6 + (calldataWords**2) / 512;
            // if triggered in a contract call, the spender may be overrefunded by appending dummy data to the call
            // so we check if it is a top level call, which would mean the sender paid calldata as part of tx.input
            // solhint-disable-next-line avoid-tx-origin
            if (msg.sender != tx.origin) {
                // We can't be sure if this calldata came from the top level tx,
                // so to be safe we tell the gas refunder there was no calldata.
                calldataSize = 0;
            }
            gasRefunder.onGasSpent(payable(msg.sender), startGasLeft - gasleft(), calldataSize);
        }
    }
}