ETH Price: $3,420.78 (+3.23%)

Contract

0x947872ad4d95e89E513d7202550A810aC1B626cC
 
Transaction Hash
Method
Block
From
To
New Relay Eon214725782024-12-24 13:18:4713 mins ago1735046327IN
0x947872ad...aC1B626cC
0 ETH0.000210066.55462865
Swap214686482024-12-24 0:06:3513 hrs ago1734998795IN
0x947872ad...aC1B626cC
0 ETH0.000713329.78479038
Reverse Swap214686152024-12-23 23:59:3513 hrs ago1734998375IN
0x947872ad...aC1B626cC
0 ETH0.000660947.87735715
Reverse Swap214630592024-12-23 5:19:4732 hrs ago1734931187IN
0x947872ad...aC1B626cC
0 ETH0.00029774.45547646
Reverse Swap214629842024-12-23 5:04:4732 hrs ago1734930287IN
0x947872ad...aC1B626cC
0 ETH0.000308514.61897282
Swap214629382024-12-23 4:55:3532 hrs ago1734929735IN
0x947872ad...aC1B626cC
0 ETH0.000350114.80186277
Swap214614932024-12-23 0:04:2337 hrs ago1734912263IN
0x947872ad...aC1B626cC
0 ETH0.00030884.5345869
Swap214611682024-12-22 22:58:4738 hrs ago1734908327IN
0x947872ad...aC1B626cC
0 ETH0.000367985.40256455
Swap214611062024-12-22 22:46:2338 hrs ago1734907583IN
0x947872ad...aC1B626cC
0 ETH0.000411716.04571135
Reverse Swap214600692024-12-22 19:16:5942 hrs ago1734895019IN
0x947872ad...aC1B626cC
0 ETH0.000471675.62237226
New Relay Eon214596902024-12-22 18:00:5943 hrs ago1734890459IN
0x947872ad...aC1B626cC
0 ETH0.000203026.3346817
Swap214594842024-12-22 17:19:3544 hrs ago1734887975IN
0x947872ad...aC1B626cC
0 ETH0.000379435.9919007
Swap214591172024-12-22 16:05:3545 hrs ago1734883535IN
0x947872ad...aC1B626cC
0 ETH0.000516877.58842037
New Relay Eon214587392024-12-22 14:49:4746 hrs ago1734878987IN
0x947872ad...aC1B626cC
0 ETH0.000264098.24028685
Reverse Swap214571152024-12-22 9:22:472 days ago1734859367IN
0x947872ad...aC1B626cC
0 ETH0.000410254.88815655
Reverse Swap214555202024-12-22 4:02:112 days ago1734840131IN
0x947872ad...aC1B626cC
0 ETH0.000432755.15764133
New Relay Eon214538782024-12-21 22:32:112 days ago1734820331IN
0x947872ad...aC1B626cC
0 ETH0.000200436.25392535
Swap214533972024-12-21 20:54:352 days ago1734814475IN
0x947872ad...aC1B626cC
0 ETH0.000464386.81908201
Swap214523222024-12-21 17:17:112 days ago1734801431IN
0x947872ad...aC1B626cC
0 ETH0.000595198.73838464
Swap214504682024-12-21 11:04:473 days ago1734779087IN
0x947872ad...aC1B626cC
0 ETH0.000512517.03025935
Swap214503402024-12-21 10:39:113 days ago1734777551IN
0x947872ad...aC1B626cC
0 ETH0.00053387.83838006
New Relay Eon214487272024-12-21 5:14:473 days ago1734758087IN
0x947872ad...aC1B626cC
0 ETH0.000214276.68573735
Reverse Swap214456512024-12-20 18:54:233 days ago1734720863IN
0x947872ad...aC1B626cC
0 ETH0.0013234315.77300614
Swap214444292024-12-20 14:48:473 days ago1734706127IN
0x947872ad...aC1B626cC
0 ETH0.0020828628.57119096
Swap214442532024-12-20 14:13:233 days ago1734704003IN
0x947872ad...aC1B626cC
0 ETH0.0019699728.92720287
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Bridge

Compiler Version
v0.7.6+commit.7338295f

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, Apache-2.0 license

Contract Source Code (Solidity)

/**
 *Submitted for verification at Etherscan.io on 2021-03-28
*/

// SPDX-License-Identifier: Apache-2.0

pragma solidity 0.7.6;



// Part: Address

/**
 * @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;
        // solhint-disable-next-line no-inline-assembly
        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");

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (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");

        // solhint-disable-next-line avoid-low-level-calls
        (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");

        // solhint-disable-next-line avoid-low-level-calls
        (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");

        // solhint-disable-next-line avoid-low-level-calls
        (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

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

// Part: Context

/*
 * @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 GSN 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 payable) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}

// Part: EnumerableSet

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;

        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping (bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) { // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
            // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.

            bytes32 lastvalue = set._values[lastIndex];

            // Move the last value to the index where the value to delete is
            set._values[toDeleteIndex] = lastvalue;
            // Update the index for the moved value
            set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        require(set._values.length > index, "EnumerableSet: index out of bounds");
        return set._values[index];
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }


    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }
}

// Part: IBridgeCommon

/**
 * @title Events for Bi-directional bridge transferring FET tokens between Ethereum and Fetch Mainnet-v2
 */
interface IBridgeCommon {

    event Swap(uint64 indexed id, address indexed from, string indexed indexedTo, string to, uint256 amount);

    event SwapRefund(uint64 indexed id, address indexed to, uint256 refundedAmount, uint256 fee);
    event ReverseSwap(uint64 indexed rid, address indexed to, string indexed from, bytes32 originTxHash, uint256 effectiveAmount, uint256 fee);
    event PausePublicApi(uint256 sinceBlock);
    event PauseRelayerApi(uint256 sinceBlock);
    event NewRelayEon(uint64 eon);

    event LimitsUpdate(uint256 max, uint256 min, uint256 fee);
    event CapUpdate(uint256 value);
    event ReverseAggregatedAllowanceUpdate(uint256 value);
    event ReverseAggregatedAllowanceApproverCapUpdate(uint256 value);
    event Withdraw(address indexed targetAddress, uint256 amount);
    event Deposit(address indexed fromAddress, uint256 amount);
    event FeesWithdrawal(address indexed targetAddress, uint256 amount);
    event DeleteContract(address targetAddress, uint256 amount);
    // NOTE(pb): It is NOT necessary to have dedicated events here for Mint & Burn operations, since ERC20 contract
    //  already emits the `Transfer(from, to, amount)` events, with `from`, resp. `to`, address parameter value set to
    //  ZERO_ADDRESS (= address(0) = 0x00...00) for `mint`, resp `burn`, calls to ERC20 contract. That way we can
    //  identify events for mint, resp. burn, calls by filtering ERC20 Transfer events with `from == ZERO_ADDR  &&
    //  to == Bridge.address` for MINT operation, resp `from == Bridge.address` and `to == ZERO_ADDR` for BURN operation.
    //event Mint(uint256 amount);
    //event Burn(uint256 amount);

    function getApproverRole() external view returns(bytes32);
    function getMonitorRole() external view returns(bytes32);
    function getRelayerRole() external view returns(bytes32);

    function getToken() external view returns(address);
    function getEarliestDelete() external view returns(uint256);
    function getSupply() external view returns(uint256);
    function getNextSwapId() external view returns(uint64);
    function getRelayEon() external view returns(uint64);
    function getRefund(uint64 swap_id) external view returns(uint256); // swapId -> original swap amount(= *includes* swapFee)
    function getSwapMax() external view returns(uint256);
    function getSwapMin() external view returns(uint256);
    function getCap() external view returns(uint256);
    function getSwapFee() external view returns(uint256);
    function getPausedSinceBlockPublicApi() external view returns(uint256);
    function getPausedSinceBlockRelayerApi() external view returns(uint256);
    function getReverseAggregatedAllowance() external view returns(uint256);
    function getReverseAggregatedAllowanceApproverCap() external view returns(uint256);

}

// Part: IERC20

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

// Part: IERC20MintFacility

interface IERC20MintFacility
{
    function mint(address to, uint256 amount) external;
    function burn(uint256 amount) external;
    function burnFrom(address from, uint256 amount) external;
}

// Part: SafeMath

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        uint256 c = a + b;
        if (c < a) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b > a) return (false, 0);
        return (true, a - b);
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) return (true, 0);
        uint256 c = a * b;
        if (c / a != b) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a / b);
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a % b);
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) return 0;
        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");
        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: division by zero");
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: modulo by zero");
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        return a - b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryDiv}.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a % b;
    }
}

// Part: AccessControl

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it.
 */
abstract contract AccessControl is Context {
    using EnumerableSet for EnumerableSet.AddressSet;
    using Address for address;

    struct RoleData {
        EnumerableSet.AddressSet members;
        bytes32 adminRole;
    }

    mapping (bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view returns (bool) {
        return _roles[role].members.contains(account);
    }

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) public view returns (uint256) {
        return _roles[role].members.length();
    }

    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) public view returns (address) {
        return _roles[role].members.at(index);
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) public virtual {
        require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");

        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) public virtual {
        require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");

        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) public virtual {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        emit RoleAdminChanged(role, _roles[role].adminRole, adminRole);
        _roles[role].adminRole = adminRole;
    }

    function _grantRole(bytes32 role, address account) private {
        if (_roles[role].members.add(account)) {
            emit RoleGranted(role, account, _msgSender());
        }
    }

    function _revokeRole(bytes32 role, address account) private {
        if (_roles[role].members.remove(account)) {
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

// Part: IBridgeMonitor

/**
 * @title *Monitor* interface of Bi-directional bridge for transfer of FET tokens between Ethereum
 *        and Fetch Mainnet-v2.
 *
 * @notice By design, all methods of this monitor-level interface can be called monitor and admin roles of
 *         the Bridge contract.
 *
 */
interface IBridgeMonitor is IBridgeCommon {
    /**
     * @notice Pauses Public API since the specified block number
     * @param blockNumber block number since which non-admin interaction will be paused (for all
     *        block.number >= blockNumber).
     * @dev Delegate only
     *      If `blocknumber < block.number`, then contract will be paused immediately = from `block.number`.
     */
    function pausePublicApiSince(uint256 blockNumber) external;

    /**
     * @notice Pauses Relayer API since the specified block number
     * @param blockNumber block number since which non-admin interaction will be paused (for all
     *        block.number >= blockNumber).
     * @dev Delegate only
     *      If `blocknumber < block.number`, then contract will be paused immediately = from `block.number`.
     */
    function pauseRelayerApiSince(uint256 blockNumber) external;
}

// Part: IBridgePublic

/**
 * @title Public interface of the Bridge for transferring FET tokens between Ethereum and Fetch Mainnet-v2
 *
 * @notice Methods of this public interface is allow users to interact with Bridge contract.
 */
interface IBridgePublic is IBridgeCommon {

    /**
      * @notice Initiates swap, which will be relayed to the other blockchain.
      *         Swap might fail, if `destinationAddress` value is invalid (see bellow), in which case the swap will be
      *         refunded back to user. Swap fee will be *WITHDRAWN* from `amount` in that case - please see details
      *         in desc. for `refund(...)` call.
      *
      * @dev Swap call will create unique identifier (swap id), which is, by design, sequentially growing by 1 per each
      *      new swap created, and so uniquely identifies each swap. This identifier is referred to as "reverse swap id"
      *      on the other blockchain.
      *      Callable by anyone.
      *
      * @param destinationAddress - address on **OTHER** blockchain where the swap effective amount will be transferred
      *                             in to.
      *                             User is **RESPONSIBLE** for providing the **CORRECT** and valid value.
      *                             The **CORRECT** means, in this context, that address is valid *AND* user really
      *                             intended this particular address value as destination = that address is NOT lets say
      *                             copy-paste mistake made by user. Reason being that when user provided valid address
      *                             value, but made mistake = address is of someone else (e.g. copy-paste mistake), then
      *                             there is **NOTHING** what can be done to recover funds back to user (= refund) once
      *                             the swap will be relayed to the other blockchain!
      *                             The **VALID** means that provided value successfully passes consistency checks of
      *                             valid address of **OTHER** blockchain. In the case when user provides invalid
      *                             address value, relayer will execute refund - please see desc. for `refund()` call
      *                             for more details.
      */
    function swap(uint256 amount, string calldata destinationAddress) external;
}

// Part: IBridgeRelayer

/**
 * @title *Relayer* interface of Bi-directional bridge for transfer of FET tokens between Ethereum
 *        and Fetch Mainnet-v2.
 *
 * @notice By design, all methods of this relayer-level interface can be called exclusively by relayer(s) of
 *         the Bridge contract.
 *         It is offers set of methods to perform relaying functionality of the Bridge = transferring swaps
 *         across chains.
 *
 * @notice This bridge allows to transfer [ERC20-FET] tokens from Ethereum Mainnet to [Native FET] tokens on Fetch
 *         Native Mainnet-v2 and **other way around** (= it is bi-directional).
 *         User will be *charged* swap fee defined in counterpart contract deployed on Fetch Native Mainnet-v2.
 *         In the case of a refund, user will be charged a swap fee configured in this contract.
 *
 *         Swap Fees for `swap(...)` operations (direction from this contract to Native Fetch Mainnet-v2 are handled by
 *         the counterpart contract on Fetch Native Mainnet-v2, **except** for refunds, for
 *         which user is charged swap fee defined by this contract (since relayer needs to send refund transaction back
 *         to this contract.
 */
interface IBridgeRelayer is IBridgeCommon {

    /**
      * @notice Starts the new relay eon.
      * @dev Relay eon concept is part of the design in order to ensure safe management of hand-over between two
      *      relayer services. It provides clean isolation of potentially still pending transactions from previous
      *      relayer svc and the current one.
      */
    function newRelayEon() external;


    /**
      * @notice Refunds swap previously created by `swap(...)` call to this contract. The `swapFee` is *NOT* refunded
      *         back to the user (this is by-design).
      *
      * @dev Callable exclusively by `relayer` role
      *
      * @param id - swap id to refund - must be swap id of swap originally created by `swap(...)` call to this contract,
      *             **NOT** *reverse* swap id!
      * @param to - address where the refund will be transferred in to(IDENTICAL to address used in associated `swap`
      *             call)
      * @param amount - original amount specified in associated `swap` call = it INCLUDES swap fee, which will be
      *                 withdrawn
      * @param relayEon_ - current relay eon, ensures safe management of relaying process
      */
    function refund(uint64 id, address to, uint256 amount, uint64 relayEon_) external;


    /**
      * @notice Refunds swap previously created by `swap(...)` call to this contract, where `swapFee` *IS* refunded
      *         back to the user (= swap fee is waived = user will receive full `amount`).
      *         Purpose of this method is to enable full refund in the situations when it si not user's fault that
      *         swap needs to be refunded (e.g. when Fetch Native Mainnet-v2 will become unavailable for prolonged
      *         period of time, etc. ...).
      *
      * @dev Callable exclusively by `relayer` role
      *
      * @param id - swap id to refund - must be swap id of swap originally created by `swap(...)` call to this contract,
      *             **NOT** *reverse* swap id!
      * @param to - address where the refund will be transferred in to(IDENTICAL to address used in associated `swap`
      *             call)
      * @param amount - original amount specified in associated `swap` call = it INCLUDES swap fee, which will be
      *                 waived = user will receive whole `amount` value.
      *                 Pleas mind that `amount > 0`, otherways relayer will pay Tx fee for executing the transaction
      *                 which will have *NO* effect (= like this function `refundInFull(...)` would *not* have been
      *                 called at all!
      * @param relayEon_ - current relay eon, ensures safe management of relaying process
      */
    function refundInFull(uint64 id, address to, uint256 amount, uint64 relayEon_) external;


    /**
      * @notice Finalises swap initiated by counterpart contract on the other blockchain.
      *         This call sends swapped tokens to `to` address value user specified in original swap on the **OTHER**
      *         blockchain.
      *
      * @dev Callable exclusively by `relayer` role
      *
      * @param rid - reverse swap id - unique identifier of the swap initiated on the **OTHER** blockchain.
      *              This id is, by definition, sequentially growing number incremented by 1 for each new swap initiated
      *              the other blockchain. **However**, it is *NOT* ensured that *all* swaps from the other blockchain
      *              will be transferred to this (Ethereum) blockchain, since some of these swaps can be refunded back
      *              to users (on the other blockchain).
      * @param to - address where the refund will be transferred in to
      * @param from - source address from which user transferred tokens from on the other blockchain. Present primarily
      *               for purposes of quick querying of events on this blockchain.
      * @param originTxHash - transaction hash for swap initiated on the **OTHER** blockchain. Present in order to
      *                       create strong bond between this and other blockchain.
      * @param amount - original amount specified in associated swap initiated on the other blockchain.
      *                 Swap fee is *withdrawn* from the `amount` user specified in the swap on the other blockchain,
      *                 what means that user receives `amount - swapFee`, or *nothing* if `amount <= swapFee`.
      *                 Pleas mind that `amount > 0`, otherways relayer will pay Tx fee for executing the transaction
      *                 which will have *NO* effect (= like this function `refundInFull(...)` would *not* have been
      *                 called at all!
      * @param relayEon_ - current relay eon, ensures safe management of relaying process
      */
    function reverseSwap(
        uint64 rid,
        address to,
        string calldata from,
        bytes32 originTxHash,
        uint256 amount,
        uint64 relayEon_
        )
        external;
}

// Part: IERC20Token

interface IERC20Token is IERC20, IERC20MintFacility {}

// Part: IBridgeAdmin

/**
 * @title *Administrative* interface of Bi-directional bridge for transfer of FET tokens between Ethereum
 *        and Fetch Mainnet-v2.
 *
 * @notice By design, all methods of this administrative interface can be called exclusively by administrator(s) of
 *         the Bridge contract, since it allows to configure essential parameters of the the Bridge, and change
 *         supply transferred across the Bridge.
 */
interface IBridgeAdmin is IBridgeCommon, IBridgeMonitor {

    /**
     * @notice Returns amount of excess FET ERC20 tokens which were sent to address of this contract via direct ERC20
     *         transfer (by calling ERC20.transfer(...)), without interacting with API of this contract, what can happen
     *         only by mistake.
     *
     * @return targetAddress : address to send tokens to
     */
    function getFeesAccrued() external view returns(uint256);


    /**
     * @notice Mints provided amount of FET tokens.
     *         This is to reflect changes in minted Native FET token supply on the Fetch Native Mainnet-v2 blockchain.
     * @param amount - number of FET tokens to mint.
     */
    function mint(uint256 amount) external;


    /**
     * @notice Burns provided amount of FET tokens.
     *         This is to reflect changes in minted Native FET token supply on the Fetch Native Mainnet-v2 blockchain.
     * @param amount - number of FET tokens to burn.
     */
    function burn(uint256 amount) external;


    /**
     * @notice Sets cap (max) value of `supply` this contract can hold = the value of tokens transferred to the other
     *         blockchain.
     *         This cap affects(limits) all operations which *increase* contract's `supply` value = `swap(...)` and
     *         `mint(...)`.
     * @param value - new cap value.
     */
    function setCap(uint256 value) external;


    /**
     * @notice Sets value of `reverseAggregatedAllowance` state variable.
     *         This affects(limits) operations which *decrease* contract's `supply` value via **RELAYER** authored
     *         operations (= `reverseSwap(...)` and `refund(...)`). It does **NOT** affect **ADMINISTRATION** authored
     *         supply decrease operations (= `withdraw(...)` & `burn(...)`).
     * @param value - new cap value.
     */
    function setReverseAggregatedAllowance(uint256 value) external;

    /**
     * @notice Sets value of `reverseAggregatedAllowanceCap` state variable.
     *         This limits APPROVER_ROLE from top - value up to which can approver rise the allowance.
     * @param value - new cap value (absolute)
     */
    function setReverseAggregatedAllowanceApproverCap(uint256 value) external;


    /**
     * @notice Sets limits for swap amount
     *         FUnction will revert if following consitency check fails: `swapfee_ <= swapMin_ <= swapMax_`
     * @param swapMax_ : >= swap amount, applies for **OUTGOING** swap (= `swap(...)` call)
     * @param swapMin_ : <= swap amount, applies for **OUTGOING** swap (= `swap(...)` call)
     * @param swapFee_ : defines swap fee for **INCOMING** swap (= `reverseSwap(...)` call), and `refund(...)`
     */
    function setLimits(uint256 swapMax_, uint256 swapMin_, uint256 swapFee_) external;


    /**
     * @notice Withdraws amount from contract's supply, which is supposed to be done exclusively for relocating funds to
     *       another Bridge system, and **NO** other purpose.
     * @param targetAddress : address to send tokens to
     * @param amount : amount of tokens to withdraw
     */
    function withdraw(address targetAddress, uint256 amount) external;


    /**
     * @dev Deposits funds back in to the contract supply.
     *      Dedicated to increase contract's supply, usually(but not necessarily) after previous withdrawal from supply.
     *      NOTE: This call needs preexisting ERC20 allowance >= `amount` for address of this Bridge contract as
     *            recipient/beneficiary and Tx sender address as sender.
     *            This means that address passed in as the Tx sender, must have already crated allowance by calling the
     *            `ERC20.approve(from, ADDR_OF_BRIDGE_CONTRACT, amount)` *before* calling this(`deposit(...)`) call.
     * @param amount : deposit amount
     */
    function deposit(uint256 amount) external;


    /**
     * @notice Withdraw fees accrued so far.
     *         !IMPORTANT!: Current design of this contract does *NOT* allow to distinguish between *swap fees accrued*
     *                      and *excess funds* sent to the contract's address via *direct* `ERC20.transfer(...)`.
     *                      Implication is that excess funds **are treated** as swap fees.
     *                      The only way how to separate these two is off-chain, by replaying events from this and
     *                      Fet ERC20 contracts and do the reconciliation.
     *
     * @param targetAddress : address to send tokens to.
     */
    function withdrawFees(address targetAddress) external;


    /**
     * @notice Delete the contract, transfers the remaining token and ether balance to the specified
     *         payoutAddress
     * @param targetAddress address to transfer the balances to. Ensure that this is able to handle ERC20 tokens
     * @dev owner only + only on or after `earliestDelete` block
     */
    function deleteContract(address payable targetAddress) external;
}

// Part: IBridge

/**
 * @title Bi-directional bridge for transferring FET tokens between Ethereum and Fetch Mainnet-v2
 *
 * @notice This bridge allows to transfer [ERC20-FET] tokens from Ethereum Mainnet to [Native FET] tokens on Fetch
 *         Native Mainnet-v2 and **other way around** (= it is bi-directional).
 *         User will be *charged* swap fee defined in counterpart contract deployed on Fetch Native Mainnet-v2.
 *         In the case of a refund, user will be charged a swap fee configured in this contract.
 *
 * @dev There are three primary actions defining business logic of this contract:
 *       * `swap(...)`: initiates swap of tokens from Ethereum to Fetch Native Mainnet-v2, callable by anyone (= users)
 *       * `reverseSwap(...)`: finalises the swap of tokens in *opposite* direction = receives swap originally
 *                             initiated on Fetch Native Mainnet-v2, callable exclusively by `relayer` role
 *       * `refund(...)`: refunds swap originally initiated in this contract(by `swap(...)` call), callable exclusively
 *                        by `relayer` role
 *
 *      Swap Fees for `swap(...)` operations (direction from this contract to are handled by the counterpart contract on Fetch Native Mainnet-v2, **except** for refunds, for
 *      which user is charged swap fee defined by this contract (since relayer needs to send refund transaction back to
 *      this contract.
 *
 *      ! IMPORTANT !: Current design of this contract does *NOT* allow to distinguish between *swap fees accrued* and
 *      *excess funds* sent to the address of this contract via *direct* `ERC20.transfer(...)`.
 *      Implication is, that excess funds **are treated** as swap fees.
 *      The only way how to separate these two is to do it *off-chain*, by replaying events from this and FET ERC20
 *      contracts, and do the reconciliation.
 */
interface IBridge is IBridgePublic, IBridgeRelayer, IBridgeAdmin {}

// File: Bridge.sol

/**
 * @title Bi-directional bridge for transferring FET tokens between Ethereum and Fetch Mainnet-v2
 *
 * @notice This bridge allows to transfer [ERC20-FET] tokens from Ethereum Mainnet to [Native FET] tokens on Fetch
 *         Native Mainnet-v2 and **other way around** (= it is bi-directional).
 *         User will be *charged* swap fee defined in counterpart contract deployed on Fetch Native Mainnet-v2.
 *         In the case of a refund, user will be charged a swap fee configured in this contract.
 *
 * @dev There are three primary actions defining business logic of this contract:
 *       * `swap(...)`: initiates swap of tokens from Ethereum to Fetch Native Mainnet-v2, callable by anyone (= users)
 *       * `reverseSwap(...)`: finalises the swap of tokens in *opposite* direction = receives swap originally
 *                             initiated on Fetch Native Mainnet-v2, callable exclusively by `relayer` role
 *       * `refund(...)`: refunds swap originally initiated in this contract(by `swap(...)` call), callable exclusively
 *                        by `relayer` role
 *
 *      Swap Fees for `swap(...)` operations (direction from this contract to are handled by the counterpart contract on Fetch Native Mainnet-v2, **except** for refunds, for
 *      which user is charged swap fee defined by this contract (since relayer needs to send refund transaction back to
 *      this contract.
 *
 *      ! IMPORTANT !: Current design of this contract does *NOT* allow to distinguish between *swap fees accrued* and
 *      *excess funds* sent to the address of this contract via *direct* `ERC20.transfer(...)`.
 *      Implication is, that excess funds **are treated** as swap fees.
 *      The only way how to separate these two is to do it *off-chain*, by replaying events from this and FET ERC20
 *      contracts, and do the reconciliation.
 */
contract Bridge is IBridge, AccessControl {
    using SafeMath for uint256;

    /// @notice **********    CONSTANTS    ***********
    bytes32 public constant APPROVER_ROLE = keccak256("APPROVER_ROLE");
    bytes32 public constant MONITOR_ROLE = keccak256("MONITOR_ROLE");
    bytes32 public constant RELAYER_ROLE = keccak256("RELAYER_ROLE");

    /// @notice *******    IMMUTABLE STATE    ********
    IERC20Token public immutable token;
    uint256 public immutable earliestDelete;
    /// @notice ********    MUTABLE STATE    *********
    uint256 public supply;
    uint64 public  nextSwapId;
    uint64 public  relayEon;
    mapping(uint64 => uint256) public refunds; // swapId -> original swap amount(= *includes* swapFee)
    uint256 public swapMax;
    uint256 public swapMin;
    uint256 public cap;
    uint256 public swapFee;
    uint256 public pausedSinceBlockPublicApi;
    uint256 public pausedSinceBlockRelayerApi;
    uint256 public reverseAggregatedAllowance;
    uint256 public reverseAggregatedAllowanceApproverCap;


    /* Only callable by owner */
    modifier onlyOwner() {
        require(_isOwner(), "Only admin role");
        _;
    }

    modifier onlyRelayer() {
        require(hasRole(RELAYER_ROLE, msg.sender), "Only relayer role");
        _;
    }

    modifier verifyTxRelayEon(uint64 relayEon_) {
        require(relayEon == relayEon_, "Tx doesn't belong to current relayEon");
        _;
    }

    modifier canPause(uint256 pauseSinceBlockNumber) {
        if (pauseSinceBlockNumber > block.number) // Checking UN-pausing (the most critical operation)
        {
            require(_isOwner(), "Only admin role");
        }
        else
        {
            require(hasRole(MONITOR_ROLE, msg.sender) || _isOwner(), "Only admin or monitor role");
        }
        _;
    }

    modifier canSetReverseAggregatedAllowance(uint256 allowance) {
        if (allowance > reverseAggregatedAllowanceApproverCap) // Check for going over the approver cap (the most critical operation)
        {
            require(_isOwner(), "Only admin role");
        }
        else
        {
            require(hasRole(APPROVER_ROLE, msg.sender) || _isOwner(), "Only admin or approver role");
        }
        _;
    }

    modifier verifyPublicAPINotPaused() {
        require(pausedSinceBlockPublicApi > block.number, "Contract has been paused");
        _verifyRelayerApiNotPaused();
        _;
    }

    modifier verifyRelayerApiNotPaused() {
        _verifyRelayerApiNotPaused();
        _;
    }

    modifier verifySwapAmount(uint256 amount) {
        // NOTE(pb): Commenting-out check against `swapFee` in order to spare gas for user's Tx, relying solely on check
        //  against `swapMin` only, which is ensured to be `>= swapFee` (by `_setLimits(...)` function).
        //require(amount > swapFee, "Amount must be higher than fee");
        require(amount >= swapMin, "Amount bellow lower limit");
        require(amount <= swapMax, "Amount exceeds upper limit");
        _;
    }

    modifier verifyReverseSwapAmount(uint256 amount) {
        require(amount <= swapMax, "Amount exceeds swap max limit");
        _;
    }

    modifier verifyRefundSwapId(uint64 id) {
        require(id < nextSwapId, "Invalid swap id");
        require(refunds[id] == 0, "Refund was already processed");
        _;
    }


    /*******************
    Contract start
    *******************/
    /**
     * @notice Contract constructor
     * @dev Input parameters offers full flexibility to configure the contract during deployment, with minimal need of
     *      further setup transactions necessary to open contract to the public.
     *
     * @param ERC20Address - address of FET ERC20 token contract
     * @param cap_ - limits contract `supply` value from top
     * @param reverseAggregatedAllowance_ - allowance value which limits how much can refund & reverseSwap transfer
     *                                      in aggregated form
     * @param reverseAggregatedAllowanceApproverCap_ - limits allowance value up to which can APPROVER_ROLE set
     *                                                 the allowance
     * @param swapMax_ - value representing UPPER limit which can be transferred (this value INCLUDES swapFee)
     * @param swapMin_ - value representing LOWER limit which can be transferred (this value INCLUDES swapFee)
     * @param swapFee_ - represents fee which user has to pay for swap execution,
     * @param pausedSinceBlockPublicApi_ - block number since which the Public API of the contract will be paused
     * @param pausedSinceBlockRelayerApi_ - block number since which the Relayer API of the contract will be paused
     * @param deleteProtectionPeriod_ - number of blocks(from contract deployment block) during which contract can
     *                                  NOT be deleted
     */
    constructor(
          address ERC20Address
        , uint256 cap_
        , uint256 reverseAggregatedAllowance_
        , uint256 reverseAggregatedAllowanceApproverCap_
        , uint256 swapMax_
        , uint256 swapMin_
        , uint256 swapFee_
        , uint256 pausedSinceBlockPublicApi_
        , uint256 pausedSinceBlockRelayerApi_
        , uint256 deleteProtectionPeriod_)
    {
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
        token = IERC20Token(ERC20Address);
        earliestDelete = block.number.add(deleteProtectionPeriod_);

        /// @dev Unnecessary initialisations, done implicitly by VM
        //supply = 0;
        //refundsFeesAccrued = 0;
        //nextSwapId = 0;

        // NOTE(pb): Initial value is by design set to MAX_LIMIT<uint64>, so that its NEXT increment(+1) will
        //           overflow to 0.
        relayEon = type(uint64).max;

        _setCap(cap_);
        _setReverseAggregatedAllowance(reverseAggregatedAllowance_);
        _setReverseAggregatedAllowanceApproverCap(reverseAggregatedAllowanceApproverCap_);
        _setLimits(swapMax_, swapMin_, swapFee_);
        _pausePublicApiSince(pausedSinceBlockPublicApi_);
        _pauseRelayerApiSince(pausedSinceBlockRelayerApi_);
    }


    // **********************************************************
    // ***********    USER-LEVEL ACCESS METHODS    **********


    /**
      * @notice Initiates swap, which will be relayed to the other blockchain.
      *         Swap might fail, if `destinationAddress` value is invalid (see bellow), in which case the swap will be
      *         refunded back to user. Swap fee will be *WITHDRAWN* from `amount` in that case - please see details
      *         in desc. for `refund(...)` call.
      *
      * @dev Swap call will create unique identifier (swap id), which is, by design, sequentially growing by 1 per each
      *      new swap created, and so uniquely identifies each swap. This identifier is referred to as "reverse swap id"
      *      on the other blockchain.
      *      Callable by anyone.
      *
      * @param destinationAddress - address on **OTHER** blockchain where the swap effective amount will be transferred
      *                             in to.
      *                             User is **RESPONSIBLE** for providing the **CORRECT** and valid value.
      *                             The **CORRECT** means, in this context, that address is valid *AND* user really
      *                             intended this particular address value as destination = that address is NOT lets say
      *                             copy-paste mistake made by user. Reason being that when user provided valid address
      *                             value, but made mistake = address is of someone else (e.g. copy-paste mistake), then
      *                             there is **NOTHING** what can be done to recover funds back to user (= refund) once
      *                             the swap will be relayed to the other blockchain!
      *                             The **VALID** means that provided value successfully passes consistency checks of
      *                             valid address of **OTHER** blockchain. In the case when user provides invalid
      *                             address value, relayer will execute refund - please see desc. for `refund()` call
      *                             for more details.
      */
    function swap(
        uint256 amount, // This is original amount (INCLUDES fee)
        string calldata destinationAddress
        )
        external
        override
        verifyPublicAPINotPaused
        verifySwapAmount(amount)
    {
        supply = supply.add(amount);
        require(cap >= supply, "Swap would exceed cap");
        token.transferFrom(msg.sender, address(this), amount);
        emit Swap(nextSwapId, msg.sender, destinationAddress, destinationAddress, amount);
        // NOTE(pb): No necessity to use SafeMath here:
        nextSwapId += 1;
    }


    /**
     * @notice Returns amount of excess FET ERC20 tokens which were sent to address of this contract via direct ERC20
     *         transfer (by calling ERC20.transfer(...)), without interacting with API of this contract, what can happen
     *         only by mistake.
     *
     * @return targetAddress : address to send tokens to
     */
    function getFeesAccrued() external view override returns(uint256) {
        // NOTE(pb): This subtraction shall NEVER fail:
        return token.balanceOf(address(this)).sub(supply, "Critical err: balance < supply");
    }

    function getApproverRole() external view override returns(bytes32) {return APPROVER_ROLE;}
    function getMonitorRole() external view override returns(bytes32) {return MONITOR_ROLE;}
    function getRelayerRole() external view override returns(bytes32) {return RELAYER_ROLE;}

    function getToken() external view override returns(address) {return address(token);}
    function getEarliestDelete() external view override returns(uint256) {return earliestDelete;}
    function getSupply() external view override returns(uint256) {return supply;}
    function getNextSwapId() external view override returns(uint64) {return nextSwapId;}
    function getRelayEon() external view override returns(uint64) {return relayEon;}
    function getRefund(uint64 swap_id) external view override returns(uint256) {return refunds[swap_id];}
    function getSwapMax() external view override returns(uint256) {return swapMax;}
    function getSwapMin() external view override returns(uint256) {return swapMin;}
    function getCap() external view override returns(uint256) {return cap;}
    function getSwapFee() external view override returns(uint256) {return swapFee;}
    function getPausedSinceBlockPublicApi() external view override returns(uint256) {return pausedSinceBlockPublicApi;}
    function getPausedSinceBlockRelayerApi() external view override returns(uint256) {return pausedSinceBlockRelayerApi;}
    function getReverseAggregatedAllowance() external view override returns(uint256) {return reverseAggregatedAllowance;}
    function getReverseAggregatedAllowanceApproverCap() external view override returns(uint256) {return reverseAggregatedAllowanceApproverCap;}


    // **********************************************************
    // ***********    RELAYER-LEVEL ACCESS METHODS    ***********


    /**
      * @notice Starts the new relay eon.
      * @dev Relay eon concept is part of the design in order to ensure safe management of hand-over between two
      *      relayer services. It provides clean isolation of potentially still pending transactions from previous
      *      relayer svc and the current one.
      */
    function newRelayEon()
        external
        override
        verifyRelayerApiNotPaused
        onlyRelayer
    {
        // NOTE(pb): No need for safe math for this increment, since the MAX_LIMIT<uint64> is huge number (~10^19),
        //  there is no way that +1 incrementing from initial 0 value can possibly cause overflow in real world - that
        //  would require to send more than 10^19 transactions to reach that point.
        //  The only case, where this increment operation will lead to overflow, by-design, is the **VERY 1st**
        //  increment = very 1st call of this contract method, when the `relayEon` is by-design & intentionally
        //  initialised to MAX_LIMIT<uint64> value, so the resulting value of the `relayEon` after increment will be `0`
        relayEon += 1;
        emit NewRelayEon(relayEon);
    }


    /**
      * @notice Refunds swap previously created by `swap(...)` call to this contract. The `swapFee` is *NOT* refunded
      *         back to the user (this is by-design).
      *
      * @dev Callable exclusively by `relayer` role
      *
      * @param id - swap id to refund - must be swap id of swap originally created by `swap(...)` call to this contract,
      *             **NOT** *reverse* swap id!
      * @param to - address where the refund will be transferred in to(IDENTICAL to address used in associated `swap`
      *             call)
      * @param amount - original amount specified in associated `swap` call = it INCLUDES swap fee, which will be
      *                 withdrawn
      * @param relayEon_ - current relay eon, ensures safe management of relaying process
      */
    function refund(
        uint64 id,
        address to,
        uint256 amount,
        uint64 relayEon_
        )
        external
        override
        verifyRelayerApiNotPaused
        verifyTxRelayEon(relayEon_)
        verifyReverseSwapAmount(amount)
        onlyRelayer
        verifyRefundSwapId(id)
    {
        // NOTE(pb): Fail as early as possible - withdrawal from aggregated allowance is most likely to fail comparing
        //  to rest of the operations bellow.
        _updateReverseAggregatedAllowance(amount);

        supply = supply.sub(amount, "Amount exceeds contract supply");

        // NOTE(pb): Same calls are repeated in both branches of the if-else in order to minimise gas impact, comparing
        //  to implementation, where these calls would be present in the code just once, after if-else block.
        if (amount > swapFee) {
            // NOTE(pb): No need to use safe math here, the overflow is prevented by `if` condition above.
            uint256 effectiveAmount = amount - swapFee;
            token.transfer(to, effectiveAmount);
            emit SwapRefund(id, to, effectiveAmount, swapFee);
        } else {
            // NOTE(pb): No transfer necessary in this case, since whole amount is taken as swap fee.
            emit SwapRefund(id, to, 0, amount);
        }

        // NOTE(pb): Here we need to record the original `amount` value (passed as input param) rather than
        //  `effectiveAmount` in order to make sure, that the value is **NOT** zero (so it is possible to detect
        //  existence of key-value record in the `refunds` mapping (this is done in the `verifyRefundSwapId(...)`
        //  modifier). This also means that relayer role shall call this `refund(...)` function only for `amount > 0`,
        //  otherways relayer will pay Tx fee for executing the transaction which will have *NO* effect.
        refunds[id] = amount;
    }


    /**
      * @notice Refunds swap previously created by `swap(...)` call to this contract, where `swapFee` *IS* refunded
      *         back to the user (= swap fee is waived = user will receive full `amount`).
      *         Purpose of this method is to enable full refund in the situations when it si not user's fault that
      *         swap needs to be refunded (e.g. when Fetch Native Mainnet-v2 will become unavailable for prolonged
      *         period of time, etc. ...).
      *
      * @dev Callable exclusively by `relayer` role
      *
      * @param id - swap id to refund - must be swap id of swap originally created by `swap(...)` call to this contract,
      *             **NOT** *reverse* swap id!
      * @param to - address where the refund will be transferred in to(IDENTICAL to address used in associated `swap`
      *             call)
      * @param amount - original amount specified in associated `swap` call = it INCLUDES swap fee, which will be
      *                 waived = user will receive whole `amount` value.
      *                 Pleas mind that `amount > 0`, otherways relayer will pay Tx fee for executing the transaction
      *                 which will have *NO* effect (= like this function `refundInFull(...)` would *not* have been
      *                 called at all!
      * @param relayEon_ - current relay eon, ensures safe management of relaying process
      */
    function refundInFull(
        uint64 id,
        address to,
        uint256 amount,
        uint64 relayEon_
        )
        external
        override
        verifyRelayerApiNotPaused
        verifyTxRelayEon(relayEon_)
        verifyReverseSwapAmount(amount)
        onlyRelayer
        verifyRefundSwapId(id)
    {
        // NOTE(pb): Fail as early as possible - withdrawal from aggregated allowance is most likely to fail comparing
        //  to rest of the operations bellow.
        _updateReverseAggregatedAllowance(amount);

        supply = supply.sub(amount, "Amount exceeds contract supply");

        token.transfer(to, amount);
        emit SwapRefund(id, to, amount, 0);

        // NOTE(pb): Here we need to record the original `amount` value (passed as input param) rather than
        //  `effectiveAmount` in order to make sure, that the value is **NOT** zero (so it is possible to detect
        //  existence of key-value record in the `refunds` mapping (this is done in the `verifyRefundSwapId(...)`
        //  modifier). This also means that relayer role shall call this function function only for `amount > 0`,
        //  otherways relayer will pay Tx fee for executing the transaction which will have *NO* effect.
        refunds[id] = amount;
    }


    /**
      * @notice Finalises swap initiated by counterpart contract on the other blockchain.
      *         This call sends swapped tokens to `to` address value user specified in original swap on the **OTHER**
      *         blockchain.
      *
      * @dev Callable exclusively by `relayer` role
      *
      * @param rid - reverse swap id - unique identifier of the swap initiated on the **OTHER** blockchain.
      *              This id is, by definition, sequentially growing number incremented by 1 for each new swap initiated
      *              the other blockchain. **However**, it is *NOT* ensured that *all* swaps from the other blockchain
      *              will be transferred to this (Ethereum) blockchain, since some of these swaps can be refunded back
      *              to users (on the other blockchain).
      * @param to - address where the refund will be transferred in to
      * @param from - source address from which user transferred tokens from on the other blockchain. Present primarily
      *               for purposes of quick querying of events on this blockchain.
      * @param originTxHash - transaction hash for swap initiated on the **OTHER** blockchain. Present in order to
      *                       create strong bond between this and other blockchain.
      * @param amount - original amount specified in associated swap initiated on the other blockchain.
      *                 Swap fee is *withdrawn* from the `amount` user specified in the swap on the other blockchain,
      *                 what means that user receives `amount - swapFee`, or *nothing* if `amount <= swapFee`.
      *                 Pleas mind that `amount > 0`, otherways relayer will pay Tx fee for executing the transaction
      *                 which will have *NO* effect (= like this function `refundInFull(...)` would *not* have been
      *                 called at all!
      * @param relayEon_ - current relay eon, ensures safe management of relaying process
      */
    function reverseSwap(
        uint64 rid, // Reverse swp id (from counterpart contract on other blockchain)
        address to,
        string calldata from,
        bytes32 originTxHash,
        uint256 amount, // This is original swap amount (= *includes* swapFee)
        uint64 relayEon_
        )
        external
        override
        verifyRelayerApiNotPaused
        verifyTxRelayEon(relayEon_)
        verifyReverseSwapAmount(amount)
        onlyRelayer
    {
        // NOTE(pb): Fail as early as possible - withdrawal from aggregated allowance is most likely to fail comparing
        //  to rest of the operations bellow.
        _updateReverseAggregatedAllowance(amount);

        supply = supply.sub(amount, "Amount exceeds contract supply");

        if (amount > swapFee) {
            // NOTE(pb): No need to use safe math here, the overflow is prevented by `if` condition above.
            uint256 effectiveAmount = amount - swapFee;
            token.transfer(to, effectiveAmount);
            emit ReverseSwap(rid, to, from, originTxHash, effectiveAmount, swapFee);
        } else {
            // NOTE(pb): No transfer, no contract supply change since whole amount is taken as swap fee.
            emit ReverseSwap(rid, to, from, originTxHash, 0, amount);
        }
    }


    // **********************************************************
    // ****   MONITOR/ADMIN-LEVEL ACCESS METHODS   *****


    /**
     * @notice Pauses Public API since the specified block number
     * @param blockNumber block number since which public interaction will be paused (for all
     *        block.number >= blockNumber).
     * @dev Delegate only
     *      If `blocknumber < block.number`, then contract will be paused immediately = from `block.number`.
     */
    function pausePublicApiSince(uint256 blockNumber)
        external
        override
        canPause(blockNumber)
    {
        _pausePublicApiSince(blockNumber);
    }


    /**
     * @notice Pauses Relayer API since the specified block number
     * @param blockNumber block number since which Relayer API interaction will be paused (for all
     *        block.number >= blockNumber).
     * @dev Delegate only
     *      If `blocknumber < block.number`, then contract will be paused immediately = from `block.number`.
     */
    function pauseRelayerApiSince(uint256 blockNumber)
        external
        override
        canPause(blockNumber)
    {
        _pauseRelayerApiSince(blockNumber);
    }


    // **********************************************************
    // ************    ADMIN-LEVEL ACCESS METHODS   *************


    /**
     * @notice Mints provided amount of FET tokens.
     *         This is to reflect changes in minted Native FET token supply on the Fetch Native Mainnet-v2 blockchain.
     * @param amount - number of FET tokens to mint.
     */
    function mint(uint256 amount)
        external
        override
        onlyOwner
    {
        // NOTE(pb): The `supply` shall be adjusted by minted amount.
        supply = supply.add(amount);
        require(cap >= supply, "Minting would exceed the cap");
        token.mint(address(this), amount);
    }

    /**
     * @notice Burns provided amount of FET tokens.
     *         This is to reflect changes in minted Native FET token supply on the Fetch Native Mainnet-v2 blockchain.
     * @param amount - number of FET tokens to burn.
     */
    function burn(uint256 amount)
        external
        override
        onlyOwner
    {
        // NOTE(pb): The `supply` shall be adjusted by burned amount.
        supply = supply.sub(amount, "Amount exceeds contract supply");
        token.burn(amount);
    }


    /**
     * @notice Sets cap (max) value of `supply` this contract can hold = the value of tokens transferred to the other
     *         blockchain.
     *         This cap affects(limits) all operations which *increase* contract's `supply` value = `swap(...)` and
     *         `mint(...)`.
     * @param value - new cap value.
     */
    function setCap(uint256 value)
        external
        override
        onlyOwner
    {
        _setCap(value);
    }


    /**
     * @notice Sets value of `reverseAggregatedAllowance` state variable.
     *         This affects(limits) operations which *decrease* contract's `supply` value via **RELAYER** authored
     *         operations (= `reverseSwap(...)` and `refund(...)`). It does **NOT** affect **ADMINISTRATION** authored
     *         supply decrease operations (= `withdraw(...)` & `burn(...)`).
     * @param value - new allowance value (absolute)
     */
    function setReverseAggregatedAllowance(uint256 value)
        external
        override
        canSetReverseAggregatedAllowance(value)
    {
        _setReverseAggregatedAllowance(value);
    }


    /**
     * @notice Sets value of `reverseAggregatedAllowanceApproverCap` state variable.
     *         This limits APPROVER_ROLE from top - value up to which can approver rise the allowance.
     * @param value - new cap value (absolute)
     */
    function setReverseAggregatedAllowanceApproverCap(uint256 value)
        external
        override
        onlyOwner
    {
        _setReverseAggregatedAllowanceApproverCap(value);
    }


    /**
     * @notice Sets limits for swap amount
     *         FUnction will revert if following consitency check fails: `swapfee_ <= swapMin_ <= swapMax_`
     * @param swapMax_ : >= swap amount, applies for **OUTGOING** swap (= `swap(...)` call)
     * @param swapMin_ : <= swap amount, applies for **OUTGOING** swap (= `swap(...)` call)
     * @param swapFee_ : defines swap fee for **INCOMING** swap (= `reverseSwap(...)` call), and `refund(...)`
     */
    function setLimits(
        uint256 swapMax_,
        uint256 swapMin_,
        uint256 swapFee_
        )
        external
        override
        onlyOwner
    {
        _setLimits(swapMax_, swapMin_, swapFee_);
    }


    /**
     * @notice Withdraws amount from contract's supply, which is supposed to be done exclusively for relocating funds to
     *       another Bridge system, and **NO** other purpose.
     * @param targetAddress : address to send tokens to
     * @param amount : amount of tokens to withdraw
     */
    function withdraw(
        address targetAddress,
        uint256 amount
        )
        external
        override
        onlyOwner
    {
        supply = supply.sub(amount, "Amount exceeds contract supply");
        token.transfer(targetAddress, amount);
        emit Withdraw(targetAddress, amount);
    }


    /**
     * @dev Deposits funds back in to the contract supply.
     *      Dedicated to increase contract's supply, usually(but not necessarily) after previous withdrawal from supply.
     *      NOTE: This call needs preexisting ERC20 allowance >= `amount` for address of this Bridge contract as
     *            recipient/beneficiary and Tx sender address as sender.
     *            This means that address passed in as the Tx sender, must have already crated allowance by calling the
     *            `ERC20.approve(from, ADDR_OF_BRIDGE_CONTRACT, amount)` *before* calling this(`deposit(...)`) call.
     * @param amount : deposit amount
     */
    function deposit(uint256 amount)
        external
        override
        onlyOwner
    {
        supply = supply.add(amount);
        require(cap >= supply, "Deposit would exceed the cap");
        token.transferFrom(msg.sender, address(this), amount);
        emit Deposit(msg.sender, amount);
    }


    /**
     * @notice Withdraw fees accrued so far.
     *         !IMPORTANT!: Current design of this contract does *NOT* allow to distinguish between *swap fees accrued*
     *                      and *excess funds* sent to the contract's address via *direct* `ERC20.transfer(...)`.
     *                      Implication is that excess funds **are treated** as swap fees.
     *                      The only way how to separate these two is off-chain, by replaying events from this and
     *                      Fet ERC20 contracts and do the reconciliation.
     *
     * @param targetAddress : address to send tokens to.
     */
    function withdrawFees(address targetAddress)
        external
        override
        onlyOwner
    {
        uint256 fees = this.getFeesAccrued();
        require(fees > 0, "No fees to withdraw");
        token.transfer(targetAddress, fees);
        emit FeesWithdrawal(targetAddress, fees);
    }


    /**
     * @notice Delete the contract, transfers the remaining token and ether balance to the specified
     *         payoutAddress
     * @param targetAddress address to transfer the balances to. Ensure that this is able to handle ERC20 tokens
     * @dev owner only + only on or after `earliestDelete` block
     */
    function deleteContract(address payable targetAddress)
        external
        override
        onlyOwner
    {
        require(earliestDelete <= block.number, "Earliest delete not reached");
        require(targetAddress != address(this), "pay addr == this contract addr");
        uint256 contractBalance = token.balanceOf(address(this));
        token.transfer(targetAddress, contractBalance);
        emit DeleteContract(targetAddress, contractBalance);
        selfdestruct(targetAddress);
    }


    // **********************************************************
    // ******************    INTERNAL METHODS   *****************


    function _isOwner() internal view returns(bool) {
        return hasRole(DEFAULT_ADMIN_ROLE, msg.sender);
    }

    function _verifyRelayerApiNotPaused() internal view {
        require(pausedSinceBlockRelayerApi > block.number, "Contract has been paused");
    }

    /**
     * @notice Pauses Public API since the specified block number
     * @param blockNumber - block number since which interaction with Public API will be paused (for all
     *                      block.number >= blockNumber)
     */
    function _pausePublicApiSince(uint256 blockNumber) internal
    {
        pausedSinceBlockPublicApi = blockNumber < block.number ? block.number : blockNumber;
        emit PausePublicApi(pausedSinceBlockPublicApi);
    }


    /**
     * @notice Pauses Relayer API since the specified block number
     * @param blockNumber - block number since which interaction with Relayer API will be paused (for all
     *                      block.number >= blockNumber)
     */
    function _pauseRelayerApiSince(uint256 blockNumber) internal
    {
        pausedSinceBlockRelayerApi = blockNumber < block.number ? block.number : blockNumber;
        emit PauseRelayerApi(pausedSinceBlockRelayerApi);
    }


    function _setLimits(
        uint256 swapMax_,
        uint256 swapMin_,
        uint256 swapFee_
        )
        internal
    {
        require((swapFee_ <= swapMin_) && (swapMin_ <= swapMax_), "fee<=lower<=upper violated");

        swapMax = swapMax_;
        swapMin = swapMin_;
        swapFee = swapFee_;

        emit LimitsUpdate(swapMax, swapMin, swapFee);
    }


    function _setCap(uint256 cap_) internal
    {
        cap = cap_;
        emit CapUpdate(cap);
    }


    function _setReverseAggregatedAllowance(uint256 allowance) internal
    {
        reverseAggregatedAllowance = allowance;
        emit ReverseAggregatedAllowanceUpdate(reverseAggregatedAllowance);
    }


    function _setReverseAggregatedAllowanceApproverCap(uint256 value) internal
    {
        reverseAggregatedAllowanceApproverCap = value;
        emit ReverseAggregatedAllowanceApproverCapUpdate(reverseAggregatedAllowanceApproverCap);
    }


    function _updateReverseAggregatedAllowance(uint256 amount) internal {
        reverseAggregatedAllowance = reverseAggregatedAllowance.sub(amount, "Operation exceeds reverse aggregated allowance");
    }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"ERC20Address","type":"address"},{"internalType":"uint256","name":"cap_","type":"uint256"},{"internalType":"uint256","name":"reverseAggregatedAllowance_","type":"uint256"},{"internalType":"uint256","name":"reverseAggregatedAllowanceApproverCap_","type":"uint256"},{"internalType":"uint256","name":"swapMax_","type":"uint256"},{"internalType":"uint256","name":"swapMin_","type":"uint256"},{"internalType":"uint256","name":"swapFee_","type":"uint256"},{"internalType":"uint256","name":"pausedSinceBlockPublicApi_","type":"uint256"},{"internalType":"uint256","name":"pausedSinceBlockRelayerApi_","type":"uint256"},{"internalType":"uint256","name":"deleteProtectionPeriod_","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"CapUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"targetAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DeleteContract","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"fromAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"targetAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FeesWithdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"max","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"min","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"LimitsUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"eon","type":"uint64"}],"name":"NewRelayEon","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"sinceBlock","type":"uint256"}],"name":"PausePublicApi","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"sinceBlock","type":"uint256"}],"name":"PauseRelayerApi","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"ReverseAggregatedAllowanceApproverCapUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"ReverseAggregatedAllowanceUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"rid","type":"uint64"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"string","name":"from","type":"string"},{"indexed":false,"internalType":"bytes32","name":"originTxHash","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"effectiveAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"ReverseSwap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"id","type":"uint64"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"string","name":"indexedTo","type":"string"},{"indexed":false,"internalType":"string","name":"to","type":"string"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"id","type":"uint64"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"refundedAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"SwapRefund","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"targetAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"APPROVER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MONITOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RELAYER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable","name":"targetAddress","type":"address"}],"name":"deleteContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"earliestDelete","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getApproverRole","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEarliestDelete","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFeesAccrued","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMonitorRole","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNextSwapId","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPausedSinceBlockPublicApi","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPausedSinceBlockRelayerApi","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"swap_id","type":"uint64"}],"name":"getRefund","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRelayEon","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRelayerRole","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReverseAggregatedAllowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReverseAggregatedAllowanceApproverCap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSwapFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSwapMax","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSwapMin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"newRelayEon","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nextSwapId","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"pausePublicApiSince","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"pauseRelayerApiSince","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pausedSinceBlockPublicApi","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pausedSinceBlockRelayerApi","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"id","type":"uint64"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint64","name":"relayEon_","type":"uint64"}],"name":"refund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"id","type":"uint64"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint64","name":"relayEon_","type":"uint64"}],"name":"refundInFull","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"}],"name":"refunds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"relayEon","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reverseAggregatedAllowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reverseAggregatedAllowanceApproverCap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"rid","type":"uint64"},{"internalType":"address","name":"to","type":"address"},{"internalType":"string","name":"from","type":"string"},{"internalType":"bytes32","name":"originTxHash","type":"bytes32"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint64","name":"relayEon_","type":"uint64"}],"name":"reverseSwap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setCap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"swapMax_","type":"uint256"},{"internalType":"uint256","name":"swapMin_","type":"uint256"},{"internalType":"uint256","name":"swapFee_","type":"uint256"}],"name":"setLimits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setReverseAggregatedAllowance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setReverseAggregatedAllowanceApproverCap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"supply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"string","name":"destinationAddress","type":"string"}],"name":"swap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swapFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"swapMax","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"swapMin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20Token","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"targetAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"targetAddress","type":"address"}],"name":"withdrawFees","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60c06040523480156200001157600080fd5b50604051620035993803806200359983398181016040526101408110156200003857600080fd5b508051602082015160408301516060840151608085015160a086015160c087015160e08801516101008901516101209099015197989697959694959394929391929091620000886000336200012e565b60608a901b6001600160601b031916608052620000b243826200013e602090811b620028c217901c565b60a05260028054600160401b600160801b0319166fffffffffffffffff0000000000000000179055620000e589620001a2565b620000f088620001dd565b620000fb8762000218565b6200010886868662000253565b62000113836200030a565b6200011e8262000355565b505050505050505050506200049b565b6200013a8282620003a0565b5050565b60008282018381101562000199576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b60068190556040805182815290517f7bf5040cdccd155f02a139290defa050a59f6bfa3caa1891ab18f792946306619181900360200190a150565b600a8190556040805182815290517f8691c4b455b7b4124baf05028588dc526466e4ec5adcc9f0fa4db96412128bee9181900360200190a150565b600b8190556040805182815290517f950df6cb5f31f381373bc4a214f633cbde0e4647b9c04557b6520e66606539639181900360200190a150565b818111158015620002645750828211155b620002b6576040805162461bcd60e51b815260206004820152601a60248201527f6665653c3d6c6f7765723c3d75707065722076696f6c61746564000000000000604482015290519081900360640190fd5b600483905560058290556007819055604080518481526020810184905280820183905290517f1932f9003fd372221b3107915b4e8655453e4f1fcb732f1bf5722b30453505589181900360600190a1505050565b4381106200031957806200031b565b435b600881905560408051918252517f622d5f267374fd0bd7a5354e16ef5f3bf1f8088b9cb43458bd0151acde1b319f9181900360200190a150565b43811062000364578062000366565b435b600981905560408051918252517fee457b823d3dc66873ea908b7fc2cc098b6627b6dab3e6138f0fcde93ddefe5d9181900360200190a150565b600082815260208181526040909120620003c59183906200291c62000419821b17901c565b156200013a57620003d562000430565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b600062000199836001600160a01b03841662000434565b3390565b600062000442838362000483565b6200047a575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556200019c565b5060006200019c565b60009081526001919091016020526040902054151590565b60805160601c60a05161308c6200050d60003980610cca5280611279528061174d525080610b045280610c455280610d9e5280610e6152806110be5280611338528061160c52806119845280611b995280611f4b52806121df52806125a6528061279952806128a0525061308c6000f3fe608060405234801561001057600080fd5b50600436106103a45760003560e01c80639010d07c116101e9578063b6b55f251161010f578063d547741f116100ad578063f3fef3a31161007c578063f3fef3a314610941578063f4b01b121461096d578063f502e68b14610975578063fc0c546a1461099b576103a4565b8063d547741f146107b8578063deb4a6d2146107e4578063e5c119ca1461085b578063e779e30a146108fe576103a4565b8063c533c4c9116100e9578063c533c4c914610783578063ca15c8731461078b578063d162d6ee146107a8578063d4cadf68146107b0576103a4565b8063b6b55f2514610756578063b8f0afbd14610773578063c1abb6d01461077b576103a4565b8063a217fddf11610187578063ab2235a311610156578063ab2235a3146106fb578063af9105811461073e578063b3de9b5d14610746578063b525aca91461074e576103a4565b8063a217fddf146106c6578063a5e992bb146106ce578063a85553d5146106d6578063aa896137146106f3576103a4565b80639c7dc431116101c35780639c7dc4311461067c5780639ecb882d146106845780639f2c88bf1461068c578063a0712d68146106a9576103a4565b80639010d07c1461061157806391d1485414610634578063926d7d7f14610674576103a4565b806347786d37116102ce57806367defa461161026c5780637af9028a1161023b5780637af9028a146105d3578063815d736e146105db578063888ff935146105e357806388f5b9a5146105eb576103a4565b806367defa4614610597578063680eb1851461059f57806368eb5155146105a75780636c9c2faf146105cb576103a4565b806354cf2aeb116102a857806354cf2aeb14610577578063554d578d1461057f578063573c0b0e14610587578063618145251461058f576103a4565b806347786d371461054a5780634d9b47e2146105675780634fe9dc481461056f576103a4565b80632637a477116103465780634245962b116103155780634245962b1461050057806342966c6814610508578063432944c014610525578063466a78c014610542576103a4565b80632637a4771461047a5780632f2ff15d146104a0578063355274ea146104cc57806336568abe146104d4576103a4565b8063164e68de11610382578063164e68de146103ea578063189ae5f21461041057806321df0da714610439578063248a9ca31461045d576103a4565b8063047fc9aa146103a95780630a80943e146103c35780630f42e312146103cb575b600080fd5b6103b16109a3565b60408051918252519081900360200190f35b6103b16109a9565b6103e8600480360360208110156103e157600080fd5b50356109af565b005b6103e86004803603602081101561040057600080fd5b50356001600160a01b0316610a06565b6103e86004803603606081101561042657600080fd5b5080359060208101359060400135610be8565b610441610c43565b604080516001600160a01b039092168252519081900360200190f35b6103b16004803603602081101561047357600080fd5b5035610c67565b6103e86004803603602081101561049057600080fd5b50356001600160a01b0316610c7c565b6103e8600480360360408110156104b657600080fd5b50803590602001356001600160a01b0316610f28565b6103b1610f94565b6103e8600480360360408110156104ea57600080fd5b50803590602001356001600160a01b0316610f9a565b6103b1610ffb565b6103e86004803603602081101561051e57600080fd5b503561101f565b6103e86004803603602081101561053b57600080fd5b5035611120565b6103b16111f9565b6103e86004803603602081101561056057600080fd5b50356111ff565b6103b1611253565b6103b1611265565b6103b161126b565b6103b1611271565b6103b1611277565b6103b161129b565b6103b16112bf565b6103b16112c5565b6105af6112cb565b604080516001600160401b039092168252519081900360200190f35b6103b16112da565b6103b16112e0565b6103b16112f2565b6103b16112f8565b6103b16004803603602081101561060157600080fd5b50356001600160401b03166113db565b6104416004803603604081101561062757600080fd5b50803590602001356113f6565b6106606004803603604081101561064a57600080fd5b50803590602001356001600160a01b0316611417565b604080519115158252519081900360200190f35b6103b161142f565b6103b1611441565b6103b1611453565b6103e8600480360360208110156106a257600080fd5b5035611459565b6103e8600480360360208110156106bf57600080fd5b5035611532565b6103b1611653565b6103b1611658565b6103e8600480360360208110156106ec57600080fd5b503561165e565b6103b161174b565b6103e86004803603608081101561071157600080fd5b506001600160401b0381358116916001600160a01b0360208201351691604082013591606001351661176f565b6105af611a97565b6103b1611aad565b6103b1611ab3565b6103e86004803603602081101561076c57600080fd5b5035611ab9565b6103b1611c46565b6103b1611c4c565b6103e8611c52565b6103b1600480360360208110156107a157600080fd5b5035611d2a565b6105af611d41565b6103b1611d57565b6103e8600480360360408110156107ce57600080fd5b50803590602001356001600160a01b0316611d5d565b6103e8600480360360408110156107fa57600080fd5b8135919081019060408101602082013564010000000081111561081c57600080fd5b82018360208201111561082e57600080fd5b8035906020019184600183028401116401000000008311171561085057600080fd5b509092509050611db6565b6103e8600480360360c081101561087157600080fd5b6001600160401b03823516916001600160a01b03602082013516918101906060810160408201356401000000008111156108aa57600080fd5b8201836020820111156108bc57600080fd5b803590602001918460018302840111640100000000831117156108de57600080fd5b9193509150803590602081013590604001356001600160401b031661207c565b6103e86004803603608081101561091457600080fd5b506001600160401b0381358116916001600160a01b03602082013516916040820135916060013516612381565b6103e86004803603604081101561095757600080fd5b506001600160a01b038135169060200135612716565b6105af61287d565b6103b16004803603602081101561098b57600080fd5b50356001600160401b031661288c565b61044161289e565b60015481565b60095490565b6109b7612931565b6109fa576040805162461bcd60e51b815260206004820152600f60248201526e4f6e6c792061646d696e20726f6c6560881b604482015290519081900360640190fd5b610a038161293d565b50565b610a0e612931565b610a51576040805162461bcd60e51b815260206004820152600f60248201526e4f6e6c792061646d696e20726f6c6560881b604482015290519081900360640190fd5b6000306001600160a01b031663888ff9356040518163ffffffff1660e01b815260040160206040518083038186803b158015610a8c57600080fd5b505afa158015610aa0573d6000803e3d6000fd5b505050506040513d6020811015610ab657600080fd5b5051905080610b02576040805162461bcd60e51b81526020600482015260136024820152724e6f206665657320746f20776974686472617760681b604482015290519081900360640190fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a9059cbb83836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015610b7957600080fd5b505af1158015610b8d573d6000803e3d6000fd5b505050506040513d6020811015610ba357600080fd5b50506040805182815290516001600160a01b038416917f6ba3b0bbad3c021cef53ad4903eaac995be31e67bf466ff8ed1cc610f3ecdda0919081900360200190a25050565b610bf0612931565b610c33576040805162461bcd60e51b815260206004820152600f60248201526e4f6e6c792061646d696e20726f6c6560881b604482015290519081900360640190fd5b610c3e838383612978565b505050565b7f000000000000000000000000000000000000000000000000000000000000000090565b60009081526020819052604090206002015490565b610c84612931565b610cc7576040805162461bcd60e51b815260206004820152600f60248201526e4f6e6c792061646d696e20726f6c6560881b604482015290519081900360640190fd5b437f00000000000000000000000000000000000000000000000000000000000000001115610d3c576040805162461bcd60e51b815260206004820152601b60248201527f4561726c696573742064656c657465206e6f7420726561636865640000000000604482015290519081900360640190fd5b6001600160a01b038116301415610d9a576040805162461bcd60e51b815260206004820152601e60248201527f7061792061646472203d3d207468697320636f6e747261637420616464720000604482015290519081900360640190fd5b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610e0957600080fd5b505afa158015610e1d573d6000803e3d6000fd5b505050506040513d6020811015610e3357600080fd5b50516040805163a9059cbb60e01b81526001600160a01b0385811660048301526024820184905291519293507f00000000000000000000000000000000000000000000000000000000000000009091169163a9059cbb916044808201926020929091908290030181600087803b158015610eac57600080fd5b505af1158015610ec0573d6000803e3d6000fd5b505050506040513d6020811015610ed657600080fd5b5050604080516001600160a01b03841681526020810183905281517f9a51f67330b0a91ec3a83d9766e4de555682ca44afb2de2200202ac4771f54dc929181900390910190a1816001600160a01b0316ff5b600082815260208190526040902060020154610f4b90610f46612a2d565b611417565b610f865760405162461bcd60e51b815260040180806020018281038252602f815260200180612f36602f913960400191505060405180910390fd5b610f908282612a31565b5050565b60065481565b610fa2612a2d565b6001600160a01b0316816001600160a01b031614610ff15760405162461bcd60e51b815260040180806020018281038252602f815260200180613028602f913960400191505060405180910390fd5b610f908282612a9a565b7f408a36151f841709116a4e8aca4e0202874f7f54687dcb863b1ea4672dc9d8cf81565b611027612931565b61106a576040805162461bcd60e51b815260206004820152600f60248201526e4f6e6c792061646d696e20726f6c6560881b604482015290519081900360640190fd5b60408051808201909152601e8152600080516020612ef4833981519152602082015260015461109a918390612b03565b60015560408051630852cd8d60e31b81526004810183905290516001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916342966c6891602480830192600092919082900301818387803b15801561110557600080fd5b505af1158015611119573d6000803e3d6000fd5b5050505050565b804381111561117957611131612931565b611174576040805162461bcd60e51b815260206004820152600f60248201526e4f6e6c792061646d696e20726f6c6560881b604482015290519081900360640190fd5b6111f0565b611191600080516020612fda83398151915233611417565b8061119f575061119f612931565b6111f0576040805162461bcd60e51b815260206004820152601a60248201527f4f6e6c792061646d696e206f72206d6f6e69746f7220726f6c65000000000000604482015290519081900360640190fd5b610f9082612b9a565b60055490565b611207612931565b61124a576040805162461bcd60e51b815260206004820152600f60248201526e4f6e6c792061646d696e20726f6c6560881b604482015290519081900360640190fd5b610a0381612be3565b600080516020612fda83398151915281565b60085490565b60075481565b60065490565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f408a36151f841709116a4e8aca4e0202874f7f54687dcb863b1ea4672dc9d8cf90565b60095481565b600a5490565b6002546001600160401b031681565b60015490565b600080516020612f6583398151915290565b60085481565b60006113d66001546040518060400160405280601e81526020017f437269746963616c206572723a2062616c616e6365203c20737570706c7900008152507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156113a357600080fd5b505afa1580156113b7573d6000803e3d6000fd5b505050506040513d60208110156113cd57600080fd5b50519190612b03565b905090565b6001600160401b031660009081526003602052604090205490565b600082815260208190526040812061140e9083612c1e565b90505b92915050565b600082815260208190526040812061140e9083612c2a565b600080516020612f6583398151915281565b600080516020612fda83398151915290565b600b5481565b80438111156114b25761146a612931565b6114ad576040805162461bcd60e51b815260206004820152600f60248201526e4f6e6c792061646d696e20726f6c6560881b604482015290519081900360640190fd5b611529565b6114ca600080516020612fda83398151915233611417565b806114d857506114d8612931565b611529576040805162461bcd60e51b815260206004820152601a60248201527f4f6e6c792061646d696e206f72206d6f6e69746f7220726f6c65000000000000604482015290519081900360640190fd5b610f9082612c3f565b61153a612931565b61157d576040805162461bcd60e51b815260206004820152600f60248201526e4f6e6c792061646d696e20726f6c6560881b604482015290519081900360640190fd5b60015461158a90826128c2565b600181905560065410156115e5576040805162461bcd60e51b815260206004820152601c60248201527f4d696e74696e6720776f756c6420657863656564207468652063617000000000604482015290519081900360640190fd5b604080516340c10f1960e01b81523060048201526024810183905290516001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916340c10f1991604480830192600092919082900301818387803b15801561110557600080fd5b600081565b60045481565b80600b548111156116b957611671612931565b6116b4576040805162461bcd60e51b815260206004820152600f60248201526e4f6e6c792061646d696e20726f6c6560881b604482015290519081900360640190fd5b611742565b6116e37f408a36151f841709116a4e8aca4e0202874f7f54687dcb863b1ea4672dc9d8cf33611417565b806116f157506116f1612931565b611742576040805162461bcd60e51b815260206004820152601b60248201527f4f6e6c792061646d696e206f7220617070726f76657220726f6c650000000000604482015290519081900360640190fd5b610f9082612c88565b7f000000000000000000000000000000000000000000000000000000000000000090565b611777612cc3565b60025481906001600160401b03808316600160401b90920416146117cc5760405162461bcd60e51b8152600401808060200182810382526025815260200180612fb56025913960400191505060405180910390fd5b82600454811115611824576040805162461bcd60e51b815260206004820152601d60248201527f416d6f756e7420657863656564732073776170206d6178206c696d6974000000604482015290519081900360640190fd5b61183c600080516020612f6583398151915233611417565b611881576040805162461bcd60e51b81526020600482015260116024820152704f6e6c792072656c6179657220726f6c6560781b604482015290519081900360640190fd5b60025486906001600160401b03908116908216106118d8576040805162461bcd60e51b815260206004820152600f60248201526e125b9d985b1a59081cddd85c081a59608a1b604482015290519081900360640190fd5b6001600160401b03811660009081526003602052604090205415611943576040805162461bcd60e51b815260206004820152601c60248201527f526566756e642077617320616c72656164792070726f63657373656400000000604482015290519081900360640190fd5b61194c85612d16565b60408051808201909152601e8152600080516020612ef4833981519152602082015260015461197c918790612b03565b6001819055507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a9059cbb87876040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b1580156119f957600080fd5b505af1158015611a0d573d6000803e3d6000fd5b505050506040513d6020811015611a2357600080fd5b5050604080518681526000602082015281516001600160a01b038916926001600160401b038b16927fe2b59a5b3c140b37f71ce7ebcccf0e66eae13749eb259d19f25efbf16d5453e7929081900390910190a35050506001600160401b039093166000908152600360205260409020555050565b600254600160401b90046001600160401b031681565b60055481565b60045490565b611ac1612931565b611b04576040805162461bcd60e51b815260206004820152600f60248201526e4f6e6c792061646d696e20726f6c6560881b604482015290519081900360640190fd5b600154611b1190826128c2565b60018190556006541015611b6c576040805162461bcd60e51b815260206004820152601c60248201527f4465706f73697420776f756c6420657863656564207468652063617000000000604482015290519081900360640190fd5b604080516323b872dd60e01b81523360048201523060248201526044810183905290516001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916323b872dd9160648083019260209291908290030181600087803b158015611be157600080fd5b505af1158015611bf5573d6000803e3d6000fd5b505050506040513d6020811015611c0b57600080fd5b505060408051828152905133917fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c919081900360200190a250565b600b5490565b600a5481565b611c5a612cc3565b611c72600080516020612f6583398151915233611417565b611cb7576040805162461bcd60e51b81526020600482015260116024820152704f6e6c792072656c6179657220726f6c6560781b604482015290519081900360640190fd5b600280546001600160401b03600160401b8083048216600101821681026fffffffffffffffff0000000000000000199093169290921792839055604080519290930416815290517f3055c09aad241a3330c7a6f5c8849f96e5e6d093010859f0f2f07ebc2e5b69c29181900360200190a1565b600081815260208190526040812061141190612d43565b600254600160401b90046001600160401b031690565b60075490565b600082815260208190526040902060020154611d7b90610f46612a2d565b610ff15760405162461bcd60e51b8152600401808060200182810382526030815260200180612f856030913960400191505060405180910390fd5b4360085411611e07576040805162461bcd60e51b815260206004820152601860248201527710dbdb9d1c9858dd081a185cc81899595b881c185d5cd95960421b604482015290519081900360640190fd5b611e0f612cc3565b82600554811015611e67576040805162461bcd60e51b815260206004820152601960248201527f416d6f756e742062656c6c6f77206c6f776572206c696d697400000000000000604482015290519081900360640190fd5b600454811115611ebe576040805162461bcd60e51b815260206004820152601a60248201527f416d6f756e742065786365656473207570706572206c696d6974000000000000604482015290519081900360640190fd5b600154611ecb90856128c2565b60018190556006541015611f1e576040805162461bcd60e51b815260206004820152601560248201527405377617020776f756c64206578636565642063617605c1b604482015290519081900360640190fd5b604080516323b872dd60e01b81523360048201523060248201526044810186905290516001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916323b872dd9160648083019260209291908290030181600087803b158015611f9357600080fd5b505af1158015611fa7573d6000803e3d6000fd5b505050506040513d6020811015611fbd57600080fd5b5050604051839083908083838082843760408051939091018390038320600254602085018c905282855291840189905295503394506001600160401b031692507f06f4f6cf6aa9ba7f896b8495cf97857c3ee1d4a4f4a8081ae436f0ec6f9bdff09188915087908a908060608101858580828437600083820152604051601f909101601f1916909201829003965090945050505050a45050600280546001600160401b038082166001011667ffffffffffffffff199091161790555050565b612084612cc3565b60025481906001600160401b03808316600160401b90920416146120d95760405162461bcd60e51b8152600401808060200182810382526025815260200180612fb56025913960400191505060405180910390fd5b82600454811115612131576040805162461bcd60e51b815260206004820152601d60248201527f416d6f756e7420657863656564732073776170206d6178206c696d6974000000604482015290519081900360640190fd5b612149600080516020612f6583398151915233611417565b61218e576040805162461bcd60e51b81526020600482015260116024820152704f6e6c792072656c6179657220726f6c6560781b604482015290519081900360640190fd5b61219784612d16565b60408051808201909152601e8152600080516020612ef483398151915260208201526001546121c7918690612b03565b600155600754841115612304576000600754850390507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a9059cbb8a836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b15801561225457600080fd5b505af1158015612268573d6000803e3d6000fd5b505050506040513d602081101561227e57600080fd5b5050604051889088908083838082843780830192505050925050506040518091039020896001600160a01b03168b6001600160401b03167f06a0c97bc4d3067ebdf8f6724e5cb4f864bc8adbcca17c7beb5a2218f66b80c5898560075460405180848152602001838152602001828152602001935050505060405180910390a450612376565b868660405180838380828437604080519390910183900383208b8452600060208501528382018b905290519095506001600160a01b038e1694506001600160401b038f1693507f06a0c97bc4d3067ebdf8f6724e5cb4f864bc8adbcca17c7beb5a2218f66b80c5928190036060019150a45b505050505050505050565b612389612cc3565b60025481906001600160401b03808316600160401b90920416146123de5760405162461bcd60e51b8152600401808060200182810382526025815260200180612fb56025913960400191505060405180910390fd5b82600454811115612436576040805162461bcd60e51b815260206004820152601d60248201527f416d6f756e7420657863656564732073776170206d6178206c696d6974000000604482015290519081900360640190fd5b61244e600080516020612f6583398151915233611417565b612493576040805162461bcd60e51b81526020600482015260116024820152704f6e6c792072656c6179657220726f6c6560781b604482015290519081900360640190fd5b60025486906001600160401b03908116908216106124ea576040805162461bcd60e51b815260206004820152600f60248201526e125b9d985b1a59081cddd85c081a59608a1b604482015290519081900360640190fd5b6001600160401b03811660009081526003602052604090205415612555576040805162461bcd60e51b815260206004820152601c60248201527f526566756e642077617320616c72656164792070726f63657373656400000000604482015290519081900360640190fd5b61255e85612d16565b60408051808201909152601e8152600080516020612ef4833981519152602082015260015461258e918790612b03565b6001556007548511156126a0576000600754860390507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a9059cbb88836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b15801561261b57600080fd5b505af115801561262f573d6000803e3d6000fd5b505050506040513d602081101561264557600080fd5b505060075460408051838152602081019290925280516001600160a01b038a16926001600160401b038c16927fe2b59a5b3c140b37f71ce7ebcccf0e66eae13749eb259d19f25efbf16d5453e792918290030190a3506126f5565b856001600160a01b0316876001600160401b03167fe2b59a5b3c140b37f71ce7ebcccf0e66eae13749eb259d19f25efbf16d5453e7600088604051808381526020018281526020019250505060405180910390a35b5050506001600160401b039093166000908152600360205260409020555050565b61271e612931565b612761576040805162461bcd60e51b815260206004820152600f60248201526e4f6e6c792061646d696e20726f6c6560881b604482015290519081900360640190fd5b60408051808201909152601e8152600080516020612ef48339815191526020820152600154612791918390612b03565b6001819055507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a9059cbb83836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b15801561280e57600080fd5b505af1158015612822573d6000803e3d6000fd5b505050506040513d602081101561283857600080fd5b50506040805182815290516001600160a01b038416917f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364919081900360200190a25050565b6002546001600160401b031690565b60036020526000908152604090205481565b7f000000000000000000000000000000000000000000000000000000000000000081565b60008282018381101561140e576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b600061140e836001600160a01b038416612d4e565b60006113d68133611417565b600b8190556040805182815290517f950df6cb5f31f381373bc4a214f633cbde0e4647b9c04557b6520e66606539639181900360200190a150565b8181111580156129885750828211155b6129d9576040805162461bcd60e51b815260206004820152601a60248201527f6665653c3d6c6f7765723c3d75707065722076696f6c61746564000000000000604482015290519081900360640190fd5b600483905560058290556007819055604080518481526020810184905280820183905290517f1932f9003fd372221b3107915b4e8655453e4f1fcb732f1bf5722b30453505589181900360600190a1505050565b3390565b6000828152602081905260409020612a49908261291c565b15610f9057612a56612a2d565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000828152602081905260409020612ab29082612d98565b15610f9057612abf612a2d565b6001600160a01b0316816001600160a01b0316837ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a45050565b60008184841115612b925760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015612b57578181015183820152602001612b3f565b50505050905090810190601f168015612b845780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b438110612ba75780612ba9565b435b600881905560408051918252517f622d5f267374fd0bd7a5354e16ef5f3bf1f8088b9cb43458bd0151acde1b319f9181900360200190a150565b60068190556040805182815290517f7bf5040cdccd155f02a139290defa050a59f6bfa3caa1891ab18f792946306619181900360200190a150565b600061140e8383612dad565b600061140e836001600160a01b038416612e11565b438110612c4c5780612c4e565b435b600981905560408051918252517fee457b823d3dc66873ea908b7fc2cc098b6627b6dab3e6138f0fcde93ddefe5d9181900360200190a150565b600a8190556040805182815290517f8691c4b455b7b4124baf05028588dc526466e4ec5adcc9f0fa4db96412128bee9181900360200190a150565b4360095411612d14576040805162461bcd60e51b815260206004820152601860248201527710dbdb9d1c9858dd081a185cc81899595b881c185d5cd95960421b604482015290519081900360640190fd5b565b612d3d816040518060600160405280602e8152602001612ffa602e9139600a549190612b03565b600a5550565b600061141182612e29565b6000612d5a8383612e11565b612d9057508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155611411565b506000611411565b600061140e836001600160a01b038416612e2d565b81546000908210612def5760405162461bcd60e51b8152600401808060200182810382526022815260200180612f146022913960400191505060405180910390fd5b826000018281548110612dfe57fe5b9060005260206000200154905092915050565b60009081526001919091016020526040902054151590565b5490565b60008181526001830160205260408120548015612ee95783546000198083019190810190600090879083908110612e6057fe5b9060005260206000200154905080876000018481548110612e7d57fe5b600091825260208083209091019290925582815260018981019092526040902090840190558654879080612ead57fe5b60019003818190600052602060002001600090559055866001016000878152602001908152602001600020600090556001945050505050611411565b600091505061141156fe416d6f756e74206578636565647320636f6e747261637420737570706c790000456e756d657261626c655365743a20696e646578206f7574206f6620626f756e6473416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e2061646d696e20746f206772616e74e2b7fb3b832174769106daebcfd6d1970523240dda11281102db9363b83b0dc4416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e2061646d696e20746f207265766f6b65547820646f65736e27742062656c6f6e6720746f2063757272656e742072656c6179456f6e8227712ef8ad39d0f26f06731ef0df8665eb7ada7f41b1ee089adf3c238862a24f7065726174696f6e20657863656564732072657665727365206167677265676174656420616c6c6f77616e6365416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636520726f6c657320666f722073656c66a2646970667358221220d418959a2d91bd6748931badff2a95aedb7a2507954760d4f499a6a62a0766ae64736f6c63430007060033000000000000000000000000aea46a60368a7bd060eec7df8cba43b7ef41ad8500000000000000000000000000000000000000000018d0bf423c03d8de00000000000000000000000000000000000000000000000000d3c21bcecceda100000000000000000000000000000000000000000000000000d3c21bcecceda1000000000000000000000000000000000000000000000000027b46536c66c8e30000000000000000000000000000000000000000000000000000056bc75e2d63100000000000000000000000000000000000000000000000000002b5e3af16b188000000000000000000000000000000000fffffffffffffffffffffffffffffffffff00000000000000000000000000000fffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000250a74

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106103a45760003560e01c80639010d07c116101e9578063b6b55f251161010f578063d547741f116100ad578063f3fef3a31161007c578063f3fef3a314610941578063f4b01b121461096d578063f502e68b14610975578063fc0c546a1461099b576103a4565b8063d547741f146107b8578063deb4a6d2146107e4578063e5c119ca1461085b578063e779e30a146108fe576103a4565b8063c533c4c9116100e9578063c533c4c914610783578063ca15c8731461078b578063d162d6ee146107a8578063d4cadf68146107b0576103a4565b8063b6b55f2514610756578063b8f0afbd14610773578063c1abb6d01461077b576103a4565b8063a217fddf11610187578063ab2235a311610156578063ab2235a3146106fb578063af9105811461073e578063b3de9b5d14610746578063b525aca91461074e576103a4565b8063a217fddf146106c6578063a5e992bb146106ce578063a85553d5146106d6578063aa896137146106f3576103a4565b80639c7dc431116101c35780639c7dc4311461067c5780639ecb882d146106845780639f2c88bf1461068c578063a0712d68146106a9576103a4565b80639010d07c1461061157806391d1485414610634578063926d7d7f14610674576103a4565b806347786d37116102ce57806367defa461161026c5780637af9028a1161023b5780637af9028a146105d3578063815d736e146105db578063888ff935146105e357806388f5b9a5146105eb576103a4565b806367defa4614610597578063680eb1851461059f57806368eb5155146105a75780636c9c2faf146105cb576103a4565b806354cf2aeb116102a857806354cf2aeb14610577578063554d578d1461057f578063573c0b0e14610587578063618145251461058f576103a4565b806347786d371461054a5780634d9b47e2146105675780634fe9dc481461056f576103a4565b80632637a477116103465780634245962b116103155780634245962b1461050057806342966c6814610508578063432944c014610525578063466a78c014610542576103a4565b80632637a4771461047a5780632f2ff15d146104a0578063355274ea146104cc57806336568abe146104d4576103a4565b8063164e68de11610382578063164e68de146103ea578063189ae5f21461041057806321df0da714610439578063248a9ca31461045d576103a4565b8063047fc9aa146103a95780630a80943e146103c35780630f42e312146103cb575b600080fd5b6103b16109a3565b60408051918252519081900360200190f35b6103b16109a9565b6103e8600480360360208110156103e157600080fd5b50356109af565b005b6103e86004803603602081101561040057600080fd5b50356001600160a01b0316610a06565b6103e86004803603606081101561042657600080fd5b5080359060208101359060400135610be8565b610441610c43565b604080516001600160a01b039092168252519081900360200190f35b6103b16004803603602081101561047357600080fd5b5035610c67565b6103e86004803603602081101561049057600080fd5b50356001600160a01b0316610c7c565b6103e8600480360360408110156104b657600080fd5b50803590602001356001600160a01b0316610f28565b6103b1610f94565b6103e8600480360360408110156104ea57600080fd5b50803590602001356001600160a01b0316610f9a565b6103b1610ffb565b6103e86004803603602081101561051e57600080fd5b503561101f565b6103e86004803603602081101561053b57600080fd5b5035611120565b6103b16111f9565b6103e86004803603602081101561056057600080fd5b50356111ff565b6103b1611253565b6103b1611265565b6103b161126b565b6103b1611271565b6103b1611277565b6103b161129b565b6103b16112bf565b6103b16112c5565b6105af6112cb565b604080516001600160401b039092168252519081900360200190f35b6103b16112da565b6103b16112e0565b6103b16112f2565b6103b16112f8565b6103b16004803603602081101561060157600080fd5b50356001600160401b03166113db565b6104416004803603604081101561062757600080fd5b50803590602001356113f6565b6106606004803603604081101561064a57600080fd5b50803590602001356001600160a01b0316611417565b604080519115158252519081900360200190f35b6103b161142f565b6103b1611441565b6103b1611453565b6103e8600480360360208110156106a257600080fd5b5035611459565b6103e8600480360360208110156106bf57600080fd5b5035611532565b6103b1611653565b6103b1611658565b6103e8600480360360208110156106ec57600080fd5b503561165e565b6103b161174b565b6103e86004803603608081101561071157600080fd5b506001600160401b0381358116916001600160a01b0360208201351691604082013591606001351661176f565b6105af611a97565b6103b1611aad565b6103b1611ab3565b6103e86004803603602081101561076c57600080fd5b5035611ab9565b6103b1611c46565b6103b1611c4c565b6103e8611c52565b6103b1600480360360208110156107a157600080fd5b5035611d2a565b6105af611d41565b6103b1611d57565b6103e8600480360360408110156107ce57600080fd5b50803590602001356001600160a01b0316611d5d565b6103e8600480360360408110156107fa57600080fd5b8135919081019060408101602082013564010000000081111561081c57600080fd5b82018360208201111561082e57600080fd5b8035906020019184600183028401116401000000008311171561085057600080fd5b509092509050611db6565b6103e8600480360360c081101561087157600080fd5b6001600160401b03823516916001600160a01b03602082013516918101906060810160408201356401000000008111156108aa57600080fd5b8201836020820111156108bc57600080fd5b803590602001918460018302840111640100000000831117156108de57600080fd5b9193509150803590602081013590604001356001600160401b031661207c565b6103e86004803603608081101561091457600080fd5b506001600160401b0381358116916001600160a01b03602082013516916040820135916060013516612381565b6103e86004803603604081101561095757600080fd5b506001600160a01b038135169060200135612716565b6105af61287d565b6103b16004803603602081101561098b57600080fd5b50356001600160401b031661288c565b61044161289e565b60015481565b60095490565b6109b7612931565b6109fa576040805162461bcd60e51b815260206004820152600f60248201526e4f6e6c792061646d696e20726f6c6560881b604482015290519081900360640190fd5b610a038161293d565b50565b610a0e612931565b610a51576040805162461bcd60e51b815260206004820152600f60248201526e4f6e6c792061646d696e20726f6c6560881b604482015290519081900360640190fd5b6000306001600160a01b031663888ff9356040518163ffffffff1660e01b815260040160206040518083038186803b158015610a8c57600080fd5b505afa158015610aa0573d6000803e3d6000fd5b505050506040513d6020811015610ab657600080fd5b5051905080610b02576040805162461bcd60e51b81526020600482015260136024820152724e6f206665657320746f20776974686472617760681b604482015290519081900360640190fd5b7f000000000000000000000000aea46a60368a7bd060eec7df8cba43b7ef41ad856001600160a01b031663a9059cbb83836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015610b7957600080fd5b505af1158015610b8d573d6000803e3d6000fd5b505050506040513d6020811015610ba357600080fd5b50506040805182815290516001600160a01b038416917f6ba3b0bbad3c021cef53ad4903eaac995be31e67bf466ff8ed1cc610f3ecdda0919081900360200190a25050565b610bf0612931565b610c33576040805162461bcd60e51b815260206004820152600f60248201526e4f6e6c792061646d696e20726f6c6560881b604482015290519081900360640190fd5b610c3e838383612978565b505050565b7f000000000000000000000000aea46a60368a7bd060eec7df8cba43b7ef41ad8590565b60009081526020819052604090206002015490565b610c84612931565b610cc7576040805162461bcd60e51b815260206004820152600f60248201526e4f6e6c792061646d696e20726f6c6560881b604482015290519081900360640190fd5b437f0000000000000000000000000000000000000000000000000000000000de21b51115610d3c576040805162461bcd60e51b815260206004820152601b60248201527f4561726c696573742064656c657465206e6f7420726561636865640000000000604482015290519081900360640190fd5b6001600160a01b038116301415610d9a576040805162461bcd60e51b815260206004820152601e60248201527f7061792061646472203d3d207468697320636f6e747261637420616464720000604482015290519081900360640190fd5b60007f000000000000000000000000aea46a60368a7bd060eec7df8cba43b7ef41ad856001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610e0957600080fd5b505afa158015610e1d573d6000803e3d6000fd5b505050506040513d6020811015610e3357600080fd5b50516040805163a9059cbb60e01b81526001600160a01b0385811660048301526024820184905291519293507f000000000000000000000000aea46a60368a7bd060eec7df8cba43b7ef41ad859091169163a9059cbb916044808201926020929091908290030181600087803b158015610eac57600080fd5b505af1158015610ec0573d6000803e3d6000fd5b505050506040513d6020811015610ed657600080fd5b5050604080516001600160a01b03841681526020810183905281517f9a51f67330b0a91ec3a83d9766e4de555682ca44afb2de2200202ac4771f54dc929181900390910190a1816001600160a01b0316ff5b600082815260208190526040902060020154610f4b90610f46612a2d565b611417565b610f865760405162461bcd60e51b815260040180806020018281038252602f815260200180612f36602f913960400191505060405180910390fd5b610f908282612a31565b5050565b60065481565b610fa2612a2d565b6001600160a01b0316816001600160a01b031614610ff15760405162461bcd60e51b815260040180806020018281038252602f815260200180613028602f913960400191505060405180910390fd5b610f908282612a9a565b7f408a36151f841709116a4e8aca4e0202874f7f54687dcb863b1ea4672dc9d8cf81565b611027612931565b61106a576040805162461bcd60e51b815260206004820152600f60248201526e4f6e6c792061646d696e20726f6c6560881b604482015290519081900360640190fd5b60408051808201909152601e8152600080516020612ef4833981519152602082015260015461109a918390612b03565b60015560408051630852cd8d60e31b81526004810183905290516001600160a01b037f000000000000000000000000aea46a60368a7bd060eec7df8cba43b7ef41ad8516916342966c6891602480830192600092919082900301818387803b15801561110557600080fd5b505af1158015611119573d6000803e3d6000fd5b5050505050565b804381111561117957611131612931565b611174576040805162461bcd60e51b815260206004820152600f60248201526e4f6e6c792061646d696e20726f6c6560881b604482015290519081900360640190fd5b6111f0565b611191600080516020612fda83398151915233611417565b8061119f575061119f612931565b6111f0576040805162461bcd60e51b815260206004820152601a60248201527f4f6e6c792061646d696e206f72206d6f6e69746f7220726f6c65000000000000604482015290519081900360640190fd5b610f9082612b9a565b60055490565b611207612931565b61124a576040805162461bcd60e51b815260206004820152600f60248201526e4f6e6c792061646d696e20726f6c6560881b604482015290519081900360640190fd5b610a0381612be3565b600080516020612fda83398151915281565b60085490565b60075481565b60065490565b7f0000000000000000000000000000000000000000000000000000000000de21b581565b7f408a36151f841709116a4e8aca4e0202874f7f54687dcb863b1ea4672dc9d8cf90565b60095481565b600a5490565b6002546001600160401b031681565b60015490565b600080516020612f6583398151915290565b60085481565b60006113d66001546040518060400160405280601e81526020017f437269746963616c206572723a2062616c616e6365203c20737570706c7900008152507f000000000000000000000000aea46a60368a7bd060eec7df8cba43b7ef41ad856001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156113a357600080fd5b505afa1580156113b7573d6000803e3d6000fd5b505050506040513d60208110156113cd57600080fd5b50519190612b03565b905090565b6001600160401b031660009081526003602052604090205490565b600082815260208190526040812061140e9083612c1e565b90505b92915050565b600082815260208190526040812061140e9083612c2a565b600080516020612f6583398151915281565b600080516020612fda83398151915290565b600b5481565b80438111156114b25761146a612931565b6114ad576040805162461bcd60e51b815260206004820152600f60248201526e4f6e6c792061646d696e20726f6c6560881b604482015290519081900360640190fd5b611529565b6114ca600080516020612fda83398151915233611417565b806114d857506114d8612931565b611529576040805162461bcd60e51b815260206004820152601a60248201527f4f6e6c792061646d696e206f72206d6f6e69746f7220726f6c65000000000000604482015290519081900360640190fd5b610f9082612c3f565b61153a612931565b61157d576040805162461bcd60e51b815260206004820152600f60248201526e4f6e6c792061646d696e20726f6c6560881b604482015290519081900360640190fd5b60015461158a90826128c2565b600181905560065410156115e5576040805162461bcd60e51b815260206004820152601c60248201527f4d696e74696e6720776f756c6420657863656564207468652063617000000000604482015290519081900360640190fd5b604080516340c10f1960e01b81523060048201526024810183905290516001600160a01b037f000000000000000000000000aea46a60368a7bd060eec7df8cba43b7ef41ad8516916340c10f1991604480830192600092919082900301818387803b15801561110557600080fd5b600081565b60045481565b80600b548111156116b957611671612931565b6116b4576040805162461bcd60e51b815260206004820152600f60248201526e4f6e6c792061646d696e20726f6c6560881b604482015290519081900360640190fd5b611742565b6116e37f408a36151f841709116a4e8aca4e0202874f7f54687dcb863b1ea4672dc9d8cf33611417565b806116f157506116f1612931565b611742576040805162461bcd60e51b815260206004820152601b60248201527f4f6e6c792061646d696e206f7220617070726f76657220726f6c650000000000604482015290519081900360640190fd5b610f9082612c88565b7f0000000000000000000000000000000000000000000000000000000000de21b590565b611777612cc3565b60025481906001600160401b03808316600160401b90920416146117cc5760405162461bcd60e51b8152600401808060200182810382526025815260200180612fb56025913960400191505060405180910390fd5b82600454811115611824576040805162461bcd60e51b815260206004820152601d60248201527f416d6f756e7420657863656564732073776170206d6178206c696d6974000000604482015290519081900360640190fd5b61183c600080516020612f6583398151915233611417565b611881576040805162461bcd60e51b81526020600482015260116024820152704f6e6c792072656c6179657220726f6c6560781b604482015290519081900360640190fd5b60025486906001600160401b03908116908216106118d8576040805162461bcd60e51b815260206004820152600f60248201526e125b9d985b1a59081cddd85c081a59608a1b604482015290519081900360640190fd5b6001600160401b03811660009081526003602052604090205415611943576040805162461bcd60e51b815260206004820152601c60248201527f526566756e642077617320616c72656164792070726f63657373656400000000604482015290519081900360640190fd5b61194c85612d16565b60408051808201909152601e8152600080516020612ef4833981519152602082015260015461197c918790612b03565b6001819055507f000000000000000000000000aea46a60368a7bd060eec7df8cba43b7ef41ad856001600160a01b031663a9059cbb87876040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b1580156119f957600080fd5b505af1158015611a0d573d6000803e3d6000fd5b505050506040513d6020811015611a2357600080fd5b5050604080518681526000602082015281516001600160a01b038916926001600160401b038b16927fe2b59a5b3c140b37f71ce7ebcccf0e66eae13749eb259d19f25efbf16d5453e7929081900390910190a35050506001600160401b039093166000908152600360205260409020555050565b600254600160401b90046001600160401b031681565b60055481565b60045490565b611ac1612931565b611b04576040805162461bcd60e51b815260206004820152600f60248201526e4f6e6c792061646d696e20726f6c6560881b604482015290519081900360640190fd5b600154611b1190826128c2565b60018190556006541015611b6c576040805162461bcd60e51b815260206004820152601c60248201527f4465706f73697420776f756c6420657863656564207468652063617000000000604482015290519081900360640190fd5b604080516323b872dd60e01b81523360048201523060248201526044810183905290516001600160a01b037f000000000000000000000000aea46a60368a7bd060eec7df8cba43b7ef41ad8516916323b872dd9160648083019260209291908290030181600087803b158015611be157600080fd5b505af1158015611bf5573d6000803e3d6000fd5b505050506040513d6020811015611c0b57600080fd5b505060408051828152905133917fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c919081900360200190a250565b600b5490565b600a5481565b611c5a612cc3565b611c72600080516020612f6583398151915233611417565b611cb7576040805162461bcd60e51b81526020600482015260116024820152704f6e6c792072656c6179657220726f6c6560781b604482015290519081900360640190fd5b600280546001600160401b03600160401b8083048216600101821681026fffffffffffffffff0000000000000000199093169290921792839055604080519290930416815290517f3055c09aad241a3330c7a6f5c8849f96e5e6d093010859f0f2f07ebc2e5b69c29181900360200190a1565b600081815260208190526040812061141190612d43565b600254600160401b90046001600160401b031690565b60075490565b600082815260208190526040902060020154611d7b90610f46612a2d565b610ff15760405162461bcd60e51b8152600401808060200182810382526030815260200180612f856030913960400191505060405180910390fd5b4360085411611e07576040805162461bcd60e51b815260206004820152601860248201527710dbdb9d1c9858dd081a185cc81899595b881c185d5cd95960421b604482015290519081900360640190fd5b611e0f612cc3565b82600554811015611e67576040805162461bcd60e51b815260206004820152601960248201527f416d6f756e742062656c6c6f77206c6f776572206c696d697400000000000000604482015290519081900360640190fd5b600454811115611ebe576040805162461bcd60e51b815260206004820152601a60248201527f416d6f756e742065786365656473207570706572206c696d6974000000000000604482015290519081900360640190fd5b600154611ecb90856128c2565b60018190556006541015611f1e576040805162461bcd60e51b815260206004820152601560248201527405377617020776f756c64206578636565642063617605c1b604482015290519081900360640190fd5b604080516323b872dd60e01b81523360048201523060248201526044810186905290516001600160a01b037f000000000000000000000000aea46a60368a7bd060eec7df8cba43b7ef41ad8516916323b872dd9160648083019260209291908290030181600087803b158015611f9357600080fd5b505af1158015611fa7573d6000803e3d6000fd5b505050506040513d6020811015611fbd57600080fd5b5050604051839083908083838082843760408051939091018390038320600254602085018c905282855291840189905295503394506001600160401b031692507f06f4f6cf6aa9ba7f896b8495cf97857c3ee1d4a4f4a8081ae436f0ec6f9bdff09188915087908a908060608101858580828437600083820152604051601f909101601f1916909201829003965090945050505050a45050600280546001600160401b038082166001011667ffffffffffffffff199091161790555050565b612084612cc3565b60025481906001600160401b03808316600160401b90920416146120d95760405162461bcd60e51b8152600401808060200182810382526025815260200180612fb56025913960400191505060405180910390fd5b82600454811115612131576040805162461bcd60e51b815260206004820152601d60248201527f416d6f756e7420657863656564732073776170206d6178206c696d6974000000604482015290519081900360640190fd5b612149600080516020612f6583398151915233611417565b61218e576040805162461bcd60e51b81526020600482015260116024820152704f6e6c792072656c6179657220726f6c6560781b604482015290519081900360640190fd5b61219784612d16565b60408051808201909152601e8152600080516020612ef483398151915260208201526001546121c7918690612b03565b600155600754841115612304576000600754850390507f000000000000000000000000aea46a60368a7bd060eec7df8cba43b7ef41ad856001600160a01b031663a9059cbb8a836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b15801561225457600080fd5b505af1158015612268573d6000803e3d6000fd5b505050506040513d602081101561227e57600080fd5b5050604051889088908083838082843780830192505050925050506040518091039020896001600160a01b03168b6001600160401b03167f06a0c97bc4d3067ebdf8f6724e5cb4f864bc8adbcca17c7beb5a2218f66b80c5898560075460405180848152602001838152602001828152602001935050505060405180910390a450612376565b868660405180838380828437604080519390910183900383208b8452600060208501528382018b905290519095506001600160a01b038e1694506001600160401b038f1693507f06a0c97bc4d3067ebdf8f6724e5cb4f864bc8adbcca17c7beb5a2218f66b80c5928190036060019150a45b505050505050505050565b612389612cc3565b60025481906001600160401b03808316600160401b90920416146123de5760405162461bcd60e51b8152600401808060200182810382526025815260200180612fb56025913960400191505060405180910390fd5b82600454811115612436576040805162461bcd60e51b815260206004820152601d60248201527f416d6f756e7420657863656564732073776170206d6178206c696d6974000000604482015290519081900360640190fd5b61244e600080516020612f6583398151915233611417565b612493576040805162461bcd60e51b81526020600482015260116024820152704f6e6c792072656c6179657220726f6c6560781b604482015290519081900360640190fd5b60025486906001600160401b03908116908216106124ea576040805162461bcd60e51b815260206004820152600f60248201526e125b9d985b1a59081cddd85c081a59608a1b604482015290519081900360640190fd5b6001600160401b03811660009081526003602052604090205415612555576040805162461bcd60e51b815260206004820152601c60248201527f526566756e642077617320616c72656164792070726f63657373656400000000604482015290519081900360640190fd5b61255e85612d16565b60408051808201909152601e8152600080516020612ef4833981519152602082015260015461258e918790612b03565b6001556007548511156126a0576000600754860390507f000000000000000000000000aea46a60368a7bd060eec7df8cba43b7ef41ad856001600160a01b031663a9059cbb88836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b15801561261b57600080fd5b505af115801561262f573d6000803e3d6000fd5b505050506040513d602081101561264557600080fd5b505060075460408051838152602081019290925280516001600160a01b038a16926001600160401b038c16927fe2b59a5b3c140b37f71ce7ebcccf0e66eae13749eb259d19f25efbf16d5453e792918290030190a3506126f5565b856001600160a01b0316876001600160401b03167fe2b59a5b3c140b37f71ce7ebcccf0e66eae13749eb259d19f25efbf16d5453e7600088604051808381526020018281526020019250505060405180910390a35b5050506001600160401b039093166000908152600360205260409020555050565b61271e612931565b612761576040805162461bcd60e51b815260206004820152600f60248201526e4f6e6c792061646d696e20726f6c6560881b604482015290519081900360640190fd5b60408051808201909152601e8152600080516020612ef48339815191526020820152600154612791918390612b03565b6001819055507f000000000000000000000000aea46a60368a7bd060eec7df8cba43b7ef41ad856001600160a01b031663a9059cbb83836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b15801561280e57600080fd5b505af1158015612822573d6000803e3d6000fd5b505050506040513d602081101561283857600080fd5b50506040805182815290516001600160a01b038416917f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364919081900360200190a25050565b6002546001600160401b031690565b60036020526000908152604090205481565b7f000000000000000000000000aea46a60368a7bd060eec7df8cba43b7ef41ad8581565b60008282018381101561140e576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b600061140e836001600160a01b038416612d4e565b60006113d68133611417565b600b8190556040805182815290517f950df6cb5f31f381373bc4a214f633cbde0e4647b9c04557b6520e66606539639181900360200190a150565b8181111580156129885750828211155b6129d9576040805162461bcd60e51b815260206004820152601a60248201527f6665653c3d6c6f7765723c3d75707065722076696f6c61746564000000000000604482015290519081900360640190fd5b600483905560058290556007819055604080518481526020810184905280820183905290517f1932f9003fd372221b3107915b4e8655453e4f1fcb732f1bf5722b30453505589181900360600190a1505050565b3390565b6000828152602081905260409020612a49908261291c565b15610f9057612a56612a2d565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000828152602081905260409020612ab29082612d98565b15610f9057612abf612a2d565b6001600160a01b0316816001600160a01b0316837ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a45050565b60008184841115612b925760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015612b57578181015183820152602001612b3f565b50505050905090810190601f168015612b845780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b438110612ba75780612ba9565b435b600881905560408051918252517f622d5f267374fd0bd7a5354e16ef5f3bf1f8088b9cb43458bd0151acde1b319f9181900360200190a150565b60068190556040805182815290517f7bf5040cdccd155f02a139290defa050a59f6bfa3caa1891ab18f792946306619181900360200190a150565b600061140e8383612dad565b600061140e836001600160a01b038416612e11565b438110612c4c5780612c4e565b435b600981905560408051918252517fee457b823d3dc66873ea908b7fc2cc098b6627b6dab3e6138f0fcde93ddefe5d9181900360200190a150565b600a8190556040805182815290517f8691c4b455b7b4124baf05028588dc526466e4ec5adcc9f0fa4db96412128bee9181900360200190a150565b4360095411612d14576040805162461bcd60e51b815260206004820152601860248201527710dbdb9d1c9858dd081a185cc81899595b881c185d5cd95960421b604482015290519081900360640190fd5b565b612d3d816040518060600160405280602e8152602001612ffa602e9139600a549190612b03565b600a5550565b600061141182612e29565b6000612d5a8383612e11565b612d9057508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155611411565b506000611411565b600061140e836001600160a01b038416612e2d565b81546000908210612def5760405162461bcd60e51b8152600401808060200182810382526022815260200180612f146022913960400191505060405180910390fd5b826000018281548110612dfe57fe5b9060005260206000200154905092915050565b60009081526001919091016020526040902054151590565b5490565b60008181526001830160205260408120548015612ee95783546000198083019190810190600090879083908110612e6057fe5b9060005260206000200154905080876000018481548110612e7d57fe5b600091825260208083209091019290925582815260018981019092526040902090840190558654879080612ead57fe5b60019003818190600052602060002001600090559055866001016000878152602001908152602001600020600090556001945050505050611411565b600091505061141156fe416d6f756e74206578636565647320636f6e747261637420737570706c790000456e756d657261626c655365743a20696e646578206f7574206f6620626f756e6473416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e2061646d696e20746f206772616e74e2b7fb3b832174769106daebcfd6d1970523240dda11281102db9363b83b0dc4416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e2061646d696e20746f207265766f6b65547820646f65736e27742062656c6f6e6720746f2063757272656e742072656c6179456f6e8227712ef8ad39d0f26f06731ef0df8665eb7ada7f41b1ee089adf3c238862a24f7065726174696f6e20657863656564732072657665727365206167677265676174656420616c6c6f77616e6365416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636520726f6c657320666f722073656c66a2646970667358221220d418959a2d91bd6748931badff2a95aedb7a2507954760d4f499a6a62a0766ae64736f6c63430007060033

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

000000000000000000000000aea46a60368a7bd060eec7df8cba43b7ef41ad8500000000000000000000000000000000000000000018d0bf423c03d8de00000000000000000000000000000000000000000000000000d3c21bcecceda100000000000000000000000000000000000000000000000000d3c21bcecceda1000000000000000000000000000000000000000000000000027b46536c66c8e30000000000000000000000000000000000000000000000000000056bc75e2d63100000000000000000000000000000000000000000000000000002b5e3af16b188000000000000000000000000000000000fffffffffffffffffffffffffffffffffff00000000000000000000000000000fffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000250a74

-----Decoded View---------------
Arg [0] : ERC20Address (address): 0xaea46A60368A7bD060eec7DF8CBa43b7EF41Ad85
Arg [1] : cap_ (uint256): 30000000000000000000000000
Arg [2] : reverseAggregatedAllowance_ (uint256): 1000000000000000000000000
Arg [3] : reverseAggregatedAllowanceApproverCap_ (uint256): 1000000000000000000000000
Arg [4] : swapMax_ (uint256): 3000000000000000000000000
Arg [5] : swapMin_ (uint256): 100000000000000000000
Arg [6] : swapFee_ (uint256): 50000000000000000000
Arg [7] : pausedSinceBlockPublicApi_ (uint256): 1393796574908163946345982392040522594123775
Arg [8] : pausedSinceBlockRelayerApi_ (uint256): 1393796574908163946345982392040522594123775
Arg [9] : deleteProtectionPeriod_ (uint256): 2427508

-----Encoded View---------------
10 Constructor Arguments found :
Arg [0] : 000000000000000000000000aea46a60368a7bd060eec7df8cba43b7ef41ad85
Arg [1] : 00000000000000000000000000000000000000000018d0bf423c03d8de000000
Arg [2] : 00000000000000000000000000000000000000000000d3c21bcecceda1000000
Arg [3] : 00000000000000000000000000000000000000000000d3c21bcecceda1000000
Arg [4] : 000000000000000000000000000000000000000000027b46536c66c8e3000000
Arg [5] : 0000000000000000000000000000000000000000000000056bc75e2d63100000
Arg [6] : 000000000000000000000000000000000000000000000002b5e3af16b1880000
Arg [7] : 00000000000000000000000000000fffffffffffffffffffffffffffffffffff
Arg [8] : 00000000000000000000000000000fffffffffffffffffffffffffffffffffff
Arg [9] : 0000000000000000000000000000000000000000000000000000000000250a74


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.