Contract Address Details

0xe72f07e44ae8206AA31106a44A0D5a72C4780f9D

Contract Name
Exchange
Creator
0xe4bc3d–a61c7a at 0xbf453c–64be79
Balance
0 ADIL ( )
Tokens
Fetching tokens...
Transactions
753 Transactions
Transfers
1 Transfers
Gas Used
291,708,625
Last Balance Update
13004958
Contract name:
Exchange




Optimization enabled
true
Compiler version
v0.8.7+commit.e28d00a7




Optimization runs
200
EVM Version
default




Verified at
2023-04-13 03:36:02.316900Z

Constructor Arguments

000000000000000000000000f7cc8e287387f2b6d0141832c2f3914bb52651bf000000000000000000000000cd7cbbe5552461272adcff071ae1d37a8cf662cf000000000000000000000000ac93e157b32c0b0e83228278f359a63b633978e9

Arg [0] (address) : 0xf7cc8e287387f2b6d0141832c2f3914bb52651bf
Arg [1] (address) : 0xcd7cbbe5552461272adcff071ae1d37a8cf662cf
Arg [2] (address) : 0xac93e157b32c0b0e83228278f359a63b633978e9

              

Contract source code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

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

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


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

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

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

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

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

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}


abstract contract OwnerOperator is Ownable {
    mapping(address => bool) public operators;

    constructor() Ownable() {}

    modifier operatorOrOwner() {
        require(
            operators[msg.sender] || owner() == msg.sender,
            "OwnerOperator: !operator, !owner"
        );
        _;
    }

    modifier onlyOperator() {
        require(operators[msg.sender], "OwnerOperator: !operator");
        _;
    }

    function addOperator(address operator) external virtual onlyOwner {
        require(
            operator != address(0),
            "OwnerOperator: operator is the zero address"
        );
        operators[operator] = true;
    }

    function removeOperator(address operator) external virtual onlyOwner {
        require(
            operator != address(0),
            "OwnerOperator: operator is the zero address"
        );
        operators[operator] = false;
    }
}


interface IERC20 {
    function balanceOf(address account) external view returns (uint256);

    function transfer(address to, uint256 amount) external;

    function allowance(address owner, address spender)
        external
        view
        returns (uint256);

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external;
}


abstract contract VerifySignature {
    function getMessageHash(
        address _to,
        address _tokenIn,
        uint256 _amountIn,
        address _tokenOut,
        uint256 _amountOutMin,
        uint256 _timestamp,
        string memory _msgHash
    ) public pure returns (bytes32) {
        return
            keccak256(
                abi.encodePacked(
                    _to,
                    _tokenIn,
                    _amountIn,
                    _tokenOut,
                    _amountOutMin,
                    _timestamp,
                    _msgHash
                )
            );
    }

    function getEthSignedMessageHash(bytes32 _messageHash)
        public
        pure
        returns (bytes32)
    {
        return
            keccak256(
                abi.encodePacked(
                    "\x19Ethereum Signed Message:\n32",
                    _messageHash
                )
            );
    }

    function verifySignature(
        address _signer,
        address _to,
        address _tokenIn,
        uint256 _amountIn,
        address _tokenOut,
        uint256 _amountOutMin,
        uint256 _timestamp,
        string memory _msgHash,
        bytes memory _signature
    ) public pure returns (bool) {
        bytes32 messageHash = getMessageHash(
            _to,
            _tokenIn,
            _amountIn,
            _tokenOut,
            _amountOutMin,
            _timestamp,
            _msgHash
        );
        bytes32 ethSignedMessageHash = getEthSignedMessageHash(messageHash);

        return recoverSigner(ethSignedMessageHash, _signature) == _signer;
    }

    function recoverSigner(
        bytes32 _ethSignedMessageHash,
        bytes memory _signature
    ) public pure returns (address) {
        (bytes32 r, bytes32 s, uint8 v) = splitSignature(_signature);
        return ecrecover(_ethSignedMessageHash, v, r, s);
    }

    function splitSignature(bytes memory _sig)
        public
        pure
        returns (
            bytes32 r,
            bytes32 s,
            uint8 v
        )
    {
        require(_sig.length == 65, "invalid signature length");
        assembly {
            r := mload(add(_sig, 32))
            s := mload(add(_sig, 64))
            v := byte(0, mload(add(_sig, 96)))
        }
    }
}


// OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol)
// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.
/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
 * now has built in overflow checking.
 */
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) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            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) {
        unchecked {
            // 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) {
        unchecked {
            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) {
        unchecked {
            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) {
        return a + b;
    }

    /**
     * @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) {
        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) {
        return a * b;
    }

    /**
     * @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.
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        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) {
        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) {
        unchecked {
            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.
     *
     * 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) {
        unchecked {
            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) {
        unchecked {
            require(b > 0, errorMessage);
            return a % b;
        }
    }
}


// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
/**
 * @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.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
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;

            if (lastIndex != toDeleteIndex) {
                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] = valueIndex; // Replace lastValue's index to valueIndex
            }

            // 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) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

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

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

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

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

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

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}


contract Exchange is OwnerOperator, VerifySignature {
    using SafeMath for uint256;
    using EnumerableSet for EnumerableSet.Bytes32Set;
    using EnumerableSet for EnumerableSet.UintSet;

    event DepositExchange(TxnInfo txnInfo);
    event WithdrawExchange(TxnInfo txnInfo);

    enum TypeFee {
        PERCENT,
        FIXED
    }

    enum TxnStatus {
        Initialize,
        Completed
    }

    struct TxnInfo {
        bytes32 txnId;
        address sender;
        address tokenIn;
        uint256 amountIn;
        address tokenOut;
        uint256 amountOut;
        ExchangeInfo exchangeInfo;
        TxnStatus status;
        uint256 timestamp;
    }

    struct ExchangeInfo {
        uint256 rate;
        uint256 fee;
        TypeFee typeFee; // default is percent
        uint256 amountBonus;
        uint8 decimalsIn;
        uint8 decimalsOut;
        uint256 amountLimit;
    }

    struct ExchangeToken {
        address from;
        address to;
    }

    uint256 public timeExpiredSignature = 30 minutes;
    uint256 public limitTime = 24 hours;
    uint256 private constant PRICE_PRECISION = 1e9;
    string private constant MESSAGE_HASH = "EXCHANGE";

    address public signerAddress;
    address public adminReceiver;
    address public adminTransfer;

    // exchange token address => token address: ExchangeInfo
    mapping(address => mapping(address => ExchangeInfo)) internal exchangeInfos;

    // address user => [timestamp by date => amount]
    mapping(address => mapping(uint256 => uint256)) internal amountSwapByUsers;

    // address user => hash (token from - token to) => timestamps[]
    mapping(address => mapping(bytes32 => EnumerableSet.UintSet))
        internal timeSwapByUsers;

    // hashTxnId => TxnInfo
    mapping(bytes32 => TxnInfo) public txnInfos;

    EnumerableSet.Bytes32Set internal arrTxnInfos;

    constructor(
        address _signerAddress,
        address _adminReceiver,
        address _adminTransfer
    ) OwnerOperator() {
        signerAddress = _signerAddress;
        adminReceiver = _adminReceiver;
        adminTransfer = _adminTransfer;
    }

    /**
        @dev get exchange info
        @param from address
        @param to address
        @return ExchangeInfo
     */
    function getExchangeInfo(address from, address to)
        external
        view
        returns (ExchangeInfo memory)
    {
        return exchangeInfos[from][to];
    }

    /**
        @dev set exchange info
        @param from address
        @param to address
        @param exchangeInfo ExchangeInfo
     */
    function setExchangeInfo(
        address from,
        address to,
        ExchangeInfo memory exchangeInfo
    ) external onlyOperator {
        exchangeInfos[from][to] = exchangeInfo;
    }

    /**
        @dev set signer address
        @param _address address
     */
    function setSignerAddress(address _address) external onlyOperator {
        signerAddress = _address;
    }

    /**
        @dev set admin receiver
        @param _address address
     */
    function setAdminReceiver(address _address) external onlyOperator {
        adminReceiver = _address;
    }

    /**
        @dev set admin transfer
        @param _address address
     */
    function setAdminTransfer(address _address) external onlyOperator {
        adminTransfer = _address;
    }

    /**
        @dev set time expired signature
        @param time uint256
     */
    function setTimeExpiredSignature(uint256 time) external onlyOperator {
        timeExpiredSignature = time;
    }

    /**
        @dev get txn info
        @param txnId bytes32
     */
    function getTxnInfo(bytes32 txnId) external view returns (TxnInfo memory) {
        return txnInfos[txnId];
    }

    /**
        @dev get txn infos
        @return bytes32[]
     */
    function getTxnInfos() external view returns (bytes32[] memory) {
        return arrTxnInfos.values();
    }

    /**
        @dev hash transaction id
     */
    function hashTxnId(
        address sender,
        address tokenIn,
        uint256 amountIn,
        address tokenOut,
        uint256 amountOut,
        uint256 timestamp
    ) internal pure returns (bytes32) {
        return
            keccak256(
                abi.encodePacked(
                    sender,
                    tokenIn,
                    amountIn,
                    tokenOut,
                    amountOut,
                    timestamp
                )
            );
    }

    /**
        @dev remove txn info
        @param txnId bytes32
     */
    function removeTxnInfo(bytes32 txnId) external onlyOperator {
        require(
            txnInfos[txnId].txnId != bytes32(0),
            "Transaction doesn't exists"
        );
        delete txnInfos[txnId];
    }

    /**
        @dev parse decimals
        @param decimals uint8
        @return uint256
     */
    function parseDecimals(uint8 decimals) internal pure returns (uint256) {
        return 10**decimals;
    }

    /**
        @dev encode exchange
        @param from address
        @param to address
     */
    function encodeExchange(address from, address to)
        private
        pure
        returns (bytes32)
    {
        return keccak256(abi.encodePacked(from, to));
    }

    /**
        @dev calc total amount by user
        @param sender address
        @param bytesAddress bytes32
     */
    function calcTotalAmountByUser(address sender, bytes32 bytesAddress)
        internal
        returns (uint256)
    {
        uint256 totalAmount = 0;
        uint256[] memory timeSwapByUser = timeSwapByUsers[sender][bytesAddress]
            .values();

        // if timenow - timeSwap > 24 hours: then remove item
        // else: totalAmount += amount swap
        for (uint256 idx = 0; idx < timeSwapByUser.length; idx++) {
            if ((block.timestamp - timeSwapByUser[idx]) > limitTime) {
                delete amountSwapByUsers[sender][timeSwapByUser[idx]];
                timeSwapByUsers[sender][bytesAddress].remove(
                    timeSwapByUser[idx]
                );
            } else {
                totalAmount = totalAmount.add(
                    amountSwapByUsers[sender][timeSwapByUser[idx]]
                );
            }
        }
        return totalAmount;
    }

    /**
        @dev Get total amount by user
        @param user address
        @param tokenFrom address
        @param tokenTo address
        @return uint256
     */
    function getTotalAmountByUser(
        address user,
        address tokenFrom,
        address tokenTo
    ) external view returns (uint256) {
        uint256 totalAmount = 0;
        bytes32 bytesAddress = encodeExchange(tokenFrom, tokenTo);
        uint256[] memory timeSwapByUser = timeSwapByUsers[user][bytesAddress]
            .values();

        for (uint256 idx = 0; idx < timeSwapByUser.length; idx++) {
            if ((block.timestamp - timeSwapByUser[idx]) <= limitTime) {
                totalAmount = totalAmount.add(
                    amountSwapByUsers[user][timeSwapByUser[idx]]
                );
            }
        }
        return totalAmount;
    }

    /**
        @dev exchange token to token
     */
    function depositExchange(
        address tokenIn,
        uint256 amountIn,
        address tokenOut,
        uint256 amountOutMin,
        uint256 timestamp,
        bytes memory signature
    ) external payable {
        require(
            verifySignature(
                signerAddress,
                msg.sender,
                tokenIn,
                amountIn,
                tokenOut,
                amountOutMin,
                timestamp,
                MESSAGE_HASH,
                signature
            ),
            "Signature is invalid"
        );
        require(timestamp <= block.timestamp, "Timestamp is invalid");
        require(
            block.timestamp - timestamp <= timeExpiredSignature,
            "Signature is expired"
        );

        ExchangeInfo memory exchangeInfo = exchangeInfos[tokenIn][tokenOut];
        require(exchangeInfo.rate > 0, "Rate token can't be zero");

        uint256 amountFee = exchangeInfo.typeFee == TypeFee.FIXED
            ? exchangeInfo.fee
            : amountIn.mul(exchangeInfo.fee).div(PRICE_PRECISION);
        require(amountFee < amountIn, "Amount fee must be less than amount");
        uint256 amountInSubFee = amountIn.sub(amountFee);
        uint256 amountOutWithoutDecimals = (
            amountInSubFee.mul(exchangeInfo.rate)
        ).div(PRICE_PRECISION);
        uint256 amountOut = (
            amountOutWithoutDecimals.mul(
                parseDecimals(exchangeInfo.decimalsOut)
            )
        ).div(parseDecimals(exchangeInfo.decimalsIn));
        require(amountOut >= amountOutMin, "Slippage exceeded");

        if (tokenIn != address(0x0)) {
            IERC20 erc20Token = IERC20(tokenIn);
            require(
                erc20Token.allowance(msg.sender, address(this)) >= amountIn,
                "Allowance insufficient."
            );
            erc20Token.transferFrom(msg.sender, adminReceiver, amountIn);
        }

        bytes32 bytesAddress = encodeExchange(tokenIn, tokenOut);
        uint256 totalAmount = calcTotalAmountByUser(msg.sender, bytesAddress);
        require(
            totalAmount + amountIn <= exchangeInfo.amountLimit,
            "Amount exceeded limit"
        );
        if (
            !timeSwapByUsers[msg.sender][bytesAddress].contains(block.timestamp)
        ) {
            timeSwapByUsers[msg.sender][bytesAddress].add(block.timestamp);
        }
        amountSwapByUsers[msg.sender][block.timestamp] += amountIn;
        bytes32 txnId = hashTxnId(
            msg.sender,
            tokenIn,
            amountIn,
            tokenOut,
            amountOut,
            timestamp
        );
        require(
            txnInfos[txnId].txnId == bytes32(0),
            "Transaction is already exists"
        );
        txnInfos[txnId] = TxnInfo(
            txnId,
            msg.sender,
            tokenIn,
            amountIn,
            tokenOut,
            amountOut,
            exchangeInfo,
            TxnStatus.Initialize,
            timestamp
        );
        arrTxnInfos.add(txnId);
        emit DepositExchange(txnInfos[txnId]);
    }

    /**
        @dev withdraw exchange
        @param txnId bytes32
     */
    function withdrawExchange(
        bytes32 txnId,
        address sender,
        address tokenIn,
        uint256 amountIn,
        address tokenOut,
        uint256 amountOut,
        ExchangeInfo memory exchangeInfo,
        uint256 timestamp
    ) external payable onlyOperator {
        TxnInfo memory txnInfo = TxnInfo(
            txnId,
            sender,
            tokenIn,
            amountIn,
            tokenOut,
            amountOut,
            exchangeInfo,
            TxnStatus.Completed,
            timestamp
        );
        require(
            txnInfos[txnId].txnId == bytes32(0),
            "Transaction is already exists"
        );
        bytes32 newTxnId = hashTxnId(
            sender,
            tokenIn,
            amountIn,
            tokenOut,
            amountOut,
            timestamp
        );
        require(newTxnId == txnId, "TxnId is invalid");

        if (exchangeInfo.amountBonus > 0) {
            require(
                msg.value >= exchangeInfo.amountBonus,
                "Amount bonus is not enough"
            );
            payable(sender).transfer(exchangeInfo.amountBonus);
        }
        arrTxnInfos.add(txnId);
        txnInfos[txnId] = txnInfo;

        if (txnInfo.tokenOut != address(0x0)) {
            IERC20 erc20Token = IERC20(txnInfo.tokenOut);
            require(
                erc20Token.allowance(adminTransfer, address(this)) >= amountOut,
                "Insufficient allowance"
            );
            erc20Token.transferFrom(adminTransfer, txnInfo.sender, amountOut);
        } else {
            require(
                msg.value >= (amountOut + exchangeInfo.amountBonus),
                "Amount out is not enough"
            );
            // native token
            payable(txnInfo.sender).transfer(amountOut);
        }
        txnInfos[txnId].status = TxnStatus.Completed;
        emit WithdrawExchange(txnInfo);
    }

    /**
        @dev clear txn infos
        @param timestamp uint256
     */
    function clearTxnInfos(uint256 timestamp) external onlyOperator {
        uint256 txnLength = arrTxnInfos.length();
        uint256 countNotDel;
        for (uint256 idx = 0; idx < txnLength; idx++) {
            bytes32 txnInfo = arrTxnInfos.at(countNotDel);
            if (txnInfos[txnInfo].timestamp <= timestamp) {
                delete txnInfos[txnInfo];
                arrTxnInfos.remove(txnInfo);
            } else {
                countNotDel += 1;
            }
        }
    }

    /**
        @dev remove exchange info
        @param from addess
        @param to address
     */
    function removeExchangeInfo(address from, address to)
        external
        onlyOperator
    {
        delete exchangeInfos[from][to];
    }

    /**
        @dev withdraw native token
        @param amount uint256
     */
    function withdraw(uint256 amount) external onlyOwner {
        require(address(this).balance >= amount, "Insufficient balance");
        payable(msg.sender).transfer(amount);
    }

    /**
        @dev withdraw erc20 token
        @param tokenAddress address
        @param amount uint256
     */
    function withdrawToken(address tokenAddress, uint256 amount)
        external
        onlyOwner
    {
        IERC20 erc20Token = IERC20(tokenAddress);
        require(
            erc20Token.balanceOf(address(this)) >= amount,
            "Insufficient balance"
        );
        erc20Token.transfer(msg.sender, amount);
    }

    receive() external payable {}
}
        

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_signerAddress","internalType":"address"},{"type":"address","name":"_adminReceiver","internalType":"address"},{"type":"address","name":"_adminTransfer","internalType":"address"}]},{"type":"event","name":"DepositExchange","inputs":[{"type":"tuple","name":"txnInfo","internalType":"struct Exchange.TxnInfo","indexed":false,"components":[{"type":"bytes32","name":"txnId","internalType":"bytes32"},{"type":"address","name":"sender","internalType":"address"},{"type":"address","name":"tokenIn","internalType":"address"},{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"address","name":"tokenOut","internalType":"address"},{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"tuple","name":"exchangeInfo","internalType":"struct Exchange.ExchangeInfo","components":[{"type":"uint256","name":"rate","internalType":"uint256"},{"type":"uint256","name":"fee","internalType":"uint256"},{"type":"uint8","name":"typeFee","internalType":"enum Exchange.TypeFee"},{"type":"uint256","name":"amountBonus","internalType":"uint256"},{"type":"uint8","name":"decimalsIn","internalType":"uint8"},{"type":"uint8","name":"decimalsOut","internalType":"uint8"},{"type":"uint256","name":"amountLimit","internalType":"uint256"}]},{"type":"uint8","name":"status","internalType":"enum Exchange.TxnStatus"},{"type":"uint256","name":"timestamp","internalType":"uint256"}]}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"WithdrawExchange","inputs":[{"type":"tuple","name":"txnInfo","internalType":"struct Exchange.TxnInfo","indexed":false,"components":[{"type":"bytes32","name":"txnId","internalType":"bytes32"},{"type":"address","name":"sender","internalType":"address"},{"type":"address","name":"tokenIn","internalType":"address"},{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"address","name":"tokenOut","internalType":"address"},{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"tuple","name":"exchangeInfo","internalType":"struct Exchange.ExchangeInfo","components":[{"type":"uint256","name":"rate","internalType":"uint256"},{"type":"uint256","name":"fee","internalType":"uint256"},{"type":"uint8","name":"typeFee","internalType":"enum Exchange.TypeFee"},{"type":"uint256","name":"amountBonus","internalType":"uint256"},{"type":"uint8","name":"decimalsIn","internalType":"uint8"},{"type":"uint8","name":"decimalsOut","internalType":"uint8"},{"type":"uint256","name":"amountLimit","internalType":"uint256"}]},{"type":"uint8","name":"status","internalType":"enum Exchange.TxnStatus"},{"type":"uint256","name":"timestamp","internalType":"uint256"}]}],"anonymous":false},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"addOperator","inputs":[{"type":"address","name":"operator","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"adminReceiver","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"adminTransfer","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"clearTxnInfos","inputs":[{"type":"uint256","name":"timestamp","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"depositExchange","inputs":[{"type":"address","name":"tokenIn","internalType":"address"},{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"address","name":"tokenOut","internalType":"address"},{"type":"uint256","name":"amountOutMin","internalType":"uint256"},{"type":"uint256","name":"timestamp","internalType":"uint256"},{"type":"bytes","name":"signature","internalType":"bytes"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"getEthSignedMessageHash","inputs":[{"type":"bytes32","name":"_messageHash","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct Exchange.ExchangeInfo","components":[{"type":"uint256","name":"rate","internalType":"uint256"},{"type":"uint256","name":"fee","internalType":"uint256"},{"type":"uint8","name":"typeFee","internalType":"enum Exchange.TypeFee"},{"type":"uint256","name":"amountBonus","internalType":"uint256"},{"type":"uint8","name":"decimalsIn","internalType":"uint8"},{"type":"uint8","name":"decimalsOut","internalType":"uint8"},{"type":"uint256","name":"amountLimit","internalType":"uint256"}]}],"name":"getExchangeInfo","inputs":[{"type":"address","name":"from","internalType":"address"},{"type":"address","name":"to","internalType":"address"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"getMessageHash","inputs":[{"type":"address","name":"_to","internalType":"address"},{"type":"address","name":"_tokenIn","internalType":"address"},{"type":"uint256","name":"_amountIn","internalType":"uint256"},{"type":"address","name":"_tokenOut","internalType":"address"},{"type":"uint256","name":"_amountOutMin","internalType":"uint256"},{"type":"uint256","name":"_timestamp","internalType":"uint256"},{"type":"string","name":"_msgHash","internalType":"string"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getTotalAmountByUser","inputs":[{"type":"address","name":"user","internalType":"address"},{"type":"address","name":"tokenFrom","internalType":"address"},{"type":"address","name":"tokenTo","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct Exchange.TxnInfo","components":[{"type":"bytes32","name":"txnId","internalType":"bytes32"},{"type":"address","name":"sender","internalType":"address"},{"type":"address","name":"tokenIn","internalType":"address"},{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"address","name":"tokenOut","internalType":"address"},{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"tuple","name":"exchangeInfo","internalType":"struct Exchange.ExchangeInfo","components":[{"type":"uint256","name":"rate","internalType":"uint256"},{"type":"uint256","name":"fee","internalType":"uint256"},{"type":"uint8","name":"typeFee","internalType":"enum Exchange.TypeFee"},{"type":"uint256","name":"amountBonus","internalType":"uint256"},{"type":"uint8","name":"decimalsIn","internalType":"uint8"},{"type":"uint8","name":"decimalsOut","internalType":"uint8"},{"type":"uint256","name":"amountLimit","internalType":"uint256"}]},{"type":"uint8","name":"status","internalType":"enum Exchange.TxnStatus"},{"type":"uint256","name":"timestamp","internalType":"uint256"}]}],"name":"getTxnInfo","inputs":[{"type":"bytes32","name":"txnId","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32[]","name":"","internalType":"bytes32[]"}],"name":"getTxnInfos","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"limitTime","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"operators","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"recoverSigner","inputs":[{"type":"bytes32","name":"_ethSignedMessageHash","internalType":"bytes32"},{"type":"bytes","name":"_signature","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"removeExchangeInfo","inputs":[{"type":"address","name":"from","internalType":"address"},{"type":"address","name":"to","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"removeOperator","inputs":[{"type":"address","name":"operator","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"removeTxnInfo","inputs":[{"type":"bytes32","name":"txnId","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setAdminReceiver","inputs":[{"type":"address","name":"_address","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setAdminTransfer","inputs":[{"type":"address","name":"_address","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setExchangeInfo","inputs":[{"type":"address","name":"from","internalType":"address"},{"type":"address","name":"to","internalType":"address"},{"type":"tuple","name":"exchangeInfo","internalType":"struct Exchange.ExchangeInfo","components":[{"type":"uint256","name":"rate","internalType":"uint256"},{"type":"uint256","name":"fee","internalType":"uint256"},{"type":"uint8","name":"typeFee","internalType":"enum Exchange.TypeFee"},{"type":"uint256","name":"amountBonus","internalType":"uint256"},{"type":"uint8","name":"decimalsIn","internalType":"uint8"},{"type":"uint8","name":"decimalsOut","internalType":"uint8"},{"type":"uint256","name":"amountLimit","internalType":"uint256"}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setSignerAddress","inputs":[{"type":"address","name":"_address","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setTimeExpiredSignature","inputs":[{"type":"uint256","name":"time","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"signerAddress","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"},{"type":"uint8","name":"v","internalType":"uint8"}],"name":"splitSignature","inputs":[{"type":"bytes","name":"_sig","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"timeExpiredSignature","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"txnId","internalType":"bytes32"},{"type":"address","name":"sender","internalType":"address"},{"type":"address","name":"tokenIn","internalType":"address"},{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"address","name":"tokenOut","internalType":"address"},{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"tuple","name":"exchangeInfo","internalType":"struct Exchange.ExchangeInfo","components":[{"type":"uint256","name":"rate","internalType":"uint256"},{"type":"uint256","name":"fee","internalType":"uint256"},{"type":"uint8","name":"typeFee","internalType":"enum Exchange.TypeFee"},{"type":"uint256","name":"amountBonus","internalType":"uint256"},{"type":"uint8","name":"decimalsIn","internalType":"uint8"},{"type":"uint8","name":"decimalsOut","internalType":"uint8"},{"type":"uint256","name":"amountLimit","internalType":"uint256"}]},{"type":"uint8","name":"status","internalType":"enum Exchange.TxnStatus"},{"type":"uint256","name":"timestamp","internalType":"uint256"}],"name":"txnInfos","inputs":[{"type":"bytes32","name":"","internalType":"bytes32"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"verifySignature","inputs":[{"type":"address","name":"_signer","internalType":"address"},{"type":"address","name":"_to","internalType":"address"},{"type":"address","name":"_tokenIn","internalType":"address"},{"type":"uint256","name":"_amountIn","internalType":"uint256"},{"type":"address","name":"_tokenOut","internalType":"address"},{"type":"uint256","name":"_amountOutMin","internalType":"uint256"},{"type":"uint256","name":"_timestamp","internalType":"uint256"},{"type":"string","name":"_msgHash","internalType":"string"},{"type":"bytes","name":"_signature","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdraw","inputs":[{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"withdrawExchange","inputs":[{"type":"bytes32","name":"txnId","internalType":"bytes32"},{"type":"address","name":"sender","internalType":"address"},{"type":"address","name":"tokenIn","internalType":"address"},{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"address","name":"tokenOut","internalType":"address"},{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"tuple","name":"exchangeInfo","internalType":"struct Exchange.ExchangeInfo","components":[{"type":"uint256","name":"rate","internalType":"uint256"},{"type":"uint256","name":"fee","internalType":"uint256"},{"type":"uint8","name":"typeFee","internalType":"enum Exchange.TypeFee"},{"type":"uint256","name":"amountBonus","internalType":"uint256"},{"type":"uint8","name":"decimalsIn","internalType":"uint8"},{"type":"uint8","name":"decimalsOut","internalType":"uint8"},{"type":"uint256","name":"amountLimit","internalType":"uint256"}]},{"type":"uint256","name":"timestamp","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdrawToken","inputs":[{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"receive","stateMutability":"payable"}]
            

Deployed ByteCode

0x6080604052600436106101f25760003560e01c80639666ee5c1161010d578063b165caba116100a0578063cc0b75571161006f578063cc0b7557146105ed578063d7b80cd71461060d578063dd032d341461062d578063f2fde38b14610643578063fa5408011461066357600080fd5b8063b165caba14610577578063b3b95e3014610597578063b82dfc8c146105ad578063c9de0556146105da57600080fd5b8063a7bb5803116100dc578063a7bb5803146104d9578063a87d9ffd14610517578063ac8a584a14610537578063af51462b1461055757600080fd5b80639666ee5c1461045957806397aba7f9146104795780639870d7fe146104995780639e281a98146104b957600080fd5b80635b7633d0116101855780638280f695116101545780638280f695146103db5780638afd119e146103fb5780638c64b3d81461041b5780638da5cb5b1461043b57600080fd5b80635b7633d0146103415780636d3ceff714610379578063715018a6146103995780637ceacbd1146103ae57600080fd5b806318732250116101c157806318732250146102b95780631b664395146102cc5780632e1a7d4d1461030157806350ed82bd1461032157600080fd5b80630414300c146101fe578063046dc166146102295780630b4e0a4d1461024b57806313e7c9d81461027957600080fd5b366101f957005b600080fd5b34801561020a57600080fd5b50610213610683565b6040516102209190612df1565b60405180910390f35b34801561023557600080fd5b506102496102443660046128bd565b610694565b005b34801561025757600080fd5b5061026b61026636600461290b565b6106ee565b604051908152602001610220565b34801561028557600080fd5b506102a96102943660046128bd565b60016020526000908152604090205460ff1681565b6040519015158152602001610220565b6102496102c7366004612b97565b6107df565b3480156102d857600080fd5b506102ec6102e7366004612b7e565b610d63565b60405161022099989796959493929190612e35565b34801561030d57600080fd5b5061024961031c366004612b7e565b610e42565b34801561032d57600080fd5b5061024961033c366004612b7e565b610ec2565b34801561034d57600080fd5b50600454610361906001600160a01b031681565b6040516001600160a01b039091168152602001610220565b34801561038557600080fd5b506102a961039436600461294e565b611000565b3480156103a557600080fd5b5061024961104f565b3480156103ba57600080fd5b506103ce6103c93660046128d8565b611063565b6040516102209190612f1b565b3480156103e757600080fd5b506102496103f63660046128d8565b61111b565b34801561040757600080fd5b50600654610361906001600160a01b031681565b34801561042757600080fd5b506102496104363660046128bd565b6111a3565b34801561044757600080fd5b506000546001600160a01b0316610361565b34801561046557600080fd5b50600554610361906001600160a01b031681565b34801561048557600080fd5b50610361610494366004612c14565b6111f4565b3480156104a557600080fd5b506102496104b43660046128bd565b611273565b3480156104c557600080fd5b506102496104d4366004612ada565b6112c8565b3480156104e557600080fd5b506104f96104f4366004612c5b565b6113f5565b60408051938452602084019290925260ff1690820152606001610220565b34801561052357600080fd5b5061026b610532366004612a4e565b611469565b34801561054357600080fd5b506102496105523660046128bd565b6114ab565b34801561056357600080fd5b50610249610572366004612b7e565b6114fa565b34801561058357600080fd5b50610249610592366004612b7e565b61152e565b3480156105a357600080fd5b5061026b60035481565b3480156105b957600080fd5b506105cd6105c8366004612b7e565b61164c565b6040516102209190612f29565b6102496105e8366004612b04565b61178a565b3480156105f957600080fd5b50610249610608366004612a12565b611f9b565b34801561061957600080fd5b506102496106283660046128bd565b612073565b34801561063957600080fd5b5061026b60025481565b34801561064f57600080fd5b5061024961065e3660046128bd565b6120c4565b34801561066f57600080fd5b5061026b61067e366004612b7e565b61213d565b606061068f600b612190565b905090565b3360009081526001602052604090205460ff166106cc5760405162461bcd60e51b81526004016106c390612e99565b60405180910390fd5b600480546001600160a01b0319166001600160a01b0392909216919091179055565b600080806106fc85856121a4565b6001600160a01b038716600090815260096020908152604080832084845290915281209192509061072c90612190565b905060005b81518110156107d35760035482828151811061074f5761074f613205565b6020026020010151426107629190613191565b116107c1576001600160a01b038816600090815260086020526040812083516107be929085908590811061079857610798613205565b6020026020010151815260200190815260200160002054856121ee90919063ffffffff16565b93505b806107cb816131a8565b915050610731565b50919695505050505050565b3360009081526001602052604090205460ff1661080e5760405162461bcd60e51b81526004016106c390612e99565b60006040518061012001604052808a8152602001896001600160a01b03168152602001886001600160a01b03168152602001878152602001866001600160a01b03168152602001858152602001848152602001600180811115610873576108736131d9565b8152602090810184905260008b8152600a9091526040902054909150156108dc5760405162461bcd60e51b815260206004820152601d60248201527f5472616e73616374696f6e20697320616c72656164792065786973747300000060448201526064016106c3565b60006108ec8989898989886121fa565b90508981146109305760405162461bcd60e51b815260206004820152601060248201526f151e1b9259081a5cc81a5b9d985b1a5960821b60448201526064016106c3565b6060840151156109c957836060015134101561098e5760405162461bcd60e51b815260206004820152601a60248201527f416d6f756e7420626f6e7573206973206e6f7420656e6f75676800000000000060448201526064016106c3565b60608401516040516001600160a01b038b169180156108fc02916000818181858888f193505050501580156109c7573d6000803e3d6000fd5b505b6109d4600b8b612260565b5060008a8152600a60209081526040918290208451815581850151600180830180546001600160a01b039384166001600160a01b031991821617909155858801516002850180549185169183169190911790556060880151600385015560808801516004850180549190941691161790915560a0860151600583015560c086015180516006840190815593810151600784015593840151600883018054889694959360ff19909116908381811115610a8e57610a8e6131d9565b021790555060608201516003820155608082015160048201805460a085015160ff9081166101000261ffff1990921693169290921791909117905560c09091015160059091015560e0820151600c8201805460ff191660018381811115610af757610af76131d9565b02179055506101009190910151600d9091015560808201516001600160a01b031615610c66576080820151600654604051636eb1769f60e11b81526001600160a01b039182166004820152306024820152879183169063dd62ed3e9060440160206040518083038186803b158015610b6e57600080fd5b505afa158015610b82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ba69190612c98565b1015610bed5760405162461bcd60e51b8152602060048201526016602482015275496e73756666696369656e7420616c6c6f77616e636560501b60448201526064016106c3565b60065460208401516040516323b872dd60e01b81526001600160a01b039283166004820152908216602482015260448101889052908216906323b872dd90606401600060405180830381600087803b158015610c4857600080fd5b505af1158015610c5c573d6000803e3d6000fd5b5050505050610d03565b6060840151610c75908661304a565b341015610cc45760405162461bcd60e51b815260206004820152601860248201527f416d6f756e74206f7574206973206e6f7420656e6f756768000000000000000060448201526064016106c3565b81602001516001600160a01b03166108fc869081150290604051600060405180830381858888f19350505050158015610d01573d6000803e3d6000fd5b505b60008a8152600a602052604090819020600c01805460ff19166001179055517f2face6f6ddd4d61e6ab4efb9b1d51572d8ff3423e5a7d167ba3bd94ef9b5c9a790610d4f908490612f29565b60405180910390a150505050505050505050565b600a6020908152600091825260409182902080546001808301546002840154600385015460048601546005870154895160e081018b52600689018054825260078a01549a82019a909a526008890154979a6001600160a01b039687169a9587169994989690931696919593949093909284019160ff90911690811115610deb57610deb6131d9565b6001811115610dfc57610dfc6131d9565b815260038201546020820152600482015460ff808216604084015261010090910481166060830152600590920154608090910152600c830154600d909301549192169089565b610e4a61226c565b80471015610e915760405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e742062616c616e636560601b60448201526064016106c3565b604051339082156108fc029083906000818181858888f19350505050158015610ebe573d6000803e3d6000fd5b5050565b3360009081526001602052604090205460ff16610ef15760405162461bcd60e51b81526004016106c390612e99565b6000610efd600b6122c6565b90506000805b82811015610ffa576000610f18600b846122d0565b6000818152600a60205260409020600d01549091508510610fd9576000818152600a6020819052604082208281556001810180546001600160a01b03199081169091556002820180548216905560038201849055600482018054909116905560058101839055600681018390556007810183905560088101805460ff1990811690915560098201849055918101805461ffff19169055600b808201849055600c82018054909316909255600d0191909155610fd390826122dc565b50610fe7565b610fe460018461304a565b92505b5080610ff2816131a8565b915050610f03565b50505050565b6000806110128a8a8a8a8a8a8a611469565b9050600061101f8261213d565b90508b6001600160a01b031661103582866111f4565b6001600160a01b0316149c9b505050505050505050505050565b61105761226c565b61106160006122e8565b565b61106b6126c3565b6001600160a01b038084166000908152600760209081526040808320938616835292815290829020825160e08101845281548152600180830154938201939093526002820154909391929184019160ff909116908111156110ce576110ce6131d9565b60018111156110df576110df6131d9565b815260038201546020820152600482015460ff808216604084015261010090910416606082015260059091015460809091015290505b92915050565b3360009081526001602052604090205460ff1661114a5760405162461bcd60e51b81526004016106c390612e99565b6001600160a01b039182166000908152600760209081526040808320939094168252919091529081208181556001810182905560028101805460ff191690556003810182905560048101805461ffff1916905560050155565b3360009081526001602052604090205460ff166111d25760405162461bcd60e51b81526004016106c390612e99565b600580546001600160a01b0319166001600160a01b0392909216919091179055565b600080600080611203856113f5565b6040805160008152602081018083528b905260ff8316918101919091526060810184905260808101839052929550909350915060019060a0016020604051602081039080840390855afa15801561125e573d6000803e3d6000fd5b5050604051601f190151979650505050505050565b61127b61226c565b6001600160a01b0381166112a15760405162461bcd60e51b81526004016106c390612ed0565b6001600160a01b03166000908152600160208190526040909120805460ff19169091179055565b6112d061226c565b6040516370a0823160e01b8152306004820152829082906001600160a01b038316906370a082319060240160206040518083038186803b15801561131357600080fd5b505afa158015611327573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061134b9190612c98565b10156113905760405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e742062616c616e636560601b60448201526064016106c3565b60405163a9059cbb60e01b8152336004820152602481018390526001600160a01b0382169063a9059cbb90604401600060405180830381600087803b1580156113d857600080fd5b505af11580156113ec573d6000803e3d6000fd5b50505050505050565b6000806000835160411461144b5760405162461bcd60e51b815260206004820152601860248201527f696e76616c6964207369676e6174757265206c656e677468000000000000000060448201526064016106c3565b50505060208101516040820151606090920151909260009190911a90565b6000878787878787876040516020016114889796959493929190612d68565b604051602081830303815290604052805190602001209050979650505050505050565b6114b361226c565b6001600160a01b0381166114d95760405162461bcd60e51b81526004016106c390612ed0565b6001600160a01b03166000908152600160205260409020805460ff19169055565b3360009081526001602052604090205460ff166115295760405162461bcd60e51b81526004016106c390612e99565b600255565b3360009081526001602052604090205460ff1661155d5760405162461bcd60e51b81526004016106c390612e99565b6000818152600a60205260409020546115b85760405162461bcd60e51b815260206004820152601a60248201527f5472616e73616374696f6e20646f65736e27742065786973747300000000000060448201526064016106c3565b6000908152600a6020819052604082208281556001810180546001600160a01b03199081169091556002820180548216905560038201849055600482018054909116905560058101839055600681018390556007810183905560088101805460ff1990811690915560098201849055918101805461ffff19169055600b8101839055600c81018054909216909155600d0155565b611654612711565b6000828152600a6020908152604091829020825161012081018452815481526001808301546001600160a01b03908116838601526002840154811683870152600384015460608401526004840154166080830152600583015460a0830152845160e081018652600684018054825260078501549582019590955260088401549295939460c0870194919390929184019160ff909116908111156116f9576116f96131d9565b600181111561170a5761170a6131d9565b81526003820154602080830191909152600483015460ff808216604085015261010090910481166060840152600590930154608090920191909152918352600c8401549290910191166001811115611764576117646131d9565b6001811115611775576117756131d9565b8152602001600d820154815250509050919050565b6117d0600460009054906101000a90046001600160a01b03163388888888886040518060400160405280600881526020016745584348414e474560c01b81525089611000565b6118135760405162461bcd60e51b815260206004820152601460248201527314da59db985d1d5c99481a5cc81a5b9d985b1a5960621b60448201526064016106c3565b4282111561185a5760405162461bcd60e51b8152602060048201526014602482015273151a5b595cdd185b5c081a5cc81a5b9d985b1a5960621b60448201526064016106c3565b6002546118678342613191565b11156118ac5760405162461bcd60e51b815260206004820152601460248201527314da59db985d1d5c99481a5cc8195e1c1a5c995960621b60448201526064016106c3565b6001600160a01b0380871660009081526007602090815260408083209388168352928152828220835160e081018552815481526001808301549382019390935260028201549394909391929084019160ff169081111561190e5761190e6131d9565b600181111561191f5761191f6131d9565b815260038201546020820152600482015460ff808216604084015261010090910416606082015260059091015460809091015280519091506119a35760405162461bcd60e51b815260206004820152601860248201527f5261746520746f6b656e2063616e2774206265207a65726f000000000000000060448201526064016106c3565b60006001826040015160018111156119bd576119bd6131d9565b146119ec576119e7633b9aca006119e184602001518a61233890919063ffffffff16565b90612344565b6119f2565b81602001515b9050868110611a4f5760405162461bcd60e51b815260206004820152602360248201527f416d6f756e7420666565206d757374206265206c657373207468616e20616d6f6044820152621d5b9d60ea1b60648201526084016106c3565b6000611a5b8883612350565b90506000611a7e633b9aca006119e186600001518561233890919063ffffffff16565b90506000611aa9611a92866080015161235c565b6119e1611aa28860a0015161235c565b8590612338565b905087811015611aef5760405162461bcd60e51b815260206004820152601160248201527014db1a5c1c1859d948195e18d959591959607a1b60448201526064016106c3565b6001600160a01b038b1615611c3b57604051636eb1769f60e11b81523360048201523060248201528b908b906001600160a01b0383169063dd62ed3e9060440160206040518083038186803b158015611b4757600080fd5b505afa158015611b5b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b7f9190612c98565b1015611bcd5760405162461bcd60e51b815260206004820152601760248201527f416c6c6f77616e636520696e73756666696369656e742e00000000000000000060448201526064016106c3565b6005546040516323b872dd60e01b81523360048201526001600160a01b039182166024820152604481018d9052908216906323b872dd90606401600060405180830381600087803b158015611c2157600080fd5b505af1158015611c35573d6000803e3d6000fd5b50505050505b6000611c478c8b6121a4565b90506000611c553383612369565b60c0880151909150611c678d8361304a565b1115611cad5760405162461bcd60e51b8152602060048201526015602482015274105b5bdd5b9d08195e18d959591959081b1a5b5a5d605a1b60448201526064016106c3565b3360009081526009602090815260408083208584529091529020611cd190426124e3565b611cfb573360009081526009602090815260408083208584529091529020611cf99042612260565b505b336000908152600860209081526040808320428452909152812080548e9290611d2590849061304a565b9091555060009050611d3b338f8f8f888f6121fa565b6000818152600a602052604090205490915015611d9a5760405162461bcd60e51b815260206004820152601d60248201527f5472616e73616374696f6e20697320616c72656164792065786973747300000060448201526064016106c3565b604051806101200160405280828152602001336001600160a01b031681526020018f6001600160a01b031681526020018e81526020018d6001600160a01b0316815260200185815260200189815260200160006001811115611dfe57611dfe6131d9565b815260209081018c90526000838152600a82526040908190208351815583830151600180830180546001600160a01b039384166001600160a01b031991821617909155868501516002850180549185169183169190911790556060870151600385015560808701516004850180549190941691161790915560a0850151600583015560c0850151805160068401908155948101516007840155928301516008830180549395939192909160ff1916908381811115611ebe57611ebe6131d9565b021790555060608201516003820155608082015160048201805460a085015160ff9081166101000261ffff1990921693169290921791909117905560c09091015160059091015560e0820151600c8201805460ff191660018381811115611f2757611f276131d9565b02179055506101009190910151600d90910155611f45600b82612260565b506000818152600a60205260409081902090517fd13737c1afa57fcf8b649aa0e5c8f7a841c4f9301da7e263d256720f935902e691611f8391612fc9565b60405180910390a15050505050505050505050505050565b3360009081526001602052604090205460ff16611fca5760405162461bcd60e51b81526004016106c390612e99565b6001600160a01b038084166000908152600760209081526040808320938616835292815290829020835181559083015160018083019190915591830151600282018054859460ff19909116908381811115612027576120276131d9565b021790555060608201516003820155608082015160048201805460a085015160ff9081166101000261ffff1990921693169290921791909117905560c090910151600590910155505050565b3360009081526001602052604090205460ff166120a25760405162461bcd60e51b81526004016106c390612e99565b600680546001600160a01b0319166001600160a01b0392909216919091179055565b6120cc61226c565b6001600160a01b0381166121315760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016106c3565b61213a816122e8565b50565b6040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101829052600090605c01604051602081830303815290604052805190602001209050919050565b6060600061219d836124fb565b9392505050565b6040516bffffffffffffffffffffffff19606084811b8216602084015283901b16603482015260009060480160405160208183030381529060405280519060200120905092915050565b600061219d828461304a565b604080516bffffffffffffffffffffffff19606098891b811660208084019190915297891b8116603483015260488201969096529390961b9093166068830152607c820152609c808201929092528351808203909201825260bc01909252815191012090565b600061219d8383612557565b6000546001600160a01b031633146110615760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016106c3565b6000611115825490565b600061219d83836125a6565b600061219d83836125d0565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600061219d8284613172565b600061219d8284613062565b600061219d8284613191565b600061111582600a6130c7565b6001600160a01b038216600090815260096020908152604080832084845290915281208190819061239990612190565b905060005b81518110156124d9576003548282815181106123bc576123bc613205565b6020026020010151426123cf9190613191565b111561246d576001600160a01b0386166000908152600860205260408120835190919084908490811061240457612404613205565b602002602001015181526020019081526020016000206000905561246782828151811061243357612433613205565b6020908102919091018101516001600160a01b03891660009081526009835260408082208a835290935291909120906122dc565b506124c7565b6001600160a01b038616600090815260086020526040812083516124c4929085908590811061249e5761249e613205565b6020026020010151815260200190815260200160002054846121ee90919063ffffffff16565b92505b806124d1816131a8565b91505061239e565b5090949350505050565b6000818152600183016020526040812054151561219d565b60608160000180548060200260200160405190810160405280929190818152602001828054801561254b57602002820191906000526020600020905b815481526020019060010190808311612537575b50505050509050919050565b600081815260018301602052604081205461259e57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155611115565b506000611115565b60008260000182815481106125bd576125bd613205565b9060005260206000200154905092915050565b600081815260018301602052604081205480156126b95760006125f4600183613191565b855490915060009061260890600190613191565b905081811461266d57600086600001828154811061262857612628613205565b906000526020600020015490508087600001848154811061264b5761264b613205565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061267e5761267e6131ef565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050611115565b6000915050611115565b6040518060e001604052806000815260200160008152602001600060018111156126ef576126ef6131d9565b8152600060208201819052604082018190526060820181905260809091015290565b6040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915260c081016127506126c3565b815260200160008152602001600081525090565b80356001600160a01b038116811461277b57600080fd5b919050565b600082601f83011261279157600080fd5b813567ffffffffffffffff808211156127ac576127ac61321b565b604051601f8301601f19908116603f011681019082821181831017156127d4576127d461321b565b816040528381528660208588010111156127ed57600080fd5b836020870160208301376000602085830101528094505050505092915050565b600060e0828403121561281f57600080fd5b60405160e0810181811067ffffffffffffffff821117156128425761284261321b565b8060405250809150823581526020830135602082015260408301356002811061286a57600080fd5b604082015260608381013590820152612885608084016128ac565b608082015261289660a084016128ac565b60a082015260c083013560c08201525092915050565b803560ff8116811461277b57600080fd5b6000602082840312156128cf57600080fd5b61219d82612764565b600080604083850312156128eb57600080fd5b6128f483612764565b915061290260208401612764565b90509250929050565b60008060006060848603121561292057600080fd5b61292984612764565b925061293760208501612764565b915061294560408501612764565b90509250925092565b60008060008060008060008060006101208a8c03121561296d57600080fd5b6129768a612764565b985061298460208b01612764565b975061299260408b01612764565b965060608a013595506129a760808b01612764565b945060a08a0135935060c08a0135925060e08a013567ffffffffffffffff808211156129d257600080fd5b6129de8d838e01612780565b93506101008c01359150808211156129f557600080fd5b50612a028c828d01612780565b9150509295985092959850929598565b60008060006101208486031215612a2857600080fd5b612a3184612764565b9250612a3f60208501612764565b9150612945856040860161280d565b600080600080600080600060e0888a031215612a6957600080fd5b612a7288612764565b9650612a8060208901612764565b955060408801359450612a9560608901612764565b93506080880135925060a0880135915060c088013567ffffffffffffffff811115612abf57600080fd5b612acb8a828b01612780565b91505092959891949750929550565b60008060408385031215612aed57600080fd5b612af683612764565b946020939093013593505050565b60008060008060008060c08789031215612b1d57600080fd5b612b2687612764565b955060208701359450612b3b60408801612764565b9350606087013592506080870135915060a087013567ffffffffffffffff811115612b6557600080fd5b612b7189828a01612780565b9150509295509295509295565b600060208284031215612b9057600080fd5b5035919050565b6000806000806000806000806101c0898b031215612bb457600080fd5b88359750612bc460208a01612764565b9650612bd260408a01612764565b955060608901359450612be760808a01612764565b935060a08901359250612bfd8a60c08b0161280d565b91506101a089013590509295985092959890939650565b60008060408385031215612c2757600080fd5b82359150602083013567ffffffffffffffff811115612c4557600080fd5b612c5185828601612780565b9150509250929050565b600060208284031215612c6d57600080fd5b813567ffffffffffffffff811115612c8457600080fd5b612c9084828501612780565b949350505050565b600060208284031215612caa57600080fd5b5051919050565b612cba81613231565b9052565b80518252602081015160208301526040810151612cda81613231565b806040840152506060810151606083015260ff608082015116608083015260ff60a08201511660a083015260c081015160c08301525050565b805482526001810154602083015260ff600282015416612d3281613231565b604083015260038101546060830152600481015460ff808216608085015260089190911c1660a08301526005015460c090910152565b60006bffffffffffffffffffffffff19808a60601b168352808960601b166014840152876028840152808760601b1660488401525084605c83015283607c830152825160005b81811015612dcb576020818601810151609c868401015201612dae565b81811115612ddd576000609c83860101525b5091909101609c0198975050505050505050565b6020808252825182820181905260009190848201906040850190845b81811015612e2957835183529284019291840191600101612e0d565b50909695505050505050565b8981526001600160a01b0389811660208301528881166040830152606082018890528616608082015260a081018590526101e08101612e7760c0830186612cbe565b612e8084613231565b6101a08201939093526101c00152979650505050505050565b60208082526018908201527f4f776e65724f70657261746f723a20216f70657261746f720000000000000000604082015260600190565b6020808252602b908201527f4f776e65724f70657261746f723a206f70657261746f7220697320746865207a60408201526a65726f206164647265737360a81b606082015260800190565b60e081016111158284612cbe565b815181526020808301516001600160a01b0316908201526040808301516101e0830191612f60908401826001600160a01b03169052565b50606083015160608301526080830151612f8560808401826001600160a01b03169052565b5060a083015160a083015260c0830151612fa260c0840182612cbe565b5060e0830151612fb66101a0840182612cb1565b506101008301516101c083015292915050565b8154815260018201546001600160a01b039081166020830152600283015481166040830152600383015460608301526004830154166080820152600582015460a08201526101e0810161302260c0830160068501612d13565b600c83015460ff166130386101a0840182612cb1565b50600d8301546101c083015292915050565b6000821982111561305d5761305d6131c3565b500190565b60008261307f57634e487b7160e01b600052601260045260246000fd5b500490565b600181815b808511156130bf5781600019048211156130a5576130a56131c3565b808516156130b257918102915b93841c9390800290613089565b509250929050565b600061219d60ff8416836000826130e057506001611115565b816130ed57506000611115565b8160018114613103576002811461310d57613129565b6001915050611115565b60ff84111561311e5761311e6131c3565b50506001821b611115565b5060208310610133831016604e8410600b841016171561314c575081810a611115565b6131568383613084565b806000190482111561316a5761316a6131c3565b029392505050565b600081600019048311821515161561318c5761318c6131c3565b500290565b6000828210156131a3576131a36131c3565b500390565b60006000198214156131bc576131bc6131c3565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6002811061213a57634e487b7160e01b600052602160045260246000fdfea26469706673582212200ade8f7789355c7ed7bfb8346348855a9deef8fa56a8c0e90ea9dccb6b1d9e4664736f6c63430008070033