Contract 0xcf4d2994088a8cde52fb584fe29608b63ec063b2

 
Txn Hash
Block
From
To
Value [Txn Fee]
0x87e25ee2c23f9a0d09245374739b575a0543e8d714363f9f9df70295edf61c2222870422021-10-17 0:28:2757 mins ago0xaba44c23941cec85a0ba9d6adda3f220a852b947 IN  0xcf4d2994088a8cde52fb584fe29608b63ec063b20 ETH0.002096412245 ETH
0x3a2d471fd63aa990619cee86e3936c28cca1a0d1925a60913da989836671772822870072021-10-17 0:26:3959 mins ago0x29a519e21d6a97cdb82270b69c98bac6426cdcf9 IN  0xcf4d2994088a8cde52fb584fe29608b63ec063b20 ETH0.001344513097 ETH
0x882713263fcf47eca5b9df72891016ffa2f3bb7f5c65eba712536c1d990e870422869382021-10-17 0:22:471 hr 2 mins ago0x29a519e21d6a97cdb82270b69c98bac6426cdcf9 IN  0xcf4d2994088a8cde52fb584fe29608b63ec063b20 ETH0.001348258211 ETH
0x7c8b6e631e6f2eb42dfcb575069754761f253f9138bc37bb08d86bc1ef5c61b322868672021-10-17 0:17:051 hr 8 mins ago0x5b88117ba41ad7c297ddddfb92595a8616342fe4 IN  0xcf4d2994088a8cde52fb584fe29608b63ec063b20 ETH0.002111369221 ETH
0x6d7bdfe6d55bfcaaecd315044f567fdb10e06f45791ceebfaa03c49cb57645be22866112021-10-17 0:03:321 hr 22 mins ago0xdfa783eae4efb89c6593df412748564b360475ce IN  0xcf4d2994088a8cde52fb584fe29608b63ec063b20 ETH0.002096377025 ETH
0xdf4fed1ad2ee061acef832483756a76ba0013d74f54d7fcf39b2bb82086d625c22865712021-10-17 0:00:021 hr 25 mins ago0x29a519e21d6a97cdb82270b69c98bac6426cdcf9 IN  0xcf4d2994088a8cde52fb584fe29608b63ec063b20 ETH0.001348264532 ETH
0x4c892df1afb9c8bf7aa1d5adf510e70d9800c62835681a1f4005358687ac20df22865422021-10-16 23:58:321 hr 27 mins ago0x0a73f81822fdea1b419a7b6e0d6eafaede6f2051 IN  0xcf4d2994088a8cde52fb584fe29608b63ec063b20 ETH0.002103888024 ETH
0xde69b2761fb204659d02dcb7779bac29a8584bf10b8953331c4f4482ffdb3cf222865262021-10-16 23:57:321 hr 28 mins ago0x29a519e21d6a97cdb82270b69c98bac6426cdcf9 IN  0xcf4d2994088a8cde52fb584fe29608b63ec063b20 ETH0.001347011947 ETH
0x6d8a4280d72761887ba7fde001707a057de48b784a5a61bf3c5148f3136401d222864882021-10-16 23:55:491 hr 29 mins ago0x7e132e36cd98226e1e352e3f57ea96e3485ec760 IN  0xcf4d2994088a8cde52fb584fe29608b63ec063b20 ETH0.002107448005 ETH
0xeed428450f17a025636e697c8a0a41253b4c03f1c57d854a85ffed32bc4ff97b22863752021-10-16 23:48:171 hr 37 mins ago0x29a519e21d6a97cdb82270b69c98bac6426cdcf9 IN  0xcf4d2994088a8cde52fb584fe29608b63ec063b20 ETH0.001345753039 ETH
0x161acc18ef9e273a58d252e5b831334a9e66b722510effd8f3eca5e4abcbaae222863142021-10-16 23:42:241 hr 43 mins ago0x96617e1fb4cc02e033244138ba2f029ddb585a2c IN  0xcf4d2994088a8cde52fb584fe29608b63ec063b20 ETH0.002110091349 ETH
0x89cf5ec98601ae1b51637cf8d9acac59fda3f2e90b72f3c46ed476384b6aef5622862752021-10-16 23:39:431 hr 45 mins ago0x29a519e21d6a97cdb82270b69c98bac6426cdcf9 IN  0xcf4d2994088a8cde52fb584fe29608b63ec063b20 ETH0.001347011947 ETH
0xedfda8a5e40e07c4f166d3210745edd9f10d3d868daad0495c4419a2c546970d22862432021-10-16 23:36:341 hr 49 mins ago0x29a519e21d6a97cdb82270b69c98bac6426cdcf9 IN  0xcf4d2994088a8cde52fb584fe29608b63ec063b20 ETH0.001348251889 ETH
0x4bad1279593eb714b56b329074fb74be2ca11cddd3967622d4e9b23798ce414022862302021-10-16 23:35:471 hr 49 mins ago0x8c150ba5569279adedd2527de04c01afcb49ce58 IN  0xcf4d2994088a8cde52fb584fe29608b63ec063b20 ETH0.002101361178 ETH
0x44200ce9c2ab61c002d1a7f383a5a334774b942ee2874e5f4e6be5ea4935e76222861672021-10-16 23:31:311 hr 54 mins ago0x55d79f18348e20e7c2b89bf0059a352abe1ac2ea IN  0xcf4d2994088a8cde52fb584fe29608b63ec063b20 ETH0.00210619 ETH
0x873d7b33f9eae180088c7b7a8e6f5a66d0d893217bf3fe02fc6d13eb462f1c7122860152021-10-16 23:18:522 hrs 6 mins ago0x29a519e21d6a97cdb82270b69c98bac6426cdcf9 IN  0xcf4d2994088a8cde52fb584fe29608b63ec063b20 ETH0.001564287409 ETH
0x9cade0c6b1e36c0f42f8ccba5eeddc1b5a709b829461470904d9323eb177e5a622860092021-10-16 23:18:522 hrs 6 mins ago0x29a519e21d6a97cdb82270b69c98bac6426cdcf9 IN  0xcf4d2994088a8cde52fb584fe29608b63ec063b20 ETH0.002142909219 ETH
0x940ff4c93082e65cb7e5151ab354b3d1822dc666d33d1775dd02d647a4c72d3c22856042021-10-16 22:45:492 hrs 39 mins ago0x29a519e21d6a97cdb82270b69c98bac6426cdcf9 IN  0xcf4d2994088a8cde52fb584fe29608b63ec063b20 ETH0.001566819032 ETH
0xb3db30637882b647dc98c5b5476a34284af74ad093756ee702174ebcda37bc4622855662021-10-16 22:44:412 hrs 40 mins ago0x29a519e21d6a97cdb82270b69c98bac6426cdcf9 IN  0xcf4d2994088a8cde52fb584fe29608b63ec063b20 ETH0.002147989052 ETH
0x59a537312b46b6c63e609d234d87e8a4932dfc49affe40b27f852a8efc9bde9f22855202021-10-16 22:40:492 hrs 44 mins ago0x29a519e21d6a97cdb82270b69c98bac6426cdcf9 IN  0xcf4d2994088a8cde52fb584fe29608b63ec063b20 ETH0.001564265291 ETH
0x217197aca6f09c8ac12c957d014fa1a16a09d7effe6fba6f88e817a78297f9e022855062021-10-16 22:40:492 hrs 44 mins ago0x29a519e21d6a97cdb82270b69c98bac6426cdcf9 IN  0xcf4d2994088a8cde52fb584fe29608b63ec063b20 ETH0.002140365615 ETH
0x18b2782f66dcfbe12ddf6559a811b2899c3b5be96a79514f4f92a4c2844db95622854972021-10-16 22:40:492 hrs 44 mins ago0x29a519e21d6a97cdb82270b69c98bac6426cdcf9 IN  0xcf4d2994088a8cde52fb584fe29608b63ec063b20 ETH0.001373354967 ETH
0xca6cd1e0231a9dedef8cc76acc148d4e7cc84d782f249eaca78fcae5886f160522853072021-10-16 22:29:002 hrs 56 mins ago0x29a519e21d6a97cdb82270b69c98bac6426cdcf9 IN  0xcf4d2994088a8cde52fb584fe29608b63ec063b20 ETH0.00156557027 ETH
0xd45d613538a42761b4f870e442cd3bc216f497088d5fa38963304a5115a538a022852952021-10-16 22:29:002 hrs 56 mins ago0x29a519e21d6a97cdb82270b69c98bac6426cdcf9 IN  0xcf4d2994088a8cde52fb584fe29608b63ec063b20 ETH0.002141654006 ETH
0x4d1540ea10ad256286c5d13974d1711b15ed836ca0b1ecc8598c4629ea72b9e322852522021-10-16 22:25:592 hrs 59 mins ago0x29a519e21d6a97cdb82270b69c98bac6426cdcf9 IN  0xcf4d2994088a8cde52fb584fe29608b63ec063b20 ETH0.001370804913 ETH
[ Download CSV Export 
Parent Txn Hash Block From To Value
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",
        "abi"
      ]
    }
  }
}

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"}]

60c06040523480156200001157600080fd5b5060405162004fd338038062004fd3833981016040819052620000349162000105565b600160005562000044336200009e565b608081905260405130906200005990620000f7565b6001600160a01b039091168152602001604051809103906000f08015801562000086573d6000803e3d6000fd5b5060601b6001600160601b03191660a052506200011e565b600180546001600160a01b038381166001600160a01b0319831681179093556000600381905560405191909216929183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b610bf080620043e383390190565b60006020828403121562000117578081fd5b5051919050565b60805160a05160601c6142876200015c600039600081816102ee015281816127710152612798015260008181610264015261078901526142876000f3fe6080604052600436106102045760003560e01c80638da5cb5b11610118578063c95f9d0e116100a0578063de38eb3a1161006f578063de38eb3a146105db578063e070da09146105f2578063e47602f714610605578063e8be0dfc1461061a578063f31abcc41461062f57600080fd5b8063c95f9d0e14610578578063d1851c921461058b578063d232c220146105a9578063d9459372146105c857600080fd5b8063b1f8100d116100e7578063b1f8100d146104f9578063be91a2ba14610519578063c0c17baf14610539578063c1a049591461054e578063c5b350df1461056357600080fd5b80638da5cb5b1461045e57806397eb00881461047c5780639b151a80146104ac578063b1d2618d146104d957600080fd5b806341258b5c1161019b5780636a41633a1161016a5780636a41633a146103e95780636a42b8f8146103fe5780636ae0b15414610414578063715018a6146104345780638741eac51461044957600080fd5b806341258b5c1461033d578063445b1e4b14610375578063543ad1df146103a55780635e679856146103bc57600080fd5b806334e9393c116101d757806334e9393c146102a75780633855b467146102c75780633a35cf17146102dc5780633cf52ffb1461032857600080fd5b80632004ef451461020957806324ca984e1461023357806332a130c9146102555780633408e47014610292575b600080fd5b34801561021557600080fd5b5061021e61064f565b60405190151581526020015b60405180910390f35b34801561023f57600080fd5b5061025361024e366004613486565b610671565b005b34801561026157600080fd5b507f00000000000000000000000000000000000000000000000000000000000000005b60405190815260200161022a565b34801561029e57600080fd5b50610284610785565b3480156102b357600080fd5b506102536102c2366004613486565b6107b5565b3480156102d357600080fd5b50610253610880565b3480156102e857600080fd5b506103107f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200161022a565b34801561033457600080fd5b50600354610284565b34801561034957600080fd5b506102846103583660046134a2565b600860209081526000928352604080842090915290825290205481565b34801561038157600080fd5b5061021e610390366004613486565b60096020526000908152604090205460ff1681565b3480156103b157600080fd5b506102846201518081565b3480156103c857600080fd5b506102846103d73660046135a7565b600b6020526000908152604090205481565b3480156103f557600080fd5b50600754610284565b34801561040a57600080fd5b5062093a80610284565b34801561042057600080fd5b5061025361042f366004613486565b61097b565b34801561044057600080fd5b50610253610a87565b34801561045557600080fd5b50610253610b8e565b34801561046a57600080fd5b506001546001600160a01b0316610310565b34801561048857600080fd5b5061021e610497366004613486565b600a6020526000908152604090205460ff1681565b3480156104b857600080fd5b506104cc6104c73660046135f8565b610bff565b60405161022a9190613fbf565b3480156104e557600080fd5b506102536104f4366004613486565b61108e565b34801561050557600080fd5b50610253610514366004613486565b61115a565b34801561052557600080fd5b506104cc6105343660046135bf565b611234565b34801561054557600080fd5b50610253611597565b34801561055a57600080fd5b50600554610284565b34801561056f57600080fd5b50610253611690565b610253610586366004613786565b61176a565b34801561059757600080fd5b506002546001600160a01b0316610310565b3480156105b557600080fd5b506001546001600160a01b03161561021e565b6104cc6105d6366004613631565b6117a6565b3480156105e757600080fd5b5061028462278d0081565b6102536106003660046137aa565b612104565b34801561061157600080fd5b50610253612141565b34801561062657600080fd5b5061021e6121b2565b34801561063b57600080fd5b5061025361064a3660046137aa565b6121d2565b6001546000906001600160a01b0316158061066c575060045460ff165b905090565b6001546001600160a01b031633146106a45760405162461bcd60e51b815260040161069b90613c3e565b60405180910390fd5b6001600160a01b0381166106e45760405162461bcd60e51b81526020600482015260076024820152662341523a30303160c81b604482015260640161069b565b6001600160a01b03811660009081526009602052604090205460ff16156107375760405162461bcd60e51b815260206004820152600760248201526611a0a91d18199960c91b604482015260640161069b565b6001600160a01b038116600081815260096020526040808220805460ff19166001179055513392917fbc68405e644da2aaf25623ce2199da82c6dfd2e1de102b400eba6a091704d4f491a350565b60007f0000000000000000000000000000000000000000000000000000000000000000806107b05750465b919050565b6001546001600160a01b031633146107df5760405162461bcd60e51b815260040161069b90613c3e565b6001600160a01b0381166000908152600a602052604090205460ff16156108325760405162461bcd60e51b815260206004820152600760248201526611a0a09d18199960c91b604482015260640161069b565b6001600160a01b0381166000818152600a6020526040808220805460ff19166001179055513392917f0bb5715f0f217c2fe9a0c877ea87d474380c641102f3440ee2a4c8b9d979091891a350565b6001546001600160a01b031633146108aa5760405162461bcd60e51b815260040161069b90613c3e565b60065460ff16156108e85760405162461bcd60e51b8152602060048201526008602482015267046a4829e746066760c31b604482015260640161069b565b6000600754116109255760405162461bcd60e51b81526020600482015260086024820152672352414f3a30333760c01b604482015260640161069b565b62093a806007544261093791906141cd565b1161096f5760405162461bcd60e51b815260206004820152600860248201526702352414f3a3033360c41b604482015260640161069b565b6109796001612357565b565b6001546001600160a01b031633146109a55760405162461bcd60e51b815260040161069b90613c3e565b6001600160a01b0381166109e55760405162461bcd60e51b81526020600482015260076024820152662352523a30303160c81b604482015260640161069b565b6001600160a01b03811660009081526009602052604090205460ff161515600114610a3c5760405162461bcd60e51b81526020600482015260076024820152662352523a30333360c81b604482015260640161069b565b6001600160a01b038116600081815260096020526040808220805460ff19169055513392917fbee3e974bb6a6f44f20096ede047c191eef60322e65e4ee4bd3392230a8716d591a350565b6001546001600160a01b03163314610ab15760405162461bcd60e51b815260040161069b90613c3e565b600060035411610aed5760405162461bcd60e51b815260206004820152600760248201526623524f3a30333760c81b604482015260640161069b565b62093a8060035442610aff91906141cd565b11610b365760405162461bcd60e51b8152602060048201526007602482015266023524f3a3033360cc1b604482015260640161069b565b6002546001600160a01b031615610b795760405162461bcd60e51b815260206004820152600760248201526611a9279d18199b60c91b604482015260640161069b565b600254610979906001600160a01b03166123a4565b6001546001600160a01b03163314610bb85760405162461bcd60e51b815260040161069b90613c3e565b60065460ff1615610bf75760405162461bcd60e51b8152602060048201526009602482015268046a0829ea4746066760bb1b604482015260640161069b565b6109796123fd565b610c076133e7565b60026000541415610c2a5760405162461bcd60e51b815260040161069b90613c5f565b60026000908155610c3a83612439565b9050610c566101a08401356101c08501356101e08601356125b7565b6000828152600b602052604090205414610c9b5760405162461bcd60e51b815260206004820152600660248201526523463a30313960d01b604482015260640161069b565b426101c08401351015610cd95760405162461bcd60e51b8152602060048201526006602482015265023463a3032360d41b604482015260640161069b565b6101e0830135610d145760405162461bcd60e51b815260206004820152600660248201526523463a30323160d01b604482015260640161069b565b610120830135610d286102408501856140c9565b604051610d36929190613bb0565b604051809103902014610d745760405162461bcd60e51b815260206004820152600660248201526508d18e8c0c8d60d21b604482015260640161069b565b610d8a6101a08401356101c085013560006125b7565b6000918252600b602052604082205580606081610da5610785565b9050610160860135811415610f1a57610dc46060870160408801613486565b6001600160a01b0316336001600160a01b031614610e0d5760405162461bcd60e51b815260206004820152600660248201526511a31d18189b60d11b604482015260640161069b565b610e1d6040870160208801613486565b6001600160a01b0316610e5b6101408801356102008901356101808a0135610e4860208c018c613486565b610e566102208d018d6140c9565b612605565b6001600160a01b031614610e9a5760405162461bcd60e51b815260206004820152600660248201526511a31d18191960d11b604482015260640161069b565b6101a086013560086000610eb460608a0160408b01613486565b6001600160a01b031681526020810191909152604001600090812090610ee060a08a0160808b01613486565b6001600160a01b03166001600160a01b031681526020019081526020016000206000828254610f0f91906141b5565b90915550610ff99050565b610f2a6040870160208801613486565b6001600160a01b0316610f526101408801356102008901358430610e566102208d018d6140c9565b6001600160a01b031614610f915760405162461bcd60e51b815260206004820152600660248201526511a31d18191960d11b604482015260640161069b565b6101a08601356102008701351115610fd45760405162461bcd60e51b815260206004820152600660248201526523463a30323360d01b604482015260640161069b565b610ff186610200810135610fec6102408301836140c9565b612687565b919550935091505b61014086013561100f6060880160408901613486565b6001600160a01b03166110286040890160208a01613486565b6001600160a01b03167f8e5df24f8b9ac0e3455417a1d7060762388ce3c1d4941aa49dc1b61943031d328988888833604051611068959493929190613d10565b60405180910390a461107f3687900387018761366a565b60016000559695505050505050565b6001546001600160a01b031633146110b85760405162461bcd60e51b815260040161069b90613c3e565b6001600160a01b0381166000908152600a602052604090205460ff16151560011461110f5760405162461bcd60e51b81526020600482015260076024820152662352413a30333360c81b604482015260640161069b565b6001600160a01b0381166000818152600a6020526040808220805460ff19169055513392917f0fa1e4606af435f32f05b3804033d2933e691fab32ee74d2db6fa82d2741f1ea91a350565b6001546001600160a01b031633146111845760405162461bcd60e51b815260040161069b90613c3e565b6002546001600160a01b0382811691161415806111a857506001600160a01b038116155b6111df5760405162461bcd60e51b815260206004820152600860248201526711a827279d18199b60c11b604482015260640161069b565b6001546001600160a01b03828116911614156112285760405162461bcd60e51b8152602060048201526008602482015267046a09c9e746066760c31b604482015260640161069b565b6112318161289f565b50565b61123c6133e7565b6002600054141561125f5760405162461bcd60e51b815260040161069b90613c5f565b6002600090815561126f83612439565b905061128b6101a08401356101c08501356101e08601356125b7565b6000828152600b6020526040902054146112d05760405162461bcd60e51b815260206004820152600660248201526523433a30313960d01b604482015260640161069b565b6101e083013561130b5760405162461bcd60e51b815260206004820152600660248201526523433a30323160d01b604482015260640161069b565b6113216101a08401356101c085013560006125b7565b6000828152600b602052604081209190915561133b610785565b90506101608401358114156113e257426101c0850135106113af576113666060850160408601613486565b6001600160a01b0316336001600160a01b0316146113af5760405162461bcd60e51b815260206004820152600660248201526523433a30323560d01b604482015260640161069b565b6113dd6113c260a0860160808701613486565b6113d260e0870160c08801613486565b6101a08701356128ed565b61150a565b426101c08501351061148f576113fe6040850160208601613486565b6001600160a01b0316336001600160a01b0316148061145a57506114286040850160208601613486565b6001600160a01b031661144f610140860135833061144a6102008a018a6140c9565b612916565b6001600160a01b0316145b61148f5760405162461bcd60e51b815260206004820152600660248201526511a19d18191960d11b604482015260640161069b565b6101a0840135600860006114a96060880160408901613486565b6001600160a01b0316815260208101919091526040016000908120906114d560c0880160a08901613486565b6001600160a01b03166001600160a01b03168152602001908152602001600020600082825461150491906141b5565b90915550505b6101408401356115206060860160408701613486565b6001600160a01b03166115396040870160208801613486565b6001600160a01b03167f56a92405e111173b950d90846413f755ca35bb7631d49a4a564778b21affe2878733604051611573929190613c96565b60405180910390a461158a3685900385018561366a565b6001600055949350505050565b6001546001600160a01b031633146115c15760405162461bcd60e51b815260040161069b90613c3e565b60045460ff16156115ff5760405162461bcd60e51b8152602060048201526008602482015267046a4a49e746066760c31b604482015260640161069b565b60006005541161163c5760405162461bcd60e51b81526020600482015260086024820152672352524f3a30333760c01b604482015260640161069b565b62093a806005544261164e91906141cd565b116116865760405162461bcd60e51b815260206004820152600860248201526702352524f3a3033360c41b604482015260640161069b565b6109796001612982565b6002546001600160a01b031633146116d45760405162461bcd60e51b8152602060048201526007602482015266234f503a30333560c81b604482015260640161069b565b6002546001546001600160a01b03908116911614156117205760405162461bcd60e51b815260206004820152600860248201526704682a09e746066760c31b604482015260640161069b565b62093a806003544261173291906141cd565b11610b795760405162461bcd60e51b815260206004820152600860248201526702341504f3a3033360c41b604482015260640161069b565b6002600054141561178d5760405162461bcd60e51b815260040161069b90613c5f565b600260005561179d8282336129c8565b50506001600055565b6117ae6133e7565b600260005414156117d15760405162461bcd60e51b815260040161069b90613c5f565b600260009081556117e86040840160208501613486565b6001600160a01b031614156118285760405162461bcd60e51b815260206004820152600660248201526523503a30303960d01b604482015260640161069b565b600061183a6060840160408501613486565b6001600160a01b0316141561187a5760405162461bcd60e51b815260206004820152600660248201526523503a30303160d01b604482015260640161069b565b61188261064f565b806118ba57506009600061189c6060850160408601613486565b6001600160a01b0316815260208101919091526040016000205460ff165b6118ef5760405162461bcd60e51b815260206004820152600660248201526523503a30303360d01b604482015260640161069b565b600061190160e0840160c08501613486565b6001600160a01b031614156119415760405162461bcd60e51b8152602060048201526006602482015265023503a3031360d41b604482015260640161069b565b6000611954610100840160e08501613486565b6001600160a01b031614156119945760405162461bcd60e51b815260206004820152600660248201526511a81d18191b60d11b604482015260640161069b565b61012082013561014083013514156119d75760405162461bcd60e51b815260206004820152600660248201526523503a30313160d01b604482015260640161069b565b60006119e1610785565b90506101208301358114806119fa575061014083013581145b611a2f5760405162461bcd60e51b815260206004820152600660248201526511a81d18189960d11b604482015260640161069b565b6000611a40426101c08601356141cd565b905062015180811015611a7e5760405162461bcd60e51b815260206004820152600660248201526523503a30313360d01b604482015260640161069b565b62278d00811115611aba5760405162461bcd60e51b815260206004820152600660248201526508d40e8c0c4d60d21b604482015260640161069b565b50604051600090611acf908590602001613dee565b60408051601f1981840301815291815281516020928301206000818152600b90935291205490915015611b2d5760405162461bcd60e51b815260206004820152600660248201526523503a30313560d01b604482015260640161069b565b6101a0840135610120850135831415611c9057611b506080860160608701613486565b6001600160a01b0316336001600160a01b031614611b995760405162461bcd60e51b815260206004820152600660248201526523503a30333960d01b604482015260640161069b565b6000856101a0013511611bd75760405162461bcd60e51b815260206004820152600660248201526511a81d18181960d11b604482015260640161069b565b611bdf6121b2565b80611c175750600a6000611bf960a0880160808901613486565b6001600160a01b0316815260208101919091526040016000205460ff165b611c4c5760405162461bcd60e51b815260206004820152600660248201526508d40e8c0c0d60d21b604482015260640161069b565b611c6a611c5f60a0870160808801613486565b866101a00135612b9c565b9050611c7c81866101c00135436125b7565b6000838152600b6020526040902055611f29565b6000611ca461012087016101008801613486565b6001600160a01b03161480611ccf5750611ccf611cc961012087016101008801613486565b3b151590565b611d045760405162461bcd60e51b815260206004820152600660248201526523503a30333160d01b604482015260640161069b565b611d0c6121b2565b80611d445750600a6000611d2660c0880160a08901613486565b6001600160a01b0316815260208101919091526040016000205460ff165b611d795760405162461bcd60e51b815260206004820152600660248201526508d40e8c0c0d60d21b604482015260640161069b565b611d896060860160408701613486565b6001600160a01b0316336001600160a01b031614611dd25760405162461bcd60e51b815260206004820152600660248201526511a81d18189b60d11b604482015260640161069b565b3415611e095760405162461bcd60e51b815260206004820152600660248201526523503a30313760d01b604482015260640161069b565b6000600881611e1e6060890160408a01613486565b6001600160a01b031681526020810191909152604001600090812090611e4a60c0890160a08a01613486565b6001600160a01b03166001600160a01b0316815260200190815260200160002054905081811015611ea65760405162461bcd60e51b8152602060048201526006602482015265046a0746062760d31b604482015260640161069b565b611eb682876101c00135436125b7565b6000848152600b6020526040808220929092558383039160089190611ee19060608b01908b01613486565b6001600160a01b031681526020810191909152604001600090812090611f0d60c08a0160a08b01613486565b6001600160a01b03168152602081019190915260400160002055505b60408051610200810190915260009080611f466020890189613486565b6001600160a01b03168152602090810190611f679060408a01908a01613486565b6001600160a01b03168152602001611f856060890160408a01613486565b6001600160a01b03168152602001611fa36080890160608a01613486565b6001600160a01b03168152602001611fc160a0890160808a01613486565b6001600160a01b03168152602001611fdf60c0890160a08a01613486565b6001600160a01b03168152602001611ffd60e0890160c08a01613486565b6001600160a01b0316815260200161201c610100890160e08a01613486565b6001600160a01b0316815260200161203c61012089016101008a01613486565b6001600160a01b031681526020018760000161016001358152602001876000016101800135815260200187600001610120013581526020018760000161014001358152602001838152602001876101c00135815260200143815250905080610140015181604001516001600160a01b031682602001516001600160a01b03167f88fbf1dbc326c404155bad4643bd0ddadd23f0636929c66442f0433208b2c90584338b6040516120ee93929190613fce565b60405180910390a4600160005595945050505050565b600260005414156121275760405162461bcd60e51b815260040161069b90613c5f565b60026000556121378383836129c8565b5050600160005550565b6001546001600160a01b0316331461216b5760405162461bcd60e51b815260040161069b90613c3e565b60045460ff16156121aa5760405162461bcd60e51b8152602060048201526009602482015268046a0a49ea4746066760bb1b604482015260640161069b565b610979612c5d565b6001546000906001600160a01b0316158061066c57505060065460ff1690565b600260005414156121f55760405162461bcd60e51b815260040161069b90613c5f565b60026000556001600160a01b03811661223a5760405162461bcd60e51b815260206004820152600760248201526623524c3a30303760c81b604482015260640161069b565b600083116122745760405162461bcd60e51b815260206004820152600760248201526611a9261d18181960c91b604482015260640161069b565b3360009081526008602090815260408083206001600160a01b0386168452909152902054838110156122d25760405162461bcd60e51b8152602060048201526007602482015266046a498746060760cb1b604482015260640161069b565b3360009081526008602090815260408083206001600160a01b0387168452909152902084820390556123058383866128ed565b604080518581526001600160a01b03848116602083015285169133917f7da12116be8cb7af4b2d9e9b4a2ca2c3a3243ddd6fd3a94411902367b8eed568910160405180910390a3505060016000555050565b6006805460ff191682151590811790915560006007556040519081527f868d89ead22a5d10f456845ac0014901d9af7203e71cf0892d70d9dc262c2fb9906020015b60405180910390a150565b600180546001600160a01b038381166001600160a01b0319831681179093556000600381905560405191909216929183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b4260078190556040519081527fa78fdca214e4619ef34a695316d423f5b0d8274bc919d29733bf8f92ec8cbb7a906020015b60405180910390a1565b604080516101a081019091526000908190806124586020860186613486565b6001600160a01b031681526020018460200160208101906124799190613486565b6001600160a01b031681526020016124976060860160408701613486565b6001600160a01b031681526020016124b56080860160608701613486565b6001600160a01b031681526020016124d360a0860160808701613486565b6001600160a01b031681526020016124f160c0860160a08701613486565b6001600160a01b0316815260200161250f60e0860160c08701613486565b6001600160a01b0316815260200161252e610100860160e08701613486565b6001600160a01b0316815260200161254e61012086016101008701613486565b6001600160a01b031681526020018461016001358152602001846101800135815260200184610120013581526020018461014001358152509050806040516020016125999190613dfd565b60405160208183030381529060405280519060200120915050919050565b604080516060808201835285825260208083018681529284018581528451808301899052935184860152518383015283518084039092018252608090920190925281519101205b9392505050565b6040805160a0810182528781526020808201889052825180840184526007815266199d5b199a5b1b60ca1b8183015282840152606082018790526001600160a01b038616608083015291516000926126799161266391849101613f64565b6040516020818303038152906040528585612c93565b9150505b9695505050505050565b60008060606101a087013586900386156126b5576126b56126ae60c08a0160a08b01613486565b33896128ed565b60006126c96101208a016101008b01613486565b6001600160a01b03161415612725578015612708576127086126f160c08a0160a08b01613486565b6127026101008b0160e08c01613486565b836128ed565b505060408051600080825260208201909252909250829150612895565b600061274761273a60c08b0160a08c01613486565b6001600160a01b03161590565b9050801580156127575750600082115b156127965761279661276f60c08b0160a08c01613486565b7f000000000000000000000000000000000000000000000000000000000000000084612d3a565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663cf9a3604826127d15760006127d3565b835b6101408c01356127eb6101208e016101008f01613486565b8d60a00160208101906127fe9190613486565b8e60e00160208101906128119190613486565b888e8e6040518963ffffffff1660e01b81526004016128369796959493929190613bdc565b6000604051808303818588803b15801561284f57600080fd5b505af1158015612863573d6000803e3d6000fd5b50505050506040513d6000823e601f3d908101601f1916820160405261288c91908101906134f4565b94509450945050505b9450945094915050565b42600355600280546001600160a01b0319166001600160a01b0383169081179091556040517f6ab4d119f23076e8ad491bc65ce85f017fb0591dce08755ba8591059cc51737a90600090a250565b6001600160a01b0383161561290c57612907838383612d3a565b505050565b6129078282612d45565b60008060405180608001604052808881526020016040518060400160405280600681526020016518d85b98d95b60d21b8152508152602001878152602001866001600160a01b03168152509050612977816040516020016126639190613f13565b979650505050505050565b6004805460ff191682151590811790915560006005556040519081527f243ebbb2f905234bbf0556bb38e1f7c23b09ffd2e441a16e58b844eb2ab7a39790602001612399565b6001600160a01b038116612a085760405162461bcd60e51b815260206004820152600760248201526623414c3a30303160c81b604482015260640161069b565b60008311612a425760405162461bcd60e51b815260206004820152600760248201526611a0a61d18181960c91b604482015260640161069b565b612a4a61064f565b80612a6d57506001600160a01b03811660009081526009602052604090205460ff165b612aa35760405162461bcd60e51b815260206004820152600760248201526623414c3a30303360c81b604482015260640161069b565b612aab6121b2565b80612ace57506001600160a01b0382166000908152600a602052604090205460ff165b612b045760405162461bcd60e51b815260206004820152600760248201526608d0530e8c0c0d60ca1b604482015260640161069b565b612b0e8284612b9c565b6001600160a01b038083166000908152600860209081526040808320938716835292905290812080549295508592909190612b4a9084906141b5565b9091555050604080518481523360208201526001600160a01b0380851692908416917f4bd28ccd068c4853d24d35f727ef2a3fea11ce55e8d93461e45f785818e1e139910160405180910390a3505050565b6000816001600160a01b038416612beb57823414612be65760405162461bcd60e51b81526020600482015260076024820152662354413a30303560c81b604482015260640161069b565b612c54565b6000612bf685612d53565b90503415612c305760405162461bcd60e51b815260206004820152600760248201526611aa209d18181b60c91b604482015260640161069b565b612c3c85333087612de7565b80612c4686612d53565b612c5091906141cd565b9150505b90505b92915050565b4260058190556040519081527fa52048c5f468d21a62e4644ac4db19bcaa1a20f0cf37d163ba49c7217d35feb89060200161242f565b6000612d32612cf685805190602001206040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101829052600090605c01604051602081830303815290604052805190602001209050919050565b84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612df992505050565b949350505050565b612907838383612e9d565b612d4f8282612f00565b5050565b60006001600160a01b03821615612de0576040516370a0823160e01b81523060048201526001600160a01b038316906370a082319060240160206040518083038186803b158015612da357600080fd5b505afa158015612db7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ddb919061376e565b612c57565b4792915050565b612df384848484613019565b50505050565b6000815160411415612e2d5760208201516040830151606084015160001a612e2386828585613051565b9350505050612c57565b815160401415612e555760208201516040830151612e4c8583836131fa565b92505050612c57565b60405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604482015260640161069b565b6040516001600160a01b03831660248201526044810182905261290790849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261321a565b80471015612f505760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e6365000000604482015260640161069b565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114612f9d576040519150601f19603f3d011682016040523d82523d6000602084013e612fa2565b606091505b50509050806129075760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d61792068617665207265766572746564000000000000606482015260840161069b565b6040516001600160a01b0380851660248301528316604482015260648101829052612df39085906323b872dd60e01b90608401612ec9565b60007f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08211156130ce5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b606482015260840161069b565b8360ff16601b14806130e357508360ff16601c145b61313a5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b606482015260840161069b565b6040805160008082526020820180845288905260ff871692820192909252606081018590526080810184905260019060a0016020604051602081039080840390855afa15801561318e573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166131f15760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e61747572650000000000000000604482015260640161069b565b95945050505050565b60006001600160ff1b03821660ff83901c601b0161267d86828785613051565b600061326f826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166132ec9092919063ffffffff16565b805190915015612907578080602001905181019061328d91906134da565b6129075760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161069b565b6060612d32848460008585843b6133455760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161069b565b600080866001600160a01b031685876040516133619190613bc0565b60006040518083038185875af1925050503d806000811461339e576040519150601f19603f3d011682016040523d82523d6000602084013e6133a3565b606091505b5091509150612977828286606083156133bd5750816125fe565b8251156133cd5782518084602001fd5b8160405162461bcd60e51b815260040161069b9190613c2b565b6040805161020081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081018290526101a081018290526101c081018290526101e081019190915290565b80356107b08161423c565b805180151581146107b057600080fd5b600060208284031215613497578081fd5b8135612c548161423c565b600080604083850312156134b4578081fd5b82356134bf8161423c565b915060208301356134cf8161423c565b809150509250929050565b6000602082840312156134eb578081fd5b6125fe82613476565b600080600060608486031215613508578081fd5b61351184613476565b925061351f60208501613476565b9150604084015167ffffffffffffffff8082111561353b578283fd5b818601915086601f83011261354e578283fd5b81518181111561356057613560614226565b613573601f8201601f191660200161413f565b9150808252876020828501011115613589578384fd5b61359a8160208401602086016141e4565b5080925050509250925092565b6000602082840312156135b8578081fd5b5035919050565b6000602082840312156135d0578081fd5b813567ffffffffffffffff8111156135e6578182fd5b82016102408185031215612c54578182fd5b600060208284031215613609578081fd5b813567ffffffffffffffff81111561361f578182fd5b82016102808185031215612c54578182fd5b600060208284031215613642578081fd5b813567ffffffffffffffff811115613658578182fd5b82016102608185031215612c54578182fd5b6000610200828403121561367c578081fd5b613684614115565b61368d8361346b565b815261369b6020840161346b565b60208201526136ac6040840161346b565b60408201526136bd6060840161346b565b60608201526136ce6080840161346b565b60808201526136df60a0840161346b565b60a08201526136f060c0840161346b565b60c082015261370160e0840161346b565b60e082015261010061371481850161346b565b9082015261012083810135908201526101408084013590820152610160808401359082015261018080840135908201526101a080840135908201526101c080840135908201526101e0928301359281019290925250919050565b60006020828403121561377f578081fd5b5051919050565b60008060408385031215613798578182fd5b8235915060208301356134cf8161423c565b6000806000606084860312156137be578081fd5b8335925060208401356137d08161423c565b915060408401356137e08161423c565b809150509250925092565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6000815180845261382c8160208601602086016141e4565b601f01601f19169290920160200192915050565b61385a8261384d8361346b565b6001600160a01b03169052565b6138666020820161346b565b6001600160a01b031660208301526138806040820161346b565b6001600160a01b0316604083015261389a6060820161346b565b6001600160a01b031660608301526138b46080820161346b565b6001600160a01b031660808301526138ce60a0820161346b565b6001600160a01b031660a08301526138e860c0820161346b565b6001600160a01b031660c083015261390260e0820161346b565b6001600160a01b031660e083015261010061391e82820161346b565b6001600160a01b03169083015261012081810135908301526101408082013590830152610160808201359083015261018090810135910152565b6139658261384d8361346b565b6139716020820161346b565b6001600160a01b0316602083015261398b6040820161346b565b6001600160a01b031660408301526139a56060820161346b565b6001600160a01b031660608301526139bf6080820161346b565b6001600160a01b031660808301526139d960a0820161346b565b6001600160a01b031660a08301526139f360c0820161346b565b6001600160a01b031660c0830152613a0d60e0820161346b565b6001600160a01b031660e0830152610100613a2982820161346b565b6001600160a01b03169083015261012081810135908301526101408082013590830152610160808201359083015261018080820135908301526101a080820135908301526101c080820135908301526101e090810135910152565b80516001600160a01b031682526020810151613aab60208401826001600160a01b03169052565b506040810151613ac660408401826001600160a01b03169052565b506060810151613ae160608401826001600160a01b03169052565b506080810151613afc60808401826001600160a01b03169052565b5060a0810151613b1760a08401826001600160a01b03169052565b5060c0810151613b3260c08401826001600160a01b03169052565b5060e0810151613b4d60e08401826001600160a01b03169052565b50610100818101516001600160a01b03169083015261012080820151908301526101408082015190830152610160808201519083015261018080820151908301526101a080820151908301526101c080820151908301526101e090810151910152565b8183823760009101908152919050565b60008251613bd28184602087016141e4565b9190910192915050565b8781526001600160a01b0387811660208301528681166040830152851660608201526080810184905260c060a08201819052600090613c1e90830184866137eb565b9998505050505050505050565b6020815260006125fe6020830184613814565b602080825260079082015266234f4f3a30323960c81b604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60408152613ca76040820184613958565b6000613cb7610200850185614170565b61024084810152613ccd610280850182846137eb565b915050613cde610220860186614170565b848303603f1901610260860152613cf68382846137eb565b935050505060018060a01b03831660208301529392505050565b60a08152613d2160a0820187613958565b6102008601356102a08201526000613d3d610220880188614170565b6102806102c0850152613d55610320850182846137eb565b915050613d66610240890189614170565b609f1980868503016102e0870152613d7f8483856137eb565b9350613d8f6102608c018c614170565b93509150808685030161030087015250613daa8383836137eb565b92505050613dbc602084018815159052565b85151560408401528281036060840152613dd68186613814565b91505061267d60808301846001600160a01b03169052565b6101a08101612c578284613840565b81516001600160a01b031681526101a081016020830151613e2960208401826001600160a01b03169052565b506040830151613e4460408401826001600160a01b03169052565b506060830151613e5f60608401826001600160a01b03169052565b506080830151613e7a60808401826001600160a01b03169052565b5060a0830151613e9560a08401826001600160a01b03169052565b5060c0830151613eb060c08401826001600160a01b03169052565b5060e0830151613ecb60e08401826001600160a01b03169052565b50610100838101516001600160a01b03169083015261012080840151908301526101408084015190830152610160808401519083015261018092830151929091019190915290565b60208152815160208201526000602083015160806040840152613f3960a0840182613814565b6040850151606085810191909152909401516001600160a01b03166080909301929092525090919050565b6020815281516020820152602082015160408201526000604083015160a06060840152613f9460c0840182613814565b6060850151608085810191909152909401516001600160a01b031660a0909301929092525090919050565b6102008101612c578284613a84565b6000610240613fdd8387613a84565b61020060018060a01b03861681850152610220828186015261400183860187613840565b6101a08601356103e08601526101c08601356104008601526140276101e0870187614170565b61026061042088015261403f6104a0880182846137eb565b91505061404e83880188614170565b935061023f19808884030161044089015261406a8386846137eb565b9450614078848a018a614170565b9450925080888603016104608901526140928585856137eb565b94506140a0868a018a614170565b9650935080888603016104808901525050506140bd8284836137eb565b98975050505050505050565b6000808335601e198436030181126140df578283fd5b83018035915067ffffffffffffffff8211156140f9578283fd5b60200191503681900382131561410e57600080fd5b9250929050565b604051610200810167ffffffffffffffff8111828210171561413957614139614226565b60405290565b604051601f8201601f1916810167ffffffffffffffff8111828210171561416857614168614226565b604052919050565b6000808335601e19843603018112614186578283fd5b830160208101925035905067ffffffffffffffff8111156141a657600080fd5b80360383131561410e57600080fd5b600082198211156141c8576141c8614210565b500190565b6000828210156141df576141df614210565b500390565b60005b838110156141ff5781810151838201526020016141e7565b83811115612df35750506000910152565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811461123157600080fdfea2646970667358221220a05c87d31e3a6ea060c72e56be8ebb28bec7975311797ac9ae2c0cf4b2d3d5a264736f6c6343000804003360a060405234801561001057600080fd5b50604051610bf0380380610bf083398101604081905261002f91610049565b600160005560601b6001600160601b031916608052610077565b60006020828403121561005a578081fd5b81516001600160a01b0381168114610070578182fd5b9392505050565b60805160601c610b5661009a600039600081816048015260a90152610b566000f3fe6080604052600436106100295760003560e01c806396f32fb81461002e578063cf9a360414610077575b600080fd5b34801561003a57600080fd5b506040516001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001681526020015b60405180910390f35b61008a6100853660046108c4565b610099565b60405161006e93929190610a79565b6000806060336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146101065760405162461bcd60e51b8152602060048201526008602482015267234f544d3a30323760c01b60448201526064015b60405180910390fd5b6001600160a01b038816158061012157610121898b8961021b565b600060608b3b158015906101a0578c6001600160a01b031684610145576000610147565b8a5b8a8a6040516101579291906109c1565b60006040518083038185875af1925050503d8060008114610194576040519150601f19603f3d011682016040523d82523d6000602084013e610199565b606091505b5090935091505b826101c0576101b08c8c8c61026b565b836101c0576101c08c8e8c61028f565b8d7f03196b76502b81bbf14393f8b5ed67dff323f1f86667b064820f1fdf293686a18e8e8e8e8e8e898b8a604051610200999897969594939291906109ed565b60405180910390a2919d919c509a5098505050505050505050565b6001600160a01b03831661025b5760405162461bcd60e51b815260206004820152600760248201526608d2504e8c0ccd60ca1b60448201526064016100fd565b6102668383836102da565b505050565b6001600160a01b03831615610285576102668383836103d4565b61026682826103df565b6001600160a01b0383166102cf5760405162461bcd60e51b815260206004820152600760248201526608d1104e8c0ccd60ca1b60448201526064016100fd565b6102668383836103ed565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e9060440160206040518083038186803b15801561032657600080fd5b505afa15801561033a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061035e919061097d565b6103689190610ab8565b6040516001600160a01b0385166024820152604481018290529091506103ce90859063095ea7b360e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261050f565b50505050565b6102668383836105e1565b6103e98282610611565b5050565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301526000919085169063dd62ed3e9060440160206040518083038186803b15801561043857600080fd5b505afa15801561044c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610470919061097d565b9050818110156104d45760405162461bcd60e51b815260206004820152602960248201527f5361666545524332303a2064656372656173656420616c6c6f77616e63652062604482015268656c6f77207a65726f60b81b60648201526084016100fd565b6040516001600160a01b0384166024820152828203604482018190529061050890869063095ea7b360e01b90606401610397565b5050505050565b6000610564826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661072a9092919063ffffffff16565b805190915015610266578080602001905181019061058291906108a4565b6102665760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016100fd565b6040516001600160a01b03831660248201526044810182905261026690849063a9059cbb60e01b90606401610397565b804710156106615760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e636500000060448201526064016100fd565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146106ae576040519150601f19603f3d011682016040523d82523d6000602084013e6106b3565b606091505b50509050806102665760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d6179206861766520726576657274656400000000000060648201526084016100fd565b60606107398484600085610743565b90505b9392505050565b6060824710156107a45760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016100fd565b843b6107f25760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016100fd565b600080866001600160a01b0316858760405161080e91906109d1565b60006040518083038185875af1925050503d806000811461084b576040519150601f19603f3d011682016040523d82523d6000602084013e610850565b606091505b509150915061086082828661086b565b979650505050505050565b6060831561087a57508161073c565b82511561088a5782518084602001fd5b8160405162461bcd60e51b81526004016100fd9190610aa5565b6000602082840312156108b5578081fd5b8151801515811461073c578182fd5b600080600080600080600060c0888a0312156108de578283fd5b8735965060208801356108f081610b08565b9550604088013561090081610b08565b9450606088013561091081610b08565b93506080880135925060a088013567ffffffffffffffff80821115610933578384fd5b818a0191508a601f830112610946578384fd5b813581811115610954578485fd5b8b6020828501011115610965578485fd5b60208301945080935050505092959891949750929550565b60006020828403121561098e578081fd5b5051919050565b600081518084526109ad816020860160208601610adc565b601f01601f19169290920160200192915050565b8183823760009101908152919050565b600082516109e3818460208701610adc565b9190910192915050565b6001600160a01b038a8116825289811660208301528816604082015260608101879052610100608082018190528101859052600061012086888285013781818885010152601f19601f8801168301818482030160a0850152610a5182820188610995565b9250505083151560c0830152610a6b60e083018415159052565b9a9950505050505050505050565b83151581528215156020820152606060408201526000610a9c6060830184610995565b95945050505050565b60208152600061073c6020830184610995565b60008219821115610ad757634e487b7160e01b81526011600452602481fd5b500190565b60005b83811015610af7578181015183820152602001610adf565b838111156103ce5750506000910152565b6001600160a01b0381168114610b1d57600080fd5b5056fea26469706673582212202269da452bba446914352b77d48fb1282f9d93d6579e7d5164e4949a9439643864736f6c63430008040033000000000000000000000000000000000000000000000000000000000000a4b1

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

000000000000000000000000000000000000000000000000000000000000a4b1

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

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000000000000000000000000000000000000000a4b1


Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
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.