Contract 0xcf4d2994088a8cde52fb584fe29608b63ec063b2 2

 
Txn Hash Method
Block
From
To
Value [Txn Fee]
0x8e3e4e15283a5cac131ba0fa66d2831df3d9057e257c1841ae9cd5434bd0de1f0xd9459372158298662022-06-26 0:06:074 mins ago0x7d265daa860a66c35e693304e0e81ddd03866de6 IN  xPollinate: Transaction Manager1.532475 ETH0.000795185413 ETH
0xf54a2c61cbe0c0eada6e32c8b210499058b68dfca1d95a34597b8b7f119343e80xd9459372158291742022-06-25 23:56:5814 mins ago0xea3549a358cee7854c111ac888de7bccc3d55c2c IN  xPollinate: Transaction Manager0.9 ETH0.000792292651 ETH
0xc5a849732c69fecd8d751bc69d57568555567ca8a4f9302a77eb5125b04e331b0xd9459372158290782022-06-25 23:56:5814 mins ago0x39ea14e8826f98af808c715de58cce3ed96cec0a IN  xPollinate: Transaction Manager0.04 ETH0.000788825357 ETH
0xf2026043d7872ccaefadbafee9eb54f9fa688e476d8644903143cba44b190cca0xd9459372158288822022-06-25 23:53:2717 mins ago0xc9c34fa3275bfd2905d7207ee641ba30133a7206 IN  xPollinate: Transaction Manager0.05388 ETH0.000792093742 ETH
0xb0436cbaf3efe50c9f3edfdd592e6829fa4b1c8b1941b17221f9e11a575ca3750xd9459372158273892022-06-25 23:25:1045 mins ago0x362406a790cdb92f76eb6c9d7b34d1526e26147c IN  xPollinate: Transaction Manager1.9 ETH0.000791712675 ETH
0xbcabbaf29f65ec5fbe2f743d8607b6d94c54bac252139697cf2db982b16c86180xd9459372158262402022-06-25 23:08:481 hr 2 mins ago0xbe0ab413037f5d135d21f3157c55790e1c47236f IN  xPollinate: Transaction Manager0.005121 ETH0.000790360514 ETH
0x164440e9f00722093a65479b253c4423f2abd2e2cea1733b45118b5b4f53b0330xd9459372158253552022-06-25 22:57:531 hr 13 mins ago0xaeb8e197c2d3bee3819de6caeade6ec41ba217f0 IN  xPollinate: Transaction Manager0 ETH0.000795851507 ETH
0x42996de6468b9e052b3bba7cbeba28f551a7634aa88152301936aa59a75e85bc0x9b151a80158249782022-06-25 22:52:281 hr 18 mins ago0x997f29174a766a1da04cf77d135d59dd12fb54d1 IN  xPollinate: Transaction Manager0 ETH0.001456290041 ETH
0xda33cff5e450ba6842283c5a48ea0c65dc1ae4089d0040bf42c612ea32063c3a0x9b151a80158241692022-06-25 22:44:011 hr 27 mins ago0x586d0cf12bac10725f488900bf24d04fbf87e8ef IN  xPollinate: Transaction Manager0 ETH0.000514326087 ETH
0x3b0ace41cf3b9a738b54ab7bb57756df49a8cc80238ac5153e8b6dc8d97d2ed30x9b151a80158238712022-06-25 22:40:111 hr 30 mins ago0x3197e03fce50fd192d1d0b6c88c970c8d1d78264 IN  xPollinate: Transaction Manager0 ETH0.000516644271 ETH
0x46a7452ae4910326687855f03c5787800bff4ed42d9d24268194aa1b28bfc3fb0xd9459372158231252022-06-25 22:31:121 hr 39 mins ago0x4074baa99dc007c4234a654f0f21214756f72296 IN  xPollinate: Transaction Manager0 ETH0.000814767434 ETH
0x8c324d5686559815a96429107d6bf2066ec75dbde0abd491568f335ef8d9fb460xd9459372158228392022-06-25 22:27:161 hr 43 mins ago0x61bc00da5b6339ebb3b21a7bf227458855c2ce8c IN  xPollinate: Transaction Manager0.024681 ETH0.000793643913 ETH
0xbac5dbe104506723fc9e221cd1e2af1aaea909df0a8e4a85d0ca1da1a06c554c0xd9459372158216902022-06-25 22:12:191 hr 58 mins ago0x859ef81b2dffcbd87eaab59978a65193ea96d227 IN  xPollinate: Transaction Manager0.01 ETH0.000700404562 ETH
0x6b591c57ea3d3fc6ee7dc1f3d0be59a2d98d3c5bc69e6e2555a55e1917c561f50xd9459372158213512022-06-25 22:08:142 hrs 2 mins ago0x527c6a63840bad5a1c18d61685c2a7a62735ee9f IN  xPollinate: Transaction Manager0.022 ETH0.000700740236 ETH
0xa18074ece10d1c1ae24f88ea324637298dbc29ad3e289e5dae3cf7ae0491b9ea0xd9459372158212992022-06-25 22:06:462 hrs 4 mins ago0x3e042bd1d67b3ea64d04f117fcc94aaccd819f03 IN  xPollinate: Transaction Manager0.003865 ETH0.000703641127 ETH
0x8ea6ee3f5844a37736107d2a724e4df7bf71d6ac3e221c0f8a0997459ab8b20f0xd9459372158187152022-06-25 21:29:032 hrs 42 mins ago0x13a5e7bde7477616c953ac4d4a1a82f751053efb IN  xPollinate: Transaction Manager0 ETH0.000717672675 ETH
0xf1a714ecdceebe65e47115a1b0e7cad18ef6410c5094730df5e998bc92dbd30e0xd9459372158186512022-06-25 21:29:032 hrs 42 mins ago0x2145c526dc80a959bf28f16cf2a7132fec283779 IN  xPollinate: Transaction Manager0.09 ETH0.000700225041 ETH
0x18ae5cb6ec817da825b1f9319882db66c4ad2f3782daa44be79027103b9472dc0xd9459372158178732022-06-25 21:17:322 hrs 53 mins ago0x38a7a3cafafdab0d14684b305db2476fde09341b IN  xPollinate: Transaction Manager0.799891 ETH0.00070483175 ETH
0xc02b3fd41fe1bb1872e994e6fcc1a454ae28f668c7b85915b9f8244c2250a8f30xd9459372158162302022-06-25 20:57:593 hrs 13 mins ago0x841de76fcb93c88edb2f03f84702be38e6d6fe51 IN  xPollinate: Transaction Manager0.07 ETH0.000699205037 ETH
0x9f25637b12000d17b56720ded52d4882bcf2bf99cddf648ad64f75f489f25ebe0xd9459372158161012022-06-25 20:55:263 hrs 15 mins ago0xdfa81e3588b1344ff198be3cf74a3b1b5a275751 IN  xPollinate: Transaction Manager0.008 ETH0.000699724682 ETH
0x3af0c38eea5e1cf915da1888c841a530cce58889d5a99b19f078e07283f91b530xd9459372158160352022-06-25 20:53:233 hrs 17 mins ago0x15c3d6298743e3115df3794f6da20ec4079d1eee IN  xPollinate: Transaction Manager0.180837 ETH0.000705858802 ETH
0x610ee472714b0b9505de9bb435349f662c79d380ec7271d3a985fa0b06cbbfb30xd9459372158159722022-06-25 20:53:233 hrs 17 mins ago0x0e34a15fa822c51fff833e49d4912fc4c4de7f0c IN  xPollinate: Transaction Manager0.382 ETH0.000703132609 ETH
0xe852602f018c766fc7ab095940ba46aeca2b0ba1c1b970e7168f70823fa5f0180xd9459372158159672022-06-25 20:53:233 hrs 17 mins ago0x38a7a3cafafdab0d14684b305db2476fde09341b IN  xPollinate: Transaction Manager0.32388 ETH0.000703641127 ETH
0xc4c7ea8e399d9b01f7ecb40287943ca54f2233a155b9532f6b3442c9b653f48e0xd9459372158152732022-06-25 20:43:243 hrs 27 mins ago0x10cf7268961d54d7d47eca6ee415fe11607af84e IN  xPollinate: Transaction Manager0.025 ETH0.000700740607 ETH
0x38374bb7416187f1bd68e925226ecef223a01d8983e7d0d5c7a1e7f74a926fb50x9b151a80158148962022-06-25 20:37:293 hrs 33 mins ago0xd0b351011709d721393af117132f5a81ecaea03b IN  xPollinate: Transaction Manager0 ETH0.000455211149 ETH
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0x12c669b97244a68d5c26f2e9b4bb6b12b5c8ac9211a3fd0031b903753516abfa158297902022-06-26 0:06:074 mins ago xPollinate: Transaction Manager0x2de944255b054466e125da744a213895a3b3cb930.017440749567081597 ETH
0x12c669b97244a68d5c26f2e9b4bb6b12b5c8ac9211a3fd0031b903753516abfa158297902022-06-26 0:06:074 mins ago xPollinate: Transaction Manager0x0ae392879a228b2484d9b1f80a5d0b7080fe79c20.000725493634866 ETH
0x12c669b97244a68d5c26f2e9b4bb6b12b5c8ac9211a3fd0031b903753516abfa158297902022-06-26 0:06:074 mins ago xPollinate: Transaction Manager0x00000000000000000000000000000000000000010 ETH
0x12c669b97244a68d5c26f2e9b4bb6b12b5c8ac9211a3fd0031b903753516abfa158297902022-06-26 0:06:074 mins ago 0x0ae392879a228b2484d9b1f80a5d0b7080fe79c2 xPollinate: Transaction Manager0 ETH
0x683e2582aa5d2829c980e7c01c04512c5f3260659335d4283abf6876c5a51945158296882022-06-26 0:05:055 mins ago 0x92495600b72ef0e1fa22453b58938a9af49918ae xPollinate: Transaction Manager0 ETH
0x504883e27470d17b90feb33a6b4568873093e4cfb658b0ca215f0cb2d23c725e158294952022-06-26 0:02:538 mins ago xPollinate: Transaction Manager0x00000000000000000000000000000000000000010 ETH
0x504883e27470d17b90feb33a6b4568873093e4cfb658b0ca215f0cb2d23c725e158294952022-06-26 0:02:538 mins ago 0x92495600b72ef0e1fa22453b58938a9af49918ae xPollinate: Transaction Manager0 ETH
0x7c76f3169355d168b1503cea1674b23d205da25dd9d58f68a2c28578fb0ac74b158294902022-06-26 0:02:128 mins ago 0x95ce8b1c273af612cd895e6b0c633039c3572827 xPollinate: Transaction Manager0 ETH
0xa3d0af0eba7cbd763d0a956352763308865a73eaca1172f1da7a3af468ff1e96158293572022-06-26 0:00:1210 mins ago xPollinate: Transaction Manager0x00000000000000000000000000000000000000010 ETH
0xa3d0af0eba7cbd763d0a956352763308865a73eaca1172f1da7a3af468ff1e96158293572022-06-26 0:00:1210 mins ago 0x92495600b72ef0e1fa22453b58938a9af49918ae xPollinate: Transaction Manager0 ETH
0x087f1e76d6b95cb7635a0747fe425d67fc848194d5e64aa6f81ae2b1215cbda7158292522022-06-25 23:58:5712 mins ago xPollinate: Transaction Manager0x00000000000000000000000000000000000000010 ETH
0x087f1e76d6b95cb7635a0747fe425d67fc848194d5e64aa6f81ae2b1215cbda7158292522022-06-25 23:58:5712 mins ago 0x92495600b72ef0e1fa22453b58938a9af49918ae xPollinate: Transaction Manager0 ETH
0xee6a7f56fb17b037817ca0f7d0c496c842fd1809f4e4834799ad8ed6679f2734158291212022-06-25 23:56:5814 mins ago xPollinate: Transaction Manager0x7e61b3dfc569caa91c0d7a87929d9df77c7fe1cb0.039878163351142187 ETH
0xee6a7f56fb17b037817ca0f7d0c496c842fd1809f4e4834799ad8ed6679f2734158291212022-06-25 23:56:5814 mins ago xPollinate: Transaction Manager0x0ae392879a228b2484d9b1f80a5d0b7080fe79c20.000725493634866 ETH
0xee6a7f56fb17b037817ca0f7d0c496c842fd1809f4e4834799ad8ed6679f2734158291212022-06-25 23:56:5814 mins ago xPollinate: Transaction Manager0x00000000000000000000000000000000000000010 ETH
0xee6a7f56fb17b037817ca0f7d0c496c842fd1809f4e4834799ad8ed6679f2734158291212022-06-25 23:56:5814 mins ago 0x0ae392879a228b2484d9b1f80a5d0b7080fe79c2 xPollinate: Transaction Manager0 ETH
0x7cb5ef183689e62de24b83dba647abce5c5cd6a29ba809c75989ef352fa51959158290652022-06-25 23:56:0315 mins ago xPollinate: Transaction Manager0x0f632180e0a1859148bbe0a6ee817305d094a4b50.145477420245183333 ETH
0x7cb5ef183689e62de24b83dba647abce5c5cd6a29ba809c75989ef352fa51959158290652022-06-25 23:56:0315 mins ago xPollinate: Transaction Manager0x0ae392879a228b2484d9b1f80a5d0b7080fe79c20.000725493634866 ETH
0x7cb5ef183689e62de24b83dba647abce5c5cd6a29ba809c75989ef352fa51959158290652022-06-25 23:56:0315 mins ago xPollinate: Transaction Manager0x00000000000000000000000000000000000000010 ETH
0x7cb5ef183689e62de24b83dba647abce5c5cd6a29ba809c75989ef352fa51959158290652022-06-25 23:56:0315 mins ago 0x0ae392879a228b2484d9b1f80a5d0b7080fe79c2 xPollinate: Transaction Manager0 ETH
0xe38bc8970d2e0fbf0b1308257d22f41c124e83a7d15e3b6b12e7902fb9ac130c158289982022-06-25 23:55:1015 mins ago xPollinate: Transaction Manager0x0725faf58e743002ff5869754b71032d3e88aa020.047887239745183333 ETH
0xe38bc8970d2e0fbf0b1308257d22f41c124e83a7d15e3b6b12e7902fb9ac130c158289982022-06-25 23:55:1015 mins ago xPollinate: Transaction Manager0x0ae392879a228b2484d9b1f80a5d0b7080fe79c20.000725493634866 ETH
0xe38bc8970d2e0fbf0b1308257d22f41c124e83a7d15e3b6b12e7902fb9ac130c158289982022-06-25 23:55:1015 mins ago xPollinate: Transaction Manager0x00000000000000000000000000000000000000010 ETH
0xe38bc8970d2e0fbf0b1308257d22f41c124e83a7d15e3b6b12e7902fb9ac130c158289982022-06-25 23:55:1015 mins ago 0x0ae392879a228b2484d9b1f80a5d0b7080fe79c2 xPollinate: Transaction Manager0 ETH
0x53d228ab715ba4d1a79156402b571e7831ae10be53a89237476a2d2b552b912f158289962022-06-25 23:55:1015 mins ago 0x92495600b72ef0e1fa22453b58938a9af49918ae xPollinate: Transaction Manager0 ETH
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
TransactionManager

Compiler Version
v0.8.4+commit.c7e474f2

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, Unlicense license

Contract Source Code (Solidity Standard Json-Input format)

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

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

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

        _;

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

File 3 of 13 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

File 4 of 13 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

File 5 of 13 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    function _verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) private pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

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

File 6 of 13 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

        return signer;
    }

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

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

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

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

  address private _proposed;
  uint256 private _proposedOwnershipTimestamp;

  bool private _routerOwnershipRenounced;
  uint256 private _routerOwnershipTimestamp;

  bool private _assetOwnershipRenounced;
  uint256 private _assetOwnershipTimestamp;

  uint256 private constant _delay = 7 days;

  event RouterOwnershipRenunciationProposed(uint256 timestamp);

  event RouterOwnershipRenounced(bool renounced);

  event AssetOwnershipRenunciationProposed(uint256 timestamp);

  event AssetOwnershipRenounced(bool renounced);

  event OwnershipProposed(address indexed proposedOwner);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    _setProposed(newlyProposed);
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

    // Update mapping
    approvedRouters[router] = true;

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

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

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

    // Update mapping
    approvedRouters[router] = false;

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

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

    // Update mapping
    approvedAssets[assetId] = true;

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

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

    // Update mapping
    approvedAssets[assetId] = false;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return txData;
  }



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

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

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

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

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

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

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

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

    uint256 _chainId = getChainId();

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

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

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

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

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

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

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

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

    return args.txData;
  }

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

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

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

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

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

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

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

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

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

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

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

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

    // Return
    return args.txData;
  }

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

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

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

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

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

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

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

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

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

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

    return trueAmount;
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

interface IFulfillInterpreter {

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

  function getTransactionManager() external returns (address);

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

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

interface ITransactionManager {
  // Structs

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  function getStoredChainId() external view returns (uint256);

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

  function removeRouter(address router) external;

  function addAssetId(address assetId) external;

  function removeAssetId(address assetId) external;

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

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

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

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

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

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

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

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

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

  constructor(address transactionManager) {
    _transactionManager = transactionManager;
  }

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

Settings
{
  "evmVersion": "istanbul",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "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.