Contract 0x0d29d9fa94a23e0d2f06efc79c25144a8f51fc4b 3

Txn Hash Method
Block
From
To
Value [Txn Fee]
0xbdf4fcc538e187de01c3e38ffac2503d2b6a8cd10a2e185a2bd404b08d58d23d0xd9459372243761732021-12-08 18:03:384 mins ago0x7c641abe40f9fcbf75614f428938e2ae241cd78d IN  xPollinate: Transaction Manager 20 FTM0.016369179963
0x8dec494e83ad80fe019e52ba70433601b34f36d2a56e575d0061ba55a75226a00x9b151a80243761402021-12-08 18:03:124 mins agoRouter: 0x7e7...7c9 IN  xPollinate: Transaction Manager 20 FTM0.012436888707
0x7ce883ff6ff5833be4f7f3a48699b988b550c8f3e52b1deca871d73f7f563b1c0x9b151a80243761142021-12-08 18:02:385 mins agoRouter: 0x29a...cf9 IN  xPollinate: Transaction Manager 20 FTM0.012252860911
0xf9b52f2e1570ab54cb5be7e83cfc46f5c80b425bb6db977753f8c9cfa5dccc720x9b151a80243760252021-12-08 18:00:536 mins agoRouter: 0x29a...cf9 IN  xPollinate: Transaction Manager 20 FTM0.013099733155
0xcf0c1241e3a53ea36abac82ea7990ac855bc5be189a886c0a1b9eb15a0d6c5320xd9459372243759942021-12-08 18:00:297 mins ago0x26586bb42b49cc04bef13c4c2800df8715e9e194 IN  xPollinate: Transaction Manager 20 FTM0.015020391595
0x0ffdf37469a671c4513526995b8767ee6aba4c4365bc10f82b62ac9da6c61eb60x9b151a80243759832021-12-08 18:00:217 mins agoRouter: 0x29a...cf9 IN  xPollinate: Transaction Manager 20 FTM0.013168277194
0x20e54dea0285e1563e327545bc772547f8611bf7caa0ffd673206d5d011a646a0x9b151a80243759832021-12-08 18:00:217 mins agoRouter: 0x29a...cf9 IN  xPollinate: Transaction Manager 20 FTM0.013168277194
0xd212ad5702bd04015e7377c2d58484d4e9fb1c87074028f650f5cdd678409a3f0x9b151a80243759382021-12-08 17:59:418 mins agoRouter: 0x29a...cf9 IN  xPollinate: Transaction Manager 20 FTM0.01319798121
0xb6560563506f710d73441b2292c63a1411fad59fec596519abbe45f9b67469880xd9459372243759302021-12-08 17:59:348 mins ago0x81664f4f1f467432832152dfc3023f8ba2221862 IN  xPollinate: Transaction Manager 20 FTM0.014812374454
0x5026379fdf073ee871c80533fdead7a0041baad4e0b9152a580da8e9353298100x9b151a80243759152021-12-08 17:59:238 mins agoRouter: 0x29a...cf9 IN  xPollinate: Transaction Manager 20 FTM0.013201168585
0x7e084d83ab9e90fc1761ef7b0671b2ef469f76e71943fdc82cc708e245730bcb0x9b151a80243758962021-12-08 17:59:078 mins agoRouter: 0x29a...cf9 IN  xPollinate: Transaction Manager 20 FTM0.013407300833
0x0093399de5538eb2de615e779b955e51769e8a72bfdb75c2ec79052859a16aba0x9b151a80243758802021-12-08 17:58:558 mins agoRouter: 0x29a...cf9 IN  xPollinate: Transaction Manager 20 FTM0.013407300833
0xaa10bfcef07a3554280a41f5a5784b354c37ae87c769b9f8580b7eb6d8663c320x9b151a80243758802021-12-08 17:58:558 mins agoRouter: 0x29a...cf9 IN  xPollinate: Transaction Manager 20 FTM0.013401777704
0x5ea303eef28bc9f9ed2b9e7b028b13c5d6128dc0bb8a8517a78a0fd17d23e96d0x9b151a80243758772021-12-08 17:58:528 mins agoRouter: 0x29a...cf9 IN  xPollinate: Transaction Manager 20 FTM0.013404063137
0x7c85b263c9c3be4d1aee447d0c157b3a8a55161c5c0884cb2fd5bf1550b6fc5a0x9b151a80243758582021-12-08 17:58:389 mins agoRouter: 0x29a...cf9 IN  xPollinate: Transaction Manager 20 FTM0.013361779259
0xe2b8cfc4f55d59c4965cb6a16ad8e3dd46df12e3d1b999b8fea8723640086cfb0xd9459372243758052021-12-08 17:57:549 mins ago0x3276be2e22253dc8e3cac855991c15c1bf8c74f5 IN  xPollinate: Transaction Manager 20 FTM0.015116561441
0xce4f8d1f8a3e8549fc7fb2945df54df859c155c10b85a62b748205aeabccd52c0xd9459372243757662021-12-08 17:57:2110 mins ago0x3276be2e22253dc8e3cac855991c15c1bf8c74f5 IN  xPollinate: Transaction Manager 20 FTM0.017328546941
0x86c0b7122a3ad56600bcbde75e5c9b19766e873a59a503c290b5b48b5f2666a00xd9459372243757482021-12-08 17:57:0310 mins ago0x3276be2e22253dc8e3cac855991c15c1bf8c74f5 IN  xPollinate: Transaction Manager 20 FTM0.016389520771
0xf63d6c18d43f72138326d66fe0e35a44a4e450eb924ba38a1ed53354e6034d1f0xd9459372243757092021-12-08 17:56:2911 mins ago0x3276be2e22253dc8e3cac855991c15c1bf8c74f5 IN  xPollinate: Transaction Manager 20 FTM0.016389520771
0x253074c1ec1daad1f2f593ba08172c61809de32fc215ba94d1e8d2c7a46e5a730xd9459372243756852021-12-08 17:56:0511 mins ago0x3276be2e22253dc8e3cac855991c15c1bf8c74f5 IN  xPollinate: Transaction Manager 20 FTM0.016389520771
0x1942a66d040dfb5b10d003bcab9447a2815b57069138cf1d8a7bcd72a504b0f00xd9459372243756832021-12-08 17:56:0111 mins agoRouter: 0x031...24f IN  xPollinate: Transaction Manager 20 FTM0.017968853408
0x08fda68ac2f514dd3b3b907f06196067fdc4ae077a277532a09a39f86ca3df3b0xd9459372243756662021-12-08 17:55:3312 mins ago0x3276be2e22253dc8e3cac855991c15c1bf8c74f5 IN  xPollinate: Transaction Manager 20 FTM0.016094834078
0x16658c5830dd60c49d2ae5d35220b71a8b2775e0e99154746da1a23680273c620xd9459372243756562021-12-08 17:55:0312 mins ago0x3276be2e22253dc8e3cac855991c15c1bf8c74f5 IN  xPollinate: Transaction Manager 20 FTM0.016098121953
0xe9e867354823775ff93d4a2c687be1dc992524f376909fcc28ff767663f63ebe0xd9459372243756302021-12-08 17:54:3513 mins ago0x3276be2e22253dc8e3cac855991c15c1bf8c74f5 IN  xPollinate: Transaction Manager 20 FTM0.017468976111
0x05cde0d6dc5ed5cafbcb056fff94c0e9a6a2ddecf102a16afc0a84adae7c228c0xd9459372243756022021-12-08 17:54:1313 mins ago0x3276be2e22253dc8e3cac855991c15c1bf8c74f5 IN  xPollinate: Transaction Manager 20 FTM0.017476114743
[ Download CSV Export 
Latest 2 internal transactions
Parent Txn Hash Block From To Value
0x1da7018de0385e6cc0c1e69dcdd5de5ca7e531cc146dfe897d55f82f8ff3ad5c182919012021-10-04 14:41:1365 days 3 hrs ago xPollinate: Transaction Manager 2  Contract Creation0 FTM
0x1da7018de0385e6cc0c1e69dcdd5de5ca7e531cc146dfe897d55f82f8ff3ad5c182919012021-10-04 14:41:1365 days 3 hrs ago 0x155b15a7e9ff0e34ceaf2439589d5c661adc9493  Contract Creation0 FTM
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
TransactionManager

Compiler Version
v0.8.4+commit.c7e474f2

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, Unlicense license

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 13 : Ownable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _setOwner(_msgSender());
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _setOwner(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _setOwner(newOwner);
    }

    function _setOwner(address newOwner) private {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 2 of 13 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

    constructor() {
        _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;
    }
}

File 3 of 13 : 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 4 of 13 : 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 5 of 13 : 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);
    }

    function _verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) private 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 6 of 13 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/*
 * @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 Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

File 7 of 13 : ECDSA.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        // Check the signature length
        // - case 65: r,s,v signature (standard)
        // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return recover(hash, v, r, s);
        } else if (signature.length == 64) {
            bytes32 r;
            bytes32 vs;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            assembly {
                r := mload(add(signature, 0x20))
                vs := mload(add(signature, 0x40))
            }
            return recover(hash, r, vs);
        } else {
            revert("ECDSA: invalid signature length");
        }
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.2._
     */
    function recover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address) {
        bytes32 s;
        uint8 v;
        assembly {
            s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
            v := add(shr(255, vs), 27)
        }
        return recover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`, `r` and `s` signature fields separately.
     */
    function recover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        require(
            uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
            "ECDSA: invalid signature 's' value"
        );
        require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        require(signer != address(0), "ECDSA: invalid signature");

        return signer;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}

File 8 of 13 : ProposedOwnable.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.4;

/**
 * @title ProposedOwnable
 * @notice Contract module which provides a basic access control mechanism, 
 * where there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed via a two step process:
 * 1. Call `proposeOwner`
 * 2. Wait out the delay period
 * 3. Call `acceptOwner`
 *
 * @dev This module is used through inheritance. It will make available the 
 * modifier `onlyOwner`, which can be applied to your functions to restrict 
 * their use to the owner.
 * 
 * @dev The majority of this code was taken from the openzeppelin Ownable 
 * contract
 *
 */
abstract contract ProposedOwnable {
  address private _owner;

  address private _proposed;
  uint256 private _proposedOwnershipTimestamp;

  bool private _routerOwnershipRenounced;
  uint256 private _routerOwnershipTimestamp;

  bool private _assetOwnershipRenounced;
  uint256 private _assetOwnershipTimestamp;

  uint256 private constant _delay = 7 days;

  event RouterOwnershipRenunciationProposed(uint256 timestamp);

  event RouterOwnershipRenounced(bool renounced);

  event AssetOwnershipRenunciationProposed(uint256 timestamp);

  event AssetOwnershipRenounced(bool renounced);

  event OwnershipProposed(address indexed proposedOwner);

  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

  /**
    * @notice Initializes the contract setting the deployer as the initial 
    * owner.
    */
  constructor() {
    _setOwner(msg.sender);
  }

  /**
    * @notice Returns the address of the current owner.
    */
  function owner() public view virtual returns (address) {
    return _owner;
  }

  /**
    * @notice Returns the address of the proposed owner.
    */
  function proposed() public view virtual returns (address) {
    return _proposed;
  }

  /**
    * @notice Returns the address of the proposed owner.
    */
  function proposedTimestamp() public view virtual returns (uint256) {
    return _proposedOwnershipTimestamp;
  }

  /**
    * @notice Returns the timestamp when router ownership was last proposed to be renounced
    */
  function routerOwnershipTimestamp() public view virtual returns (uint256) {
    return _routerOwnershipTimestamp;
  }

  /**
    * @notice Returns the timestamp when asset ownership was last proposed to be renounced
    */
  function assetOwnershipTimestamp() public view virtual returns (uint256) {
    return _assetOwnershipTimestamp;
  }

  /**
    * @notice Returns the delay period before a new owner can be accepted.
    */
  function delay() public view virtual returns (uint256) {
    return _delay;
  }

  /**
    * @notice Throws if called by any account other than the owner.
    */
  modifier onlyOwner() {
      require(_owner == msg.sender, "#OO:029");
      _;
  }

  /**
    * @notice Throws if called by any account other than the proposed owner.
    */
  modifier onlyProposed() {
      require(_proposed == msg.sender, "#OP:035");
      _;
  }

  /** 
    * @notice Indicates if the ownership of the router whitelist has
    * been renounced
    */
  function isRouterOwnershipRenounced() public view returns (bool) {
    return _owner == address(0) || _routerOwnershipRenounced;
  }

  /** 
    * @notice Indicates if the ownership of the router whitelist has
    * been renounced
    */
  function proposeRouterOwnershipRenunciation() public virtual onlyOwner {
    // Use contract as source of truth
    // Will fail if all ownership is renounced by modifier
    require(!_routerOwnershipRenounced, "#PROR:038");

    // Begin delay, emit event
    _setRouterOwnershipTimestamp();
  }

  /** 
    * @notice Indicates if the ownership of the asset whitelist has
    * been renounced
    */
  function renounceRouterOwnership() public virtual onlyOwner {
    // Contract as sournce of truth
    // Will fail if all ownership is renounced by modifier
    require(!_routerOwnershipRenounced, "#RRO:038");

    // Ensure there has been a proposal cycle started
    require(_routerOwnershipTimestamp > 0, "#RRO:037");

    // Delay has elapsed
    require((block.timestamp - _routerOwnershipTimestamp) > _delay, "#RRO:030");

    // Set renounced, emit event, reset timestamp to 0
    _setRouterOwnership(true);
  }

  /** 
    * @notice Indicates if the ownership of the asset whitelist has
    * been renounced
    */
  function isAssetOwnershipRenounced() public view returns (bool) {
    return _owner == address(0) || _assetOwnershipRenounced;
  }

  /** 
    * @notice Indicates if the ownership of the asset whitelist has
    * been renounced
    */
  function proposeAssetOwnershipRenunciation() public virtual onlyOwner {
    // Contract as sournce of truth
    // Will fail if all ownership is renounced by modifier
    require(!_assetOwnershipRenounced, "#PAOR:038");

    // Start cycle, emit event
    _setAssetOwnershipTimestamp();
  }

  /** 
    * @notice Indicates if the ownership of the asset whitelist has
    * been renounced
    */
  function renounceAssetOwnership() public virtual onlyOwner {
    // Contract as sournce of truth
    // Will fail if all ownership is renounced by modifier
    require(!_assetOwnershipRenounced, "#RAO:038");

    // Ensure there has been a proposal cycle started
    require(_assetOwnershipTimestamp > 0, "#RAO:037");

    // Ensure delay has elapsed
    require((block.timestamp - _assetOwnershipTimestamp) > _delay, "#RAO:030");

    // Set ownership, reset timestamp, emit event
    _setAssetOwnership(true);
  }

  /** 
    * @notice Indicates if the ownership has been renounced() by
    * checking if current owner is address(0)
    */
  function renounced() public view returns (bool) {
    return _owner == address(0);
  }

  /**
    * @notice Sets the timestamp for an owner to be proposed, and sets the
    * newly proposed owner as step 1 in a 2-step process
   */
  function proposeNewOwner(address newlyProposed) public virtual onlyOwner {
    // Contract as source of truth
    require(_proposed != newlyProposed || newlyProposed == address(0), "#PNO:036");

    // Sanity check: reasonable proposal
    require(_owner != newlyProposed, "#PNO:038");

    _setProposed(newlyProposed);
  }

  /**
    * @notice Renounces ownership of the contract after a delay
    */
  function renounceOwnership() public virtual onlyOwner {
    // Ensure there has been a proposal cycle started
    require(_proposedOwnershipTimestamp > 0, "#RO:037");

    // Ensure delay has elapsed
    require((block.timestamp - _proposedOwnershipTimestamp) > _delay, "#RO:030");

    // Require proposed is set to 0
    require(_proposed == address(0), "#RO:036");

    // Emit event, set new owner, reset timestamp
    _setOwner(_proposed);
  }

  /**
    * @notice Transfers ownership of the contract to a new account (`newOwner`).
    * Can only be called by the current owner.
    */
  function acceptProposedOwner() public virtual onlyProposed {
    // Contract as source of truth
    require(_owner != _proposed, "#APO:038");

    // NOTE: no need to check if _proposedOwnershipTimestamp > 0 because
    // the only time this would happen is if the _proposed was never
    // set (will fail from modifier) or if the owner == _proposed (checked
    // above)

    // Ensure delay has elapsed
    require((block.timestamp - _proposedOwnershipTimestamp) > _delay, "#APO:030");

    // Emit event, set new owner, reset timestamp
    _setOwner(_proposed);
  }

  ////// INTERNAL //////

  function _setRouterOwnershipTimestamp() private {
    _routerOwnershipTimestamp = block.timestamp;
    emit RouterOwnershipRenunciationProposed(_routerOwnershipTimestamp);
  }

  function _setRouterOwnership(bool value) private {
    _routerOwnershipRenounced = value;
    _routerOwnershipTimestamp = 0;
    emit RouterOwnershipRenounced(value);
  }

  function _setAssetOwnershipTimestamp() private {
    _assetOwnershipTimestamp = block.timestamp;
    emit AssetOwnershipRenunciationProposed(_assetOwnershipTimestamp);
  }

  function _setAssetOwnership(bool value) private {
    _assetOwnershipRenounced = value;
    _assetOwnershipTimestamp = 0;
    emit AssetOwnershipRenounced(value);
  }

  function _setOwner(address newOwner) private {
    address oldOwner = _owner;
    _owner = newOwner;
    _proposedOwnershipTimestamp = 0;
    emit OwnershipTransferred(oldOwner, newOwner);
  }

  function _setProposed(address newlyProposed) private {
    _proposedOwnershipTimestamp = block.timestamp;
    _proposed = newlyProposed;
    emit OwnershipProposed(_proposed);
  }
}

File 9 of 13 : TransactionManager.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.4;

import "./interfaces/IFulfillInterpreter.sol";
import "./interfaces/ITransactionManager.sol";
import "./interpreters/FulfillInterpreter.sol";
import "./ProposedOwnable.sol";
import "./lib/LibAsset.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";


/**
  *
  * @title TransactionManager
  * @author Connext <[email protected]>
  * @notice This contract holds the logic to facilitate crosschain transactions.
  *         Transactions go through three phases in the happy case:
  *
  *         1. Route Auction (offchain): User broadcasts to our network 
  *         signalling their desired route. Routers respond with sealed bids 
  *         containing commitments to fulfilling the transaction within a 
  *         certain time and price range.
  *
  *         2. Prepare: Once the auction is completed, the transaction can be 
  *         prepared. The user submits a transaction to `TransactionManager` 
  *         contract on sender-side chain containing router's signed bid. This 
  *         transaction locks up the users funds on the sending chain. Upon 
  *         detecting an event containing their signed bid from the chain, 
  *         router submits the same transaction to `TransactionManager` on the 
  *         receiver-side chain, and locks up a corresponding amount of 
  *         liquidity. The amount locked on the receiving chain is `sending 
  *         amount - auction fee` so the router is incentivized to complete the 
  *         transaction.
  *
  *         3. Fulfill: Upon detecting the `TransactionPrepared` event on the 
  *         receiver-side chain, the user signs a message and sends it to a 
  *         relayer, who will earn a fee for submission. The relayer (which may 
  *         be the router) then submits the message to the `TransactionManager` 
  *         to complete their transaction on receiver-side chain and claim the 
  *         funds locked by the router. A relayer is used here to allow users 
  *         to submit transactions with arbitrary calldata on the receiving 
  *         chain without needing gas to do so. The router then submits the 
  *         same signed message and completes transaction on sender-side, 
  *         unlocking the original `amount`.
  *
  *         If a transaction is not fulfilled within a fixed timeout, it 
  *         reverts and can be reclaimed by the party that called `prepare` on 
  *         each chain (initiator). Additionally, transactions can be cancelled 
  *         unilaterally by the person owed funds on that chain (router for 
  *         sending chain, user for receiving chain) prior to expiry.
  */
contract TransactionManager is ReentrancyGuard, ProposedOwnable, ITransactionManager {
  /**
   * @dev Mapping of router to balance specific to asset
   */
  mapping(address => mapping(address => uint256)) public routerBalances;

  /**
    * @dev Mapping of allowed router addresses. Must be added to both
    *      sending and receiving chains when forwarding a transfer.
    */
  mapping(address => bool) public approvedRouters;

  /**
    * @dev Mapping of allowed assetIds on same chain as contract
    */
  mapping(address => bool) public approvedAssets;

  /**
    * @dev Mapping of hash of `InvariantTransactionData` to the hash
    *      of the `VariantTransactionData`
    */
  mapping(bytes32 => bytes32) public variantTransactionData;

  /**
  * @dev The stored chain id of the contract, may be passed in to avoid any 
  *      evm issues
  */
  uint256 private immutable chainId;

  /**
    * @dev Minimum timeout (will be the lowest on the receiving chain)
    */
  uint256 public constant MIN_TIMEOUT = 1 days; // 24 hours

  /**
    * @dev Maximum timeout (will be the highest on the sending chain)
    */
  uint256 public constant MAX_TIMEOUT = 30 days; // 720 hours

  /**
    * @dev The external contract that will execute crosschain
    *      calldata
    */
  IFulfillInterpreter public immutable interpreter;

  constructor(uint256 _chainId) {
    chainId = _chainId;
    interpreter = new FulfillInterpreter(address(this));
  }

  /** 
   * @notice Gets the chainId for this contract. If not specified during init
   *         will use the block.chainId
   */
  function getChainId() public view override returns (uint256 _chainId) {
    // Hold in memory to reduce sload calls
    uint256 chain = chainId;
    if (chain == 0) {
      // If not provided, pull from block
      chain = block.chainid;
    }
    return chain;
  }

  /**
   * @notice Allows us to get the chainId that this contract has stored
   */
  function getStoredChainId() external view override returns (uint256) {
    return chainId;
  }

  /**
    * @notice Used to add routers that can transact crosschain
    * @param router Router address to add
    */
  function addRouter(address router) external override onlyOwner {
    // Sanity check: not empty
    require(router != address(0), "#AR:001");

    // Sanity check: needs approval
    require(approvedRouters[router] == false, "#AR:032");

    // Update mapping
    approvedRouters[router] = true;

    // Emit event
    emit RouterAdded(router, msg.sender);
  }

  /**
    * @notice Used to remove routers that can transact crosschain
    * @param router Router address to remove
    */
  function removeRouter(address router) external override onlyOwner {
    // Sanity check: not empty
    require(router != address(0), "#RR:001");

    // Sanity check: needs removal
    require(approvedRouters[router] == true, "#RR:033");

    // Update mapping
    approvedRouters[router] = false;

    // Emit event
    emit RouterRemoved(router, msg.sender);
  }

  /**
    * @notice Used to add assets on same chain as contract that can
    *         be transferred.
    * @param assetId AssetId to add
    */
  function addAssetId(address assetId) external override onlyOwner {
    // Sanity check: needs approval
    require(approvedAssets[assetId] == false, "#AA:032");

    // Update mapping
    approvedAssets[assetId] = true;

    // Emit event
    emit AssetAdded(assetId, msg.sender);
  }

  /**
    * @notice Used to remove assets on same chain as contract that can
    *         be transferred.
    * @param assetId AssetId to remove
    */
  function removeAssetId(address assetId) external override onlyOwner {
    // Sanity check: already approval
    require(approvedAssets[assetId] == true, "#RA:033");

    // Update mapping
    approvedAssets[assetId] = false;

    // Emit event
    emit AssetRemoved(assetId, msg.sender);
  }

  /**
    * @notice This is used by anyone to increase a router's available
    *         liquidity for a given asset.
    * @param amount The amount of liquidity to add for the router
    * @param assetId The address (or `address(0)` if native asset) of the
    *                asset you're adding liquidity for
    * @param router The router you are adding liquidity on behalf of
    */
  function addLiquidityFor(uint256 amount, address assetId, address router) external payable override nonReentrant {
    _addLiquidityForRouter(amount, assetId, router);
  }

  /**
    * @notice This is used by any router to increase their available
    *         liquidity for a given asset.
    * @param amount The amount of liquidity to add for the router
    * @param assetId The address (or `address(0)` if native asset) of the
    *                asset you're adding liquidity for
    */
  function addLiquidity(uint256 amount, address assetId) external payable override nonReentrant {
    _addLiquidityForRouter(amount, assetId, msg.sender);
  }

  /**
    * @notice This is used by any router to decrease their available
    *         liquidity for a given asset.
    * @param amount The amount of liquidity to remove for the router
    * @param assetId The address (or `address(0)` if native asset) of the
    *                asset you're removing liquidity for
    * @param recipient The address that will receive the liquidity being removed
    */
  function removeLiquidity(
    uint256 amount,
    address assetId,
    address payable recipient
  ) external override nonReentrant {
    // Sanity check: recipient is sensible
    require(recipient != address(0), "#RL:007");

    // Sanity check: nonzero amounts
    require(amount > 0, "#RL:002");

    uint256 routerBalance = routerBalances[msg.sender][assetId];
    // Sanity check: amount can be deducted for the router
    require(routerBalance >= amount, "#RL:008");

    // Update router balances
    unchecked {
      routerBalances[msg.sender][assetId] = routerBalance - amount;
    }

    // Transfer from contract to specified recipient
    LibAsset.transferAsset(assetId, recipient, amount);

    // Emit event
    emit LiquidityRemoved(msg.sender, assetId, amount, recipient);
  }

  /**
    * @notice This function creates a crosschain transaction. When called on
    *         the sending chain, the user is expected to lock up funds. When
    *         called on the receiving chain, the router deducts the transfer
    *         amount from the available liquidity. The majority of the
    *         information about a given transfer does not change between chains,
    *         with three notable exceptions: `amount`, `expiry`, and 
    *         `preparedBlock`. The `amount` and `expiry` are decremented
    *         between sending and receiving chains to provide an incentive for 
    *         the router to complete the transaction and time for the router to
    *         fulfill the transaction on the sending chain after the unlocking
    *         signature is revealed, respectively.
    * @param args TODO
    */
  function prepare(
    PrepareArgs calldata args
  ) external payable override nonReentrant returns (TransactionData memory) {
    // Sanity check: user is sensible
    require(args.invariantData.user != address(0), "#P:009");

    // Sanity check: router is sensible
    require(args.invariantData.router != address(0), "#P:001");

    // Router is approved *on both chains*
    require(isRouterOwnershipRenounced() || approvedRouters[args.invariantData.router], "#P:003");

    // Sanity check: sendingChainFallback is sensible
    require(args.invariantData.sendingChainFallback != address(0), "#P:010");

    // Sanity check: valid fallback
    require(args.invariantData.receivingAddress != address(0), "#P:026");

    // Make sure the chains are different
    require(args.invariantData.sendingChainId != args.invariantData.receivingChainId, "#P:011");

    // Make sure the chains are relevant
    uint256 _chainId = getChainId();
    require(args.invariantData.sendingChainId == _chainId || args.invariantData.receivingChainId == _chainId, "#P:012");

    { // Expiry scope
      // Make sure the expiry is greater than min
      uint256 buffer = args.expiry - block.timestamp;
      require(buffer >= MIN_TIMEOUT, "#P:013");

      // Make sure the expiry is lower than max
      require(buffer <= MAX_TIMEOUT, "#P:014");
    }

    // Make sure the hash is not a duplicate
    bytes32 digest = keccak256(abi.encode(args.invariantData));
    require(variantTransactionData[digest] == bytes32(0), "#P:015");

    // NOTE: the `encodedBid` and `bidSignature` are simply passed through
    //       to the contract emitted event to ensure the availability of
    //       this information. Their validity is asserted offchain, and
    //       is out of scope of this contract. They are used as inputs so
    //       in the event of a router or user crash, they may recover the
    //       correct bid information without requiring an offchain store.

    // Amount actually used (if fee-on-transfer will be different than
    // supplied)
    uint256 amount = args.amount;

    // First determine if this is sender side or receiver side
    if (args.invariantData.sendingChainId == _chainId) {
      // Check the sender is correct
      require(msg.sender == args.invariantData.initiator, "#P:039");

      // Sanity check: amount is sensible
      // Only check on sending chain to enforce router fees. Transactions could
      // be 0-valued on receiving chain if it is just a value-less call to some
      // `IFulfillHelper`
      require(args.amount > 0, "#P:002");

      // Assets are approved
      // NOTE: Cannot check this on receiving chain because of differing
      // chain contexts
      require(isAssetOwnershipRenounced() || approvedAssets[args.invariantData.sendingAssetId], "#P:004");

      // This is sender side prepare. The user is beginning the process of 
      // submitting an onchain tx after accepting some bid. They should
      // lock their funds in the contract for the router to claim after
      // they have revealed their signature on the receiving chain via
      // submitting a corresponding `fulfill` tx

      // Validate correct amounts on msg and transfer from user to
      // contract
      amount = transferAssetToContract(
        args.invariantData.sendingAssetId,
        args.amount
      );

      // Store the transaction variants. This happens after transferring to
      // account for fee on transfer tokens
      variantTransactionData[digest] = hashVariantTransactionData(
        amount,
        args.expiry,
        block.number
      );
    } else {
      // This is receiver side prepare. The router has proposed a bid on the
      // transfer which the user has accepted. They can now lock up their
      // own liquidity on th receiving chain, which the user can unlock by
      // calling `fulfill`. When creating the `amount` and `expiry` on the
      // receiving chain, the router should have decremented both. The
      // expiry should be decremented to ensure the router has time to
      // complete the sender-side transaction after the user completes the
      // receiver-side transactoin. The amount should be decremented to act as
      // a fee to incentivize the router to complete the transaction properly.

      // Check that the callTo is a contract
      // NOTE: This cannot happen on the sending chain (different chain 
      // contexts), so a user could mistakenly create a transfer that must be
      // cancelled if this is incorrect
      require(args.invariantData.callTo == address(0) || Address.isContract(args.invariantData.callTo), "#P:031");

      // Check that the asset is approved
      // NOTE: This cannot happen on both chains because of differing chain 
      // contexts. May be possible for user to create transaction that is not
      // prepare-able on the receiver chain.
      require(isAssetOwnershipRenounced() || approvedAssets[args.invariantData.receivingAssetId], "#P:004");

      // Check that the caller is the router
      require(msg.sender == args.invariantData.router, "#P:016");

      // Check that the router isnt accidentally locking funds in the contract
      require(msg.value == 0, "#P:017");

      // Check that router has liquidity
      uint256 balance = routerBalances[args.invariantData.router][args.invariantData.receivingAssetId];
      require(balance >= amount, "#P:018");

      // Store the transaction variants
      variantTransactionData[digest] = hashVariantTransactionData(
        amount,
        args.expiry,
        block.number
      );

      // Decrement the router liquidity
      // using unchecked because underflow protected against with require
      unchecked {
        routerBalances[args.invariantData.router][args.invariantData.receivingAssetId] = balance - amount;
      }
    }

    // Emit event
    TransactionData memory txData = TransactionData({
      receivingChainTxManagerAddress: args.invariantData.receivingChainTxManagerAddress,
      user: args.invariantData.user,
      router: args.invariantData.router,
      initiator: args.invariantData.initiator,
      sendingAssetId: args.invariantData.sendingAssetId,
      receivingAssetId: args.invariantData.receivingAssetId,
      sendingChainFallback: args.invariantData.sendingChainFallback,
      callTo: args.invariantData.callTo,
      receivingAddress: args.invariantData.receivingAddress,
      callDataHash: args.invariantData.callDataHash,
      transactionId: args.invariantData.transactionId,
      sendingChainId: args.invariantData.sendingChainId,
      receivingChainId: args.invariantData.receivingChainId,
      amount: amount,
      expiry: args.expiry,
      preparedBlockNumber: block.number
    });

    emit TransactionPrepared(
      txData.user,
      txData.router,
      txData.transactionId,
      txData,
      msg.sender,
      args
    );

    return txData;
  }



    /**
    * @notice This function completes a crosschain transaction. When called on
    *         the receiving chain, the user reveals their signature on the
    *         transactionId and is sent the amount corresponding to the number
    *         of shares the router locked when calling `prepare`. The router 
    *         then uses this signature to unlock the corresponding funds on the 
    *         receiving chain, which are then added back to their available 
    *         liquidity. The user includes a relayer fee since it is not 
    *         assumed they will have gas on the receiving chain. This function 
    *         *must* be called before the transaction expiry has elapsed.
    * @param args TODO
    */
  function fulfill(
    FulfillArgs calldata args
  ) external override nonReentrant returns (TransactionData memory) {
    // Get the hash of the invariant tx data. This hash is the same
    // between sending and receiving chains. The variant data is stored
    // in the contract when `prepare` is called within the mapping.

    { // scope: validation and effects
      bytes32 digest = hashInvariantTransactionData(args.txData);

      // Make sure that the variant data matches what was stored
      require(variantTransactionData[digest] == hashVariantTransactionData(
        args.txData.amount,
        args.txData.expiry,
        args.txData.preparedBlockNumber
      ), "#F:019");

      // Make sure the expiry has not elapsed
      require(args.txData.expiry >= block.timestamp, "#F:020");

      // Make sure the transaction wasn't already completed
      require(args.txData.preparedBlockNumber > 0, "#F:021");

      // Check provided callData matches stored hash
      require(keccak256(args.callData) == args.txData.callDataHash, "#F:024");

      // To prevent `fulfill` / `cancel` from being called multiple times, the
      // preparedBlockNumber is set to 0 before being hashed. The value of the
      // mapping is explicitly *not* zeroed out so users who come online without
      // a store can tell the difference between a transaction that has not been
      // prepared, and a transaction that was already completed on the receiver
      // chain.
      variantTransactionData[digest] = hashVariantTransactionData(
        args.txData.amount,
        args.txData.expiry,
        0
      );
    }

    // Declare these variables for the event emission. Are only assigned
    // IFF there is an external call on the receiving chain
    bool success;
    bool isContract;
    bytes memory returnData;

    uint256 _chainId = getChainId();

    if (args.txData.sendingChainId == _chainId) {
      // The router is completing the transaction, they should get the
      // amount that the user deposited credited to their liquidity
      // reserves.

      // Make sure that the user is not accidentally fulfilling the transaction
      // on the sending chain
      require(msg.sender == args.txData.router, "#F:016");

      // Validate the user has signed
      require(
        recoverFulfillSignature(
          args.txData.transactionId,
          args.relayerFee,
          args.txData.receivingChainId,
          args.txData.receivingChainTxManagerAddress,
          args.signature
        ) == args.txData.user, "#F:022"
      );

      // Complete tx to router for original sending amount
      routerBalances[args.txData.router][args.txData.sendingAssetId] += args.txData.amount;

    } else {
      // Validate the user has signed, using domain of contract
      require(
        recoverFulfillSignature(
          args.txData.transactionId,
          args.relayerFee,
          _chainId,
          address(this),
          args.signature
        ) == args.txData.user, "#F:022"
      );

      // Sanity check: fee <= amount. Allow `=` in case of only 
      // wanting to execute 0-value crosschain tx, so only providing 
      // the fee amount
      require(args.relayerFee <= args.txData.amount, "#F:023");

      (success, isContract, returnData) = _receivingChainFulfill(
        args.txData,
        args.relayerFee,
        args.callData
      );
    }

    // Emit event
    emit TransactionFulfilled(
      args.txData.user,
      args.txData.router,
      args.txData.transactionId,
      args,
      success,
      isContract,
      returnData,
      msg.sender
    );

    return args.txData;
  }

  /**
    * @notice Any crosschain transaction can be cancelled after it has been
    *         created to prevent indefinite lock up of funds. After the
    *         transaction has expired, anyone can cancel it. Before the
    *         expiry, only the recipient of the funds on the given chain is
    *         able to cancel. On the sending chain, this means only the router
    *         is able to cancel before the expiry, while only the user can
    *         prematurely cancel on the receiving chain.
    * @param args TODO
    */
  function cancel(CancelArgs calldata args)
    external
    override
    nonReentrant
    returns (TransactionData memory)
  {
    // Make sure params match against stored data
    // Also checks that there is an active transfer here
    // Also checks that sender or receiver chainID is this chainId (bc we checked it previously)

    // Get the hash of the invariant tx data. This hash is the same
    // between sending and receiving chains. The variant data is stored
    // in the contract when `prepare` is called within the mapping.
    bytes32 digest = hashInvariantTransactionData(args.txData);

    // Verify the variant data is correct
    require(variantTransactionData[digest] == hashVariantTransactionData(args.txData.amount, args.txData.expiry, args.txData.preparedBlockNumber), "#C:019");

    // Make sure the transaction wasn't already completed
    require(args.txData.preparedBlockNumber > 0, "#C:021");

    // To prevent `fulfill` / `cancel` from being called multiple times, the
    // preparedBlockNumber is set to 0 before being hashed. The value of the
    // mapping is explicitly *not* zeroed out so users who come online without
    // a store can tell the difference between a transaction that has not been
    // prepared, and a transaction that was already completed on the receiver
    // chain.
    variantTransactionData[digest] = hashVariantTransactionData(args.txData.amount, args.txData.expiry, 0);

    // Get chainId for gas
    uint256 _chainId = getChainId();

    // Return the appropriate locked funds
    if (args.txData.sendingChainId == _chainId) {
      // Sender side, funds must be returned to the user
      if (args.txData.expiry >= block.timestamp) {
        // Timeout has not expired and tx may only be cancelled by router
        // NOTE: no need to validate the signature here, since you are requiring
        // the router must be the sender when the cancellation is during the
        // fulfill-able window
        require(msg.sender == args.txData.router, "#C:025");
      }

      // Return users locked funds
      // NOTE: no need to check if amount > 0 because cant be prepared on
      // sending chain with 0 value
      LibAsset.transferAsset(
        args.txData.sendingAssetId,
        payable(args.txData.sendingChainFallback),
        args.txData.amount
      );

    } else {
      // Receiver side, router liquidity is returned
      if (args.txData.expiry >= block.timestamp) {
        // Timeout has not expired and tx may only be cancelled by user
        // Validate signature
        require(msg.sender == args.txData.user || recoverCancelSignature(args.txData.transactionId, _chainId, address(this), args.signature) == args.txData.user, "#C:022");

        // NOTE: there is no incentive here for relayers to submit this on
        // behalf of the user (i.e. fee not respected) because the user has not
        // locked funds on this contract. However, if the user reveals their
        // cancel signature to the router, they are incentivized to submit it
        // to unlock their own funds
      }

      // Return liquidity to router
      routerBalances[args.txData.router][args.txData.receivingAssetId] += args.txData.amount;
    }

    // Emit event
    emit TransactionCancelled(
      args.txData.user,
      args.txData.router,
      args.txData.transactionId,
      args,
      msg.sender
    );

    // Return
    return args.txData;
  }

  //////////////////////////
  /// Private functions ///
  //////////////////////////

  /**
    * @notice Contains the logic to verify + increment a given routers liquidity
    * @param amount The amount of liquidity to add for the router
    * @param assetId The address (or `address(0)` if native asset) of the
    *                asset you're adding liquidity for
    * @param router The router you are adding liquidity on behalf of
    */
  function _addLiquidityForRouter(
    uint256 amount,
    address assetId,
    address router
  ) internal {
    // Sanity check: router is sensible
    require(router != address(0), "#AL:001");

    // Sanity check: nonzero amounts
    require(amount > 0, "#AL:002");

    // Router is approved
    require(isRouterOwnershipRenounced() || approvedRouters[router], "#AL:003");

    // Asset is approved
    require(isAssetOwnershipRenounced() || approvedAssets[assetId], "#AL:004");

    // Transfer funds to contract
    amount = transferAssetToContract(assetId, amount);

    // Update the router balances. Happens after pulling funds to account for
    // the fee on transfer tokens
    routerBalances[router][assetId] += amount;

    // Emit event
    emit LiquidityAdded(router, assetId, amount, msg.sender);
  }

  /**
   * @notice Handles transferring funds from msg.sender to the
   *         transaction manager contract. Used in prepare, addLiquidity
   * @param assetId The address to transfer
   * @param specifiedAmount The specified amount to transfer. May not be the 
   *                        actual amount transferred (i.e. fee on transfer 
   *                        tokens)
   */
  function transferAssetToContract(address assetId, uint256 specifiedAmount) internal returns (uint256) {
    uint256 trueAmount = specifiedAmount;

    // Validate correct amounts are transferred
    if (LibAsset.isNativeAsset(assetId)) {
      require(msg.value == specifiedAmount, "#TA:005");
    } else {
      uint256 starting = LibAsset.getOwnBalance(assetId);
      require(msg.value == 0, "#TA:006");
      LibAsset.transferFromERC20(assetId, msg.sender, address(this), specifiedAmount);
      // Calculate the *actual* amount that was sent here
      trueAmount = LibAsset.getOwnBalance(assetId) - starting;
    }

    return trueAmount;
  }

  /// @notice Recovers the signer from the signature provided by the user
  /// @param transactionId Transaction identifier of tx being recovered
  /// @param signature The signature you are recovering the signer from
  function recoverCancelSignature(
    bytes32 transactionId,
    uint256 receivingChainId,
    address receivingChainTxManagerAddress,
    bytes calldata signature
  ) internal pure returns (address) {
    // Create the signed payload
    SignedCancelData memory payload = SignedCancelData({
      transactionId: transactionId,
      functionIdentifier: "cancel",
      receivingChainId: receivingChainId,
      receivingChainTxManagerAddress: receivingChainTxManagerAddress
    });

    // Recover
    return recoverSignature(abi.encode(payload), signature);
  }

  /**
    * @notice Recovers the signer from the signature provided by the user
    * @param transactionId Transaction identifier of tx being recovered
    * @param relayerFee The fee paid to the relayer for submitting the
    *                   tx on behalf of the user.
    * @param signature The signature you are recovering the signer from
    */
  function recoverFulfillSignature(
    bytes32 transactionId,
    uint256 relayerFee,
    uint256 receivingChainId,
    address receivingChainTxManagerAddress,
    bytes calldata signature
  ) internal pure returns (address) {
    // Create the signed payload
    SignedFulfillData memory payload = SignedFulfillData({
      transactionId: transactionId,
      relayerFee: relayerFee,
      functionIdentifier: "fulfill",
      receivingChainId: receivingChainId,
      receivingChainTxManagerAddress: receivingChainTxManagerAddress
    });

    // Recover
    return recoverSignature(abi.encode(payload), signature);
  }

  /**
    * @notice Holds the logic to recover the signer from an encoded payload.
    *         Will hash and convert to an eth signed message.
    * @param encodedPayload The payload that was signed
    * @param signature The signature you are recovering the signer from
    */
  function recoverSignature(bytes memory encodedPayload, bytes calldata  signature) internal pure returns (address) {
    // Recover
    return ECDSA.recover(
      ECDSA.toEthSignedMessageHash(keccak256(encodedPayload)),
      signature
    );
  }

  /**
    * @notice Returns the hash of only the invariant portions of a given
    *         crosschain transaction
    * @param txData TransactionData to hash
    */
  function hashInvariantTransactionData(TransactionData calldata txData) internal pure returns (bytes32) {
    InvariantTransactionData memory invariant = InvariantTransactionData({
      receivingChainTxManagerAddress: txData.receivingChainTxManagerAddress,
      user: txData.user,
      router: txData.router,
      initiator: txData.initiator,
      sendingAssetId: txData.sendingAssetId,
      receivingAssetId: txData.receivingAssetId,
      sendingChainFallback: txData.sendingChainFallback,
      callTo: txData.callTo,
      receivingAddress: txData.receivingAddress,
      sendingChainId: txData.sendingChainId,
      receivingChainId: txData.receivingChainId,
      callDataHash: txData.callDataHash,
      transactionId: txData.transactionId
    });
    return keccak256(abi.encode(invariant));
  }

  /**
    * @notice Returns the hash of only the variant portions of a given
    *         crosschain transaction
    * @param amount amount to hash
    * @param expiry expiry to hash
    * @param preparedBlockNumber preparedBlockNumber to hash
    * @return Hash of the variant data
    *
    */
  function hashVariantTransactionData(uint256 amount, uint256 expiry, uint256 preparedBlockNumber) internal pure returns (bytes32) {
    VariantTransactionData memory variant = VariantTransactionData({
      amount: amount,
      expiry: expiry,
      preparedBlockNumber: preparedBlockNumber
    });
    return keccak256(abi.encode(variant));
  }

  /**
   * @notice Handles the receiving-chain fulfillment. This function should
   *         pay the relayer and either send funds to the specified address
   *         or execute the calldata. Will return a tuple of boolean,bytes
   *         indicating the success and return data of the external call.
   * @dev Separated from fulfill function to avoid stack too deep errors
   *
   * @param txData The TransactionData that needs to be fulfilled
   * @param relayerFee The fee to be paid to the relayer for submission
   * @param callData The data to be executed on the receiving chain
   *
   * @return Tuple representing (success, returnData) of the external call
   */
  function _receivingChainFulfill(
    TransactionData calldata txData,
    uint256 relayerFee,
    bytes calldata callData
  ) internal returns (bool, bool, bytes memory) {
    // The user is completing the transaction, they should get the
    // amount that the router deposited less fees for relayer.

    // Get the amount to send
    uint256 toSend;
    unchecked {
      toSend = txData.amount - relayerFee;
    }

    // Send the relayer the fee
    if (relayerFee > 0) {
      LibAsset.transferAsset(txData.receivingAssetId, payable(msg.sender), relayerFee);
    }

    // Handle receiver chain external calls if needed
    if (txData.callTo == address(0)) {
      // No external calls, send directly to receiving address
      if (toSend > 0) {
        LibAsset.transferAsset(txData.receivingAssetId, payable(txData.receivingAddress), toSend);
      }
      return (false, false, new bytes(0));
    } else {
      // Handle external calls with a fallback to the receiving
      // address in case the call fails so the funds dont remain
      // locked.

      bool isNativeAsset = LibAsset.isNativeAsset(txData.receivingAssetId);

      // First, transfer the funds to the helper if needed
      if (!isNativeAsset && toSend > 0) {
        LibAsset.transferERC20(txData.receivingAssetId, address(interpreter), toSend);
      }

      // Next, call `execute` on the helper. Helpers should internally
      // track funds to make sure no one user is able to take all funds
      // for tx, and handle the case of reversions
      return interpreter.execute{ value: isNativeAsset ? toSend : 0}(
        txData.transactionId,
        payable(txData.callTo),
        txData.receivingAssetId,
        payable(txData.receivingAddress),
        toSend,
        callData
      );
    }
  }
}

File 10 of 13 : IFulfillInterpreter.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.4;

interface IFulfillInterpreter {

  event Executed(
    bytes32 indexed transactionId,
    address payable callTo,
    address assetId,
    address payable fallbackAddress,
    uint256 amount,
    bytes callData,
    bytes returnData,
    bool success,
    bool isContract
  );

  function getTransactionManager() external returns (address);

  function execute(
    bytes32 transactionId,
    address payable callTo,
    address assetId,
    address payable fallbackAddress,
    uint256 amount,
    bytes calldata callData
  ) external payable returns (bool success, bool isContract, bytes memory returnData);
}

File 11 of 13 : ITransactionManager.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.4;

interface ITransactionManager {
  // Structs

  // Holds all data that is constant between sending and
  // receiving chains. The hash of this is what gets signed
  // to ensure the signature can be used on both chains.
  struct InvariantTransactionData {
    address receivingChainTxManagerAddress;
    address user;
    address router;
    address initiator; // msg.sender of sending side
    address sendingAssetId;
    address receivingAssetId;
    address sendingChainFallback; // funds sent here on cancel
    address receivingAddress;
    address callTo;
    uint256 sendingChainId;
    uint256 receivingChainId;
    bytes32 callDataHash; // hashed to prevent free option
    bytes32 transactionId;
  }

  // Holds all data that varies between sending and receiving
  // chains. The hash of this is stored onchain to ensure the
  // information passed in is valid.
  struct VariantTransactionData {
    uint256 amount;
    uint256 expiry;
    uint256 preparedBlockNumber;
  }

  // All Transaction data, constant and variable
  struct TransactionData {
    address receivingChainTxManagerAddress;
    address user;
    address router;
    address initiator; // msg.sender of sending side
    address sendingAssetId;
    address receivingAssetId;
    address sendingChainFallback;
    address receivingAddress;
    address callTo;
    bytes32 callDataHash;
    bytes32 transactionId;
    uint256 sendingChainId;
    uint256 receivingChainId;
    uint256 amount;
    uint256 expiry;
    uint256 preparedBlockNumber; // Needed for removal of active blocks on fulfill/cancel
  }

  // The structure of the signed data for fulfill
  struct SignedFulfillData {
    bytes32 transactionId;
    uint256 relayerFee;
    string functionIdentifier; // "fulfill" or "cancel"
    uint256 receivingChainId; // For domain separation
    address receivingChainTxManagerAddress; // For domain separation
  }

  // The structure of the signed data for cancellation
  struct SignedCancelData {
    bytes32 transactionId;
    string functionIdentifier;
    uint256 receivingChainId;
    address receivingChainTxManagerAddress; // For domain separation
  }

  /**
    * Arguments for calling prepare()
    * @param invariantData The data for a crosschain transaction that will
    *                      not change between sending and receiving chains.
    *                      The hash of this data is used as the key to store 
    *                      the inforamtion that does change between chains 
    *                      (amount,expiry,preparedBlock) for verification
    * @param amount The amount of the transaction on this chain
    * @param expiry The block.timestamp when the transaction will no longer be
    *               fulfillable and is freely cancellable on this chain
    * @param encryptedCallData The calldata to be executed when the tx is
    *                          fulfilled. Used in the function to allow the user
    *                          to reconstruct the tx from events. Hash is stored
    *                          onchain to prevent shenanigans.
    * @param encodedBid The encoded bid that was accepted by the user for this
    *                   crosschain transfer. It is supplied as a param to the
    *                   function but is only used in event emission
    * @param bidSignature The signature of the bidder on the encoded bid for
    *                     this transaction. Only used within the function for
    *                     event emission. The validity of the bid and
    *                     bidSignature are enforced offchain
    * @param encodedMeta The meta for the function
    */
  struct PrepareArgs {
    InvariantTransactionData invariantData;
    uint256 amount;
    uint256 expiry;
    bytes encryptedCallData;
    bytes encodedBid;
    bytes bidSignature;
    bytes encodedMeta;
  }

  /**
    * @param txData All of the data (invariant and variant) for a crosschain
    *               transaction. The variant data provided is checked against
    *               what was stored when the `prepare` function was called.
    * @param relayerFee The fee that should go to the relayer when they are
    *                   calling the function on the receiving chain for the user
    * @param signature The users signature on the transaction id + fee that
    *                  can be used by the router to unlock the transaction on 
    *                  the sending chain
    * @param callData The calldata to be sent to and executed by the 
    *                 `FulfillHelper`
    * @param encodedMeta The meta for the function
    */
  struct FulfillArgs {
    TransactionData txData;
    uint256 relayerFee;
    bytes signature;
    bytes callData;
    bytes encodedMeta;
  }

  /**
    * Arguments for calling cancel()
    * @param txData All of the data (invariant and variant) for a crosschain
    *               transaction. The variant data provided is checked against
    *               what was stored when the `prepare` function was called.
    * @param signature The user's signature that allows a transaction to be
    *                  cancelled by a relayer
    * @param encodedMeta The meta for the function
    */
  struct CancelArgs {
    TransactionData txData;
    bytes signature;
    bytes encodedMeta;
  }

  // Adding/removing asset events
  event RouterAdded(address indexed addedRouter, address indexed caller);

  event RouterRemoved(address indexed removedRouter, address indexed caller);

  // Adding/removing router events
  event AssetAdded(address indexed addedAssetId, address indexed caller);

  event AssetRemoved(address indexed removedAssetId, address indexed caller);

  // Liquidity events
  event LiquidityAdded(address indexed router, address indexed assetId, uint256 amount, address caller);

  event LiquidityRemoved(address indexed router, address indexed assetId, uint256 amount, address recipient);

  // Transaction events
  event TransactionPrepared(
    address indexed user,
    address indexed router,
    bytes32 indexed transactionId,
    TransactionData txData,
    address caller,
    PrepareArgs args
  );

  event TransactionFulfilled(
    address indexed user,
    address indexed router,
    bytes32 indexed transactionId,
    FulfillArgs args,
    bool success,
    bool isContract,
    bytes returnData,
    address caller
  );

  event TransactionCancelled(
    address indexed user,
    address indexed router,
    bytes32 indexed transactionId,
    CancelArgs args,
    address caller
  );

  // Getters
  function getChainId() external view returns (uint256);

  function getStoredChainId() external view returns (uint256);

  // Owner only methods
  function addRouter(address router) external;

  function removeRouter(address router) external;

  function addAssetId(address assetId) external;

  function removeAssetId(address assetId) external;

  // Router only methods
  function addLiquidityFor(uint256 amount, address assetId, address router) external payable;

  function addLiquidity(uint256 amount, address assetId) external payable;

  function removeLiquidity(
    uint256 amount,
    address assetId,
    address payable recipient
  ) external;

  // Methods for crosschain transfers
  // called in the following order (in happy case)
  // 1. prepare by user on sending chain
  // 2. prepare by router on receiving chain
  // 3. fulfill by user on receiving chain
  // 4. fulfill by router on sending chain
  function prepare(
    PrepareArgs calldata args
  ) external payable returns (TransactionData memory);

  function fulfill(
    FulfillArgs calldata args
  ) external returns (TransactionData memory);

  function cancel(CancelArgs calldata args) external returns (TransactionData memory);
}

File 12 of 13 : FulfillInterpreter.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.4;

import "../interfaces/IFulfillInterpreter.sol";
import "../lib/LibAsset.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

/**
  * @title FulfillInterpreter
  * @author Connext <[email protected]>
  * @notice This library contains an `execute` function that is callabale by
  *         an associated TransactionManager contract. This is used to execute
  *         arbitrary calldata on a receiving chain.
  */
contract FulfillInterpreter is ReentrancyGuard, IFulfillInterpreter {
  address private immutable _transactionManager;

  constructor(address transactionManager) {
    _transactionManager = transactionManager;
  }

  /**
  * @notice Errors if the sender is not the transaction manager
  */
  modifier onlyTransactionManager {
    require(msg.sender == _transactionManager, "#OTM:027");
    _;
  }

  /** 
    * @notice Returns the transaction manager address (only address that can 
    *         call the `execute` function)
    * @return The address of the associated transaction manager
    */
  function getTransactionManager() override external view returns (address) {
    return _transactionManager;
  }

  /** 
    * @notice Executes some arbitrary call data on a given address. The
    *         call data executes can be payable, and will have `amount` sent
    *         along with the function (or approved to the contract). If the
    *         call fails, rather than reverting, funds are sent directly to 
    *         some provided fallbaack address
    * @param transactionId Unique identifier of transaction id that necessitated
    *        calldata execution
    * @param callTo The address to execute the calldata on
    * @param assetId The assetId of the funds to approve to the contract or
    *                send along with the call
    * @param fallbackAddress The address to send funds to if the `call` fails
    * @param amount The amount to approve or send with the call
    * @param callData The data to execute
    */
  function execute(
    bytes32 transactionId,
    address payable callTo,
    address assetId,
    address payable fallbackAddress,
    uint256 amount,
    bytes calldata callData
  ) override external payable onlyTransactionManager returns (bool, bool, bytes memory) {
    // If it is not ether, approve the callTo
    // We approve here rather than transfer since many external contracts
    // simply require an approval, and it is unclear if they can handle 
    // funds transferred directly to them (i.e. Uniswap)
    bool isNative = LibAsset.isNativeAsset(assetId);
    if (!isNative) {
      LibAsset.increaseERC20Allowance(assetId, callTo, amount);
    }

    // Check if the callTo is a contract
    bool success;
    bytes memory returnData;
    bool isContract = Address.isContract(callTo);
    if (isContract) {
      // Try to execute the callData
      // the low level call will return `false` if its execution reverts
      (success, returnData) = callTo.call{value: isNative ? amount : 0}(callData);
    }

    // Handle failure cases
    if (!success) {
      // If it fails, transfer to fallback
      LibAsset.transferAsset(assetId, fallbackAddress, amount);
      // Decrease allowance
      if (!isNative) {
        LibAsset.decreaseERC20Allowance(assetId, callTo, amount);
      }
    }

    // Emit event
    emit Executed(
      transactionId,
      callTo,
      assetId,
      fallbackAddress,
      amount,
      callData,
      returnData,
      success,
      isContract
    );
    return (success, isContract, returnData);
  }
}

File 13 of 13 : LibAsset.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.4;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";


/**
* @title LibAsset
* @author Connext <[email protected]>
* @notice This library contains helpers for dealing with onchain transfers
*         of assets, including accounting for the native asset `assetId`
*         conventions and any noncompliant ERC20 transfers
*/
library LibAsset {
  /** 
  * @dev All native assets use the empty address for their asset id
  *      by convention
  */
  address constant NATIVE_ASSETID = address(0);

  /** 
  * @notice Determines whether the given assetId is the native asset
  * @param assetId The asset identifier to evaluate
  * @return Boolean indicating if the asset is the native asset
  */
  function isNativeAsset(address assetId) internal pure returns (bool) {
    return assetId == NATIVE_ASSETID;
  }

  /** 
  * @notice Gets the balance of the inheriting contract for the given asset
  * @param assetId The asset identifier to get the balance of
  * @return Balance held by contracts using this library
  */
  function getOwnBalance(address assetId) internal view returns (uint256) {
    return
      isNativeAsset(assetId)
        ? address(this).balance
        : IERC20(assetId).balanceOf(address(this));
  }

  /** 
  * @notice Transfers ether from the inheriting contract to a given
  *         recipient
  * @param recipient Address to send ether to
  * @param amount Amount to send to given recipient
  */
  function transferNativeAsset(address payable recipient, uint256 amount)
      internal
  {
    Address.sendValue(recipient, amount);
  }

  /** 
  * @notice Transfers tokens from the inheriting contract to a given
  *         recipient
  * @param assetId Token address to transfer
  * @param recipient Address to send ether to
  * @param amount Amount to send to given recipient
  */
  function transferERC20(
      address assetId,
      address recipient,
      uint256 amount
  ) internal {
    SafeERC20.safeTransfer(IERC20(assetId), recipient, amount);
  }

  /** 
  * @notice Transfers tokens from a sender to a given recipient
  * @param assetId Token address to transfer
  * @param from Address of sender/owner
  * @param to Address of recipient/spender
  * @param amount Amount to transfer from owner to spender
  */
  function transferFromERC20(
    address assetId,
    address from,
    address to,
    uint256 amount
  ) internal {
    SafeERC20.safeTransferFrom(IERC20(assetId), from, to, amount);
  }

  /** 
  * @notice Increases the allowance of a token to a spender
  * @param assetId Token address of asset to increase allowance of
  * @param spender Account whos allowance is increased
  * @param amount Amount to increase allowance by
  */
  function increaseERC20Allowance(
    address assetId,
    address spender,
    uint256 amount
  ) internal {
    require(!isNativeAsset(assetId), "#IA:034");
    SafeERC20.safeIncreaseAllowance(IERC20(assetId), spender, amount);
  }

  /**
  * @notice Decreases the allowance of a token to a spender
  * @param assetId Token address of asset to decrease allowance of
  * @param spender Account whos allowance is decreased
  * @param amount Amount to decrease allowance by
  */
  function decreaseERC20Allowance(
    address assetId,
    address spender,
    uint256 amount
  ) internal {
    require(!isNativeAsset(assetId), "#DA:034");
    SafeERC20.safeDecreaseAllowance(IERC20(assetId), spender, amount);
  }

  /**
  * @notice Wrapper function to transfer a given asset (native or erc20) to
  *         some recipient. Should handle all non-compliant return value
  *         tokens as well by using the SafeERC20 contract by open zeppelin.
  * @param assetId Asset id for transfer (address(0) for native asset, 
  *                token address for erc20s)
  * @param recipient Address to send asset to
  * @param amount Amount to send to given recipient
  */
  function transferAsset(
      address assetId,
      address payable recipient,
      uint256 amount
  ) internal {
    isNativeAsset(assetId)
      ? transferNativeAsset(recipient, amount)
      : transferERC20(assetId, recipient, amount);
  }
}

Settings
{
  "evmVersion": "istanbul",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"uint256","name":"_chainId","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"addedAssetId","type":"address"},{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"AssetAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"renounced","type":"bool"}],"name":"AssetOwnershipRenounced","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"AssetOwnershipRenunciationProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"removedAssetId","type":"address"},{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"AssetRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"router","type":"address"},{"indexed":true,"internalType":"address","name":"assetId","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"LiquidityAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"router","type":"address"},{"indexed":true,"internalType":"address","name":"assetId","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"}],"name":"LiquidityRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"proposedOwner","type":"address"}],"name":"OwnershipProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"addedRouter","type":"address"},{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"RouterAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"renounced","type":"bool"}],"name":"RouterOwnershipRenounced","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"RouterOwnershipRenunciationProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"removedRouter","type":"address"},{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"RouterRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"router","type":"address"},{"indexed":true,"internalType":"bytes32","name":"transactionId","type":"bytes32"},{"components":[{"components":[{"internalType":"address","name":"receivingChainTxManagerAddress","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"sendingAssetId","type":"address"},{"internalType":"address","name":"receivingAssetId","type":"address"},{"internalType":"address","name":"sendingChainFallback","type":"address"},{"internalType":"address","name":"receivingAddress","type":"address"},{"internalType":"address","name":"callTo","type":"address"},{"internalType":"bytes32","name":"callDataHash","type":"bytes32"},{"internalType":"bytes32","name":"transactionId","type":"bytes32"},{"internalType":"uint256","name":"sendingChainId","type":"uint256"},{"internalType":"uint256","name":"receivingChainId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"preparedBlockNumber","type":"uint256"}],"internalType":"struct ITransactionManager.TransactionData","name":"txData","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes","name":"encodedMeta","type":"bytes"}],"indexed":false,"internalType":"struct ITransactionManager.CancelArgs","name":"args","type":"tuple"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"TransactionCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"router","type":"address"},{"indexed":true,"internalType":"bytes32","name":"transactionId","type":"bytes32"},{"components":[{"components":[{"internalType":"address","name":"receivingChainTxManagerAddress","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"sendingAssetId","type":"address"},{"internalType":"address","name":"receivingAssetId","type":"address"},{"internalType":"address","name":"sendingChainFallback","type":"address"},{"internalType":"address","name":"receivingAddress","type":"address"},{"internalType":"address","name":"callTo","type":"address"},{"internalType":"bytes32","name":"callDataHash","type":"bytes32"},{"internalType":"bytes32","name":"transactionId","type":"bytes32"},{"internalType":"uint256","name":"sendingChainId","type":"uint256"},{"internalType":"uint256","name":"receivingChainId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"preparedBlockNumber","type":"uint256"}],"internalType":"struct ITransactionManager.TransactionData","name":"txData","type":"tuple"},{"internalType":"uint256","name":"relayerFee","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"bytes","name":"encodedMeta","type":"bytes"}],"indexed":false,"internalType":"struct ITransactionManager.FulfillArgs","name":"args","type":"tuple"},{"indexed":false,"internalType":"bool","name":"success","type":"bool"},{"indexed":false,"internalType":"bool","name":"isContract","type":"bool"},{"indexed":false,"internalType":"bytes","name":"returnData","type":"bytes"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"TransactionFulfilled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"router","type":"address"},{"indexed":true,"internalType":"bytes32","name":"transactionId","type":"bytes32"},{"components":[{"internalType":"address","name":"receivingChainTxManagerAddress","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"sendingAssetId","type":"address"},{"internalType":"address","name":"receivingAssetId","type":"address"},{"internalType":"address","name":"sendingChainFallback","type":"address"},{"internalType":"address","name":"receivingAddress","type":"address"},{"internalType":"address","name":"callTo","type":"address"},{"internalType":"bytes32","name":"callDataHash","type":"bytes32"},{"internalType":"bytes32","name":"transactionId","type":"bytes32"},{"internalType":"uint256","name":"sendingChainId","type":"uint256"},{"internalType":"uint256","name":"receivingChainId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"preparedBlockNumber","type":"uint256"}],"indexed":false,"internalType":"struct ITransactionManager.TransactionData","name":"txData","type":"tuple"},{"indexed":false,"internalType":"address","name":"caller","type":"address"},{"components":[{"components":[{"internalType":"address","name":"receivingChainTxManagerAddress","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"sendingAssetId","type":"address"},{"internalType":"address","name":"receivingAssetId","type":"address"},{"internalType":"address","name":"sendingChainFallback","type":"address"},{"internalType":"address","name":"receivingAddress","type":"address"},{"internalType":"address","name":"callTo","type":"address"},{"internalType":"uint256","name":"sendingChainId","type":"uint256"},{"internalType":"uint256","name":"receivingChainId","type":"uint256"},{"internalType":"bytes32","name":"callDataHash","type":"bytes32"},{"internalType":"bytes32","name":"transactionId","type":"bytes32"}],"internalType":"struct ITransactionManager.InvariantTransactionData","name":"invariantData","type":"tuple"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"bytes","name":"encryptedCallData","type":"bytes"},{"internalType":"bytes","name":"encodedBid","type":"bytes"},{"internalType":"bytes","name":"bidSignature","type":"bytes"},{"internalType":"bytes","name":"encodedMeta","type":"bytes"}],"indexed":false,"internalType":"struct ITransactionManager.PrepareArgs","name":"args","type":"tuple"}],"name":"TransactionPrepared","type":"event"},{"inputs":[],"name":"MAX_TIMEOUT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_TIMEOUT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptProposedOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"assetId","type":"address"}],"name":"addAssetId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"assetId","type":"address"}],"name":"addLiquidity","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"assetId","type":"address"},{"internalType":"address","name":"router","type":"address"}],"name":"addLiquidityFor","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"router","type":"address"}],"name":"addRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"approvedAssets","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"approvedRouters","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"assetOwnershipTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"receivingChainTxManagerAddress","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"sendingAssetId","type":"address"},{"internalType":"address","name":"receivingAssetId","type":"address"},{"internalType":"address","name":"sendingChainFallback","type":"address"},{"internalType":"address","name":"receivingAddress","type":"address"},{"internalType":"address","name":"callTo","type":"address"},{"internalType":"bytes32","name":"callDataHash","type":"bytes32"},{"internalType":"bytes32","name":"transactionId","type":"bytes32"},{"internalType":"uint256","name":"sendingChainId","type":"uint256"},{"internalType":"uint256","name":"receivingChainId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"preparedBlockNumber","type":"uint256"}],"internalType":"struct ITransactionManager.TransactionData","name":"txData","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes","name":"encodedMeta","type":"bytes"}],"internalType":"struct ITransactionManager.CancelArgs","name":"args","type":"tuple"}],"name":"cancel","outputs":[{"components":[{"internalType":"address","name":"receivingChainTxManagerAddress","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"sendingAssetId","type":"address"},{"internalType":"address","name":"receivingAssetId","type":"address"},{"internalType":"address","name":"sendingChainFallback","type":"address"},{"internalType":"address","name":"receivingAddress","type":"address"},{"internalType":"address","name":"callTo","type":"address"},{"internalType":"bytes32","name":"callDataHash","type":"bytes32"},{"internalType":"bytes32","name":"transactionId","type":"bytes32"},{"internalType":"uint256","name":"sendingChainId","type":"uint256"},{"internalType":"uint256","name":"receivingChainId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"preparedBlockNumber","type":"uint256"}],"internalType":"struct ITransactionManager.TransactionData","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"delay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"receivingChainTxManagerAddress","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"sendingAssetId","type":"address"},{"internalType":"address","name":"receivingAssetId","type":"address"},{"internalType":"address","name":"sendingChainFallback","type":"address"},{"internalType":"address","name":"receivingAddress","type":"address"},{"internalType":"address","name":"callTo","type":"address"},{"internalType":"bytes32","name":"callDataHash","type":"bytes32"},{"internalType":"bytes32","name":"transactionId","type":"bytes32"},{"internalType":"uint256","name":"sendingChainId","type":"uint256"},{"internalType":"uint256","name":"receivingChainId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"preparedBlockNumber","type":"uint256"}],"internalType":"struct ITransactionManager.TransactionData","name":"txData","type":"tuple"},{"internalType":"uint256","name":"relayerFee","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"bytes","name":"encodedMeta","type":"bytes"}],"internalType":"struct ITransactionManager.FulfillArgs","name":"args","type":"tuple"}],"name":"fulfill","outputs":[{"components":[{"internalType":"address","name":"receivingChainTxManagerAddress","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"sendingAssetId","type":"address"},{"internalType":"address","name":"receivingAssetId","type":"address"},{"internalType":"address","name":"sendingChainFallback","type":"address"},{"internalType":"address","name":"receivingAddress","type":"address"},{"internalType":"address","name":"callTo","type":"address"},{"internalType":"bytes32","name":"callDataHash","type":"bytes32"},{"internalType":"bytes32","name":"transactionId","type":"bytes32"},{"internalType":"uint256","name":"sendingChainId","type":"uint256"},{"internalType":"uint256","name":"receivingChainId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"preparedBlockNumber","type":"uint256"}],"internalType":"struct ITransactionManager.TransactionData","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getChainId","outputs":[{"internalType":"uint256","name":"_chainId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStoredChainId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"interpreter","outputs":[{"internalType":"contract IFulfillInterpreter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isAssetOwnershipRenounced","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isRouterOwnershipRenounced","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"receivingChainTxManagerAddress","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"sendingAssetId","type":"address"},{"internalType":"address","name":"receivingAssetId","type":"address"},{"internalType":"address","name":"sendingChainFallback","type":"address"},{"internalType":"address","name":"receivingAddress","type":"address"},{"internalType":"address","name":"callTo","type":"address"},{"internalType":"uint256","name":"sendingChainId","type":"uint256"},{"internalType":"uint256","name":"receivingChainId","type":"uint256"},{"internalType":"bytes32","name":"callDataHash","type":"bytes32"},{"internalType":"bytes32","name":"transactionId","type":"bytes32"}],"internalType":"struct ITransactionManager.InvariantTransactionData","name":"invariantData","type":"tuple"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"bytes","name":"encryptedCallData","type":"bytes"},{"internalType":"bytes","name":"encodedBid","type":"bytes"},{"internalType":"bytes","name":"bidSignature","type":"bytes"},{"internalType":"bytes","name":"encodedMeta","type":"bytes"}],"internalType":"struct ITransactionManager.PrepareArgs","name":"args","type":"tuple"}],"name":"prepare","outputs":[{"components":[{"internalType":"address","name":"receivingChainTxManagerAddress","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"sendingAssetId","type":"address"},{"internalType":"address","name":"receivingAssetId","type":"address"},{"internalType":"address","name":"sendingChainFallback","type":"address"},{"internalType":"address","name":"receivingAddress","type":"address"},{"internalType":"address","name":"callTo","type":"address"},{"internalType":"bytes32","name":"callDataHash","type":"bytes32"},{"internalType":"bytes32","name":"transactionId","type":"bytes32"},{"internalType":"uint256","name":"sendingChainId","type":"uint256"},{"internalType":"uint256","name":"receivingChainId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"preparedBlockNumber","type":"uint256"}],"internalType":"struct ITransactionManager.TransactionData","name":"","type":"tuple"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"proposeAssetOwnershipRenunciation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newlyProposed","type":"address"}],"name":"proposeNewOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"proposeRouterOwnershipRenunciation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"proposed","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proposedTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"assetId","type":"address"}],"name":"removeAssetId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"assetId","type":"address"},{"internalType":"address payable","name":"recipient","type":"address"}],"name":"removeLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"router","type":"address"}],"name":"removeRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceAssetOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceRouterOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounced","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"routerBalances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"routerOwnershipTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"variantTransactionData","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"}]

60c06040523480156200001157600080fd5b5060405162004fd338038062004fd3833981016040819052620000349162000105565b600160005562000044336200009e565b608081905260405130906200005990620000f7565b6001600160a01b039091168152602001604051809103906000f08015801562000086573d6000803e3d6000fd5b5060601b6001600160601b03191660a052506200011e565b600180546001600160a01b038381166001600160a01b0319831681179093556000600381905560405191909216929183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b610bf080620043e383390190565b60006020828403121562000117578081fd5b5051919050565b60805160a05160601c6142876200015c600039600081816102ee015281816127710152612798015260008181610264015261078901526142876000f3fe6080604052600436106102045760003560e01c80638da5cb5b11610118578063c95f9d0e116100a0578063de38eb3a1161006f578063de38eb3a146105db578063e070da09146105f2578063e47602f714610605578063e8be0dfc1461061a578063f31abcc41461062f57600080fd5b8063c95f9d0e14610578578063d1851c921461058b578063d232c220146105a9578063d9459372146105c857600080fd5b8063b1f8100d116100e7578063b1f8100d146104f9578063be91a2ba14610519578063c0c17baf14610539578063c1a049591461054e578063c5b350df1461056357600080fd5b80638da5cb5b1461045e57806397eb00881461047c5780639b151a80146104ac578063b1d2618d146104d957600080fd5b806341258b5c1161019b5780636a41633a1161016a5780636a41633a146103e95780636a42b8f8146103fe5780636ae0b15414610414578063715018a6146104345780638741eac51461044957600080fd5b806341258b5c1461033d578063445b1e4b14610375578063543ad1df146103a55780635e679856146103bc57600080fd5b806334e9393c116101d757806334e9393c146102a75780633855b467146102c75780633a35cf17146102dc5780633cf52ffb1461032857600080fd5b80632004ef451461020957806324ca984e1461023357806332a130c9146102555780633408e47014610292575b600080fd5b34801561021557600080fd5b5061021e61064f565b60405190151581526020015b60405180910390f35b34801561023f57600080fd5b5061025361024e366004613486565b610671565b005b34801561026157600080fd5b507f00000000000000000000000000000000000000000000000000000000000000005b60405190815260200161022a565b34801561029e57600080fd5b50610284610785565b3480156102b357600080fd5b506102536102c2366004613486565b6107b5565b3480156102d357600080fd5b50610253610880565b3480156102e857600080fd5b506103107f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200161022a565b34801561033457600080fd5b50600354610284565b34801561034957600080fd5b506102846103583660046134a2565b600860209081526000928352604080842090915290825290205481565b34801561038157600080fd5b5061021e610390366004613486565b60096020526000908152604090205460ff1681565b3480156103b157600080fd5b506102846201518081565b3480156103c857600080fd5b506102846103d73660046135a7565b600b6020526000908152604090205481565b3480156103f557600080fd5b50600754610284565b34801561040a57600080fd5b5062093a80610284565b34801561042057600080fd5b5061025361042f366004613486565b61097b565b34801561044057600080fd5b50610253610a87565b34801561045557600080fd5b50610253610b8e565b34801561046a57600080fd5b506001546001600160a01b0316610310565b34801561048857600080fd5b5061021e610497366004613486565b600a6020526000908152604090205460ff1681565b3480156104b857600080fd5b506104cc6104c73660046135f8565b610bff565b60405161022a9190613fbf565b3480156104e557600080fd5b506102536104f4366004613486565b61108e565b34801561050557600080fd5b50610253610514366004613486565b61115a565b34801561052557600080fd5b506104cc6105343660046135bf565b611234565b34801561054557600080fd5b50610253611597565b34801561055a57600080fd5b50600554610284565b34801561056f57600080fd5b50610253611690565b610253610586366004613786565b61176a565b34801561059757600080fd5b506002546001600160a01b0316610310565b3480156105b557600080fd5b506001546001600160a01b03161561021e565b6104cc6105d6366004613631565b6117a6565b3480156105e757600080fd5b5061028462278d0081565b6102536106003660046137aa565b612104565b34801561061157600080fd5b50610253612141565b34801561062657600080fd5b5061021e6121b2565b34801561063b57600080fd5b5061025361064a3660046137aa565b6121d2565b6001546000906001600160a01b0316158061066c575060045460ff165b905090565b6001546001600160a01b031633146106a45760405162461bcd60e51b815260040161069b90613c3e565b60405180910390fd5b6001600160a01b0381166106e45760405162461bcd60e51b81526020600482015260076024820152662341523a30303160c81b604482015260640161069b565b6001600160a01b03811660009081526009602052604090205460ff16156107375760405162461bcd60e51b815260206004820152600760248201526611a0a91d18199960c91b604482015260640161069b565b6001600160a01b038116600081815260096020526040808220805460ff19166001179055513392917fbc68405e644da2aaf25623ce2199da82c6dfd2e1de102b400eba6a091704d4f491a350565b60007f0000000000000000000000000000000000000000000000000000000000000000806107b05750465b919050565b6001546001600160a01b031633146107df5760405162461bcd60e51b815260040161069b90613c3e565b6001600160a01b0381166000908152600a602052604090205460ff16156108325760405162461bcd60e51b815260206004820152600760248201526611a0a09d18199960c91b604482015260640161069b565b6001600160a01b0381166000818152600a6020526040808220805460ff19166001179055513392917f0bb5715f0f217c2fe9a0c877ea87d474380c641102f3440ee2a4c8b9d979091891a350565b6001546001600160a01b031633146108aa5760405162461bcd60e51b815260040161069b90613c3e565b60065460ff16156108e85760405162461bcd60e51b8152602060048201526008602482015267046a4829e746066760c31b604482015260640161069b565b6000600754116109255760405162461bcd60e51b81526020600482015260086024820152672352414f3a30333760c01b604482015260640161069b565b62093a806007544261093791906141cd565b1161096f5760405162461bcd60e51b815260206004820152600860248201526702352414f3a3033360c41b604482015260640161069b565b6109796001612357565b565b6001546001600160a01b031633146109a55760405162461bcd60e51b815260040161069b90613c3e565b6001600160a01b0381166109e55760405162461bcd60e51b81526020600482015260076024820152662352523a30303160c81b604482015260640161069b565b6001600160a01b03811660009081526009602052604090205460ff161515600114610a3c5760405162461bcd60e51b81526020600482015260076024820152662352523a30333360c81b604482015260640161069b565b6001600160a01b038116600081815260096020526040808220805460ff19169055513392917fbee3e974bb6a6f44f20096ede047c191eef60322e65e4ee4bd3392230a8716d591a350565b6001546001600160a01b03163314610ab15760405162461bcd60e51b815260040161069b90613c3e565b600060035411610aed5760405162461bcd60e51b815260206004820152600760248201526623524f3a30333760c81b604482015260640161069b565b62093a8060035442610aff91906141cd565b11610b365760405162461bcd60e51b8152602060048201526007602482015266023524f3a3033360cc1b604482015260640161069b565b6002546001600160a01b031615610b795760405162461bcd60e51b815260206004820152600760248201526611a9279d18199b60c91b604482015260640161069b565b600254610979906001600160a01b03166123a4565b6001546001600160a01b03163314610bb85760405162461bcd60e51b815260040161069b90613c3e565b60065460ff1615610bf75760405162461bcd60e51b8152602060048201526009602482015268046a0829ea4746066760bb1b604482015260640161069b565b6109796123fd565b610c076133e7565b60026000541415610c2a5760405162461bcd60e51b815260040161069b90613c5f565b60026000908155610c3a83612439565b9050610c566101a08401356101c08501356101e08601356125b7565b6000828152600b602052604090205414610c9b5760405162461bcd60e51b815260206004820152600660248201526523463a30313960d01b604482015260640161069b565b426101c08401351015610cd95760405162461bcd60e51b8152602060048201526006602482015265023463a3032360d41b604482015260640161069b565b6101e0830135610d145760405162461bcd60e51b815260206004820152600660248201526523463a30323160d01b604482015260640161069b565b610120830135610d286102408501856140c9565b604051610d36929190613bb0565b604051809103902014610d745760405162461bcd60e51b815260206004820152600660248201526508d18e8c0c8d60d21b604482015260640161069b565b610d8a6101a08401356101c085013560006125b7565b6000918252600b602052604082205580606081610da5610785565b9050610160860135811415610f1a57610dc46060870160408801613486565b6001600160a01b0316336001600160a01b031614610e0d5760405162461bcd60e51b815260206004820152600660248201526511a31d18189b60d11b604482015260640161069b565b610e1d6040870160208801613486565b6001600160a01b0316610e5b6101408801356102008901356101808a0135610e4860208c018c613486565b610e566102208d018d6140c9565b612605565b6001600160a01b031614610e9a5760405162461bcd60e51b815260206004820152600660248201526511a31d18191960d11b604482015260640161069b565b6101a086013560086000610eb460608a0160408b01613486565b6001600160a01b031681526020810191909152604001600090812090610ee060a08a0160808b01613486565b6001600160a01b03166001600160a01b031681526020019081526020016000206000828254610f0f91906141b5565b90915550610ff99050565b610f2a6040870160208801613486565b6001600160a01b0316610f526101408801356102008901358430610e566102208d018d6140c9565b6001600160a01b031614610f915760405162461bcd60e51b815260206004820152600660248201526511a31d18191960d11b604482015260640161069b565b6101a08601356102008701351115610fd45760405162461bcd60e51b815260206004820152600660248201526523463a30323360d01b604482015260640161069b565b610ff186610200810135610fec6102408301836140c9565b612687565b919550935091505b61014086013561100f6060880160408901613486565b6001600160a01b03166110286040890160208a01613486565b6001600160a01b03167f8e5df24f8b9ac0e3455417a1d7060762388ce3c1d4941aa49dc1b61943031d328988888833604051611068959493929190613d10565b60405180910390a461107f3687900387018761366a565b60016000559695505050505050565b6001546001600160a01b031633146110b85760405162461bcd60e51b815260040161069b90613c3e565b6001600160a01b0381166000908152600a602052604090205460ff16151560011461110f5760405162461bcd60e51b81526020600482015260076024820152662352413a30333360c81b604482015260640161069b565b6001600160a01b0381166000818152600a6020526040808220805460ff19169055513392917f0fa1e4606af435f32f05b3804033d2933e691fab32ee74d2db6fa82d2741f1ea91a350565b6001546001600160a01b031633146111845760405162461bcd60e51b815260040161069b90613c3e565b6002546001600160a01b0382811691161415806111a857506001600160a01b038116155b6111df5760405162461bcd60e51b815260206004820152600860248201526711a827279d18199b60c11b604482015260640161069b565b6001546001600160a01b03828116911614156112285760405162461bcd60e51b8152602060048201526008602482015267046a09c9e746066760c31b604482015260640161069b565b6112318161289f565b50565b61123c6133e7565b6002600054141561125f5760405162461bcd60e51b815260040161069b90613c5f565b6002600090815561126f83612439565b905061128b6101a08401356101c08501356101e08601356125b7565b6000828152600b6020526040902054146112d05760405162461bcd60e51b815260206004820152600660248201526523433a30313960d01b604482015260640161069b565b6101e083013561130b5760405162461bcd60e51b815260206004820152600660248201526523433a30323160d01b604482015260640161069b565b6113216101a08401356101c085013560006125b7565b6000828152600b602052604081209190915561133b610785565b90506101608401358114156113e257426101c0850135106113af576113666060850160408601613486565b6001600160a01b0316336001600160a01b0316146113af5760405162461bcd60e51b815260206004820152600660248201526523433a30323560d01b604482015260640161069b565b6113dd6113c260a0860160808701613486565b6113d260e0870160c08801613486565b6101a08701356128ed565b61150a565b426101c08501351061148f576113fe6040850160208601613486565b6001600160a01b0316336001600160a01b0316148061145a57506114286040850160208601613486565b6001600160a01b031661144f610140860135833061144a6102008a018a6140c9565b612916565b6001600160a01b0316145b61148f5760405162461bcd60e51b815260206004820152600660248201526511a19d18191960d11b604482015260640161069b565b6101a0840135600860006114a96060880160408901613486565b6001600160a01b0316815260208101919091526040016000908120906114d560c0880160a08901613486565b6001600160a01b03166001600160a01b03168152602001908152602001600020600082825461150491906141b5565b90915550505b6101408401356115206060860160408701613486565b6001600160a01b03166115396040870160208801613486565b6001600160a01b03167f56a92405e111173b950d90846413f755ca35bb7631d49a4a564778b21affe2878733604051611573929190613c96565b60405180910390a461158a3685900385018561366a565b6001600055949350505050565b6001546001600160a01b031633146115c15760405162461bcd60e51b815260040161069b90613c3e565b60045460ff16156115ff5760405162461bcd60e51b8152602060048201526008602482015267046a4a49e746066760c31b604482015260640161069b565b60006005541161163c5760405162461bcd60e51b81526020600482015260086024820152672352524f3a30333760c01b604482015260640161069b565b62093a806005544261164e91906141cd565b116116865760405162461bcd60e51b815260206004820152600860248201526702352524f3a3033360c41b604482015260640161069b565b6109796001612982565b6002546001600160a01b031633146116d45760405162461bcd60e51b8152602060048201526007602482015266234f503a30333560c81b604482015260640161069b565b6002546001546001600160a01b03908116911614156117205760405162461bcd60e51b815260206004820152600860248201526704682a09e746066760c31b604482015260640161069b565b62093a806003544261173291906141cd565b11610b795760405162461bcd60e51b815260206004820152600860248201526702341504f3a3033360c41b604482015260640161069b565b6002600054141561178d5760405162461bcd60e51b815260040161069b90613c5f565b600260005561179d8282336129c8565b50506001600055565b6117ae6133e7565b600260005414156117d15760405162461bcd60e51b815260040161069b90613c5f565b600260009081556117e86040840160208501613486565b6001600160a01b031614156118285760405162461bcd60e51b815260206004820152600660248201526523503a30303960d01b604482015260640161069b565b600061183a6060840160408501613486565b6001600160a01b0316141561187a5760405162461bcd60e51b815260206004820152600660248201526523503a30303160d01b604482015260640161069b565b61188261064f565b806118ba57506009600061189c6060850160408601613486565b6001600160a01b0316815260208101919091526040016000205460ff165b6118ef5760405162461bcd60e51b815260206004820152600660248201526523503a30303360d01b604482015260640161069b565b600061190160e0840160c08501613486565b6001600160a01b031614156119415760405162461bcd60e51b8152602060048201526006602482015265023503a3031360d41b604482015260640161069b565b6000611954610100840160e08501613486565b6001600160a01b031614156119945760405162461bcd60e51b815260206004820152600660248201526511a81d18191b60d11b604482015260640161069b565b61012082013561014083013514156119d75760405162461bcd60e51b815260206004820152600660248201526523503a30313160d01b604482015260640161069b565b60006119e1610785565b90506101208301358114806119fa575061014083013581145b611a2f5760405162461bcd60e51b815260206004820152600660248201526511a81d18189960d11b604482015260640161069b565b6000611a40426101c08601356141cd565b905062015180811015611a7e5760405162461bcd60e51b815260206004820152600660248201526523503a30313360d01b604482015260640161069b565b62278d00811115611aba5760405162461bcd60e51b815260206004820152600660248201526508d40e8c0c4d60d21b604482015260640161069b565b50604051600090611acf908590602001613dee565b60408051601f1981840301815291815281516020928301206000818152600b90935291205490915015611b2d5760405162461bcd60e51b815260206004820152600660248201526523503a30313560d01b604482015260640161069b565b6101a0840135610120850135831415611c9057611b506080860160608701613486565b6001600160a01b0316336001600160a01b031614611b995760405162461bcd60e51b815260206004820152600660248201526523503a30333960d01b604482015260640161069b565b6000856101a0013511611bd75760405162461bcd60e51b815260206004820152600660248201526511a81d18181960d11b604482015260640161069b565b611bdf6121b2565b80611c175750600a6000611bf960a0880160808901613486565b6001600160a01b0316815260208101919091526040016000205460ff165b611c4c5760405162461bcd60e51b815260206004820152600660248201526508d40e8c0c0d60d21b604482015260640161069b565b611c6a611c5f60a0870160808801613486565b866101a00135612b9c565b9050611c7c81866101c00135436125b7565b6000838152600b6020526040902055611f29565b6000611ca461012087016101008801613486565b6001600160a01b03161480611ccf5750611ccf611cc961012087016101008801613486565b3b151590565b611d045760405162461bcd60e51b815260206004820152600660248201526523503a30333160d01b604482015260640161069b565b611d0c6121b2565b80611d445750600a6000611d2660c0880160a08901613486565b6001600160a01b0316815260208101919091526040016000205460ff165b611d795760405162461bcd60e51b815260206004820152600660248201526508d40e8c0c0d60d21b604482015260640161069b565b611d896060860160408701613486565b6001600160a01b0316336001600160a01b031614611dd25760405162461bcd60e51b815260206004820152600660248201526511a81d18189b60d11b604482015260640161069b565b3415611e095760405162461bcd60e51b815260206004820152600660248201526523503a30313760d01b604482015260640161069b565b6000600881611e1e6060890160408a01613486565b6001600160a01b031681526020810191909152604001600090812090611e4a60c0890160a08a01613486565b6001600160a01b03166001600160a01b0316815260200190815260200160002054905081811015611ea65760405162461bcd60e51b8152602060048201526006602482015265046a0746062760d31b604482015260640161069b565b611eb682876101c00135436125b7565b6000848152600b6020526040808220929092558383039160089190611ee19060608b01908b01613486565b6001600160a01b031681526020810191909152604001600090812090611f0d60c08a0160a08b01613486565b6001600160a01b03168152602081019190915260400160002055505b60408051610200810190915260009080611f466020890189613486565b6001600160a01b03168152602090810190611f679060408a01908a01613486565b6001600160a01b03168152602001611f856060890160408a01613486565b6001600160a01b03168152602001611fa36080890160608a01613486565b6001600160a01b03168152602001611fc160a0890160808a01613486565b6001600160a01b03168152602001611fdf60c0890160a08a01613486565b6001600160a01b03168152602001611ffd60e0890160c08a01613486565b6001600160a01b0316815260200161201c610100890160e08a01613486565b6001600160a01b0316815260200161203c61012089016101008a01613486565b6001600160a01b031681526020018760000161016001358152602001876000016101800135815260200187600001610120013581526020018760000161014001358152602001838152602001876101c00135815260200143815250905080610140015181604001516001600160a01b031682602001516001600160a01b03167f88fbf1dbc326c404155bad4643bd0ddadd23f0636929c66442f0433208b2c90584338b6040516120ee93929190613fce565b60405180910390a4600160005595945050505050565b600260005414156121275760405162461bcd60e51b815260040161069b90613c5f565b60026000556121378383836129c8565b5050600160005550565b6001546001600160a01b0316331461216b5760405162461bcd60e51b815260040161069b90613c3e565b60045460ff16156121aa5760405162461bcd60e51b8152602060048201526009602482015268046a0a49ea4746066760bb1b604482015260640161069b565b610979612c5d565b6001546000906001600160a01b0316158061066c57505060065460ff1690565b600260005414156121f55760405162461bcd60e51b815260040161069b90613c5f565b60026000556001600160a01b03811661223a5760405162461bcd60e51b815260206004820152600760248201526623524c3a30303760c81b604482015260640161069b565b600083116122745760405162461bcd60e51b815260206004820152600760248201526611a9261d18181960c91b604482015260640161069b565b3360009081526008602090815260408083206001600160a01b0386168452909152902054838110156122d25760405162461bcd60e51b8152602060048201526007602482015266046a498746060760cb1b604482015260640161069b565b3360009081526008602090815260408083206001600160a01b0387168452909152902084820390556123058383866128ed565b604080518581526001600160a01b03848116602083015285169133917f7da12116be8cb7af4b2d9e9b4a2ca2c3a3243ddd6fd3a94411902367b8eed568910160405180910390a3505060016000555050565b6006805460ff191682151590811790915560006007556040519081527f868d89ead22a5d10f456845ac0014901d9af7203e71cf0892d70d9dc262c2fb9906020015b60405180910390a150565b600180546001600160a01b038381166001600160a01b0319831681179093556000600381905560405191909216929183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b4260078190556040519081527fa78fdca214e4619ef34a695316d423f5b0d8274bc919d29733bf8f92ec8cbb7a906020015b60405180910390a1565b604080516101a081019091526000908190806124586020860186613486565b6001600160a01b031681526020018460200160208101906124799190613486565b6001600160a01b031681526020016124976060860160408701613486565b6001600160a01b031681526020016124b56080860160608701613486565b6001600160a01b031681526020016124d360a0860160808701613486565b6001600160a01b031681526020016124f160c0860160a08701613486565b6001600160a01b0316815260200161250f60e0860160c08701613486565b6001600160a01b0316815260200161252e610100860160e08701613486565b6001600160a01b0316815260200161254e61012086016101008701613486565b6001600160a01b031681526020018461016001358152602001846101800135815260200184610120013581526020018461014001358152509050806040516020016125999190613dfd565b60405160208183030381529060405280519060200120915050919050565b604080516060808201835285825260208083018681529284018581528451808301899052935184860152518383015283518084039092018252608090920190925281519101205b9392505050565b6040805160a0810182528781526020808201889052825180840184526007815266199d5b199a5b1b60ca1b8183015282840152606082018790526001600160a01b038616608083015291516000926126799161266391849101613f64565b6040516020818303038152906040528585612c93565b9150505b9695505050505050565b60008060606101a087013586900386156126b5576126b56126ae60c08a0160a08b01613486565b33896128ed565b60006126c96101208a016101008b01613486565b6001600160a01b03161415612725578015612708576127086126f160c08a0160a08b01613486565b6127026101008b0160e08c01613486565b836128ed565b505060408051600080825260208201909252909250829150612895565b600061274761273a60c08b0160a08c01613486565b6001600160a01b03161590565b9050801580156127575750600082115b156127965761279661276f60c08b0160a08c01613486565b7f000000000000000000000000000000000000000000000000000000000000000084612d3a565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663cf9a3604826127d15760006127d3565b835b6101408c01356127eb6101208e016101008f01613486565b8d60a00160208101906127fe9190613486565b8e60e00160208101906128119190613486565b888e8e6040518963ffffffff1660e01b81526004016128369796959493929190613bdc565b6000604051808303818588803b15801561284f57600080fd5b505af1158015612863573d6000803e3d6000fd5b50505050506040513d6000823e601f3d908101601f1916820160405261288c91908101906134f4565b94509450945050505b9450945094915050565b42600355600280546001600160a01b0319166001600160a01b0383169081179091556040517f6ab4d119f23076e8ad491bc65ce85f017fb0591dce08755ba8591059cc51737a90600090a250565b6001600160a01b0383161561290c57612907838383612d3a565b505050565b6129078282612d45565b60008060405180608001604052808881526020016040518060400160405280600681526020016518d85b98d95b60d21b8152508152602001878152602001866001600160a01b03168152509050612977816040516020016126639190613f13565b979650505050505050565b6004805460ff191682151590811790915560006005556040519081527f243ebbb2f905234bbf0556bb38e1f7c23b09ffd2e441a16e58b844eb2ab7a39790602001612399565b6001600160a01b038116612a085760405162461bcd60e51b815260206004820152600760248201526623414c3a30303160c81b604482015260640161069b565b60008311612a425760405162461bcd60e51b815260206004820152600760248201526611a0a61d18181960c91b604482015260640161069b565b612a4a61064f565b80612a6d57506001600160a01b03811660009081526009602052604090205460ff165b612aa35760405162461bcd60e51b815260206004820152600760248201526623414c3a30303360c81b604482015260640161069b565b612aab6121b2565b80612ace57506001600160a01b0382166000908152600a602052604090205460ff165b612b045760405162461bcd60e51b815260206004820152600760248201526608d0530e8c0c0d60ca1b604482015260640161069b565b612b0e8284612b9c565b6001600160a01b038083166000908152600860209081526040808320938716835292905290812080549295508592909190612b4a9084906141b5565b9091555050604080518481523360208201526001600160a01b0380851692908416917f4bd28ccd068c4853d24d35f727ef2a3fea11ce55e8d93461e45f785818e1e139910160405180910390a3505050565b6000816001600160a01b038416612beb57823414612be65760405162461bcd60e51b81526020600482015260076024820152662354413a30303560c81b604482015260640161069b565b612c54565b6000612bf685612d53565b90503415612c305760405162461bcd60e51b815260206004820152600760248201526611aa209d18181b60c91b604482015260640161069b565b612c3c85333087612de7565b80612c4686612d53565b612c5091906141cd565b9150505b90505b92915050565b4260058190556040519081527fa52048c5f468d21a62e4644ac4db19bcaa1a20f0cf37d163ba49c7217d35feb89060200161242f565b6000612d32612cf685805190602001206040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101829052600090605c01604051602081830303815290604052805190602001209050919050565b84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612df992505050565b949350505050565b612907838383612e9d565b612d4f8282612f00565b5050565b60006001600160a01b03821615612de0576040516370a0823160e01b81523060048201526001600160a01b038316906370a082319060240160206040518083038186803b158015612da357600080fd5b505afa158015612db7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ddb919061376e565b612c57565b4792915050565b612df384848484613019565b50505050565b6000815160411415612e2d5760208201516040830151606084015160001a612e2386828585613051565b9350505050612c57565b815160401415612e555760208201516040830151612e4c8583836131fa565b92505050612c57565b60405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604482015260640161069b565b6040516001600160a01b03831660248201526044810182905261290790849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261321a565b80471015612f505760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e6365000000604482015260640161069b565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114612f9d576040519150601f19603f3d011682016040523d82523d6000602084013e612fa2565b606091505b50509050806129075760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d61792068617665207265766572746564000000000000606482015260840161069b565b6040516001600160a01b0380851660248301528316604482015260648101829052612df39085906323b872dd60e01b90608401612ec9565b60007f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08211156130ce5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b606482015260840161069b565b8360ff16601b14806130e357508360ff16601c145b61313a5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b606482015260840161069b565b6040805160008082526020820180845288905260ff871692820192909252606081018590526080810184905260019060a0016020604051602081039080840390855afa15801561318e573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166131f15760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e61747572650000000000000000604482015260640161069b565b95945050505050565b60006001600160ff1b03821660ff83901c601b0161267d86828785613051565b600061326f826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166132ec9092919063ffffffff16565b805190915015612907578080602001905181019061328d91906134da565b6129075760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161069b565b6060612d32848460008585843b6133455760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161069b565b600080866001600160a01b031685876040516133619190613bc0565b60006040518083038185875af1925050503d806000811461339e576040519150601f19603f3d011682016040523d82523d6000602084013e6133a3565b606091505b5091509150612977828286606083156133bd5750816125fe565b8251156133cd5782518084602001fd5b8160405162461bcd60e51b815260040161069b9190613c2b565b6040805161020081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081018290526101a081018290526101c081018290526101e081019190915290565b80356107b08161423c565b805180151581146107b057600080fd5b600060208284031215613497578081fd5b8135612c548161423c565b600080604083850312156134b4578081fd5b82356134bf8161423c565b915060208301356134cf8161423c565b809150509250929050565b6000602082840312156134eb578081fd5b6125fe82613476565b600080600060608486031215613508578081fd5b61351184613476565b925061351f60208501613476565b9150604084015167ffffffffffffffff8082111561353b578283fd5b818601915086601f83011261354e578283fd5b81518181111561356057613560614226565b613573601f8201601f191660200161413f565b9150808252876020828501011115613589578384fd5b61359a8160208401602086016141e4565b5080925050509250925092565b6000602082840312156135b8578081fd5b5035919050565b6000602082840312156135d0578081fd5b813567ffffffffffffffff8111156135e6578182fd5b82016102408185031215612c54578182fd5b600060208284031215613609578081fd5b813567ffffffffffffffff81111561361f578182fd5b82016102808185031215612c54578182fd5b600060208284031215613642578081fd5b813567ffffffffffffffff811115613658578182fd5b82016102608185031215612c54578182fd5b6000610200828403121561367c578081fd5b613684614115565b61368d8361346b565b815261369b6020840161346b565b60208201526136ac6040840161346b565b60408201526136bd6060840161346b565b60608201526136ce6080840161346b565b60808201526136df60a0840161346b565b60a08201526136f060c0840161346b565b60c082015261370160e0840161346b565b60e082015261010061371481850161346b565b9082015261012083810135908201526101408084013590820152610160808401359082015261018080840135908201526101a080840135908201526101c080840135908201526101e0928301359281019290925250919050565b60006020828403121561377f578081fd5b5051919050565b60008060408385031215613798578182fd5b8235915060208301356134cf8161423c565b6000806000606084860312156137be578081fd5b8335925060208401356137d08161423c565b915060408401356137e08161423c565b809150509250925092565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6000815180845261382c8160208601602086016141e4565b601f01601f19169290920160200192915050565b61385a8261384d8361346b565b6001600160a01b03169052565b6138666020820161346b565b6001600160a01b031660208301526138806040820161346b565b6001600160a01b0316604083015261389a6060820161346b565b6001600160a01b031660608301526138b46080820161346b565b6001600160a01b031660808301526138ce60a0820161346b565b6001600160a01b031660a08301526138e860c0820161346b565b6001600160a01b031660c083015261390260e0820161346b565b6001600160a01b031660e083015261010061391e82820161346b565b6001600160a01b03169083015261012081810135908301526101408082013590830152610160808201359083015261018090810135910152565b6139658261384d8361346b565b6139716020820161346b565b6001600160a01b0316602083015261398b6040820161346b565b6001600160a01b031660408301526139a56060820161346b565b6001600160a01b031660608301526139bf6080820161346b565b6001600160a01b031660808301526139d960a0820161346b565b6001600160a01b031660a08301526139f360c0820161346b565b6001600160a01b031660c0830152613a0d60e0820161346b565b6001600160a01b031660e0830152610100613a2982820161346b565b6001600160a01b03169083015261012081810135908301526101408082013590830152610160808201359083015261018080820135908301526101a080820135908301526101c080820135908301526101e090810135910152565b80516001600160a01b031682526020810151613aab60208401826001600160a01b03169052565b506040810151613ac660408401826001600160a01b03169052565b506060810151613ae160608401826001600160a01b03169052565b506080810151613afc60808401826001600160a01b03169052565b5060a0810151613b1760a08401826001600160a01b03169052565b5060c0810151613b3260c08401826001600160a01b03169052565b5060e0810151613b4d60e08401826001600160a01b03169052565b50610100818101516001600160a01b03169083015261012080820151908301526101408082015190830152610160808201519083015261018080820151908301526101a080820151908301526101c080820151908301526101e090810151910152565b8183823760009101908152919050565b60008251613bd28184602087016141e4565b9190910192915050565b8781526001600160a01b0387811660208301528681166040830152851660608201526080810184905260c060a08201819052600090613c1e90830184866137eb565b9998505050505050505050565b6020815260006125fe6020830184613814565b602080825260079082015266234f4f3a30323960c81b604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60408152613ca76040820184613958565b6000613cb7610200850185614170565b61024084810152613ccd610280850182846137eb565b915050613cde610220860186614170565b848303603f1901610260860152613cf68382846137eb565b935050505060018060a01b03831660208301529392505050565b60a08152613d2160a0820187613958565b6102008601356102a08201526000613d3d610220880188614170565b6102806102c0850152613d55610320850182846137eb565b915050613d66610240890189614170565b609f1980868503016102e0870152613d7f8483856137eb565b9350613d8f6102608c018c614170565b93509150808685030161030087015250613daa8383836137eb565b92505050613dbc602084018815159052565b85151560408401528281036060840152613dd68186613814565b91505061267d60808301846001600160a01b03169052565b6101a08101612c578284613840565b81516001600160a01b031681526101a081016020830151613e2960208401826001600160a01b03169052565b506040830151613e4460408401826001600160a01b03169052565b506060830151613e5f60608401826001600160a01b03169052565b506080830151613e7a60808401826001600160a01b03169052565b5060a0830151613e9560a08401826001600160a01b03169052565b5060c0830151613eb060c08401826001600160a01b03169052565b5060e0830151613ecb60e08401826001600160a01b03169052565b50610100838101516001600160a01b03169083015261012080840151908301526101408084015190830152610160808401519083015261018092830151929091019190915290565b60208152815160208201526000602083015160806040840152613f3960a0840182613814565b6040850151606085810191909152909401516001600160a01b03166080909301929092525090919050565b6020815281516020820152602082015160408201526000604083015160a06060840152613f9460c0840182613814565b6060850151608085810191909152909401516001600160a01b031660a0909301929092525090919050565b6102008101612c578284613a84565b6000610240613fdd8387613a84565b61020060018060a01b03861681850152610220828186015261400183860187613840565b6101a08601356103e08601526101c08601356104008601526140276101e0870187614170565b61026061042088015261403f6104a0880182846137eb565b91505061404e83880188614170565b935061023f19808884030161044089015261406a8386846137eb565b9450614078848a018a614170565b9450925080888603016104608901526140928585856137eb565b94506140a0868a018a614170565b9650935080888603016104808901525050506140bd8284836137eb565b98975050505050505050565b6000808335601e198436030181126140df578283fd5b83018035915067ffffffffffffffff8211156140f9578283fd5b60200191503681900382131561410e57600080fd5b9250929050565b604051610200810167ffffffffffffffff8111828210171561413957614139614226565b60405290565b604051601f8201601f1916810167ffffffffffffffff8111828210171561416857614168614226565b604052919050565b6000808335601e19843603018112614186578283fd5b830160208101925035905067ffffffffffffffff8111156141a657600080fd5b80360383131561410e57600080fd5b600082198211156141c8576141c8614210565b500190565b6000828210156141df576141df614210565b500390565b60005b838110156141ff5781810151838201526020016141e7565b83811115612df35750506000910152565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811461123157600080fdfea2646970667358221220a05c87d31e3a6ea060c72e56be8ebb28bec7975311797ac9ae2c0cf4b2d3d5a264736f6c6343000804003360a060405234801561001057600080fd5b50604051610bf0380380610bf083398101604081905261002f91610049565b600160005560601b6001600160601b031916608052610077565b60006020828403121561005a578081fd5b81516001600160a01b0381168114610070578182fd5b9392505050565b60805160601c610b5661009a600039600081816048015260a90152610b566000f3fe6080604052600436106100295760003560e01c806396f32fb81461002e578063cf9a360414610077575b600080fd5b34801561003a57600080fd5b506040516001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001681526020015b60405180910390f35b61008a6100853660046108c4565b610099565b60405161006e93929190610a79565b6000806060336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146101065760405162461bcd60e51b8152602060048201526008602482015267234f544d3a30323760c01b60448201526064015b60405180910390fd5b6001600160a01b038816158061012157610121898b8961021b565b600060608b3b158015906101a0578c6001600160a01b031684610145576000610147565b8a5b8a8a6040516101579291906109c1565b60006040518083038185875af1925050503d8060008114610194576040519150601f19603f3d011682016040523d82523d6000602084013e610199565b606091505b5090935091505b826101c0576101b08c8c8c61026b565b836101c0576101c08c8e8c61028f565b8d7f03196b76502b81bbf14393f8b5ed67dff323f1f86667b064820f1fdf293686a18e8e8e8e8e8e898b8a604051610200999897969594939291906109ed565b60405180910390a2919d919c509a5098505050505050505050565b6001600160a01b03831661025b5760405162461bcd60e51b815260206004820152600760248201526608d2504e8c0ccd60ca1b60448201526064016100fd565b6102668383836102da565b505050565b6001600160a01b03831615610285576102668383836103d4565b61026682826103df565b6001600160a01b0383166102cf5760405162461bcd60e51b815260206004820152600760248201526608d1104e8c0ccd60ca1b60448201526064016100fd565b6102668383836103ed565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e9060440160206040518083038186803b15801561032657600080fd5b505afa15801561033a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061035e919061097d565b6103689190610ab8565b6040516001600160a01b0385166024820152604481018290529091506103ce90859063095ea7b360e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261050f565b50505050565b6102668383836105e1565b6103e98282610611565b5050565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301526000919085169063dd62ed3e9060440160206040518083038186803b15801561043857600080fd5b505afa15801561044c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610470919061097d565b9050818110156104d45760405162461bcd60e51b815260206004820152602960248201527f5361666545524332303a2064656372656173656420616c6c6f77616e63652062604482015268656c6f77207a65726f60b81b60648201526084016100fd565b6040516001600160a01b0384166024820152828203604482018190529061050890869063095ea7b360e01b90606401610397565b5050505050565b6000610564826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661072a9092919063ffffffff16565b805190915015610266578080602001905181019061058291906108a4565b6102665760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016100fd565b6040516001600160a01b03831660248201526044810182905261026690849063a9059cbb60e01b90606401610397565b804710156106615760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e636500000060448201526064016100fd565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146106ae576040519150601f19603f3d011682016040523d82523d6000602084013e6106b3565b606091505b50509050806102665760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d6179206861766520726576657274656400000000000060648201526084016100fd565b60606107398484600085610743565b90505b9392505050565b6060824710156107a45760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016100fd565b843b6107f25760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016100fd565b600080866001600160a01b0316858760405161080e91906109d1565b60006040518083038185875af1925050503d806000811461084b576040519150601f19603f3d011682016040523d82523d6000602084013e610850565b606091505b509150915061086082828661086b565b979650505050505050565b6060831561087a57508161073c565b82511561088a5782518084602001fd5b8160405162461bcd60e51b81526004016100fd9190610aa5565b6000602082840312156108b5578081fd5b8151801515811461073c578182fd5b600080600080600080600060c0888a0312156108de578283fd5b8735965060208801356108f081610b08565b9550604088013561090081610b08565b9450606088013561091081610b08565b93506080880135925060a088013567ffffffffffffffff80821115610933578384fd5b818a0191508a601f830112610946578384fd5b813581811115610954578485fd5b8b6020828501011115610965578485fd5b60208301945080935050505092959891949750929550565b60006020828403121561098e578081fd5b5051919050565b600081518084526109ad816020860160208601610adc565b601f01601f19169290920160200192915050565b8183823760009101908152919050565b600082516109e3818460208701610adc565b9190910192915050565b6001600160a01b038a8116825289811660208301528816604082015260608101879052610100608082018190528101859052600061012086888285013781818885010152601f19601f8801168301818482030160a0850152610a5182820188610995565b9250505083151560c0830152610a6b60e083018415159052565b9a9950505050505050505050565b83151581528215156020820152606060408201526000610a9c6060830184610995565b95945050505050565b60208152600061073c6020830184610995565b60008219821115610ad757634e487b7160e01b81526011600452602481fd5b500190565b60005b83811015610af7578181015183820152602001610adf565b838111156103ce5750506000910152565b6001600160a01b0381168114610b1d57600080fd5b5056fea26469706673582212202269da452bba446914352b77d48fb1282f9d93d6579e7d5164e4949a9439643864736f6c6343000804003300000000000000000000000000000000000000000000000000000000000000fa

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000000000000000000000000000000000000000000fa

-----Decoded View---------------
Arg [0] : _chainId (uint256): 250

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 00000000000000000000000000000000000000000000000000000000000000fa


Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Validator ID :
0 FTM

Amount Staked
0

Amount Delegated
0

Staking Total
0

Staking Start Epoch
0

Staking Start Time
0

Proof of Importance
0

Origination Score
0

Validation Score
0

Active
0

Online
0

Downtime
0 s
Address Amount claimed Rewards Created On Epoch Created On
Block Uncle Number Difficulty Gas Used Reward
Loading
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.