ETH Price: $3,617.20 (+5.51%)

Contract

0x6F400810b62df8E13fded51bE75fF5393eaa841F
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Withdraw237041512025-11-01 11:20:359 days ago1761996035IN
DXdao: Mesa
0 ETH0.000174492.08762852
Request Withdraw237041452025-11-01 11:19:239 days ago1761995963IN
DXdao: Mesa
0 ETH0.000144962.08962006
Withdraw236851232025-10-29 19:25:4711 days ago1761765947IN
DXdao: Mesa
0 ETH0.000056610.9857639
Request Withdraw236850662025-10-29 19:14:2311 days ago1761765263IN
DXdao: Mesa
0 ETH0.000116621.68024426
Withdraw236850612025-10-29 19:13:2311 days ago1761765203IN
DXdao: Mesa
0 ETH0.000100421.74861198
Withdraw236850522025-10-29 19:11:3511 days ago1761765095IN
DXdao: Mesa
0 ETH0.000077341.56464613
Withdraw236850462025-10-29 19:10:2311 days ago1761765023IN
DXdao: Mesa
0 ETH0.000124681.66306721
Withdraw236850372025-10-29 19:08:3511 days ago1761764915IN
DXdao: Mesa
0 ETH0.000077811.57940132
Request Withdraw236849192025-10-29 18:44:4711 days ago1761763487IN
DXdao: Mesa
0 ETH0.000246785.06513298
Request Withdraw236848722025-10-29 18:35:2311 days ago1761762923IN
DXdao: Mesa
0 ETH0.000047420.68323009
Request Withdraw236848572025-10-29 18:32:1111 days ago1761762731IN
DXdao: Mesa
0 ETH0.000037440.53995255
Request Withdraw236848482025-10-29 18:30:2311 days ago1761762623IN
DXdao: Mesa
0 ETH0.000034730.50050956
Withdraw236841302025-10-29 16:05:4711 days ago1761753947IN
DXdao: Mesa
0 ETH0.000048180.97834484
Request Withdraw236841012025-10-29 15:59:5911 days ago1761753599IN
DXdao: Mesa
0 ETH0.000052960.76318167
Withdraw236840922025-10-29 15:58:1111 days ago1761753491IN
DXdao: Mesa
0 ETH0.000037810.79499282
Withdraw236250832025-10-21 9:35:2320 days ago1761039323IN
DXdao: Mesa
0 ETH0.000007290.10064631
Request Withdraw236250652025-10-21 9:31:4720 days ago1761039107IN
DXdao: Mesa
0 ETH0.000007480.10788337
Submit Solution236250592025-10-21 9:30:3520 days ago1761039035IN
DXdao: Mesa
0 ETH0.000047580.11335912
Place Order236250452025-10-21 9:27:4720 days ago1761038867IN
DXdao: Mesa
0 ETH0.000010260.10908021
Place Order236250022025-10-21 9:19:1120 days ago1761038351IN
DXdao: Mesa
0 ETH0.000009920.10546989
Place Order236250022025-10-21 9:19:1120 days ago1761038351IN
DXdao: Mesa
0 ETH0.000009920.10546989
Deposit236245552025-10-21 7:48:3520 days ago1761032915IN
DXdao: Mesa
0 ETH0.000010160.10852048
Withdraw236244852025-10-21 7:34:3520 days ago1761032075IN
DXdao: Mesa
0 ETH0.000009730.12988679
Request Withdraw236244552025-10-21 7:28:3520 days ago1761031715IN
DXdao: Mesa
0 ETH0.000008160.11768826
Submit Solution236244552025-10-21 7:28:3520 days ago1761031715IN
DXdao: Mesa
0 ETH0.000047740.11768826
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Method Block
From
To
-105871582020-08-03 13:50:241924 days ago1596462624
DXdao: Mesa
0.1 ETH
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
BatchExchange

Compiler Version
v0.5.6+commit.b259423e

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
/**
 *Submitted for verification at Etherscan.io on 2020-01-23
*/

pragma solidity ^0.5.0;


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

library SafeMath {
    /**
     * @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) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     *
     * NOTE: This is a feature of the next version of OpenZeppelin Contracts.
     * @dev Get it via `npm install @openzeppelin/contracts@next`.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

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

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts 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) {
        return div(a, b, "SafeMath: division by zero");
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts 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.

     * NOTE: This is a feature of the next version of OpenZeppelin Contracts.
     * @dev Get it via `npm install @openzeppelin/contracts@next`.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts 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 mod(a, b, "SafeMath: modulo by zero");
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts with custom message 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.
     *
     * NOTE: This is a feature of the next version of OpenZeppelin Contracts.
     * @dev Get it via `npm install @openzeppelin/contracts@next`.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}

library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * This test is non-exhaustive, and there may be false-negatives: during the
     * execution of a contract's constructor, its address will be reported as
     * not containing 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.
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies in extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
        // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
        // for accounts without code, i.e. `keccak256('')`
        bytes32 codehash;
        bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
        // solhint-disable-next-line no-inline-assembly
        assembly { codehash := extcodehash(account) }
        return (codehash != 0x0 && codehash != accountHash);
    }

    /**
     * @dev Converts an `address` into `address payable`. Note that this is
     * simply a type cast: the actual underlying value is not changed.
     *
     * NOTE: This is a feature of the next version of OpenZeppelin Contracts.
     * @dev Get it via `npm install @openzeppelin/contracts@next`.
     */
    function toPayable(address account) internal pure returns (address payable) {
        return address(uint160(account));
    }
}

library SafeERC20 {
    using SafeMath for uint256;
    using Address for address;

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

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

    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        // solhint-disable-next-line max-line-length
        require((value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

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

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
        callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves.

        // A Solidity high level call has three parts:
        //  1. The target address is checked to verify it contains contract code
        //  2. The call itself is made, and success asserted
        //  3. The return value is decoded, which in turn checks the size of the returned data.
        // solhint-disable-next-line max-line-length
        require(address(token).isContract(), "SafeERC20: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = address(token).call(data);
        require(success, "SafeERC20: low-level call failed");

        if (returndata.length > 0) { // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

library Math {
    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow, so we distribute
        return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
    }
}

contract EpochTokenLocker {
    using SafeMath for uint256;

    /** @dev Number of seconds a batch is lasting*/
    uint32 public constant BATCH_TIME = 300;

    // User => Token => BalanceState
    mapping(address => mapping(address => BalanceState)) private balanceStates;

    // user => token => lastCreditBatchId
    mapping(address => mapping(address => uint32)) public lastCreditBatchId;

    struct BalanceState {
        uint256 balance;
        PendingFlux pendingDeposits; // deposits will be credited in any future epoch, i.e. currentStateIndex > batchId
        PendingFlux pendingWithdraws; // withdraws are allowed in any future epoch, i.e. currentStateIndex > batchId
    }

    struct PendingFlux {
        uint256 amount;
        uint32 batchId;
    }

    event Deposit(address indexed user, address indexed token, uint256 amount, uint32 batchId);

    event WithdrawRequest(address indexed user, address indexed token, uint256 amount, uint32 batchId);

    event Withdraw(address indexed user, address indexed token, uint256 amount);

    /** @dev credits user with deposit amount on next epoch (given by getCurrentBatchId)
      * @param token address of token to be deposited
      * @param amount number of token(s) to be credited to user's account
      *
      * Emits an {Deposit} event with relevent deposit information.
      *
      * Requirements:
      * - token transfer to contract is successfull
      */
    function deposit(address token, uint256 amount) public {
        updateDepositsBalance(msg.sender, token);
        SafeERC20.safeTransferFrom(IERC20(token), msg.sender, address(this), amount);
        // solhint-disable-next-line max-line-length
        balanceStates[msg.sender][token].pendingDeposits.amount = balanceStates[msg.sender][token].pendingDeposits.amount.add(
            amount
        );
        balanceStates[msg.sender][token].pendingDeposits.batchId = getCurrentBatchId();
        emit Deposit(msg.sender, token, amount, getCurrentBatchId());
    }

    /** @dev Signals and initiates user's intent to withdraw.
      * @param token address of token to be withdrawn
      * @param amount number of token(s) to be withdrawn
      *
      * Emits an {WithdrawRequest} event with relevent request information.
      */
    function requestWithdraw(address token, uint256 amount) public {
        requestFutureWithdraw(token, amount, getCurrentBatchId());
    }

    /** @dev Signals and initiates user's intent to withdraw.
      * @param token address of token to be withdrawn
      * @param amount number of token(s) to be withdrawn
      * @param batchId state index at which request is to be made.
      *
      * Emits an {WithdrawRequest} event with relevent request information.
      */
    function requestFutureWithdraw(address token, uint256 amount, uint32 batchId) public {
        // First process pendingWithdraw (if any), as otherwise balances might increase for currentBatchId - 1
        if (hasValidWithdrawRequest(msg.sender, token)) {
            withdraw(msg.sender, token);
        }
        require(batchId >= getCurrentBatchId(), "Request cannot be made in the past");
        balanceStates[msg.sender][token].pendingWithdraws = PendingFlux({amount: amount, batchId: batchId});
        emit WithdrawRequest(msg.sender, token, amount, batchId);
    }

    /** @dev Claims pending withdraw - can be called on behalf of others
      * @param token address of token to be withdrawn
      * @param user address of user who withdraw is being claimed.
      *
      * Emits an {Withdraw} event stating that `user` withdrew `amount` of `token`
      *
      * Requirements:
      * - withdraw was requested in previous epoch
      * - token was received from exchange in current auction batch
      */
    function withdraw(address user, address token) public {
        updateDepositsBalance(user, token); // withdrawn amount may have been deposited in previous epoch
        require(
            balanceStates[user][token].pendingWithdraws.batchId < getCurrentBatchId(),
            "withdraw was not registered previously"
        );
        require(
            lastCreditBatchId[user][token] < getCurrentBatchId(),
            "Withdraw not possible for token that is traded in the current auction"
        );
        uint256 amount = Math.min(balanceStates[user][token].balance, balanceStates[user][token].pendingWithdraws.amount);

        balanceStates[user][token].balance = balanceStates[user][token].balance.sub(amount);
        delete balanceStates[user][token].pendingWithdraws;

        SafeERC20.safeTransfer(IERC20(token), user, amount);
        emit Withdraw(user, token, amount);
    }
    /**
     * Public view functions
     */

    /** @dev getter function used to display pending deposit
      * @param user address of user
      * @param token address of ERC20 token
      * return amount and batchId of deposit's transfer if any (else 0)
      */
    function getPendingDeposit(address user, address token) public view returns (uint256, uint32) {
        PendingFlux memory pendingDeposit = balanceStates[user][token].pendingDeposits;
        return (pendingDeposit.amount, pendingDeposit.batchId);
    }

    /** @dev getter function used to display pending withdraw
      * @param user address of user
      * @param token address of ERC20 token
      * return amount and batchId when withdraw was requested if any (else 0)
      */
    function getPendingWithdraw(address user, address token) public view returns (uint256, uint32) {
        PendingFlux memory pendingWithdraw = balanceStates[user][token].pendingWithdraws;
        return (pendingWithdraw.amount, pendingWithdraw.batchId);
    }

    /** @dev getter function to determine current auction id.
      * return current batchId
      */
    function getCurrentBatchId() public view returns (uint32) {
        // solhint-disable-next-line not-rely-on-time
        return uint32(now / BATCH_TIME);
    }

    /** @dev used to determine how much time is left in a batch
      * return seconds remaining in current batch
      */
    function getSecondsRemainingInBatch() public view returns (uint256) {
        // solhint-disable-next-line not-rely-on-time
        return BATCH_TIME - (now % BATCH_TIME);
    }

    /** @dev fetches and returns user's balance
      * @param user address of user
      * @param token address of ERC20 token
      * return Current `token` balance of `user`'s account
      */
    function getBalance(address user, address token) public view returns (uint256) {
        uint256 balance = balanceStates[user][token].balance;
        if (balanceStates[user][token].pendingDeposits.batchId < getCurrentBatchId()) {
            balance = balance.add(balanceStates[user][token].pendingDeposits.amount);
        }
        if (balanceStates[user][token].pendingWithdraws.batchId < getCurrentBatchId()) {
            balance = balance.sub(Math.min(balanceStates[user][token].pendingWithdraws.amount, balance));
        }
        return balance;
    }

    /** @dev Used to determine if user has a valid pending withdraw request of specific token
      * @param user address of user
      * @param token address of ERC20 token
      * return true if `user` has valid withdraw request for `token`, otherwise false
      */
    function hasValidWithdrawRequest(address user, address token) public view returns (bool) {
        return
            balanceStates[user][token].pendingWithdraws.batchId < getCurrentBatchId() &&
            balanceStates[user][token].pendingWithdraws.batchId > 0;
    }

    /**
     * internal functions
     */
    /**
     * The following function should be used to update any balances within an epoch, which
     * will not be immediately final. E.g. the BatchExchange credits new balances to
     * the buyers in an auction, but as there are might be better solutions, the updates are
     * not final. In order to prevent withdraws from non-final updates, we disallow withdraws
     * by setting lastCreditBatchId to the current batchId and allow only withdraws in batches
     * with a higher batchId.
     */
    function addBalanceAndBlockWithdrawForThisBatch(address user, address token, uint256 amount) internal {
        if (hasValidWithdrawRequest(user, token)) {
            lastCreditBatchId[user][token] = getCurrentBatchId();
        }
        addBalance(user, token, amount);
    }

    function addBalance(address user, address token, uint256 amount) internal {
        updateDepositsBalance(user, token);
        balanceStates[user][token].balance = balanceStates[user][token].balance.add(amount);
    }

    /**
     * The following function should be used to subtract amounts from the current balances state.
     * For the substraction the current withdrawRequests are considered and they are effectively reducing
     * the available balance.
     */
    function subtractBalance(address user, address token, uint256 amount) internal {
        require(amount <= getBalance(user, token), "Amount exceeds user's balance.");
        subtractBalanceUnchecked(user, token, amount);
    }

    /**
     * The following function should be used to substract amounts from the current balance
     * state, if the pending withdrawRequests are not considered and should not effectively reduce
     * the available balance.
     * For example, the reversion of trades from a previous batch-solution do not
     * need to consider withdrawRequests. This is the case as withdraws are blocked for one
     * batch for accounts having credited funds in a previous submission.
     * PendingWithdraws must also be ignored since otherwise for the reversion of trades,
     * a solution reversion could be blocked: A bigger withdrawRequest could set the return value of
     * getBalance(user, token) to zero, although the user was just credited tokens in
     * the last submission. In this situation, during the unwinding of the previous orders,
     * the check `amount <= getBalance(user, token)` would fail and the reversion would be blocked.
     */
    function subtractBalanceUnchecked(address user, address token, uint256 amount) internal {
        updateDepositsBalance(user, token);
        balanceStates[user][token].balance = balanceStates[user][token].balance.sub(amount);
    }

    function updateDepositsBalance(address user, address token) private {
        uint256 batchId = balanceStates[user][token].pendingDeposits.batchId;
        if (batchId > 0 && batchId < getCurrentBatchId()) {
            // batchId > 0 is checked in order save an SSTORE in case there is no pending deposit
            balanceStates[user][token].balance = balanceStates[user][token].balance.add(
                balanceStates[user][token].pendingDeposits.amount
            );
            delete balanceStates[user][token].pendingDeposits;
        }
    }
}

library IdToAddressBiMap {
    struct Data {
        mapping(uint16 => address) idToAddress;
        mapping(address => uint16) addressToId;
    }

    function hasId(Data storage self, uint16 id) public view returns (bool) {
        return self.idToAddress[id + 1] != address(0);
    }

    function hasAddress(Data storage self, address addr) public view returns (bool) {
        return self.addressToId[addr] != 0;
    }

    function getAddressAt(Data storage self, uint16 id) public view returns (address) {
        require(hasId(self, id), "Must have ID to get Address");
        return self.idToAddress[id + 1];
    }

    function getId(Data storage self, address addr) public view returns (uint16) {
        require(hasAddress(self, addr), "Must have Address to get ID");
        return self.addressToId[addr] - 1;
    }

    function insert(Data storage self, uint16 id, address addr) public returns (bool) {
        require(addr != address(0), "Cannot insert zero address");
        require(id != uint16(-1), "Cannot insert max uint16");
        // Ensure bijectivity of the mappings
        if (self.addressToId[addr] != 0 || self.idToAddress[id + 1] != address(0)) {
            return false;
        }
        self.idToAddress[id + 1] = addr;
        self.addressToId[addr] = id + 1;
        return true;
    }

}

library IterableAppendOnlySet {
    struct Data {
        mapping(address => address) nextMap;
        address last;
    }

    function insert(Data storage self, address value) public returns (bool) {
        if (contains(self, value)) {
            return false;
        }
        self.nextMap[self.last] = value;
        self.last = value;
        return true;
    }

    function contains(Data storage self, address value) public view returns (bool) {
        require(value != address(0), "Inserting address(0) is not supported");
        return self.nextMap[value] != address(0) || (self.last == value);
    }

    function first(Data storage self) public view returns (address) {
        require(self.last != address(0), "Trying to get first from empty set");
        return self.nextMap[address(0)];
    }

    function next(Data storage self, address value) public view returns (address) {
        require(contains(self, value), "Trying to get next of non-existent element");
        require(value != self.last, "Trying to get next of last element");
        return self.nextMap[value];
    }

    function size(Data storage self) public view returns (uint256) {
        if (self.last == address(0)) {
            return 0;
        }
        uint256 count = 1;
        address current = first(self);
        while (current != self.last) {
            current = next(self, current);
            count++;
        }
        return count;
    }

    function atIndex(Data storage self, uint256 index) public view returns (address) {
        require(index < size(self), "requested index too large");
        address res = first(self);
        for (uint256 i = 0; i < index; i++) {
            res = next(self, res);
        }
        return res;
    }
}

library GnosisMath {
    /*
     *  Constants
     */
    // This is equal to 1 in our calculations
    uint public constant ONE = 0x10000000000000000;
    uint public constant LN2 = 0xb17217f7d1cf79ac;
    uint public constant LOG2_E = 0x171547652b82fe177;

    /*
     *  Public functions
     */
    /// @dev Returns natural exponential function value of given x
    /// @param x x
    /// @return e**x
    function exp(int x) public pure returns (uint) {
        // revert if x is > MAX_POWER, where
        // MAX_POWER = int(mp.floor(mp.log(mpf(2**256 - 1) / ONE) * ONE))
        require(x <= 2454971259878909886679);
        // return 0 if exp(x) is tiny, using
        // MIN_POWER = int(mp.floor(mp.log(mpf(1) / ONE) * ONE))
        if (x < -818323753292969962227) return 0;
        // Transform so that e^x -> 2^x
        x = x * int(ONE) / int(LN2);
        // 2^x = 2^whole(x) * 2^frac(x)
        //       ^^^^^^^^^^ is a bit shift
        // so Taylor expand on z = frac(x)
        int shift;
        uint z;
        if (x >= 0) {
            shift = x / int(ONE);
            z = uint(x % int(ONE));
        } else {
            shift = x / int(ONE) - 1;
            z = ONE - uint(-x % int(ONE));
        }
        // 2^x = 1 + (ln 2) x + (ln 2)^2/2! x^2 + ...
        //
        // Can generate the z coefficients using mpmath and the following lines
        // >>> from mpmath import mp
        // >>> mp.dps = 100
        // >>> ONE =  0x10000000000000000
        // >>> print('\n'.join(hex(int(mp.log(2)**i / mp.factorial(i) * ONE)) for i in range(1, 7)))
        // 0xb17217f7d1cf79ab
        // 0x3d7f7bff058b1d50
        // 0xe35846b82505fc5
        // 0x276556df749cee5
        // 0x5761ff9e299cc4
        // 0xa184897c363c3
        uint zpow = z;
        uint result = ONE;
        result += 0xb17217f7d1cf79ab * zpow / ONE;
        zpow = zpow * z / ONE;
        result += 0x3d7f7bff058b1d50 * zpow / ONE;
        zpow = zpow * z / ONE;
        result += 0xe35846b82505fc5 * zpow / ONE;
        zpow = zpow * z / ONE;
        result += 0x276556df749cee5 * zpow / ONE;
        zpow = zpow * z / ONE;
        result += 0x5761ff9e299cc4 * zpow / ONE;
        zpow = zpow * z / ONE;
        result += 0xa184897c363c3 * zpow / ONE;
        zpow = zpow * z / ONE;
        result += 0xffe5fe2c4586 * zpow / ONE;
        zpow = zpow * z / ONE;
        result += 0x162c0223a5c8 * zpow / ONE;
        zpow = zpow * z / ONE;
        result += 0x1b5253d395e * zpow / ONE;
        zpow = zpow * z / ONE;
        result += 0x1e4cf5158b * zpow / ONE;
        zpow = zpow * z / ONE;
        result += 0x1e8cac735 * zpow / ONE;
        zpow = zpow * z / ONE;
        result += 0x1c3bd650 * zpow / ONE;
        zpow = zpow * z / ONE;
        result += 0x1816193 * zpow / ONE;
        zpow = zpow * z / ONE;
        result += 0x131496 * zpow / ONE;
        zpow = zpow * z / ONE;
        result += 0xe1b7 * zpow / ONE;
        zpow = zpow * z / ONE;
        result += 0x9c7 * zpow / ONE;
        if (shift >= 0) {
            if (result >> (256 - shift) > 0) return (2 ** 256 - 1);
            return result << shift;
        } else return result >> (-shift);
    }

    /// @dev Returns natural logarithm value of given x
    /// @param x x
    /// @return ln(x)
    function ln(uint x) public pure returns (int) {
        require(x > 0);
        // binary search for floor(log2(x))
        int ilog2 = floorLog2(x);
        int z;
        if (ilog2 < 0) z = int(x << uint(-ilog2));
        else z = int(x >> uint(ilog2));
        // z = x * 2^-⌊log₂x⌋
        // so 1 <= z < 2
        // and ln z = ln x - ⌊log₂x⌋/log₂e
        // so just compute ln z using artanh series
        // and calculate ln x from that
        int term = (z - int(ONE)) * int(ONE) / (z + int(ONE));
        int halflnz = term;
        int termpow = term * term / int(ONE) * term / int(ONE);
        halflnz += termpow / 3;
        termpow = termpow * term / int(ONE) * term / int(ONE);
        halflnz += termpow / 5;
        termpow = termpow * term / int(ONE) * term / int(ONE);
        halflnz += termpow / 7;
        termpow = termpow * term / int(ONE) * term / int(ONE);
        halflnz += termpow / 9;
        termpow = termpow * term / int(ONE) * term / int(ONE);
        halflnz += termpow / 11;
        termpow = termpow * term / int(ONE) * term / int(ONE);
        halflnz += termpow / 13;
        termpow = termpow * term / int(ONE) * term / int(ONE);
        halflnz += termpow / 15;
        termpow = termpow * term / int(ONE) * term / int(ONE);
        halflnz += termpow / 17;
        termpow = termpow * term / int(ONE) * term / int(ONE);
        halflnz += termpow / 19;
        termpow = termpow * term / int(ONE) * term / int(ONE);
        halflnz += termpow / 21;
        termpow = termpow * term / int(ONE) * term / int(ONE);
        halflnz += termpow / 23;
        termpow = termpow * term / int(ONE) * term / int(ONE);
        halflnz += termpow / 25;
        return (ilog2 * int(ONE)) * int(ONE) / int(LOG2_E) + 2 * halflnz;
    }

    /// @dev Returns base 2 logarithm value of given x
    /// @param x x
    /// @return logarithmic value
    function floorLog2(uint x) public pure returns (int lo) {
        lo = -64;
        int hi = 193;
        // I use a shift here instead of / 2 because it floors instead of rounding towards 0
        int mid = (hi + lo) >> 1;
        while ((lo + 1) < hi) {
            if (mid < 0 && x << uint(-mid) < ONE || mid >= 0 && x >> uint(mid) < ONE) hi = mid;
            else lo = mid;
            mid = (hi + lo) >> 1;
        }
    }

    /// @dev Returns maximum of an array
    /// @param nums Numbers to look through
    /// @return Maximum number
    function max(int[] memory nums) public pure returns (int maxNum) {
        require(nums.length > 0);
        maxNum = -2 ** 255;
        for (uint i = 0; i < nums.length; i++) if (nums[i] > maxNum) maxNum = nums[i];
    }

    /// @dev Returns whether an add operation causes an overflow
    /// @param a First addend
    /// @param b Second addend
    /// @return Did no overflow occur?
    function safeToAdd(uint a, uint b) internal pure returns (bool) {
        return a + b >= a;
    }

    /// @dev Returns whether a subtraction operation causes an underflow
    /// @param a Minuend
    /// @param b Subtrahend
    /// @return Did no underflow occur?
    function safeToSub(uint a, uint b) internal pure returns (bool) {
        return a >= b;
    }

    /// @dev Returns whether a multiply operation causes an overflow
    /// @param a First factor
    /// @param b Second factor
    /// @return Did no overflow occur?
    function safeToMul(uint a, uint b) internal pure returns (bool) {
        return b == 0 || a * b / b == a;
    }

    /// @dev Returns sum if no overflow occurred
    /// @param a First addend
    /// @param b Second addend
    /// @return Sum
    function add(uint a, uint b) internal pure returns (uint) {
        require(safeToAdd(a, b));
        return a + b;
    }

    /// @dev Returns difference if no overflow occurred
    /// @param a Minuend
    /// @param b Subtrahend
    /// @return Difference
    function sub(uint a, uint b) internal pure returns (uint) {
        require(safeToSub(a, b));
        return a - b;
    }

    /// @dev Returns product if no overflow occurred
    /// @param a First factor
    /// @param b Second factor
    /// @return Product
    function mul(uint a, uint b) internal pure returns (uint) {
        require(safeToMul(a, b));
        return a * b;
    }

    /// @dev Returns whether an add operation causes an overflow
    /// @param a First addend
    /// @param b Second addend
    /// @return Did no overflow occur?
    function safeToAdd(int a, int b) internal pure returns (bool) {
        return (b >= 0 && a + b >= a) || (b < 0 && a + b < a);
    }

    /// @dev Returns whether a subtraction operation causes an underflow
    /// @param a Minuend
    /// @param b Subtrahend
    /// @return Did no underflow occur?
    function safeToSub(int a, int b) internal pure returns (bool) {
        return (b >= 0 && a - b <= a) || (b < 0 && a - b > a);
    }

    /// @dev Returns whether a multiply operation causes an overflow
    /// @param a First factor
    /// @param b Second factor
    /// @return Did no overflow occur?
    function safeToMul(int a, int b) internal pure returns (bool) {
        return (b == 0) || (a * b / b == a);
    }

    /// @dev Returns sum if no overflow occurred
    /// @param a First addend
    /// @param b Second addend
    /// @return Sum
    function add(int a, int b) internal pure returns (int) {
        require(safeToAdd(a, b));
        return a + b;
    }

    /// @dev Returns difference if no overflow occurred
    /// @param a Minuend
    /// @param b Subtrahend
    /// @return Difference
    function sub(int a, int b) internal pure returns (int) {
        require(safeToSub(a, b));
        return a - b;
    }

    /// @dev Returns product if no overflow occurred
    /// @param a First factor
    /// @param b Second factor
    /// @return Product
    function mul(int a, int b) internal pure returns (int) {
        require(safeToMul(a, b));
        return a * b;
    }
}

contract Token {
    /*
     *  Events
     */
    event Transfer(address indexed from, address indexed to, uint value);
    event Approval(address indexed owner, address indexed spender, uint value);

    /*
     *  Public functions
     */
    function transfer(address to, uint value) public returns (bool);
    function transferFrom(address from, address to, uint value) public returns (bool);
    function approve(address spender, uint value) public returns (bool);
    function balanceOf(address owner) public view returns (uint);
    function allowance(address owner, address spender) public view returns (uint);
    function totalSupply() public view returns (uint);
}

contract Proxied {
    address public masterCopy;
}

contract Proxy is Proxied {
    /// @dev Constructor function sets address of master copy contract.
    /// @param _masterCopy Master copy address.
    constructor(address _masterCopy) public {
        require(_masterCopy != address(0), "The master copy is required");
        masterCopy = _masterCopy;
    }

    /// @dev Fallback function forwards all transactions and returns all received return data.
    function() external payable {
        address _masterCopy = masterCopy;
        assembly {
            calldatacopy(0, 0, calldatasize)
            let success := delegatecall(not(0), _masterCopy, 0, calldatasize, 0, 0)
            returndatacopy(0, 0, returndatasize)
            switch success
                case 0 {
                    revert(0, returndatasize)
                }
                default {
                    return(0, returndatasize)
                }
        }
    }
}

contract StandardTokenData {
    /*
     *  Storage
     */
    mapping(address => uint) balances;
    mapping(address => mapping(address => uint)) allowances;
    uint totalTokens;
}

contract GnosisStandardToken is Token, StandardTokenData {
    using GnosisMath for *;

    /*
     *  Public functions
     */
    /// @dev Transfers sender's tokens to a given address. Returns success
    /// @param to Address of token receiver
    /// @param value Number of tokens to transfer
    /// @return Was transfer successful?
    function transfer(address to, uint value) public returns (bool) {
        if (!balances[msg.sender].safeToSub(value) || !balances[to].safeToAdd(value)) {
            return false;
        }

        balances[msg.sender] -= value;
        balances[to] += value;
        emit Transfer(msg.sender, to, value);
        return true;
    }

    /// @dev Allows allowed third party to transfer tokens from one address to another. Returns success
    /// @param from Address from where tokens are withdrawn
    /// @param to Address to where tokens are sent
    /// @param value Number of tokens to transfer
    /// @return Was transfer successful?
    function transferFrom(address from, address to, uint value) public returns (bool) {
        if (!balances[from].safeToSub(value) || !allowances[from][msg.sender].safeToSub(
            value
        ) || !balances[to].safeToAdd(value)) {
            return false;
        }
        balances[from] -= value;
        allowances[from][msg.sender] -= value;
        balances[to] += value;
        emit Transfer(from, to, value);
        return true;
    }

    /// @dev Sets approved amount of tokens for spender. Returns success
    /// @param spender Address of allowed account
    /// @param value Number of approved tokens
    /// @return Was approval successful?
    function approve(address spender, uint value) public returns (bool) {
        allowances[msg.sender][spender] = value;
        emit Approval(msg.sender, spender, value);
        return true;
    }

    /// @dev Returns number of allowed tokens for given address
    /// @param owner Address of token owner
    /// @param spender Address of token spender
    /// @return Remaining allowance for spender
    function allowance(address owner, address spender) public view returns (uint) {
        return allowances[owner][spender];
    }

    /// @dev Returns number of tokens owned by given address
    /// @param owner Address of token owner
    /// @return Balance of owner
    function balanceOf(address owner) public view returns (uint) {
        return balances[owner];
    }

    /// @dev Returns total supply of tokens
    /// @return Total supply
    function totalSupply() public view returns (uint) {
        return totalTokens;
    }
}

contract TokenOWL is Proxied, GnosisStandardToken {
    using GnosisMath for *;

    string public constant name = "OWL Token";
    string public constant symbol = "OWL";
    uint8 public constant decimals = 18;

    struct masterCopyCountdownType {
        address masterCopy;
        uint timeWhenAvailable;
    }

    masterCopyCountdownType masterCopyCountdown;

    address public creator;
    address public minter;

    event Minted(address indexed to, uint256 amount);
    event Burnt(address indexed from, address indexed user, uint256 amount);

    modifier onlyCreator() {
        // R1
        require(msg.sender == creator, "Only the creator can perform the transaction");
        _;
    }
    /// @dev trickers the update process via the proxyMaster for a new address _masterCopy
    /// updating is only possible after 30 days
    function startMasterCopyCountdown(address _masterCopy) public onlyCreator {
        require(address(_masterCopy) != address(0), "The master copy must be a valid address");

        // Update masterCopyCountdown
        masterCopyCountdown.masterCopy = _masterCopy;
        masterCopyCountdown.timeWhenAvailable = now + 30 days;
    }

    /// @dev executes the update process via the proxyMaster for a new address _masterCopy
    function updateMasterCopy() public onlyCreator {
        require(address(masterCopyCountdown.masterCopy) != address(0), "The master copy must be a valid address");
        require(
            block.timestamp >= masterCopyCountdown.timeWhenAvailable,
            "It's not possible to update the master copy during the waiting period"
        );

        // Update masterCopy
        masterCopy = masterCopyCountdown.masterCopy;
    }

    function getMasterCopy() public view returns (address) {
        return masterCopy;
    }

    /// @dev Set minter. Only the creator of this contract can call this.
    /// @param newMinter The new address authorized to mint this token
    function setMinter(address newMinter) public onlyCreator {
        minter = newMinter;
    }

    /// @dev change owner/creator of the contract. Only the creator/owner of this contract can call this.
    /// @param newOwner The new address, which should become the owner
    function setNewOwner(address newOwner) public onlyCreator {
        creator = newOwner;
    }

    /// @dev Mints OWL.
    /// @param to Address to which the minted token will be given
    /// @param amount Amount of OWL to be minted
    function mintOWL(address to, uint amount) public {
        require(minter != address(0), "The minter must be initialized");
        require(msg.sender == minter, "Only the minter can mint OWL");
        balances[to] = balances[to].add(amount);
        totalTokens = totalTokens.add(amount);
        emit Minted(to, amount);
        emit Transfer(address(0), to, amount);
    }

    /// @dev Burns OWL.
    /// @param user Address of OWL owner
    /// @param amount Amount of OWL to be burnt
    function burnOWL(address user, uint amount) public {
        allowances[user][msg.sender] = allowances[user][msg.sender].sub(amount);
        balances[user] = balances[user].sub(amount);
        totalTokens = totalTokens.sub(amount);
        emit Burnt(msg.sender, user, amount);
        emit Transfer(user, address(0), amount);
    }

    function getMasterCopyCountdown() public view returns (address, uint) {
        return (masterCopyCountdown.masterCopy, masterCopyCountdown.timeWhenAvailable);
    }
}

library SafeCast {

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value < 2**128, "SafeCast: value doesn\'t fit in 128 bits");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value < 2**64, "SafeCast: value doesn\'t fit in 64 bits");
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value < 2**32, "SafeCast: value doesn\'t fit in 32 bits");
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value < 2**16, "SafeCast: value doesn\'t fit in 16 bits");
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value < 2**8, "SafeCast: value doesn\'t fit in 8 bits");
        return uint8(value);
    }
}

library BytesLib {
    function concat(
        bytes memory _preBytes,
        bytes memory _postBytes
    )
        internal
        pure
        returns (bytes memory)
    {
        bytes memory tempBytes;

        assembly {
            // Get a location of some free memory and store it in tempBytes as
            // Solidity does for memory variables.
            tempBytes := mload(0x40)

            // Store the length of the first bytes array at the beginning of
            // the memory for tempBytes.
            let length := mload(_preBytes)
            mstore(tempBytes, length)

            // Maintain a memory counter for the current write location in the
            // temp bytes array by adding the 32 bytes for the array length to
            // the starting location.
            let mc := add(tempBytes, 0x20)
            // Stop copying when the memory counter reaches the length of the
            // first bytes array.
            let end := add(mc, length)

            for {
                // Initialize a copy counter to the start of the _preBytes data,
                // 32 bytes into its memory.
                let cc := add(_preBytes, 0x20)
            } lt(mc, end) {
                // Increase both counters by 32 bytes each iteration.
                mc := add(mc, 0x20)
                cc := add(cc, 0x20)
            } {
                // Write the _preBytes data into the tempBytes memory 32 bytes
                // at a time.
                mstore(mc, mload(cc))
            }

            // Add the length of _postBytes to the current length of tempBytes
            // and store it as the new length in the first 32 bytes of the
            // tempBytes memory.
            length := mload(_postBytes)
            mstore(tempBytes, add(length, mload(tempBytes)))

            // Move the memory counter back from a multiple of 0x20 to the
            // actual end of the _preBytes data.
            mc := end
            // Stop copying when the memory counter reaches the new combined
            // length of the arrays.
            end := add(mc, length)

            for {
                let cc := add(_postBytes, 0x20)
            } lt(mc, end) {
                mc := add(mc, 0x20)
                cc := add(cc, 0x20)
            } {
                mstore(mc, mload(cc))
            }

            // Update the free-memory pointer by padding our last write location
            // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
            // next 32 byte block, then round down to the nearest multiple of
            // 32. If the sum of the length of the two arrays is zero then add 
            // one before rounding down to leave a blank 32 bytes (the length block with 0).
            mstore(0x40, and(
              add(add(end, iszero(add(length, mload(_preBytes)))), 31),
              not(31) // Round down to the nearest 32 bytes.
            ))
        }

        return tempBytes;
    }

    function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
        assembly {
            // Read the first 32 bytes of _preBytes storage, which is the length
            // of the array. (We don't need to use the offset into the slot
            // because arrays use the entire slot.)
            let fslot := sload(_preBytes_slot)
            // Arrays of 31 bytes or less have an even value in their slot,
            // while longer arrays have an odd value. The actual length is
            // the slot divided by two for odd values, and the lowest order
            // byte divided by two for even values.
            // If the slot is even, bitwise and the slot with 255 and divide by
            // two to get the length. If the slot is odd, bitwise and the slot
            // with -1 and divide by two.
            let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
            let mlength := mload(_postBytes)
            let newlength := add(slength, mlength)
            // slength can contain both the length and contents of the array
            // if length < 32 bytes so let's prepare for that
            // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
            switch add(lt(slength, 32), lt(newlength, 32))
            case 2 {
                // Since the new array still fits in the slot, we just need to
                // update the contents of the slot.
                // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
                sstore(
                    _preBytes_slot,
                    // all the modifications to the slot are inside this
                    // next block
                    add(
                        // we can just add to the slot contents because the
                        // bytes we want to change are the LSBs
                        fslot,
                        add(
                            mul(
                                div(
                                    // load the bytes from memory
                                    mload(add(_postBytes, 0x20)),
                                    // zero all bytes to the right
                                    exp(0x100, sub(32, mlength))
                                ),
                                // and now shift left the number of bytes to
                                // leave space for the length in the slot
                                exp(0x100, sub(32, newlength))
                            ),
                            // increase length by the double of the memory
                            // bytes length
                            mul(mlength, 2)
                        )
                    )
                )
            }
            case 1 {
                // The stored value fits in the slot, but the combined value
                // will exceed it.
                // get the keccak hash to get the contents of the array
                mstore(0x0, _preBytes_slot)
                let sc := add(keccak256(0x0, 0x20), div(slength, 32))

                // save new length
                sstore(_preBytes_slot, add(mul(newlength, 2), 1))

                // The contents of the _postBytes array start 32 bytes into
                // the structure. Our first read should obtain the `submod`
                // bytes that can fit into the unused space in the last word
                // of the stored array. To get this, we read 32 bytes starting
                // from `submod`, so the data we read overlaps with the array
                // contents by `submod` bytes. Masking the lowest-order
                // `submod` bytes allows us to add that value directly to the
                // stored value.

                let submod := sub(32, slength)
                let mc := add(_postBytes, submod)
                let end := add(_postBytes, mlength)
                let mask := sub(exp(0x100, submod), 1)

                sstore(
                    sc,
                    add(
                        and(
                            fslot,
                            0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00
                        ),
                        and(mload(mc), mask)
                    )
                )

                for {
                    mc := add(mc, 0x20)
                    sc := add(sc, 1)
                } lt(mc, end) {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } {
                    sstore(sc, mload(mc))
                }

                mask := exp(0x100, sub(mc, end))

                sstore(sc, mul(div(mload(mc), mask), mask))
            }
            default {
                // get the keccak hash to get the contents of the array
                mstore(0x0, _preBytes_slot)
                // Start copying to the last used word of the stored array.
                let sc := add(keccak256(0x0, 0x20), div(slength, 32))

                // save new length
                sstore(_preBytes_slot, add(mul(newlength, 2), 1))

                // Copy over the first `submod` bytes of the new data as in
                // case 1 above.
                let slengthmod := mod(slength, 32)
                let mlengthmod := mod(mlength, 32)
                let submod := sub(32, slengthmod)
                let mc := add(_postBytes, submod)
                let end := add(_postBytes, mlength)
                let mask := sub(exp(0x100, submod), 1)

                sstore(sc, add(sload(sc), and(mload(mc), mask)))
                
                for { 
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } lt(mc, end) {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } {
                    sstore(sc, mload(mc))
                }

                mask := exp(0x100, sub(mc, end))

                sstore(sc, mul(div(mload(mc), mask), mask))
            }
        }
    }

    function slice(
        bytes memory _bytes,
        uint _start,
        uint _length
    )
        internal
        pure
        returns (bytes memory)
    {
        require(_bytes.length >= (_start + _length));

        bytes memory tempBytes;

        assembly {
            switch iszero(_length)
            case 0 {
                // Get a location of some free memory and store it in tempBytes as
                // Solidity does for memory variables.
                tempBytes := mload(0x40)

                // The first word of the slice result is potentially a partial
                // word read from the original array. To read it, we calculate
                // the length of that partial word and start copying that many
                // bytes into the array. The first word we copy will start with
                // data we don't care about, but the last `lengthmod` bytes will
                // land at the beginning of the contents of the new array. When
                // we're done copying, we overwrite the full first word with
                // the actual length of the slice.
                let lengthmod := and(_length, 31)

                // The multiplication in the next line is necessary
                // because when slicing multiples of 32 bytes (lengthmod == 0)
                // the following copy loop was copying the origin's length
                // and then ending prematurely not copying everything it should.
                let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                let end := add(mc, _length)

                for {
                    // The multiplication in the next line has the same exact purpose
                    // as the one above.
                    let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                } lt(mc, end) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    mstore(mc, mload(cc))
                }

                mstore(tempBytes, _length)

                //update free-memory pointer
                //allocating the array padded to 32 bytes like the compiler does now
                mstore(0x40, and(add(mc, 31), not(31)))
            }
            //if we want a zero-length slice let's just return a zero-length array
            default {
                tempBytes := mload(0x40)

                mstore(0x40, add(tempBytes, 0x20))
            }
        }

        return tempBytes;
    }

    function toAddress(bytes memory _bytes, uint _start) internal  pure returns (address) {
        require(_bytes.length >= (_start + 20));
        address tempAddress;

        assembly {
            tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
        }

        return tempAddress;
    }

    function toUint8(bytes memory _bytes, uint _start) internal  pure returns (uint8) {
        require(_bytes.length >= (_start + 1));
        uint8 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x1), _start))
        }

        return tempUint;
    }

    function toUint16(bytes memory _bytes, uint _start) internal  pure returns (uint16) {
        require(_bytes.length >= (_start + 2));
        uint16 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x2), _start))
        }

        return tempUint;
    }

    function toUint32(bytes memory _bytes, uint _start) internal  pure returns (uint32) {
        require(_bytes.length >= (_start + 4));
        uint32 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x4), _start))
        }

        return tempUint;
    }

    function toUint64(bytes memory _bytes, uint _start) internal  pure returns (uint64) {
        require(_bytes.length >= (_start + 8));
        uint64 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x8), _start))
        }

        return tempUint;
    }

    function toUint96(bytes memory _bytes, uint _start) internal  pure returns (uint96) {
        require(_bytes.length >= (_start + 12));
        uint96 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0xc), _start))
        }

        return tempUint;
    }

    function toUint128(bytes memory _bytes, uint _start) internal  pure returns (uint128) {
        require(_bytes.length >= (_start + 16));
        uint128 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x10), _start))
        }

        return tempUint;
    }

    function toUint(bytes memory _bytes, uint _start) internal  pure returns (uint256) {
        require(_bytes.length >= (_start + 32));
        uint256 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x20), _start))
        }

        return tempUint;
    }

    function toBytes32(bytes memory _bytes, uint _start) internal  pure returns (bytes32) {
        require(_bytes.length >= (_start + 32));
        bytes32 tempBytes32;

        assembly {
            tempBytes32 := mload(add(add(_bytes, 0x20), _start))
        }

        return tempBytes32;
    }

    function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
        bool success = true;

        assembly {
            let length := mload(_preBytes)

            // if lengths don't match the arrays are not equal
            switch eq(length, mload(_postBytes))
            case 1 {
                // cb is a circuit breaker in the for loop since there's
                //  no said feature for inline assembly loops
                // cb = 1 - don't breaker
                // cb = 0 - break
                let cb := 1

                let mc := add(_preBytes, 0x20)
                let end := add(mc, length)

                for {
                    let cc := add(_postBytes, 0x20)
                // the next line is the loop condition:
                // while(uint(mc < end) + cb == 2)
                } eq(add(lt(mc, end), cb), 2) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    // if any of these checks fails then arrays are not equal
                    if iszero(eq(mload(mc), mload(cc))) {
                        // unsuccess:
                        success := 0
                        cb := 0
                    }
                }
            }
            default {
                // unsuccess:
                success := 0
            }
        }

        return success;
    }

    function equalStorage(
        bytes storage _preBytes,
        bytes memory _postBytes
    )
        internal
        view
        returns (bool)
    {
        bool success = true;

        assembly {
            // we know _preBytes_offset is 0
            let fslot := sload(_preBytes_slot)
            // Decode the length of the stored array like in concatStorage().
            let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
            let mlength := mload(_postBytes)

            // if lengths don't match the arrays are not equal
            switch eq(slength, mlength)
            case 1 {
                // slength can contain both the length and contents of the array
                // if length < 32 bytes so let's prepare for that
                // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                if iszero(iszero(slength)) {
                    switch lt(slength, 32)
                    case 1 {
                        // blank the last byte which is the length
                        fslot := mul(div(fslot, 0x100), 0x100)

                        if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
                            // unsuccess:
                            success := 0
                        }
                    }
                    default {
                        // cb is a circuit breaker in the for loop since there's
                        //  no said feature for inline assembly loops
                        // cb = 1 - don't breaker
                        // cb = 0 - break
                        let cb := 1

                        // get the keccak hash to get the contents of the array
                        mstore(0x0, _preBytes_slot)
                        let sc := keccak256(0x0, 0x20)

                        let mc := add(_postBytes, 0x20)
                        let end := add(mc, mlength)

                        // the next line is the loop condition:
                        // while(uint(mc < end) + cb == 2)
                        for {} eq(add(lt(mc, end), cb), 2) {
                            sc := add(sc, 1)
                            mc := add(mc, 0x20)
                        } {
                            if iszero(eq(sload(sc), mload(mc))) {
                                // unsuccess:
                                success := 0
                                cb := 0
                            }
                        }
                    }
                }
            }
            default {
                // unsuccess:
                success := 0
            }
        }

        return success;
    }
}

library SignedSafeMath {
    int256 constant private INT256_MIN = -2**255;

    /**
     * @dev Multiplies two signed integers, reverts on overflow.
     */
    function mul(int256 a, int256 b) internal pure returns (int256) {
        // 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 0;
        }

        require(!(a == -1 && b == INT256_MIN), "SignedSafeMath: multiplication overflow");

        int256 c = a * b;
        require(c / a == b, "SignedSafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Integer division of two signed integers truncating the quotient, reverts on division by zero.
     */
    function div(int256 a, int256 b) internal pure returns (int256) {
        require(b != 0, "SignedSafeMath: division by zero");
        require(!(b == -1 && a == INT256_MIN), "SignedSafeMath: division overflow");

        int256 c = a / b;

        return c;
    }

    /**
     * @dev Subtracts two signed integers, reverts on overflow.
     */
    function sub(int256 a, int256 b) internal pure returns (int256) {
        int256 c = a - b;
        require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow");

        return c;
    }

    /**
     * @dev Adds two signed integers, reverts on overflow.
     */
    function add(int256 a, int256 b) internal pure returns (int256) {
        int256 c = a + b;
        require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow");

        return c;
    }
}

library TokenConservation {
    using SignedSafeMath for int256;

    /** @dev initialize the token conservation data structure
      * @param tokenIdsForPrice sorted list of tokenIds for which token conservation should be checked
      */
    function init(uint16[] memory tokenIdsForPrice) internal pure returns (int256[] memory) {
        return new int256[](tokenIdsForPrice.length + 1);
    }

    /** @dev returns the token imbalance of the fee token
      * @param self internal datastructure created by TokenConservation.init()
      */
    function feeTokenImbalance(int256[] memory self) internal pure returns (int256) {
        return self[0];
    }

    /** @dev updated token conservation array.
      * @param self internal datastructure created by TokenConservation.init()
      * @param buyToken id of token whose imbalance should be subtracted from
      * @param sellToken id of token whose imbalance should be added to
      * @param tokenIdsForPrice sorted list of tokenIds
      * @param buyAmount amount to be subtracted at `self[buyTokenIndex]`
      * @param sellAmount amount to be added at `self[sellTokenIndex]`
      */
    function updateTokenConservation(
        int256[] memory self,
        uint16 buyToken,
        uint16 sellToken,
        uint16[] memory tokenIdsForPrice,
        uint128 buyAmount,
        uint128 sellAmount
    ) internal pure {
        uint256 buyTokenIndex = findPriceIndex(buyToken, tokenIdsForPrice);
        uint256 sellTokenIndex = findPriceIndex(sellToken, tokenIdsForPrice);
        self[buyTokenIndex] = self[buyTokenIndex].sub(int256(buyAmount));
        self[sellTokenIndex] = self[sellTokenIndex].add(int256(sellAmount));
    }

    /** @dev Ensures all array's elements are zero except the first.
      * @param self internal datastructure created by TokenConservation.init()
      * @return true if all, but first element of self are zero else false
      */
    function checkTokenConservation(int256[] memory self) internal pure {
        require(self[0] > 0, "Token conservation at 0 must be positive.");
        for (uint256 i = 1; i < self.length; i++) {
            require(self[i] == 0, "Token conservation does not hold");
        }
    }

    /** @dev Token ordering is verified by submitSolution. Required because binary search is used to fetch token info.
      * @param tokenIdsForPrice list of tokenIds
      * @return true if tokenIdsForPrice is sorted else false
      */
    function checkPriceOrdering(uint16[] memory tokenIdsForPrice) internal pure returns (bool) {
        for (uint256 i = 1; i < tokenIdsForPrice.length; i++) {
            if (tokenIdsForPrice[i] <= tokenIdsForPrice[i - 1]) {
                return false;
            }
        }
        return true;
    }

    /** @dev implementation of binary search on sorted list returns token id
      * @param tokenId element whose index is to be found
      * @param tokenIdsForPrice list of (sorted) tokenIds for which binary search is applied.
      * @return `index` in `tokenIdsForPrice` where `tokenId` appears (reverts if not found).
      */
    function findPriceIndex(uint16 tokenId, uint16[] memory tokenIdsForPrice) private pure returns (uint256) {
        // Fee token is not included in tokenIdsForPrice
        if (tokenId == 0) {
            return 0;
        }
        // binary search for the other tokens
        uint256 leftValue = 0;
        uint256 rightValue = tokenIdsForPrice.length - 1;
        while (rightValue >= leftValue) {
            uint256 middleValue = (leftValue + rightValue) / 2;
            if (tokenIdsForPrice[middleValue] == tokenId) {
                // shifted one to the right to account for fee token at index 0
                return middleValue + 1;
            } else if (tokenIdsForPrice[middleValue] < tokenId) {
                leftValue = middleValue + 1;
            } else {
                rightValue = middleValue - 1;
            }
        }
        revert("Price not provided for token");
    }
}

contract BatchExchange is EpochTokenLocker {
    using SafeCast for uint256;
    using SafeMath for uint128;
    using BytesLib for bytes32;
    using BytesLib for bytes;
    using TokenConservation for int256[];
    using TokenConservation for uint16[];
    using IterableAppendOnlySet for IterableAppendOnlySet.Data;

    /** @dev Maximum number of touched orders in auction (used in submitSolution) */
    uint256 public constant MAX_TOUCHED_ORDERS = 30;

    /** @dev Fee charged for adding a token */
    uint256 public constant FEE_FOR_LISTING_TOKEN_IN_OWL = 10 ether;

    /** @dev minimum allowed value (in WEI) of any prices or executed trade amounts */
    uint128 public constant AMOUNT_MINIMUM = 10**4;

    /** @dev Numerator or denominator used in orders, which do not track its usedAmount*/
    uint128 public constant UNLIMITED_ORDER_AMOUNT = uint128(-1);

    /** Corresponds to percentage that competing solution must improve on current
      * (p = IMPROVEMENT_DENOMINATOR + 1 / IMPROVEMENT_DENOMINATOR)
      */
    uint256 public constant IMPROVEMENT_DENOMINATOR = 100; // 1%

    /** @dev A fixed integer used to evaluate fees as a fraction of trade execution 1/FEE_DENOMINATOR */
    uint128 public constant FEE_DENOMINATOR = 1000;

    /** @dev The number of bytes a single auction element is serialized into */
    uint128 public constant ENCODED_AUCTION_ELEMENT_WIDTH = 112;

    /** @dev maximum number of tokens that can be listed for exchange */
    // solhint-disable-next-line var-name-mixedcase
    uint256 public MAX_TOKENS;

    /** @dev Current number of tokens listed/available for exchange */
    uint16 public numTokens;

    /** @dev The feeToken of the exchange will be the OWL Token */
    TokenOWL public feeToken;

    /** @dev mapping of type userAddress -> List[Order] where all the user's orders are stored */
    mapping(address => Order[]) public orders;

    /** @dev mapping of type tokenId -> curentPrice of tokenId */
    mapping(uint16 => uint128) public currentPrices;

    /** @dev Sufficient information for current winning auction solution */
    SolutionData public latestSolution;

    // Iterable set of all users, required to collect auction information
    IterableAppendOnlySet.Data private allUsers;
    IdToAddressBiMap.Data private registeredTokens;

    struct Order {
        uint16 buyToken;
        uint16 sellToken;
        uint32 validFrom; // order is valid from auction collection period: validFrom inclusive
        uint32 validUntil; // order is valid till auction collection period: validUntil inclusive
        uint128 priceNumerator;
        uint128 priceDenominator;
        uint128 usedAmount; // remainingAmount = priceDenominator - usedAmount
    }

    struct TradeData {
        address owner;
        uint128 volume;
        uint16 orderId;
    }

    struct SolutionData {
        uint32 batchId;
        TradeData[] trades;
        uint16[] tokenIdsForPrice;
        address solutionSubmitter;
        uint256 feeReward;
        uint256 objectiveValue;
    }

    event OrderPlacement(
        address indexed owner,
        uint16 index,
        uint16 indexed buyToken,
        uint16 indexed sellToken,
        uint32 validFrom,
        uint32 validUntil,
        uint128 priceNumerator,
        uint128 priceDenominator
    );

    event TokenListing(address token, uint16 id);

    /** @dev Event emitted when an order is cancelled but still valid in the batch that is
     * currently being solved. It remains in storage but will not be tradable in any future
     * batch to be solved.
     */
    event OrderCancellation(address indexed owner, uint16 id);

    /** @dev Event emitted when an order is removed from storage.
     */
    event OrderDeletion(address indexed owner, uint16 id);

    /** @dev Event emitted when a new trade is settled
     */
    event Trade(
        address indexed owner,
        uint16 indexed orderId,
        uint16 indexed sellToken,
        // Solidity only supports three indexed arguments
        uint16 buyToken,
        uint128 executedSellAmount,
        uint128 executedBuyAmount
    );

    /** @dev Event emitted when an already exectued trade gets reverted
     */
    event TradeReversion(
        address indexed owner,
        uint16 indexed orderId,
        uint16 indexed sellToken,
        // Solidity only supports three indexed arguments
        uint16 buyToken,
        uint128 executedSellAmount,
        uint128 executedBuyAmount
    );

    /** @dev Event emitted for each solution that is submitted
     */
    event SolutionSubmission(
        address indexed submitter,
        uint256 utility,
        uint256 disregardedUtility,
        uint256 burntFees,
        uint256 lastAuctionBurntFees,
        uint128[] prices,
        uint16[] tokenIdsForPrice
    );

    /** @dev Constructor determines exchange parameters
      * @param maxTokens The maximum number of tokens that can be listed.
      * @param _feeToken Address of ERC20 fee token.
      */
    constructor(uint256 maxTokens, address _feeToken) public {
        // All solutions for the batches must have normalized prices. The following line sets the
        // price of OWL to 10**18 for all solutions and hence enforces a normalization.
        currentPrices[0] = 1 ether;
        MAX_TOKENS = maxTokens;
        feeToken = TokenOWL(_feeToken);
        // The burn functionallity of OWL requires an approval.
        // In the following line the approval is set for all future burn calls.
        feeToken.approve(address(this), uint256(-1));
        addToken(_feeToken); // feeToken will always have the token index 0
    }

    /** @dev Used to list a new token on the contract: Hence, making it available for exchange in an auction.
      * @param token ERC20 token to be listed.
      *
      * Requirements:
      * - `maxTokens` has not already been reached
      * - `token` has not already been added
      */
    function addToken(address token) public {
        require(numTokens < MAX_TOKENS, "Max tokens reached");
        if (numTokens > 0) {
            // Only charge fees for tokens other than the fee token itself
            feeToken.burnOWL(msg.sender, FEE_FOR_LISTING_TOKEN_IN_OWL);
        }
        require(IdToAddressBiMap.insert(registeredTokens, numTokens, token), "Token already registered");
        emit TokenListing(token, numTokens);
        numTokens++;
    }

    /** @dev A user facing function used to place limit sell orders in auction with expiry defined by batchId
      * @param buyToken id of token to be bought
      * @param sellToken id of token to be sold
      * @param validUntil batchId representing order's expiry
      * @param buyAmount relative minimum amount of requested buy amount
      * @param sellAmount maximum amount of sell token to be exchanged
      * @return orderId defined as the index in user's order array
      *
      * Emits an {OrderPlacement} event with all relevant order details.
      */
    function placeOrder(uint16 buyToken, uint16 sellToken, uint32 validUntil, uint128 buyAmount, uint128 sellAmount)
        public
        returns (uint256)
    {
        return placeOrderInternal(buyToken, sellToken, getCurrentBatchId(), validUntil, buyAmount, sellAmount);
    }

    /** @dev A user facing function used to place limit sell orders in auction with expiry defined by batchId
      * Note that parameters are passed as arrays and the indices correspond to each order.
      * @param buyTokens ids of tokens to be bought
      * @param sellTokens ids of tokens to be sold
      * @param validFroms batchIds representing order's validity start time
      * @param validUntils batchIds represnnting order's expiry
      * @param buyAmounts relative minimum amount of requested buy amounts
      * @param sellAmounts maximum amounts of sell token to be exchanged
      * @return `orderIds` an array of indices in which `msg.sender`'s orders are included
      *
      * Emits an {OrderPlacement} event with all relevant order details.
      */
    function placeValidFromOrders(
        uint16[] memory buyTokens,
        uint16[] memory sellTokens,
        uint32[] memory validFroms,
        uint32[] memory validUntils,
        uint128[] memory buyAmounts,
        uint128[] memory sellAmounts
    ) public returns (uint16[] memory orderIds) {
        orderIds = new uint16[](buyTokens.length);
        for (uint256 i = 0; i < buyTokens.length; i++) {
            orderIds[i] = placeOrderInternal(
                buyTokens[i],
                sellTokens[i],
                validFroms[i],
                validUntils[i],
                buyAmounts[i],
                sellAmounts[i]
            );
        }
    }

    /** @dev a user facing function used to cancel orders. If the order is valid for the batch that is currently
      * being solved, it sets order expiry to that batchId. Otherwise it removes it from storage. Can be called
      * multiple times (e.g. to eventually free storage once order is expired).
      *
      * @param orderIds referencing the indices of user's orders to be cancelled
      *
      * Emits an {OrderCancellation} or {OrderDeletion} with sender's address and orderId
      */
    function cancelOrders(uint16[] memory orderIds) public {
        uint32 batchIdBeingSolved = getCurrentBatchId() - 1;
        for (uint16 i = 0; i < orderIds.length; i++) {
            if (!checkOrderValidity(orders[msg.sender][orderIds[i]], batchIdBeingSolved)) {
                delete orders[msg.sender][orderIds[i]];
                emit OrderDeletion(msg.sender, orderIds[i]);
            } else {
                orders[msg.sender][orderIds[i]].validUntil = batchIdBeingSolved;
                emit OrderCancellation(msg.sender, orderIds[i]);
            }
        }
    }

    /** @dev A user facing wrapper to cancel and place new orders in the same transaction.
      * @param cancellations indices of orders to be cancelled
      * @param buyTokens ids of tokens to be bought in new orders
      * @param sellTokens ids of tokens to be sold in new orders
      * @param validFroms batchIds representing order's validity start time in new orders
      * @param validUntils batchIds represnnting order's expiry in new orders
      * @param buyAmounts relative minimum amount of requested buy amounts in new orders
      * @param sellAmounts maximum amounts of sell token to be exchanged in new orders
      * @return an array of indices in which `msg.sender`'s new orders are included
      *
      * Emits {OrderCancellation} events for all cancelled orders and {OrderPlacement} events with relevant new order details.
      */
    function replaceOrders(
        uint16[] memory cancellations,
        uint16[] memory buyTokens,
        uint16[] memory sellTokens,
        uint32[] memory validFroms,
        uint32[] memory validUntils,
        uint128[] memory buyAmounts,
        uint128[] memory sellAmounts
    ) public returns (uint16[] memory) {
        cancelOrders(cancellations);
        return placeValidFromOrders(buyTokens, sellTokens, validFroms, validUntils, buyAmounts, sellAmounts);
    }

    /** @dev a solver facing function called for auction settlement
      * @param batchId index of auction solution is referring to
      * @param owners array of addresses corresponding to touched orders
      * @param orderIds array of order indices used in parallel with owners to identify touched order
      * @param buyVolumes executed buy amounts for each order identified by index of owner-orderId arrays
      * @param prices list of prices for touched tokens indexed by next parameter
      * @param tokenIdsForPrice price[i] is the price for the token with tokenID tokenIdsForPrice[i]
      * @return the computed objective value of the solution
      *
      * Requirements:
      * - Solutions for this `batchId` are currently being accepted.
      * - Claimed objetive value is a great enough improvement on the current winning solution
      * - Fee Token price is non-zero
      * - `tokenIdsForPrice` is sorted.
      * - Number of touched orders does not exceed `MAX_TOUCHED_ORDERS`.
      * - Each touched order is valid at current `batchId`.
      * - Each touched order's `executedSellAmount` does not exceed its remaining amount.
      * - Limit Price of each touched order is respected.
      * - Solution's objective evaluation must be positive.
      *
      * Sub Requirements: Those nested within other functions
      * - checkAndOverrideObjectiveValue; Objetive value is a great enough improvement on the current winning solution
      * - checkTokenConservation; for all, non-fee, tokens total amount sold == total amount bought
      */
    function submitSolution(
        uint32 batchId,
        uint256 claimedObjectiveValue,
        address[] memory owners,
        uint16[] memory orderIds,
        uint128[] memory buyVolumes,
        uint128[] memory prices,
        uint16[] memory tokenIdsForPrice
    ) public returns (uint256) {
        require(acceptingSolutions(batchId), "Solutions are no longer accepted for this batch");
        require(
            isObjectiveValueSufficientlyImproved(claimedObjectiveValue),
            "Claimed objective doesn't sufficiently improve current solution"
        );
        require(verifyAmountThreshold(prices), "At least one price lower than AMOUNT_MINIMUM");
        require(tokenIdsForPrice[0] != 0, "Fee token has fixed price!");
        require(tokenIdsForPrice.checkPriceOrdering(), "prices are not ordered by tokenId");
        require(owners.length <= MAX_TOUCHED_ORDERS, "Solution exceeds MAX_TOUCHED_ORDERS");
        // Further assumptions are: owners.length == orderIds.length && owners.length == buyVolumes.length
        // && prices.length == tokenIdsForPrice.length
        // These assumptions are not checked explicitly, as violations of these constraints can not be used
        // to create a beneficial situation
        uint256 lastAuctionBurntFees = burnPreviousAuctionFees();
        undoCurrentSolution();
        updateCurrentPrices(prices, tokenIdsForPrice);
        delete latestSolution.trades;
        int256[] memory tokenConservation = TokenConservation.init(tokenIdsForPrice);
        uint256 utility = 0;
        for (uint256 i = 0; i < owners.length; i++) {
            Order memory order = orders[owners[i]][orderIds[i]];
            require(checkOrderValidity(order, batchId), "Order is invalid");
            (uint128 executedBuyAmount, uint128 executedSellAmount) = getTradedAmounts(buyVolumes[i], order);
            require(executedBuyAmount >= AMOUNT_MINIMUM, "buy amount less than AMOUNT_MINIMUM");
            require(executedSellAmount >= AMOUNT_MINIMUM, "sell amount less than AMOUNT_MINIMUM");
            tokenConservation.updateTokenConservation(
                order.buyToken,
                order.sellToken,
                tokenIdsForPrice,
                executedBuyAmount,
                executedSellAmount
            );
            require(getRemainingAmount(order) >= executedSellAmount, "executedSellAmount bigger than specified in order");
            // Ensure executed price is not lower than the order price:
            //       executedSellAmount / executedBuyAmount <= order.priceDenominator / order.priceNumerator
            require(
                executedSellAmount.mul(order.priceNumerator) <= executedBuyAmount.mul(order.priceDenominator),
                "limit price not satisfied"
            );
            // accumulate utility before updateRemainingOrder, but after limitPrice verified!
            utility = utility.add(evaluateUtility(executedBuyAmount, order));
            updateRemainingOrder(owners[i], orderIds[i], executedSellAmount);
            addBalanceAndBlockWithdrawForThisBatch(owners[i], tokenIdToAddressMap(order.buyToken), executedBuyAmount);
            emit Trade(owners[i], orderIds[i], order.sellToken, order.buyToken, executedSellAmount, executedBuyAmount);
        }
        // Perform all subtractions after additions to avoid negative values
        for (uint256 i = 0; i < owners.length; i++) {
            Order memory order = orders[owners[i]][orderIds[i]];
            (, uint128 executedSellAmount) = getTradedAmounts(buyVolumes[i], order);
            subtractBalance(owners[i], tokenIdToAddressMap(order.sellToken), executedSellAmount);
        }
        uint256 disregardedUtility = 0;
        for (uint256 i = 0; i < owners.length; i++) {
            disregardedUtility = disregardedUtility.add(evaluateDisregardedUtility(orders[owners[i]][orderIds[i]], owners[i]));
        }
        uint256 burntFees = uint256(tokenConservation.feeTokenImbalance()) / 2;
        // burntFees ensures direct trades (when available) yield better solutions than longer rings
        uint256 objectiveValue = utility.add(burntFees).sub(disregardedUtility);
        checkAndOverrideObjectiveValue(objectiveValue);
        grantRewardToSolutionSubmitter(burntFees);
        tokenConservation.checkTokenConservation();
        documentTrades(batchId, owners, orderIds, buyVolumes, tokenIdsForPrice);

        emit SolutionSubmission(
            msg.sender,
            utility,
            disregardedUtility,
            burntFees,
            lastAuctionBurntFees,
            prices,
            tokenIdsForPrice
        );
        return (objectiveValue);
    }
    /**
     * Public View Methods
     */

    /** @dev View returning ID of listed tokens
      * @param addr address of listed token.
      * @return tokenId as stored within the contract.
      */
    function tokenAddressToIdMap(address addr) public view returns (uint16) {
        return IdToAddressBiMap.getId(registeredTokens, addr);
    }

    /** @dev View returning address of listed token by ID
      * @param id tokenId as stored, via BiMap, within the contract.
      * @return address of (listed) token
      */
    function tokenIdToAddressMap(uint16 id) public view returns (address) {
        return IdToAddressBiMap.getAddressAt(registeredTokens, id);
    }

    /** @dev View returning a bool attesting whether token was already added
      * @param addr address of the token to be checked
      * @return bool attesting whether token was already added
      */
    function hasToken(address addr) public view returns (bool) {
        return IdToAddressBiMap.hasAddress(registeredTokens, addr);
    }

    /** @dev View returning all byte-encoded sell orders for specified user
      * @param user address of user whose orders are being queried
      * @param offset uint determining the starting orderIndex
      * @param pageSize uint determining the count of elements to be viewed
      * @return encoded bytes representing all orders
      */
    function getEncodedUserOrdersPaginated(address user, uint16 offset, uint16 pageSize)
        public
        view
        returns (bytes memory elements)
    {
        for (uint16 i = offset; i < Math.min(orders[user].length, offset + pageSize); i++) {
            elements = elements.concat(
                encodeAuctionElement(user, getBalance(user, tokenIdToAddressMap(orders[user][i].sellToken)), orders[user][i])
            );
        }
        return elements;
    }

    /** @dev View returning all byte-encoded users in paginated form
      * @param previousPageUser address of last user received in last pages (address(0) for first page)
      * @param pageSize uint determining the count of users to be returned per page
      * @return encoded packed bytes of user addresses
      */
    function getUsersPaginated(address previousPageUser, uint16 pageSize) public view returns (bytes memory users) {
        if (allUsers.size() == 0) {
            return users;
        }
        uint16 count = 0;
        address current = previousPageUser;
        if (current == address(0)) {
            current = allUsers.first();
            users = users.concat(abi.encodePacked(current));
            count++;
        }
        while (count < pageSize && current != allUsers.last) {
            current = allUsers.next(current);
            users = users.concat(abi.encodePacked(current));
            count++;
        }
        return users;
    }

    /** @dev View returning all byte-encoded sell orders for specified user
      * @param user address of user whose orders are being queried
      * @return encoded bytes representing all orders
      */
    function getEncodedUserOrders(address user) public view returns (bytes memory elements) {
        return getEncodedUserOrdersPaginated(user, 0, uint16(-1));
    }

    /** @dev View returning byte-encoded sell orders in paginated form
      * @param previousPageUser address of last user received in the previous page (address(0) for first page)
      * @param previousPageUserOffset the number of orders received for the last user on the previous page (0 for first page).
      * @param pageSize uint determining the count of orders to be returned per page
      * @return encoded bytes representing a page of orders ordered by (user, index)
      */
    function getEncodedUsersPaginated(address previousPageUser, uint16 previousPageUserOffset, uint16 pageSize)
        public
        view
        returns (bytes memory elements)
    {
        if (allUsers.size() == 0) {
            return elements;
        }
        uint16 currentOffset = previousPageUserOffset;
        address currentUser = previousPageUser;
        if (currentUser == address(0x0)) {
            currentUser = allUsers.first();
        }
        while (elements.length / ENCODED_AUCTION_ELEMENT_WIDTH < pageSize) {
            elements = elements.concat(
                getEncodedUserOrdersPaginated(
                    currentUser,
                    currentOffset,
                    pageSize - uint16(elements.length / ENCODED_AUCTION_ELEMENT_WIDTH)
                )
            );
            if (currentUser == allUsers.last) {
                return elements;
            }
            currentOffset = 0;
            currentUser = allUsers.next(currentUser);
        }
    }

    /** @dev View returning all byte-encoded sell orders
      * @return encoded bytes representing all orders ordered by (user, index)
      */
    function getEncodedOrders() public view returns (bytes memory elements) {
        if (allUsers.size() > 0) {
            address user = allUsers.first();
            bool stop = false;
            while (!stop) {
                elements = elements.concat(getEncodedUserOrders(user));
                if (user == allUsers.last) {
                    stop = true;
                } else {
                    user = allUsers.next(user);
                }
            }
        }
        return elements;
    }

    function acceptingSolutions(uint32 batchId) public view returns (bool) {
        return batchId == getCurrentBatchId() - 1 && getSecondsRemainingInBatch() >= 1 minutes;
    }

    /** @dev gets the objective value of currently winning solution.
      * @return objective function evaluation of the currently winning solution, or zero if no solution proposed.
      */
    function getCurrentObjectiveValue() public view returns (uint256) {
        if (latestSolution.batchId == getCurrentBatchId() - 1) {
            return latestSolution.objectiveValue;
        } else {
            return 0;
        }
    }
    /**
     * Private Functions
     */

    function placeOrderInternal(
        uint16 buyToken,
        uint16 sellToken,
        uint32 validFrom,
        uint32 validUntil,
        uint128 buyAmount,
        uint128 sellAmount
    ) private returns (uint16) {
        require(IdToAddressBiMap.hasId(registeredTokens, buyToken), "Buy token must be listed");
        require(IdToAddressBiMap.hasId(registeredTokens, sellToken), "Sell token must be listed");
        require(buyToken != sellToken, "Exchange tokens not distinct");
        require(validFrom >= getCurrentBatchId(), "Orders can't be placed in the past");
        orders[msg.sender].push(
            Order({
                buyToken: buyToken,
                sellToken: sellToken,
                validFrom: validFrom,
                validUntil: validUntil,
                priceNumerator: buyAmount,
                priceDenominator: sellAmount,
                usedAmount: 0
            })
        );
        uint16 orderId = (orders[msg.sender].length - 1).toUint16();
        emit OrderPlacement(msg.sender, orderId, buyToken, sellToken, validFrom, validUntil, buyAmount, sellAmount);
        allUsers.insert(msg.sender);
        return orderId;
    }

    /** @dev called at the end of submitSolution with a value of tokenConservation / 2
      * @param feeReward amount to be rewarded to the solver
      */
    function grantRewardToSolutionSubmitter(uint256 feeReward) private {
        latestSolution.feeReward = feeReward;
        addBalanceAndBlockWithdrawForThisBatch(msg.sender, tokenIdToAddressMap(0), feeReward);
    }

    /** @dev called during solution submission to burn fees from previous auction
      * @return amount of OWL burnt
      */
    function burnPreviousAuctionFees() private returns (uint256) {
        if (!currentBatchHasSolution()) {
            feeToken.burnOWL(address(this), latestSolution.feeReward);
            return latestSolution.feeReward;
        }
        return 0;
    }

    /** @dev Called from within submitSolution to update the token prices.
      * @param prices list of prices for touched tokens only, first price is always fee token price
      * @param tokenIdsForPrice price[i] is the price for the token with tokenID tokenIdsForPrice[i]
      */
    function updateCurrentPrices(uint128[] memory prices, uint16[] memory tokenIdsForPrice) private {
        for (uint256 i = 0; i < latestSolution.tokenIdsForPrice.length; i++) {
            currentPrices[latestSolution.tokenIdsForPrice[i]] = 0;
        }
        for (uint256 i = 0; i < tokenIdsForPrice.length; i++) {
            currentPrices[tokenIdsForPrice[i]] = prices[i];
        }
    }

    /** @dev Updates an order's remaing requested sell amount upon (partial) execution of a standing order
      * @param owner order's corresponding user address
      * @param orderId index of order in list of owner's orders
      * @param executedAmount proportion of order's requested sellAmount that was filled.
      */
    function updateRemainingOrder(address owner, uint16 orderId, uint128 executedAmount) private {
        if (isOrderWithLimitedAmount(orders[owner][orderId])) {
            orders[owner][orderId].usedAmount = orders[owner][orderId].usedAmount.add(executedAmount).toUint128();
        }
    }

    /** @dev The inverse of updateRemainingOrder, called when reverting a solution in favour of a better one.
      * @param owner order's corresponding user address
      * @param orderId index of order in list of owner's orders
      * @param executedAmount proportion of order's requested sellAmount that was filled.
      */
    function revertRemainingOrder(address owner, uint16 orderId, uint128 executedAmount) private {
        if (isOrderWithLimitedAmount(orders[owner][orderId])) {
            orders[owner][orderId].usedAmount = orders[owner][orderId].usedAmount.sub(executedAmount).toUint128();
        }
    }

    /** @dev Checks whether an order is intended to track its usedAmount
      * @param order order under inspection
      * @return true if the given order does track its usedAmount
      */
    function isOrderWithLimitedAmount(Order memory order) private pure returns (bool) {
        return order.priceNumerator != UNLIMITED_ORDER_AMOUNT && order.priceDenominator != UNLIMITED_ORDER_AMOUNT;
    }

    /** @dev This function writes solution information into contract storage
      * @param batchId index of referenced auction
      * @param owners array of addresses corresponding to touched orders
      * @param orderIds array of order indices used in parallel with owners to identify touched order
      * @param volumes executed buy amounts for each order identified by index of owner-orderId arrays
      * @param tokenIdsForPrice price[i] is the price for the token with tokenID tokenIdsForPrice[i]
      */
    function documentTrades(
        uint32 batchId,
        address[] memory owners,
        uint16[] memory orderIds,
        uint128[] memory volumes,
        uint16[] memory tokenIdsForPrice
    ) private {
        latestSolution.batchId = batchId;
        for (uint256 i = 0; i < owners.length; i++) {
            latestSolution.trades.push(TradeData({owner: owners[i], orderId: orderIds[i], volume: volumes[i]}));
        }
        latestSolution.tokenIdsForPrice = tokenIdsForPrice;
        latestSolution.solutionSubmitter = msg.sender;
    }

    /** @dev reverts all relevant contract storage relating to an overwritten auction solution.
      */
    function undoCurrentSolution() private {
        if (currentBatchHasSolution()) {
            for (uint256 i = 0; i < latestSolution.trades.length; i++) {
                address owner = latestSolution.trades[i].owner;
                uint16 orderId = latestSolution.trades[i].orderId;
                Order memory order = orders[owner][orderId];
                (, uint128 sellAmount) = getTradedAmounts(latestSolution.trades[i].volume, order);
                addBalance(owner, tokenIdToAddressMap(order.sellToken), sellAmount);
            }
            for (uint256 i = 0; i < latestSolution.trades.length; i++) {
                address owner = latestSolution.trades[i].owner;
                uint16 orderId = latestSolution.trades[i].orderId;
                Order memory order = orders[owner][orderId];
                (uint128 buyAmount, uint128 sellAmount) = getTradedAmounts(latestSolution.trades[i].volume, order);
                revertRemainingOrder(owner, orderId, sellAmount);
                subtractBalanceUnchecked(owner, tokenIdToAddressMap(order.buyToken), buyAmount);
                emit TradeReversion(owner, orderId, order.sellToken, order.buyToken, sellAmount, buyAmount);
            }
            // subtract granted fees:
            subtractBalanceUnchecked(latestSolution.solutionSubmitter, tokenIdToAddressMap(0), latestSolution.feeReward);
        }
    }

    /** @dev determines if value is better than currently and updates if it is.
      * @param newObjectiveValue proposed value to be updated if a great enough improvement on the current objective value
      */
    function checkAndOverrideObjectiveValue(uint256 newObjectiveValue) private {
        require(
            isObjectiveValueSufficientlyImproved(newObjectiveValue),
            "New objective doesn't sufficiently improve current solution"
        );
        latestSolution.objectiveValue = newObjectiveValue;
    }

    // Private view
    /** @dev Evaluates utility of executed trade
      * @param execBuy represents proportion of order executed (in terms of buy amount)
      * @param order the sell order whose utility is being evaluated
      * @return Utility = ((execBuy * order.sellAmt - execSell * order.buyAmt) * price.buyToken) / order.sellAmt
      */
    function evaluateUtility(uint128 execBuy, Order memory order) private view returns (uint256) {
        // Utility = ((execBuy * order.sellAmt - execSell * order.buyAmt) * price.buyToken) / order.sellAmt
        uint256 execSellTimesBuy = getExecutedSellAmount(execBuy, currentPrices[order.buyToken], currentPrices[order.sellToken])
            .mul(order.priceNumerator);

        uint256 roundedUtility = execBuy.sub(execSellTimesBuy.div(order.priceDenominator)).mul(currentPrices[order.buyToken]);
        uint256 utilityError = execSellTimesBuy.mod(order.priceDenominator).mul(currentPrices[order.buyToken]).div(
            order.priceDenominator
        );
        return roundedUtility.sub(utilityError);
    }

    /** @dev computes a measure of how much of an order was disregarded (only valid when limit price is respected)
      * @param order the sell order whose disregarded utility is being evaluated
      * @param user address of order's owner
      * @return disregardedUtility of the order (after it has been applied)
      * Note that:
      * |disregardedUtility| = (limitTerm * leftoverSellAmount) / order.sellAmount
      * where limitTerm = price.SellToken * order.sellAmt - order.buyAmt * price.buyToken / (1 - phi)
      * and leftoverSellAmount = order.sellAmt - execSellAmt
      * Balances and orders have all been updated so: sellAmount - execSellAmt == remainingAmount(order).
      * For correctness, we take the minimum of this with the user's token balance.
      */
    function evaluateDisregardedUtility(Order memory order, address user) private view returns (uint256) {
        uint256 leftoverSellAmount = Math.min(getRemainingAmount(order), getBalance(user, tokenIdToAddressMap(order.sellToken)));
        uint256 limitTermLeft = currentPrices[order.sellToken].mul(order.priceDenominator);
        uint256 limitTermRight = order.priceNumerator.mul(currentPrices[order.buyToken]).mul(FEE_DENOMINATOR).div(
            FEE_DENOMINATOR - 1
        );
        uint256 limitTerm = 0;
        if (limitTermLeft > limitTermRight) {
            limitTerm = limitTermLeft.sub(limitTermRight);
        }
        return leftoverSellAmount.mul(limitTerm).div(order.priceDenominator);
    }

    /** @dev Evaluates executedBuy amount based on prices and executedBuyAmout (fees included)
      * @param executedBuyAmount amount of buyToken executed for purchase in batch auction
      * @param buyTokenPrice uniform clearing price of buyToken
      * @param sellTokenPrice uniform clearing price of sellToken
      * @return executedSellAmount as expressed in Equation (2)
      * https://github.com/gnosis/dex-contracts/issues/173#issuecomment-526163117
      * execSellAmount * p[sellToken] * (1 - phi) == execBuyAmount * p[buyToken]
      * where phi = 1/FEE_DENOMINATOR
      * Note that: 1 - phi = (FEE_DENOMINATOR - 1) / FEE_DENOMINATOR
      * And so, 1/(1-phi) = FEE_DENOMINATOR / (FEE_DENOMINATOR - 1)
      * execSellAmount = (execBuyAmount * p[buyToken]) / (p[sellToken] * (1 - phi))
      *                = (execBuyAmount * buyTokenPrice / sellTokenPrice) * FEE_DENOMINATOR / (FEE_DENOMINATOR - 1)
      * in order to minimize rounding errors, the order of operations is switched
      *                = ((executedBuyAmount * buyTokenPrice) / (FEE_DENOMINATOR - 1)) * FEE_DENOMINATOR) / sellTokenPrice
      */
    function getExecutedSellAmount(uint128 executedBuyAmount, uint128 buyTokenPrice, uint128 sellTokenPrice)
        private
        pure
        returns (uint128)
    {
        /* solium-disable indentation */
        return
            uint256(executedBuyAmount)
                .mul(buyTokenPrice)
                .div(FEE_DENOMINATOR - 1)
                .mul(FEE_DENOMINATOR)
                .div(sellTokenPrice)
                .toUint128();
        /* solium-enable indentation */
    }

    /** @dev used to determine if solution if first provided in current batch
      * @return true if `latestSolution` is storing a solution for current batch, else false
      */
    function currentBatchHasSolution() private view returns (bool) {
        return latestSolution.batchId == getCurrentBatchId() - 1;
    }

    // Private view
    /** @dev Compute trade execution based on executedBuyAmount and relevant token prices
      * @param executedBuyAmount executed buy amount
      * @param order contains relevant buy-sell token information
      * @return (executedBuyAmount, executedSellAmount)
      */
    function getTradedAmounts(uint128 executedBuyAmount, Order memory order) private view returns (uint128, uint128) {
        uint128 executedSellAmount = getExecutedSellAmount(
            executedBuyAmount,
            currentPrices[order.buyToken],
            currentPrices[order.sellToken]
        );
        return (executedBuyAmount, executedSellAmount);
    }

    /** @dev Checks that the proposed objective value is a significant enough improvement on the latest one
      * @param objectiveValue the proposed objective value to check
      * @return true if the objectiveValue is a significant enough improvement, false otherwise
      */
    function isObjectiveValueSufficientlyImproved(uint256 objectiveValue) private view returns (bool) {
        return (objectiveValue.mul(IMPROVEMENT_DENOMINATOR) > getCurrentObjectiveValue().mul(IMPROVEMENT_DENOMINATOR + 1));
    }

    // Private pure
    /** @dev used to determine if an order is valid for specific auction/batch
      * @param order object whose validity is in question
      * @param batchId auction index of validity
      * @return true if order is valid in auction batchId else false
      */
    function checkOrderValidity(Order memory order, uint32 batchId) private pure returns (bool) {
        return order.validFrom <= batchId && order.validUntil >= batchId;
    }

    /** @dev computes the remaining sell amount for a given order
      * @param order the order for which remaining amount should be calculated
      * @return the remaining sell amount
      */
    function getRemainingAmount(Order memory order) private pure returns (uint128) {
        return order.priceDenominator - order.usedAmount;
    }

    /** @dev called only by getEncodedOrders and used to pack auction info into bytes
      * @param user list of tokenIds
      * @param sellTokenBalance user's account balance of sell token
      * @param order a sell order
      * @return byte encoded, packed, concatenation of relevant order information
      */
    function encodeAuctionElement(address user, uint256 sellTokenBalance, Order memory order)
        private
        pure
        returns (bytes memory element)
    {
        element = abi.encodePacked(user);
        element = element.concat(abi.encodePacked(sellTokenBalance));
        element = element.concat(abi.encodePacked(order.buyToken));
        element = element.concat(abi.encodePacked(order.sellToken));
        element = element.concat(abi.encodePacked(order.validFrom));
        element = element.concat(abi.encodePacked(order.validUntil));
        element = element.concat(abi.encodePacked(order.priceNumerator));
        element = element.concat(abi.encodePacked(order.priceDenominator));
        element = element.concat(abi.encodePacked(getRemainingAmount(order)));
        return element;
    }

    /** @dev determines if value is better than currently and updates if it is.
      * @param amounts array of values to be verified with AMOUNT_MINIMUM
      */
    function verifyAmountThreshold(uint128[] memory amounts) private pure returns (bool) {
        for (uint256 i = 0; i < amounts.length; i++) {
            if (amounts[i] < AMOUNT_MINIMUM) {
                return false;
            }
        }
        return true;
    }
}

Contract Security Audit

Contract ABI

API
[{"constant":true,"inputs":[],"name":"IMPROVEMENT_DENOMINATOR","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getSecondsRemainingInBatch","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getEncodedOrders","outputs":[{"name":"elements","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"buyToken","type":"uint16"},{"name":"sellToken","type":"uint16"},{"name":"validUntil","type":"uint32"},{"name":"buyAmount","type":"uint128"},{"name":"sellAmount","type":"uint128"}],"name":"placeOrder","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"batchId","type":"uint32"},{"name":"claimedObjectiveValue","type":"uint256"},{"name":"owners","type":"address[]"},{"name":"orderIds","type":"uint16[]"},{"name":"buyVolumes","type":"uint128[]"},{"name":"prices","type":"uint128[]"},{"name":"tokenIdsForPrice","type":"uint16[]"}],"name":"submitSolution","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"id","type":"uint16"}],"name":"tokenIdToAddressMap","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"amount","type":"uint256"}],"name":"requestWithdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"FEE_FOR_LISTING_TOKEN_IN_OWL","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"previousPageUser","type":"address"},{"name":"pageSize","type":"uint16"}],"name":"getUsersPaginated","outputs":[{"name":"users","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"amount","type":"uint256"}],"name":"deposit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"orderIds","type":"uint16[]"}],"name":"cancelOrders","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"AMOUNT_MINIMUM","outputs":[{"name":"","type":"uint128"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"feeToken","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"buyTokens","type":"uint16[]"},{"name":"sellTokens","type":"uint16[]"},{"name":"validFroms","type":"uint32[]"},{"name":"validUntils","type":"uint32[]"},{"name":"buyAmounts","type":"uint128[]"},{"name":"sellAmounts","type":"uint128[]"}],"name":"placeValidFromOrders","outputs":[{"name":"orderIds","type":"uint16[]"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint16"}],"name":"currentPrices","outputs":[{"name":"","type":"uint128"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"user","type":"address"}],"name":"getEncodedUserOrders","outputs":[{"name":"elements","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"uint256"}],"name":"orders","outputs":[{"name":"buyToken","type":"uint16"},{"name":"sellToken","type":"uint16"},{"name":"validFrom","type":"uint32"},{"name":"validUntil","type":"uint32"},{"name":"priceNumerator","type":"uint128"},{"name":"priceDenominator","type":"uint128"},{"name":"usedAmount","type":"uint128"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UNLIMITED_ORDER_AMOUNT","outputs":[{"name":"","type":"uint128"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"numTokens","outputs":[{"name":"","type":"uint16"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"lastCreditBatchId","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"previousPageUser","type":"address"},{"name":"previousPageUserOffset","type":"uint16"},{"name":"pageSize","type":"uint16"}],"name":"getEncodedUsersPaginated","outputs":[{"name":"elements","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"hasToken","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"latestSolution","outputs":[{"name":"batchId","type":"uint32"},{"name":"solutionSubmitter","type":"address"},{"name":"feeReward","type":"uint256"},{"name":"objectiveValue","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"user","type":"address"},{"name":"token","type":"address"}],"name":"getPendingDeposit","outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"cancellations","type":"uint16[]"},{"name":"buyTokens","type":"uint16[]"},{"name":"sellTokens","type":"uint16[]"},{"name":"validFroms","type":"uint32[]"},{"name":"validUntils","type":"uint32[]"},{"name":"buyAmounts","type":"uint128[]"},{"name":"sellAmounts","type":"uint128[]"}],"name":"replaceOrders","outputs":[{"name":"","type":"uint16[]"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"user","type":"address"},{"name":"token","type":"address"}],"name":"getPendingWithdraw","outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"batchId","type":"uint32"}],"name":"acceptingSolutions","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"}],"name":"addToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"user","type":"address"},{"name":"token","type":"address"}],"name":"getBalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FEE_DENOMINATOR","outputs":[{"name":"","type":"uint128"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ENCODED_AUCTION_ELEMENT_WIDTH","outputs":[{"name":"","type":"uint128"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"BATCH_TIME","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getCurrentBatchId","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"user","type":"address"},{"name":"offset","type":"uint16"},{"name":"pageSize","type":"uint16"}],"name":"getEncodedUserOrdersPaginated","outputs":[{"name":"elements","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"tokenAddressToIdMap","outputs":[{"name":"","type":"uint16"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"amount","type":"uint256"},{"name":"batchId","type":"uint32"}],"name":"requestFutureWithdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"user","type":"address"},{"name":"token","type":"address"}],"name":"hasValidWithdrawRequest","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"MAX_TOKENS","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"},{"name":"token","type":"address"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"MAX_TOUCHED_ORDERS","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getCurrentObjectiveValue","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"maxTokens","type":"uint256"},{"name":"_feeToken","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":false,"name":"index","type":"uint16"},{"indexed":true,"name":"buyToken","type":"uint16"},{"indexed":true,"name":"sellToken","type":"uint16"},{"indexed":false,"name":"validFrom","type":"uint32"},{"indexed":false,"name":"validUntil","type":"uint32"},{"indexed":false,"name":"priceNumerator","type":"uint128"},{"indexed":false,"name":"priceDenominator","type":"uint128"}],"name":"OrderPlacement","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"token","type":"address"},{"indexed":false,"name":"id","type":"uint16"}],"name":"TokenListing","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":false,"name":"id","type":"uint16"}],"name":"OrderCancellation","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":false,"name":"id","type":"uint16"}],"name":"OrderDeletion","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"orderId","type":"uint16"},{"indexed":true,"name":"sellToken","type":"uint16"},{"indexed":false,"name":"buyToken","type":"uint16"},{"indexed":false,"name":"executedSellAmount","type":"uint128"},{"indexed":false,"name":"executedBuyAmount","type":"uint128"}],"name":"Trade","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"orderId","type":"uint16"},{"indexed":true,"name":"sellToken","type":"uint16"},{"indexed":false,"name":"buyToken","type":"uint16"},{"indexed":false,"name":"executedSellAmount","type":"uint128"},{"indexed":false,"name":"executedBuyAmount","type":"uint128"}],"name":"TradeReversion","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"submitter","type":"address"},{"indexed":false,"name":"utility","type":"uint256"},{"indexed":false,"name":"disregardedUtility","type":"uint256"},{"indexed":false,"name":"burntFees","type":"uint256"},{"indexed":false,"name":"lastAuctionBurntFees","type":"uint256"},{"indexed":false,"name":"prices","type":"uint128[]"},{"indexed":false,"name":"tokenIdsForPrice","type":"uint16[]"}],"name":"SolutionSubmission","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"user","type":"address"},{"indexed":true,"name":"token","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"batchId","type":"uint32"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"user","type":"address"},{"indexed":true,"name":"token","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"batchId","type":"uint32"}],"name":"WithdrawRequest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"user","type":"address"},{"indexed":true,"name":"token","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"}]

60806040523480156200001157600080fd5b5060405160408062005f18833981018060405260408110156200003357600080fd5b5080516020918201516000808052600584527f05b8ccbb9d4d8fb16ea74ce3c29a41f1b461fbdaff4714a0d9a8eb05499746bc80546001600160801b031916670de0b6b3a76400001790556002839055600380546001600160a01b038085166201000090810262010000600160b01b03199093169290921792839055604080517f095ea7b3000000000000000000000000000000000000000000000000000000008152306004820152600019602482015290519697959692909304169363095ea7b3936044808501949293928390030190829087803b1580156200011657600080fd5b505af11580156200012b573d6000803e3d6000fd5b505050506040513d60208110156200014257600080fd5b50620001579050816200015f602090811b901c565b5050620003fd565b60025460035461ffff1610620001d657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f4d617820746f6b656e7320726561636865640000000000000000000000000000604482015290519081900360640190fd5b60035461ffff16156200027657600354604080517f4417f4db000000000000000000000000000000000000000000000000000000008152336004820152678ac7230489e8000060248201529051620100009092046001600160a01b031691634417f4db9160448082019260009290919082900301818387803b1580156200025c57600080fd5b505af115801562000271573d6000803e3d6000fd5b505050505b600354604080517f9f3cb655000000000000000000000000000000000000000000000000000000008152600e600482015261ffff90921660248301526001600160a01b03831660448301525173ed4d05496c71e71cc2a8726af1242c22108d176191639f3cb655916064808301926020929190829003018186803b158015620002fe57600080fd5b505af415801562000313573d6000803e3d6000fd5b505050506040513d60208110156200032a57600080fd5b50516200039857604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f546f6b656e20616c726561647920726567697374657265640000000000000000604482015290519081900360640190fd5b600354604080516001600160a01b038416815261ffff909216602083015280517fe4b282c4351ffe36572a572de193a7de086edc47c9e62669fe6ab49fc53a33139281900390910190a1506003805461ffff8082166001011661ffff19909116179055565b615b0b806200040d6000396000f3fe608060405234801561001057600080fd5b50600436106102535760003560e01c806395466a4611610146578063e1d5f64e116100c3578063f36b635511610087578063f36b635514611217578063f3f479821461124f578063f47c84c51461127d578063f940e38514611285578063fb736d32146112b3578063ff97c626146112bb57610253565b8063e1d5f64e146111a1578063e48c015e146111a9578063e720ac8e146111b1578063ed2da357146111b9578063ef574d23146111f157610253565b8063c33eb9f61161010a578063c33eb9f6146110f4578063c49598fb14611122578063d48bfca714611145578063d4fac45d1461116b578063d73792a91461119957610253565b806395466a4614610c4c5780639bb0f59914610c845780639cc84ed314610cbe578063b3c0afa114610cfb578063c1ef283814610d4757610253565b80634bdc1b4c116101d457806372f3dd391161019857806372f3dd3914610b38578063793b8c6d14610b5e5780637fb47b0614610bde5780638e499bcf14610be6578063907767c014610c0557610253565b80634bdc1b4c146106cf57806361ed16d014610770578063647846a51461079457806365cc3e781461079c57806366367c1014610b1757610253565b80632f10d0821161021b5780632f10d08214610600578063397a1b281461063d57806341e383ed1461066b57806343383ac31461067357806347e7ef24146106a357610253565b8063094c7e191461025857806317569c1d1461027257806323d4a3c91461027a57806326c3d394146102f75780632e4c83bd14610345575b600080fd5b6102606112c3565b60408051918252519081900360200190f35b6102606112c8565b6102826112d5565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102bc5781810151838201526020016102a4565b50505050905090810190601f1680156102e95780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610260600480360360a081101561030d57600080fd5b5061ffff813581169160208101359091169063ffffffff604082013516906001600160801b03606082013581169160800135166114be565b610260600480360360e081101561035b57600080fd5b63ffffffff82351691602081013591810190606081016040820135600160201b81111561038757600080fd5b82018360208201111561039957600080fd5b803590602001918460208302840111600160201b831117156103ba57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561040957600080fd5b82018360208201111561041b57600080fd5b803590602001918460208302840111600160201b8311171561043c57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561048b57600080fd5b82018360208201111561049d57600080fd5b803590602001918460208302840111600160201b831117156104be57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561050d57600080fd5b82018360208201111561051f57600080fd5b803590602001918460208302840111600160201b8311171561054057600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561058f57600080fd5b8201836020820111156105a157600080fd5b803590602001918460208302840111600160201b831117156105c257600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506114e3945050505050565b6106216004803603602081101561061657600080fd5b503561ffff16611f34565b604080516001600160a01b039092168252519081900360200190f35b6106696004803603604081101561065357600080fd5b506001600160a01b038135169060200135611fcb565b005b610260611fe1565b6102826004803603604081101561068957600080fd5b5080356001600160a01b0316906020013561ffff16611fed565b610669600480360360408110156106b957600080fd5b506001600160a01b038135169060200135612259565b610669600480360360208110156106e557600080fd5b810190602081018135600160201b8111156106ff57600080fd5b82018360208201111561071157600080fd5b803590602001918460208302840111600160201b8311171561073257600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955061235a945050505050565b6107786125d9565b604080516001600160801b039092168252519081900360200190f35b6106216125df565b610ac7600480360360c08110156107b257600080fd5b810190602081018135600160201b8111156107cc57600080fd5b8201836020820111156107de57600080fd5b803590602001918460208302840111600160201b831117156107ff57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561084e57600080fd5b82018360208201111561086057600080fd5b803590602001918460208302840111600160201b8311171561088157600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b8111156108d057600080fd5b8201836020820111156108e257600080fd5b803590602001918460208302840111600160201b8311171561090357600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561095257600080fd5b82018360208201111561096457600080fd5b803590602001918460208302840111600160201b8311171561098557600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b8111156109d457600080fd5b8201836020820111156109e657600080fd5b803590602001918460208302840111600160201b83111715610a0757600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115610a5657600080fd5b820183602082011115610a6857600080fd5b803590602001918460208302840111600160201b83111715610a8957600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506125f4945050505050565b60408051602080825283518183015283519192839290830191858101910280838360005b83811015610b03578181015183820152602001610aeb565b505050509050019250505060405180910390f35b61077860048036036020811015610b2d57600080fd5b503561ffff166126e2565b61028260048036036020811015610b4e57600080fd5b50356001600160a01b03166126fd565b610b8a60048036036040811015610b7457600080fd5b506001600160a01b03813516906020013561270d565b6040805161ffff988916815296909716602087015263ffffffff948516868801529290931660608501526001600160801b03908116608085015291821660a08401521660c082015290519081900360e00190f35b61077861278b565b610bee612791565b6040805161ffff9092168252519081900360200190f35b610c3360048036036040811015610c1b57600080fd5b506001600160a01b038135811691602001351661279b565b6040805163ffffffff9092168252519081900360200190f35b61028260048036036060811015610c6257600080fd5b506001600160a01b038135169061ffff602082013581169160400135166127be565b610caa60048036036020811015610c9a57600080fd5b50356001600160a01b03166129d7565b604080519115158252519081900360200190f35b610cc6612a3f565b6040805163ffffffff90951685526001600160a01b039093166020850152838301919091526060830152519081900360800190f35b610d2960048036036040811015610d1157600080fd5b506001600160a01b0381358116916020013516612a63565b6040805192835263ffffffff90911660208301528051918290030190f35b610ac7600480360360e0811015610d5d57600080fd5b810190602081018135600160201b811115610d7757600080fd5b820183602082011115610d8957600080fd5b803590602001918460208302840111600160201b83111715610daa57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115610df957600080fd5b820183602082011115610e0b57600080fd5b803590602001918460208302840111600160201b83111715610e2c57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115610e7b57600080fd5b820183602082011115610e8d57600080fd5b803590602001918460208302840111600160201b83111715610eae57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115610efd57600080fd5b820183602082011115610f0f57600080fd5b803590602001918460208302840111600160201b83111715610f3057600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115610f7f57600080fd5b820183602082011115610f9157600080fd5b803590602001918460208302840111600160201b83111715610fb257600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561100157600080fd5b82018360208201111561101357600080fd5b803590602001918460208302840111600160201b8311171561103457600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561108357600080fd5b82018360208201111561109557600080fd5b803590602001918460208302840111600160201b831117156110b657600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550612abd945050505050565b610d296004803603604081101561110a57600080fd5b506001600160a01b0381358116916020013516612ae2565b610caa6004803603602081101561113857600080fd5b503563ffffffff16612b3c565b6106696004803603602081101561115b57600080fd5b50356001600160a01b0316612b70565b6102606004803603604081101561118157600080fd5b506001600160a01b0381358116916020013516612dac565b610778612ed5565b610778612edb565b610c33612ee0565b610c33612ee6565b610282600480360360608110156111cf57600080fd5b506001600160a01b038135169061ffff60208201358116916040013516612eee565b610bee6004803603602081101561120757600080fd5b50356001600160a01b0316613064565b6106696004803603606081101561122d57600080fd5b5080356001600160a01b0316906020810135906040013563ffffffff166130cc565b610caa6004803603604081101561126557600080fd5b506001600160a01b03813581169160200135166131d6565b61026061324e565b6106696004803603604081101561129b57600080fd5b506001600160a01b0381358116916020013516613254565b610260613451565b610260613456565b606481565b61012c4281900690035b90565b60606000600c73cddb32b6bb2808d5b5115daab207479ce98d263663d58c862c90916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561132b57600080fd5b505af415801561133f573d6000803e3d6000fd5b505050506040513d602081101561135557600080fd5b505111156112d2576000600c73cddb32b6bb2808d5b5115daab207479ce98d26366368fb2bc290916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156113b157600080fd5b505af41580156113c5573d6000803e3d6000fd5b505050506040513d60208110156113db57600080fd5b5051905060005b806114b9576114006113f3836126fd565b849063ffffffff61348916565b600d549093506001600160a01b0383811691161415611421575060016114b4565b60408051600160e21b63218777b3028152600c60048201526001600160a01b0384166024820152905173cddb32b6bb2808d5b5115daab207479ce98d26369163861ddecc916044808301926020929190829003018186803b15801561148557600080fd5b505af4158015611499573d6000803e3d6000fd5b505050506040513d60208110156114af57600080fd5b505191505b6113e2565b505090565b60006114d586866114cd612ee6565b878787613506565b61ffff169695505050505050565b60006114ee88612b3c565b61152c57604051600160e51b62461bcd02815260040180806020018281038252602f815260200180615889602f913960400191505060405180910390fd5b61153587613a87565b61157357604051600160e51b62461bcd02815260040180806020018281038252603f8152602001806159e4603f913960400191505060405180910390fd5b61157c83613aba565b6115ba57604051600160e51b62461bcd02815260040180806020018281038252602c8152602001806158fd602c913960400191505060405180910390fd5b816000815181106115c757fe5b602002602001015161ffff166000141561162b5760408051600160e51b62461bcd02815260206004820152601a60248201527f46656520746f6b656e2068617320666978656420707269636521000000000000604482015290519081900360640190fd5b61163482613b11565b61167257604051600160e51b62461bcd02815260040180806020018281038252602181526020018061599d6021913960400191505060405180910390fd5b601e865111156116b657604051600160e51b62461bcd0281526004018080602001828103825260238152602001806157e36023913960400191505060405180910390fd5b60006116c0613b69565b90506116ca613bfe565b6116d48484613fcd565b6116e060076000615620565b60606116eb846140bb565b90506000805b8951811015611b5557611702615641565b600460008c848151811061171257fe5b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000208a838151811061174657fe5b602002602001015161ffff168154811061175c57fe5b60009182526020918290206040805160e0810182526002909302909101805461ffff8082168552620100008204169484019490945263ffffffff600160201b8504811692840192909252600160401b840490911660608301526001600160801b03600160601b909304831660808301526001015480831660a0830152600160801b900490911660c082015290506117f3818e6140f2565b6118475760408051600160e51b62461bcd02815260206004820152601060248201527f4f7264657220697320696e76616c696400000000000000000000000000000000604482015290519081900360640190fd5b6000806118678b858151811061185957fe5b602002602001015184614125565b90925090506127106001600160801b03831610156118b957604051600160e51b62461bcd0281526004018080602001828103825260238152602001806157c06023913960400191505060405180910390fd5b6127106001600160801b038216101561190657604051600160e51b62461bcd028152600401808060200182810382526024815260200180615a4c6024913960400191505060405180910390fd5b825160208401516119219188918c868663ffffffff61417316565b806001600160801b031661193484614227565b6001600160801b0316101561197d57604051600160e51b62461bcd02815260040180806020018281038252603181526020018061594b6031913960400191505060405180910390fd5b60a083015161199e906001600160801b03848116911663ffffffff61423616565b60808401516119bf906001600160801b03848116911663ffffffff61423616565b1115611a155760408051600160e51b62461bcd02815260206004820152601960248201527f6c696d6974207072696365206e6f742073617469736669656400000000000000604482015290519081900360640190fd5b611a2f611a228385614292565b869063ffffffff6143c516565b9450611a628d8581518110611a4057fe5b60200260200101518d8681518110611a5457fe5b602002602001015183614422565b611a958d8581518110611a7157fe5b6020026020010151611a868560000151611f34565b846001600160801b03166145b9565b826020015161ffff168c8581518110611aaa57fe5b602002602001015161ffff168e8681518110611ac257fe5b60200260200101516001600160a01b03167fafa5bc1fb80950b7cb2353ba0cf16a6d68de75801f2dac54b2dae9268450010a86600001518587604051808461ffff1661ffff168152602001836001600160801b03166001600160801b03168152602001826001600160801b03166001600160801b03168152602001935050505060405180910390a45050506001016116f1565b5060005b8951811015611caf57611b6a615641565b600460008c8481518110611b7a57fe5b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000208a8381518110611bae57fe5b602002602001015161ffff1681548110611bc457fe5b600091825260208083206040805160e0810182526002909402909101805461ffff8082168652620100008204169385019390935263ffffffff600160201b8404811692850192909252600160401b830490911660608401526001600160801b03600160601b909204821660808401526001015480821660a0840152600160801b90041660c08201528a51909250611c6f908b9085908110611c6157fe5b602002602001015183614125565b915050611ca58c8481518110611c8157fe5b6020026020010151611c968460200151611f34565b836001600160801b0316614619565b5050600101611b59565b506000805b8a51811015611dda57611dd0611dc3600460008e8581518110611cd357fe5b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000208c8481518110611d0757fe5b602002602001015161ffff1681548110611d1d57fe5b60009182526020918290206040805160e0810182526002909302909101805461ffff8082168552620100008204169484019490945263ffffffff600160201b8504811692840192909252600160401b840490911660608301526001600160801b03600160601b909304831660808301526001015480831660a0830152600160801b900490911660c08201528d518e9085908110611db657fe5b6020026020010151614685565b839063ffffffff6143c516565b9150600101611cb4565b5060006002611de885614788565b81611def57fe5b0490506000611e1483611e08868563ffffffff6143c516565b9063ffffffff6147a616565b9050611e1f816147e8565b611e2882614834565b611e3185614850565b611e3e8e8d8d8d8c614925565b336001600160a01b03167f2140b6253bf38aea0a4ac9e9e6427b256e4035d60df4a85bb139ce975eb6b41d8585858a8e8e604051808781526020018681526020018581526020018481526020018060200180602001838103835285818151815260200191508051906020019060200280838360005b83811015611ecb578181015183820152602001611eb3565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015611f0a578181015183820152602001611ef2565b505050509050019850505050505050505060405180910390a29d9c50505050505050505050505050565b60408051600160e21b632ffeb2d7028152600e600482015261ffff83166024820152905160009173ed4d05496c71e71cc2a8726af1242c22108d17619163bffacb5c91604480820192602092909190829003018186803b158015611f9757600080fd5b505af4158015611fab573d6000803e3d6000fd5b505050506040513d6020811015611fc157600080fd5b505190505b919050565b611fdd8282611fd8612ee6565b6130cc565b5050565b678ac7230489e8000081565b6060600c73cddb32b6bb2808d5b5115daab207479ce98d263663d58c862c90916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561204157600080fd5b505af4158015612055573d6000803e3d6000fd5b505050506040513d602081101561206b57600080fd5b505161207657612253565b6000836001600160a01b03811661214d5760408051600160e11b63347d95e1028152600c6004820152905173cddb32b6bb2808d5b5115daab207479ce98d2636916368fb2bc2916024808301926020929190829003018186803b1580156120dc57600080fd5b505af41580156120f0573d6000803e3d6000fd5b505050506040513d602081101561210657600080fd5b5051604080516001600160a01b03831660601b602082015281518082036014018152603490910190915290915061214490849063ffffffff61348916565b92506001909101905b8361ffff168261ffff161080156121725750600d546001600160a01b03828116911614155b156122505760408051600160e21b63218777b3028152600c60048201526001600160a01b0383166024820152905173cddb32b6bb2808d5b5115daab207479ce98d26369163861ddecc916044808301926020929190829003018186803b1580156121db57600080fd5b505af41580156121ef573d6000803e3d6000fd5b505050506040513d602081101561220557600080fd5b5051604080516001600160a01b03831660601b602082015281518082036014018152603490910190915290915061224390849063ffffffff61348916565b925060019091019061214d565b50505b92915050565b6122633383614a75565b61226f82333084614b35565b336000908152602081815260408083206001600160a01b03861684529091529020600101546122a4908263ffffffff6143c516565b336000908152602081815260408083206001600160a01b03871684529091529020600101556122d1612ee6565b336000818152602081815260408083206001600160a01b0388168085529252909120600201805463ffffffff191663ffffffff94909416939093179092557fc11cc34e93c67a93382b99f2498e9937198798f3c1c2888008ffc0eeb82f68c483612339612ee6565b6040805192835263ffffffff90911660208301528051918290030190a35050565b60006001612366612ee6565b03905060005b82518161ffff1610156125d45733600090815260046020526040902083516124469190859061ffff851690811061239f57fe5b602002602001015161ffff16815481106123b557fe5b60009182526020918290206040805160e0810182526002909302909101805461ffff8082168552620100008204169484019490945263ffffffff600160201b8504811692840192909252600160401b840490911660608301526001600160801b03600160601b909304831660808301526001015480831660a0830152600160801b900490911660c0820152836140f2565b612502573360009081526004602052604090208351849061ffff841690811061246b57fe5b602002602001015161ffff168154811061248157fe5b60009182526020822060029091020180546001600160e01b031916815560010155825133907f7b0a9854603fbbe7606a58b70d113bd0d1ec8475f1b8cc9603c2d377e67835cd90859061ffff85169081106124d857fe5b6020026020010151604051808261ffff1661ffff16815260200191505060405180910390a26125cc565b3360009081526004602052604090208351839190859061ffff851690811061252657fe5b602002602001015161ffff168154811061253c57fe5b906000526020600020906002020160000160086101000a81548163ffffffff021916908363ffffffff160217905550336001600160a01b03167f7a02963a37046835196f1a3185a036fd67cfca72283e46e4b3cdb99939851937848361ffff16815181106125a657fe5b6020026020010151604051808261ffff1661ffff16815260200191505060405180910390a25b60010161236c565b505050565b61271081565b6003546201000090046001600160a01b031681565b60608651604051908082528060200260200182016040528015612621578160200160208202803883390190505b50905060005b87518110156126d7576126b088828151811061263f57fe5b602002602001015188838151811061265357fe5b602002602001015188848151811061266757fe5b602002602001015188858151811061267b57fe5b602002602001015188868151811061268f57fe5b60200260200101518887815181106126a357fe5b6020026020010151613506565b8282815181106126bc57fe5b61ffff90921660209283029190910190910152600101612627565b509695505050505050565b6005602052600090815260409020546001600160801b031681565b6060612253826000600019612eee565b6004602052816000526040600020818154811061272657fe5b60009182526020909120600290910201805460019091015461ffff808316945062010000830416925063ffffffff600160201b8304811692600160401b8104909116916001600160801b03600160601b90920482169181811691600160801b90041687565b60001981565b60035461ffff1681565b600160209081526000928352604080842090915290825290205463ffffffff1681565b6060600c73cddb32b6bb2808d5b5115daab207479ce98d263663d58c862c90916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561281257600080fd5b505af4158015612826573d6000803e3d6000fd5b505050506040513d602081101561283c57600080fd5b5051612847576129d0565b82846001600160a01b0381166128db5760408051600160e11b63347d95e1028152600c6004820152905173cddb32b6bb2808d5b5115daab207479ce98d2636916368fb2bc2916024808301926020929190829003018186803b1580156128ac57600080fd5b505af41580156128c0573d6000803e3d6000fd5b505050506040513d60208110156128d657600080fd5b505190505b825161ffff8516906070900410156129cd576129116113f3828460706001600160801b031687518161290957fe5b048803612eee565b600d549093506001600160a01b038281169116141561293257506129d09050565b60408051600160e21b63218777b3028152600c60048201526001600160a01b038316602482015290516000935073cddb32b6bb2808d5b5115daab207479ce98d26369163861ddecc916044808301926020929190829003018186803b15801561299a57600080fd5b505af41580156129ae573d6000803e3d6000fd5b505050506040513d60208110156129c457600080fd5b505190506128db565b50505b9392505050565b60408051600160e11b636e8930a3028152600e60048201526001600160a01b0383166024820152905160009173ed4d05496c71e71cc2a8726af1242c22108d17619163dd12614691604480820192602092909190829003018186803b158015611f9757600080fd5b600654600954600a54600b5463ffffffff909316926001600160a01b039092169184565b600080612a6e61567d565b5050506001600160a01b039182166000908152602081815260408083209390941682529182528290208251808401909352600181015480845260029091015463ffffffff169290910182905291565b6060612ac88861235a565b612ad68787878787876125f4565b98975050505050505050565b600080612aed61567d565b5050506001600160a01b039182166000908152602081815260408083209390941682529182528290208251808401909352600381015480845260049091015463ffffffff169290910182905291565b60006001612b48612ee6565b0363ffffffff168263ffffffff161480156122535750603c612b686112c8565b101592915050565b60025460035461ffff1610612bcf5760408051600160e51b62461bcd02815260206004820152601260248201527f4d617820746f6b656e7320726561636865640000000000000000000000000000604482015290519081900360640190fd5b60035461ffff1615612c565760035460408051600160e01b634417f4db028152336004820152678ac7230489e8000060248201529051620100009092046001600160a01b031691634417f4db9160448082019260009290919082900301818387803b158015612c3d57600080fd5b505af1158015612c51573d6000803e3d6000fd5b505050505b60035460408051600160e01b639f3cb655028152600e600482015261ffff90921660248301526001600160a01b03831660448301525173ed4d05496c71e71cc2a8726af1242c22108d176191639f3cb655916064808301926020929190829003018186803b158015612cc757600080fd5b505af4158015612cdb573d6000803e3d6000fd5b505050506040513d6020811015612cf157600080fd5b5051612d475760408051600160e51b62461bcd02815260206004820152601860248201527f546f6b656e20616c726561647920726567697374657265640000000000000000604482015290519081900360640190fd5b600354604080516001600160a01b038416815261ffff909216602083015280517fe4b282c4351ffe36572a572de193a7de086edc47c9e62669fe6ab49fc53a33139281900390910190a1506003805461ffff8082166001011661ffff19909116179055565b6001600160a01b03808316600090815260208181526040808320938516835292905290812054612dda612ee6565b6001600160a01b038086166000908152602081815260408083209388168352929052206002015463ffffffff91821691161015612e4c576001600160a01b0380851660009081526020818152604080832093871683529290522060010154612e4990829063ffffffff6143c516565b90505b612e54612ee6565b6001600160a01b038086166000908152602081815260408083209388168352929052206004015463ffffffff918216911610156129d0576001600160a01b0380851660009081526020818152604080832093871683529290522060030154612ecd90612ec09083614b98565b829063ffffffff6147a616565b949350505050565b6103e881565b607081565b61012c81565b61012c420490565b6060825b6001600160a01b038516600090815260046020526040902054612f1b9061ffff86860116614b98565b8161ffff16101561305c5761305261304586612f8d88612f88600460008c6001600160a01b03166001600160a01b031681526020019081526020016000208761ffff1681548110612f6857fe5b600091825260209091206002909102015462010000900461ffff16611f34565b612dac565b6001600160a01b0389166000908152600460205260409020805461ffff8716908110612fb557fe5b60009182526020918290206040805160e0810182526002909302909101805461ffff8082168552620100008204169484019490945263ffffffff600160201b8504811692840192909252600160401b840490911660608301526001600160801b03600160601b909304831660808301526001015480831660a0830152600160801b900490911660c0820152614bae565b839063ffffffff61348916565b9150600101612ef2565b509392505050565b60408051600160e01b63b3129e1f028152600e60048201526001600160a01b0383166024820152905160009173ed4d05496c71e71cc2a8726af1242c22108d17619163b3129e1f91604480820192602092909190829003018186803b158015611f9757600080fd5b6130d633846131d6565b156130e5576130e53384613254565b6130ed612ee6565b63ffffffff168163ffffffff16101561313a57604051600160e51b62461bcd028152600401808060200182810382526022815260200180615abe6022913960400191505060405180910390fd5b60408051808201825283815263ffffffff83811660208084018281523360008181528084528781206001600160a01b038c16808352908552908890209651600388015591516004909601805463ffffffff19169690951695909517909355845187815290810191909152835191937f2c6245af506f0fc1089918c02c1d01bde9cc807609b334b3e7644d6dfb5a6c5e92918290030190a3505050565b60006131e0612ee6565b6001600160a01b038085166000908152602081815260408083209387168352929052206004015463ffffffff91821691161080156129d05750506001600160a01b0391821660009081526020818152604080832093909416825291909152206004015463ffffffff16151590565b60025481565b61325e8282614a75565b613266612ee6565b6001600160a01b038084166000908152602081815260408083209386168352929052206004015463ffffffff9182169116106132d657604051600160e51b62461bcd0281526004018080602001828103825260268152602001806159be6026913960400191505060405180910390fd5b6132de612ee6565b6001600160a01b0380841660009081526001602090815260408083209386168352929052205463ffffffff91821691161061334d57604051600160e51b62461bcd0281526004018080602001828103825260458152602001806158b86045913960600191505060405180910390fd5b6001600160a01b03828116600090815260208181526040808320938516835292905290812080546003909101546133849190614b98565b6001600160a01b03808516600090815260208181526040808320938716835292905220549091506133bb908263ffffffff6147a616565b6001600160a01b0384811660009081526020818152604080832093871683529290529081209182556003820155600401805463ffffffff19169055613401828483614dc0565b816001600160a01b0316836001600160a01b03167f9b1bfa7fa9ee420a16e124f794c35ac9f90472acc99140eb2f6447c714cad8eb836040518082815260200191505060405180910390a3505050565b601e81565b60006001613462612ee6565b60065463ffffffff908116929091031614156134815750600b546112d2565b5060006112d2565b6060806040519050835180825260208201818101602087015b818310156134ba5780518352602092830192016134a2565b50855184518101855292509050808201602086015b818310156134e75780518352602092830192016134cf565b508651929092011591909101601f01601f191660405250905092915050565b60408051600160e01b63ab8978b7028152600e600482015261ffff88166024820152905160009173ed4d05496c71e71cc2a8726af1242c22108d17619163ab8978b791604480820192602092909190829003018186803b15801561356957600080fd5b505af415801561357d573d6000803e3d6000fd5b505050506040513d602081101561359357600080fd5b50516135e95760408051600160e51b62461bcd02815260206004820152601860248201527f42757920746f6b656e206d757374206265206c69737465640000000000000000604482015290519081900360640190fd5b60408051600160e01b63ab8978b7028152600e600482015261ffff88166024820152905173ed4d05496c71e71cc2a8726af1242c22108d17619163ab8978b7916044808301926020929190829003018186803b15801561364857600080fd5b505af415801561365c573d6000803e3d6000fd5b505050506040513d602081101561367257600080fd5b50516136c85760408051600160e51b62461bcd02815260206004820152601960248201527f53656c6c20746f6b656e206d757374206265206c697374656400000000000000604482015290519081900360640190fd5b8561ffff168761ffff1614156137285760408051600160e51b62461bcd02815260206004820152601c60248201527f45786368616e676520746f6b656e73206e6f742064697374696e637400000000604482015290519081900360640190fd5b613730612ee6565b63ffffffff168563ffffffff16101561377d57604051600160e51b62461bcd0281526004018080602001828103825260228152602001806159296022913960400191505060405180910390fd5b60046000336001600160a01b03166001600160a01b031681526020019081526020016000206040518060e001604052808961ffff1681526020018861ffff1681526020018763ffffffff1681526020018663ffffffff168152602001856001600160801b03168152602001846001600160801b0316815260200160006001600160801b03168152509080600181540180825580915050906001820390600052602060002090600202016000909192909190915060008201518160000160006101000a81548161ffff021916908361ffff16021790555060208201518160000160026101000a81548161ffff021916908361ffff16021790555060408201518160000160046101000a81548163ffffffff021916908363ffffffff16021790555060608201518160000160086101000a81548163ffffffff021916908363ffffffff160217905550608082015181600001600c6101000a8154816001600160801b0302191690836001600160801b0316021790555060a08201518160010160006101000a8154816001600160801b0302191690836001600160801b0316021790555060c08201518160010160106101000a8154816001600160801b0302191690836001600160801b031602179055505050506000613984600160046000336001600160a01b03166001600160a01b031681526020019081526020016000208054905003614e15565b6040805161ffff808416825263ffffffff808b1660208401528916828401526001600160801b038089166060840152871660808301529151929350818a1692918b169133917fdecf6fde8243981299f7b7a776f29a9fc67a2c9848e25d77c50eb11fa58a7e21919081900360a00190a460408051600160e11b634743bd03028152600c6004820152336024820152905173cddb32b6bb2808d5b5115daab207479ce98d263691638e877a06916044808301926020929190829003018186803b158015613a4f57600080fd5b505af4158015613a63573d6000803e3d6000fd5b505050506040513d6020811015613a7957600080fd5b509098975050505050505050565b6000613aa26065613a96613456565b9063ffffffff61423616565b613ab383606463ffffffff61423616565b1192915050565b6000805b8251811015613b08576127106001600160801b0316838281518110613adf57fe5b60200260200101516001600160801b03161015613b00576000915050611fc6565b600101613abe565b50600192915050565b600060015b8251811015613b0857826001820381518110613b2e57fe5b602002602001015161ffff16838281518110613b4657fe5b602002602001015161ffff1611613b61576000915050611fc6565b600101613b16565b6000613b73614e5f565b613bf857600354600a5460408051600160e01b634417f4db028152306004820152602481019290925251620100009092046001600160a01b031691634417f4db9160448082019260009290919082900301818387803b158015613bd557600080fd5b505af1158015613be9573d6000803e3d6000fd5b5050600a5492506112d2915050565b50600090565b613c06614e5f565b15613fcb5760005b600754811015613d9b57600060066001018281548110613c2a57fe5b60009182526020822060029091020154600780546001600160a01b0390921693509084908110613c5657fe5b906000526020600020906002020160010160109054906101000a900461ffff169050613c80615641565b6001600160a01b0383166000908152600460205260409020805461ffff8416908110613ca857fe5b600091825260208083206040805160e0810182526002909402909101805461ffff8082168652620100008204169385019390935263ffffffff600160201b8404811692850192909252600160401b830490911660608401526001600160801b03600160601b909204821660808401526001015480821660a0840152600160801b90041660c082015260078054919350613d689187908110613d4557fe5b60009182526020909120600160029092020101546001600160801b031683614125565b915050613d8b84613d7c8460200151611f34565b836001600160801b0316614e81565b505060019092019150613c0e9050565b5060005b600754811015613fa857600060066001018281548110613dbb57fe5b60009182526020822060029091020154600780546001600160a01b0390921693509084908110613de757fe5b906000526020600020906002020160010160109054906101000a900461ffff169050613e11615641565b6001600160a01b0383166000908152600460205260409020805461ffff8416908110613e3957fe5b600091825260208083206040805160e0810182526002909402909101805461ffff8082168652620100008204169385019390935263ffffffff600160201b8404811692850192909252600160401b830490911660608401526001600160801b03600160601b909204821660808401526001015480821660a0840152600160801b90041660c0820152600780549193508291613efc919088908110613ed957fe5b60009182526020909120600160029092020101546001600160801b031684614125565b91509150613f0b858583614eee565b613f2b85613f1c8560000151611f34565b846001600160801b0316614f8e565b60208084015184516040805161ffff92831681526001600160801b0380871695820195909552938616848201525191811692908716916001600160a01b038916917fb7214f648cea2a7c47aaea7e7aafef610de8d04366d26f66879d076516964eae919081900360600190a4505060019093019250613d9f915050565b50600954613fcb906001600160a01b0316613fc36000611f34565b600a54614f8e565b565b60005b6008548110156140465760006005600060066002018481548110613ff057fe5b60009182526020808320601083040154600f9092166002026101000a90910461ffff168352820192909252604001902080546001600160801b0319166001600160801b0392909216919091179055600101613fd0565b5060005b81518110156125d45782818151811061405f57fe5b60200260200101516005600084848151811061407757fe5b60209081029190910181015161ffff16825281019190915260400160002080546001600160801b0319166001600160801b039290921691909117905560010161404a565b606081516001016040519080825280602002602001820160405280156140eb578160200160208202803883390190505b5092915050565b60008163ffffffff16836040015163ffffffff16111580156129d057505060609091015163ffffffff9182169116101590565b805161ffff9081166000908152600560209081526040808320549185015190931682529181205490918291829161416a9187916001600160801b039081169116614fcc565b94959350505050565b600061417f8685615003565b9050600061418d8686615003565b90506141be846001600160801b03168984815181106141a857fe5b60200260200101516150ec90919063ffffffff16565b8883815181106141ca57fe5b602002602001018181525050614205836001600160801b03168983815181106141ef57fe5b602002602001015161515490919063ffffffff16565b88828151811061421157fe5b6020026020010181815250505050505050505050565b60c081015160a0909101510390565b60008261424557506000612253565b8282028284828161425257fe5b04146129d057604051600160e51b62461bcd02815260040180806020018281038252602181526020018061597c6021913960400191505060405180910390fd5b6080810151815161ffff90811660009081526005602090815260408083205491860151909316825291812054909283926142f7926001600160801b03928316926142e29289929082169116614fcc565b6001600160801b03169063ffffffff61423616565b835161ffff1660009081526005602052604081205460a08601519293509091614352916001600160801b0390811691613a969161433c9187911663ffffffff6151bc16565b6001600160801b0389169063ffffffff6147a616565b60a085018051865161ffff166000908152600560205260408120549251939450926143a9926001600160801b039283169261439d9291811691613a969189911663ffffffff6151fe16565b9063ffffffff6151bc16565b90506143bb828263ffffffff6147a616565b9695505050505050565b6000828201838110156129d05760408051600160e51b62461bcd02815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6001600160a01b038316600090815260046020526040902080546144df919061ffff851690811061444f57fe5b60009182526020918290206040805160e0810182526002909302909101805461ffff8082168552620100008204169484019490945263ffffffff600160201b8504811692840192909252600160401b840490911660608301526001600160801b03600160601b909304831660808301526001015480831660a0830152600160801b900490911660c0820152615240565b156125d457614557614552826001600160801b031660046000876001600160a01b03166001600160a01b031681526020019081526020016000208561ffff168154811061452857fe5b6000918252602090912060029091020160010154600160801b90046001600160801b0316906143c5565b61527a565b6001600160a01b0384166000908152600460205260409020805461ffff851690811061457f57fe5b906000526020600020906002020160010160106101000a8154816001600160801b0302191690836001600160801b03160217905550505050565b6145c383836131d6565b1561460e576145d0612ee6565b6001600160a01b038481166000908152600160209081526040808320938716835292905220805463ffffffff191663ffffffff929092169190911790555b6125d4838383614e81565b6146238383612dac565b81111561467a5760408051600160e51b62461bcd02815260206004820152601e60248201527f416d6f756e742065786365656473207573657227732062616c616e63652e0000604482015290519081900360640190fd5b6125d4838383614f8e565b6000806146b361469485614227565b6001600160801b03166146ae85612f888860200151611f34565b614b98565b60a085015160208087015161ffff16600090815260059091526040812054929350916146f1916001600160801b03918216911663ffffffff61423616565b855161ffff166000908152600560205260408120546080880151929350909161473c916103e79161439d916103e891613a96916001600160801b03918216911663ffffffff61423616565b905060008183111561475b57614758838363ffffffff6147a616565b90505b60a087015161477d906001600160801b031661439d868463ffffffff61423616565b979650505050505050565b60008160008151811061479757fe5b60200260200101519050919050565b60006129d083836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506152c1565b6147f181613a87565b61482f57604051600160e51b62461bcd02815260040180806020018281038252603b815260200180615806603b913960400191505060405180910390fd5b600b55565b600a81905561484d336148476000611f34565b836145b9565b50565b60008160008151811061485f57fe5b6020026020010151136148a657604051600160e51b62461bcd028152600401808060200182810382526029815260200180615a236029913960400191505060405180910390fd5b60015b8151811015611fdd578181815181106148be57fe5b602002602001015160001461491d5760408051600160e51b62461bcd02815260206004820181905260248201527f546f6b656e20636f6e736572766174696f6e20646f6573206e6f7420686f6c64604482015290519081900360640190fd5b6001016148a9565b6006805463ffffffff191663ffffffff871617905560005b8451811015614a47576006600101604051806060016040528087848151811061496257fe5b60200260200101516001600160a01b0316815260200185848151811061498457fe5b60200260200101516001600160801b031681526020018684815181106149a657fe5b60209081029190910181015161ffff908116909252835460018082018655600095865294829020845160029092020180546001600160a01b0319166001600160a01b039092169190911781559083015190840180546040909401516001600160801b03199094166001600160801b039092169190911771ffff000000000000000000000000000000001916600160801b93909216929092021790550161493d565b508051614a5b906008906020840190615694565b5050600980546001600160a01b0319163317905550505050565b6001600160a01b038083166000908152602081815260408083209385168352929052206002015463ffffffff168015801590614abd5750614ab4612ee6565b63ffffffff1681105b156125d4576001600160a01b0383811660009081526020818152604080832093861683529290522060018101549054614af5916143c5565b6001600160a01b0393841660009081526020818152604080832095909616825293909352928220928355506001820155600201805463ffffffff19169055565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b0316600160e01b6323b872dd02179052614b9290859061535b565b50505050565b6000818310614ba757816129d0565b5090919050565b604080516001600160a01b03851660601b6020820152815180820360140181526034820183526054808301869052835180840390910181526074909201909252614bff90829063ffffffff61348916565b82516040805161ffff90921660f01b60208301528051808303600201815260229092019052909150614c3890829063ffffffff61348916565b9050614c7b8260200151604051602001808261ffff1661ffff1660f01b81526002019150506040516020818303038152906040528261348990919063ffffffff16565b604080840151815163ffffffff91821660e01b6020820152825180820360040181526024909101909252919250614cb59183919061348916565b60608301516040805163ffffffff92831660e01b6020820152815180820360040181526024909101909152919250614cef91839161348916565b9050614d3c826080015160405160200180826001600160801b03166001600160801b031660801b81526010019150506040516020818303038152906040528261348990919063ffffffff16565b60a0830151604080516001600160801b0390921660801b60208301528051808303601001815260309092019052909150614d7d90829063ffffffff61348916565b9050612ecd614d8b83614227565b604080516001600160801b0390921660801b60208301528051808303601001815260309092019052829063ffffffff61348916565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b0316600160e01b63a9059cbb021790526125d490849061535b565b6000620100008210614e5b57604051600160e51b62461bcd02815260040180806020018281038252602681526020018061579a6026913960400191505060405180910390fd5b5090565b60006001614e6b612ee6565b60065463ffffffff908116929091031614905090565b614e8b8383614a75565b6001600160a01b0380841660009081526020818152604080832093861683529290522054614ebf908263ffffffff6143c516565b6001600160a01b0393841660009081526020818152604080832095909616825293909352929091209190915550565b6001600160a01b03831660009081526004602052604090208054614f1b919061ffff851690811061444f57fe5b156125d457614557614552826001600160801b031660046000876001600160a01b03166001600160a01b031681526020019081526020016000208561ffff1681548110614f6457fe5b6000918252602090912060029091020160010154600160801b90046001600160801b0316906147a6565b614f988383614a75565b6001600160a01b0380841660009081526020818152604080832093861683529290522054614ebf908263ffffffff6147a616565b6000612ecd6145526001600160801b038085169061439d906103e890613a96906103e79084908c8116908c1663ffffffff61423616565b600061ffff831661501657506000612253565b8151600090600019015b81811061509c57600060028383010490508561ffff1685828151811061504257fe5b602002602001015161ffff161415615061576001019250612253915050565b8561ffff1685828151811061507257fe5b602002602001015161ffff16101561508f57806001019250615096565b6001810391505b50615020565b60408051600160e51b62461bcd02815260206004820152601c60248201527f5072696365206e6f742070726f766964656420666f7220746f6b656e00000000604482015290519081900360640190fd5b60008183038183128015906151015750838113155b80615116575060008312801561511657508381135b6129d057604051600160e51b62461bcd028152600401808060200182810382526024815260200180615a706024913960400191505060405180910390fd5b60008282018183128015906151695750838112155b8061517e575060008312801561517e57508381125b6129d057604051600160e51b62461bcd0281526004018080602001828103825260218152602001806158686021913960400191505060405180910390fd5b60006129d083836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061551c565b60006129d083836040518060400160405280601881526020017f536166654d6174683a206d6f64756c6f206279207a65726f0000000000000000815250615584565b60006000196001600160801b031682608001516001600160801b03161415801561225357505060a001516001600160801b03908116141590565b6000600160801b8210614e5b57604051600160e51b62461bcd0281526004018080602001828103825260278152602001806158416027913960400191505060405180910390fd5b6000818484111561535357604051600160e51b62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360005b83811015615318578181015183820152602001615300565b50505050905090810190601f1680156153455780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b61536d826001600160a01b03166155e9565b6153c15760408051600160e51b62461bcd02815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b602083106153ff5780518252601f1990920191602091820191016153e0565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114615461576040519150601f19603f3d011682016040523d82523d6000602084013e615466565b606091505b5091509150816154c05760408051600160e51b62461bcd02815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115614b92578080602001905160208110156154dc57600080fd5b5051614b9257604051600160e51b62461bcd02815260040180806020018281038252602a815260200180615a94602a913960400191505060405180910390fd5b6000818361556e57604051600160e51b62461bcd028152602060048201818152835160248401528351909283926044909101919085019080838360008315615318578181015183820152602001615300565b50600083858161557a57fe5b0495945050505050565b600081836155d657604051600160e51b62461bcd028152602060048201818152835160248401528351909283926044909101919085019080838360008315615318578181015183820152602001615300565b508284816155e057fe5b06949350505050565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708115801590612ecd5750141592915050565b508054600082556002029060005260206000209081019061484d9190615739565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565b604080518082019091526000808252602082015290565b82805482825590600052602060002090600f0160109004810192821561572d5791602002820160005b838211156156fd57835183826101000a81548161ffff021916908361ffff16021790555092602001926002016020816001010492830192600103026156bd565b801561572b5782816101000a81549061ffff02191690556002016020816001010492830192600103026156fd565b505b50614e5b92915061577a565b6112d291905b80821115614e5b5780546001600160a01b031916815560018101805471ffffffffffffffffffffffffffffffffffff1916905560020161573f565b6112d291905b80821115614e5b57805461ffff1916815560010161578056fe53616665436173743a2076616c756520646f65736e27742066697420696e203136206269747362757920616d6f756e74206c657373207468616e20414d4f554e545f4d494e494d554d536f6c7574696f6e2065786365656473204d41585f544f55434845445f4f52444552534e6577206f626a65637469766520646f65736e27742073756666696369656e746c7920696d70726f76652063757272656e7420736f6c7574696f6e53616665436173743a2076616c756520646f65736e27742066697420696e2031323820626974735369676e6564536166654d6174683a206164646974696f6e206f766572666c6f77536f6c7574696f6e7320617265206e6f206c6f6e67657220616363657074656420666f7220746869732062617463685769746864726177206e6f7420706f737369626c6520666f7220746f6b656e20746861742069732074726164656420696e207468652063757272656e742061756374696f6e4174206c65617374206f6e65207072696365206c6f776572207468616e20414d4f554e545f4d494e494d554d4f72646572732063616e277420626520706c6163656420696e207468652070617374657865637574656453656c6c416d6f756e7420626967676572207468616e2073706563696669656420696e206f72646572536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7770726963657320617265206e6f74206f72646572656420627920746f6b656e4964776974686472617720776173206e6f7420726567697374657265642070726576696f75736c79436c61696d6564206f626a65637469766520646f65736e27742073756666696369656e746c7920696d70726f76652063757272656e7420736f6c7574696f6e546f6b656e20636f6e736572766174696f6e2061742030206d75737420626520706f7369746976652e73656c6c20616d6f756e74206c657373207468616e20414d4f554e545f4d494e494d554d5369676e6564536166654d6174683a207375627472616374696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564526571756573742063616e6e6f74206265206d61646520696e207468652070617374a165627a7a7230582073ef6c40df46039e6be6d195731df39de7c70e851409dcab570af49ff8ec1aca0029000000000000000000000000000000000000000000000000000000000000ffff0000000000000000000000001a5f9352af8af974bfc03399e3767df6370d82e4

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106102535760003560e01c806395466a4611610146578063e1d5f64e116100c3578063f36b635511610087578063f36b635514611217578063f3f479821461124f578063f47c84c51461127d578063f940e38514611285578063fb736d32146112b3578063ff97c626146112bb57610253565b8063e1d5f64e146111a1578063e48c015e146111a9578063e720ac8e146111b1578063ed2da357146111b9578063ef574d23146111f157610253565b8063c33eb9f61161010a578063c33eb9f6146110f4578063c49598fb14611122578063d48bfca714611145578063d4fac45d1461116b578063d73792a91461119957610253565b806395466a4614610c4c5780639bb0f59914610c845780639cc84ed314610cbe578063b3c0afa114610cfb578063c1ef283814610d4757610253565b80634bdc1b4c116101d457806372f3dd391161019857806372f3dd3914610b38578063793b8c6d14610b5e5780637fb47b0614610bde5780638e499bcf14610be6578063907767c014610c0557610253565b80634bdc1b4c146106cf57806361ed16d014610770578063647846a51461079457806365cc3e781461079c57806366367c1014610b1757610253565b80632f10d0821161021b5780632f10d08214610600578063397a1b281461063d57806341e383ed1461066b57806343383ac31461067357806347e7ef24146106a357610253565b8063094c7e191461025857806317569c1d1461027257806323d4a3c91461027a57806326c3d394146102f75780632e4c83bd14610345575b600080fd5b6102606112c3565b60408051918252519081900360200190f35b6102606112c8565b6102826112d5565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102bc5781810151838201526020016102a4565b50505050905090810190601f1680156102e95780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610260600480360360a081101561030d57600080fd5b5061ffff813581169160208101359091169063ffffffff604082013516906001600160801b03606082013581169160800135166114be565b610260600480360360e081101561035b57600080fd5b63ffffffff82351691602081013591810190606081016040820135600160201b81111561038757600080fd5b82018360208201111561039957600080fd5b803590602001918460208302840111600160201b831117156103ba57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561040957600080fd5b82018360208201111561041b57600080fd5b803590602001918460208302840111600160201b8311171561043c57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561048b57600080fd5b82018360208201111561049d57600080fd5b803590602001918460208302840111600160201b831117156104be57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561050d57600080fd5b82018360208201111561051f57600080fd5b803590602001918460208302840111600160201b8311171561054057600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561058f57600080fd5b8201836020820111156105a157600080fd5b803590602001918460208302840111600160201b831117156105c257600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506114e3945050505050565b6106216004803603602081101561061657600080fd5b503561ffff16611f34565b604080516001600160a01b039092168252519081900360200190f35b6106696004803603604081101561065357600080fd5b506001600160a01b038135169060200135611fcb565b005b610260611fe1565b6102826004803603604081101561068957600080fd5b5080356001600160a01b0316906020013561ffff16611fed565b610669600480360360408110156106b957600080fd5b506001600160a01b038135169060200135612259565b610669600480360360208110156106e557600080fd5b810190602081018135600160201b8111156106ff57600080fd5b82018360208201111561071157600080fd5b803590602001918460208302840111600160201b8311171561073257600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955061235a945050505050565b6107786125d9565b604080516001600160801b039092168252519081900360200190f35b6106216125df565b610ac7600480360360c08110156107b257600080fd5b810190602081018135600160201b8111156107cc57600080fd5b8201836020820111156107de57600080fd5b803590602001918460208302840111600160201b831117156107ff57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561084e57600080fd5b82018360208201111561086057600080fd5b803590602001918460208302840111600160201b8311171561088157600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b8111156108d057600080fd5b8201836020820111156108e257600080fd5b803590602001918460208302840111600160201b8311171561090357600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561095257600080fd5b82018360208201111561096457600080fd5b803590602001918460208302840111600160201b8311171561098557600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b8111156109d457600080fd5b8201836020820111156109e657600080fd5b803590602001918460208302840111600160201b83111715610a0757600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115610a5657600080fd5b820183602082011115610a6857600080fd5b803590602001918460208302840111600160201b83111715610a8957600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506125f4945050505050565b60408051602080825283518183015283519192839290830191858101910280838360005b83811015610b03578181015183820152602001610aeb565b505050509050019250505060405180910390f35b61077860048036036020811015610b2d57600080fd5b503561ffff166126e2565b61028260048036036020811015610b4e57600080fd5b50356001600160a01b03166126fd565b610b8a60048036036040811015610b7457600080fd5b506001600160a01b03813516906020013561270d565b6040805161ffff988916815296909716602087015263ffffffff948516868801529290931660608501526001600160801b03908116608085015291821660a08401521660c082015290519081900360e00190f35b61077861278b565b610bee612791565b6040805161ffff9092168252519081900360200190f35b610c3360048036036040811015610c1b57600080fd5b506001600160a01b038135811691602001351661279b565b6040805163ffffffff9092168252519081900360200190f35b61028260048036036060811015610c6257600080fd5b506001600160a01b038135169061ffff602082013581169160400135166127be565b610caa60048036036020811015610c9a57600080fd5b50356001600160a01b03166129d7565b604080519115158252519081900360200190f35b610cc6612a3f565b6040805163ffffffff90951685526001600160a01b039093166020850152838301919091526060830152519081900360800190f35b610d2960048036036040811015610d1157600080fd5b506001600160a01b0381358116916020013516612a63565b6040805192835263ffffffff90911660208301528051918290030190f35b610ac7600480360360e0811015610d5d57600080fd5b810190602081018135600160201b811115610d7757600080fd5b820183602082011115610d8957600080fd5b803590602001918460208302840111600160201b83111715610daa57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115610df957600080fd5b820183602082011115610e0b57600080fd5b803590602001918460208302840111600160201b83111715610e2c57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115610e7b57600080fd5b820183602082011115610e8d57600080fd5b803590602001918460208302840111600160201b83111715610eae57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115610efd57600080fd5b820183602082011115610f0f57600080fd5b803590602001918460208302840111600160201b83111715610f3057600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115610f7f57600080fd5b820183602082011115610f9157600080fd5b803590602001918460208302840111600160201b83111715610fb257600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561100157600080fd5b82018360208201111561101357600080fd5b803590602001918460208302840111600160201b8311171561103457600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561108357600080fd5b82018360208201111561109557600080fd5b803590602001918460208302840111600160201b831117156110b657600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550612abd945050505050565b610d296004803603604081101561110a57600080fd5b506001600160a01b0381358116916020013516612ae2565b610caa6004803603602081101561113857600080fd5b503563ffffffff16612b3c565b6106696004803603602081101561115b57600080fd5b50356001600160a01b0316612b70565b6102606004803603604081101561118157600080fd5b506001600160a01b0381358116916020013516612dac565b610778612ed5565b610778612edb565b610c33612ee0565b610c33612ee6565b610282600480360360608110156111cf57600080fd5b506001600160a01b038135169061ffff60208201358116916040013516612eee565b610bee6004803603602081101561120757600080fd5b50356001600160a01b0316613064565b6106696004803603606081101561122d57600080fd5b5080356001600160a01b0316906020810135906040013563ffffffff166130cc565b610caa6004803603604081101561126557600080fd5b506001600160a01b03813581169160200135166131d6565b61026061324e565b6106696004803603604081101561129b57600080fd5b506001600160a01b0381358116916020013516613254565b610260613451565b610260613456565b606481565b61012c4281900690035b90565b60606000600c73cddb32b6bb2808d5b5115daab207479ce98d263663d58c862c90916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561132b57600080fd5b505af415801561133f573d6000803e3d6000fd5b505050506040513d602081101561135557600080fd5b505111156112d2576000600c73cddb32b6bb2808d5b5115daab207479ce98d26366368fb2bc290916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156113b157600080fd5b505af41580156113c5573d6000803e3d6000fd5b505050506040513d60208110156113db57600080fd5b5051905060005b806114b9576114006113f3836126fd565b849063ffffffff61348916565b600d549093506001600160a01b0383811691161415611421575060016114b4565b60408051600160e21b63218777b3028152600c60048201526001600160a01b0384166024820152905173cddb32b6bb2808d5b5115daab207479ce98d26369163861ddecc916044808301926020929190829003018186803b15801561148557600080fd5b505af4158015611499573d6000803e3d6000fd5b505050506040513d60208110156114af57600080fd5b505191505b6113e2565b505090565b60006114d586866114cd612ee6565b878787613506565b61ffff169695505050505050565b60006114ee88612b3c565b61152c57604051600160e51b62461bcd02815260040180806020018281038252602f815260200180615889602f913960400191505060405180910390fd5b61153587613a87565b61157357604051600160e51b62461bcd02815260040180806020018281038252603f8152602001806159e4603f913960400191505060405180910390fd5b61157c83613aba565b6115ba57604051600160e51b62461bcd02815260040180806020018281038252602c8152602001806158fd602c913960400191505060405180910390fd5b816000815181106115c757fe5b602002602001015161ffff166000141561162b5760408051600160e51b62461bcd02815260206004820152601a60248201527f46656520746f6b656e2068617320666978656420707269636521000000000000604482015290519081900360640190fd5b61163482613b11565b61167257604051600160e51b62461bcd02815260040180806020018281038252602181526020018061599d6021913960400191505060405180910390fd5b601e865111156116b657604051600160e51b62461bcd0281526004018080602001828103825260238152602001806157e36023913960400191505060405180910390fd5b60006116c0613b69565b90506116ca613bfe565b6116d48484613fcd565b6116e060076000615620565b60606116eb846140bb565b90506000805b8951811015611b5557611702615641565b600460008c848151811061171257fe5b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000208a838151811061174657fe5b602002602001015161ffff168154811061175c57fe5b60009182526020918290206040805160e0810182526002909302909101805461ffff8082168552620100008204169484019490945263ffffffff600160201b8504811692840192909252600160401b840490911660608301526001600160801b03600160601b909304831660808301526001015480831660a0830152600160801b900490911660c082015290506117f3818e6140f2565b6118475760408051600160e51b62461bcd02815260206004820152601060248201527f4f7264657220697320696e76616c696400000000000000000000000000000000604482015290519081900360640190fd5b6000806118678b858151811061185957fe5b602002602001015184614125565b90925090506127106001600160801b03831610156118b957604051600160e51b62461bcd0281526004018080602001828103825260238152602001806157c06023913960400191505060405180910390fd5b6127106001600160801b038216101561190657604051600160e51b62461bcd028152600401808060200182810382526024815260200180615a4c6024913960400191505060405180910390fd5b825160208401516119219188918c868663ffffffff61417316565b806001600160801b031661193484614227565b6001600160801b0316101561197d57604051600160e51b62461bcd02815260040180806020018281038252603181526020018061594b6031913960400191505060405180910390fd5b60a083015161199e906001600160801b03848116911663ffffffff61423616565b60808401516119bf906001600160801b03848116911663ffffffff61423616565b1115611a155760408051600160e51b62461bcd02815260206004820152601960248201527f6c696d6974207072696365206e6f742073617469736669656400000000000000604482015290519081900360640190fd5b611a2f611a228385614292565b869063ffffffff6143c516565b9450611a628d8581518110611a4057fe5b60200260200101518d8681518110611a5457fe5b602002602001015183614422565b611a958d8581518110611a7157fe5b6020026020010151611a868560000151611f34565b846001600160801b03166145b9565b826020015161ffff168c8581518110611aaa57fe5b602002602001015161ffff168e8681518110611ac257fe5b60200260200101516001600160a01b03167fafa5bc1fb80950b7cb2353ba0cf16a6d68de75801f2dac54b2dae9268450010a86600001518587604051808461ffff1661ffff168152602001836001600160801b03166001600160801b03168152602001826001600160801b03166001600160801b03168152602001935050505060405180910390a45050506001016116f1565b5060005b8951811015611caf57611b6a615641565b600460008c8481518110611b7a57fe5b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000208a8381518110611bae57fe5b602002602001015161ffff1681548110611bc457fe5b600091825260208083206040805160e0810182526002909402909101805461ffff8082168652620100008204169385019390935263ffffffff600160201b8404811692850192909252600160401b830490911660608401526001600160801b03600160601b909204821660808401526001015480821660a0840152600160801b90041660c08201528a51909250611c6f908b9085908110611c6157fe5b602002602001015183614125565b915050611ca58c8481518110611c8157fe5b6020026020010151611c968460200151611f34565b836001600160801b0316614619565b5050600101611b59565b506000805b8a51811015611dda57611dd0611dc3600460008e8581518110611cd357fe5b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000208c8481518110611d0757fe5b602002602001015161ffff1681548110611d1d57fe5b60009182526020918290206040805160e0810182526002909302909101805461ffff8082168552620100008204169484019490945263ffffffff600160201b8504811692840192909252600160401b840490911660608301526001600160801b03600160601b909304831660808301526001015480831660a0830152600160801b900490911660c08201528d518e9085908110611db657fe5b6020026020010151614685565b839063ffffffff6143c516565b9150600101611cb4565b5060006002611de885614788565b81611def57fe5b0490506000611e1483611e08868563ffffffff6143c516565b9063ffffffff6147a616565b9050611e1f816147e8565b611e2882614834565b611e3185614850565b611e3e8e8d8d8d8c614925565b336001600160a01b03167f2140b6253bf38aea0a4ac9e9e6427b256e4035d60df4a85bb139ce975eb6b41d8585858a8e8e604051808781526020018681526020018581526020018481526020018060200180602001838103835285818151815260200191508051906020019060200280838360005b83811015611ecb578181015183820152602001611eb3565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015611f0a578181015183820152602001611ef2565b505050509050019850505050505050505060405180910390a29d9c50505050505050505050505050565b60408051600160e21b632ffeb2d7028152600e600482015261ffff83166024820152905160009173ed4d05496c71e71cc2a8726af1242c22108d17619163bffacb5c91604480820192602092909190829003018186803b158015611f9757600080fd5b505af4158015611fab573d6000803e3d6000fd5b505050506040513d6020811015611fc157600080fd5b505190505b919050565b611fdd8282611fd8612ee6565b6130cc565b5050565b678ac7230489e8000081565b6060600c73cddb32b6bb2808d5b5115daab207479ce98d263663d58c862c90916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561204157600080fd5b505af4158015612055573d6000803e3d6000fd5b505050506040513d602081101561206b57600080fd5b505161207657612253565b6000836001600160a01b03811661214d5760408051600160e11b63347d95e1028152600c6004820152905173cddb32b6bb2808d5b5115daab207479ce98d2636916368fb2bc2916024808301926020929190829003018186803b1580156120dc57600080fd5b505af41580156120f0573d6000803e3d6000fd5b505050506040513d602081101561210657600080fd5b5051604080516001600160a01b03831660601b602082015281518082036014018152603490910190915290915061214490849063ffffffff61348916565b92506001909101905b8361ffff168261ffff161080156121725750600d546001600160a01b03828116911614155b156122505760408051600160e21b63218777b3028152600c60048201526001600160a01b0383166024820152905173cddb32b6bb2808d5b5115daab207479ce98d26369163861ddecc916044808301926020929190829003018186803b1580156121db57600080fd5b505af41580156121ef573d6000803e3d6000fd5b505050506040513d602081101561220557600080fd5b5051604080516001600160a01b03831660601b602082015281518082036014018152603490910190915290915061224390849063ffffffff61348916565b925060019091019061214d565b50505b92915050565b6122633383614a75565b61226f82333084614b35565b336000908152602081815260408083206001600160a01b03861684529091529020600101546122a4908263ffffffff6143c516565b336000908152602081815260408083206001600160a01b03871684529091529020600101556122d1612ee6565b336000818152602081815260408083206001600160a01b0388168085529252909120600201805463ffffffff191663ffffffff94909416939093179092557fc11cc34e93c67a93382b99f2498e9937198798f3c1c2888008ffc0eeb82f68c483612339612ee6565b6040805192835263ffffffff90911660208301528051918290030190a35050565b60006001612366612ee6565b03905060005b82518161ffff1610156125d45733600090815260046020526040902083516124469190859061ffff851690811061239f57fe5b602002602001015161ffff16815481106123b557fe5b60009182526020918290206040805160e0810182526002909302909101805461ffff8082168552620100008204169484019490945263ffffffff600160201b8504811692840192909252600160401b840490911660608301526001600160801b03600160601b909304831660808301526001015480831660a0830152600160801b900490911660c0820152836140f2565b612502573360009081526004602052604090208351849061ffff841690811061246b57fe5b602002602001015161ffff168154811061248157fe5b60009182526020822060029091020180546001600160e01b031916815560010155825133907f7b0a9854603fbbe7606a58b70d113bd0d1ec8475f1b8cc9603c2d377e67835cd90859061ffff85169081106124d857fe5b6020026020010151604051808261ffff1661ffff16815260200191505060405180910390a26125cc565b3360009081526004602052604090208351839190859061ffff851690811061252657fe5b602002602001015161ffff168154811061253c57fe5b906000526020600020906002020160000160086101000a81548163ffffffff021916908363ffffffff160217905550336001600160a01b03167f7a02963a37046835196f1a3185a036fd67cfca72283e46e4b3cdb99939851937848361ffff16815181106125a657fe5b6020026020010151604051808261ffff1661ffff16815260200191505060405180910390a25b60010161236c565b505050565b61271081565b6003546201000090046001600160a01b031681565b60608651604051908082528060200260200182016040528015612621578160200160208202803883390190505b50905060005b87518110156126d7576126b088828151811061263f57fe5b602002602001015188838151811061265357fe5b602002602001015188848151811061266757fe5b602002602001015188858151811061267b57fe5b602002602001015188868151811061268f57fe5b60200260200101518887815181106126a357fe5b6020026020010151613506565b8282815181106126bc57fe5b61ffff90921660209283029190910190910152600101612627565b509695505050505050565b6005602052600090815260409020546001600160801b031681565b6060612253826000600019612eee565b6004602052816000526040600020818154811061272657fe5b60009182526020909120600290910201805460019091015461ffff808316945062010000830416925063ffffffff600160201b8304811692600160401b8104909116916001600160801b03600160601b90920482169181811691600160801b90041687565b60001981565b60035461ffff1681565b600160209081526000928352604080842090915290825290205463ffffffff1681565b6060600c73cddb32b6bb2808d5b5115daab207479ce98d263663d58c862c90916040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561281257600080fd5b505af4158015612826573d6000803e3d6000fd5b505050506040513d602081101561283c57600080fd5b5051612847576129d0565b82846001600160a01b0381166128db5760408051600160e11b63347d95e1028152600c6004820152905173cddb32b6bb2808d5b5115daab207479ce98d2636916368fb2bc2916024808301926020929190829003018186803b1580156128ac57600080fd5b505af41580156128c0573d6000803e3d6000fd5b505050506040513d60208110156128d657600080fd5b505190505b825161ffff8516906070900410156129cd576129116113f3828460706001600160801b031687518161290957fe5b048803612eee565b600d549093506001600160a01b038281169116141561293257506129d09050565b60408051600160e21b63218777b3028152600c60048201526001600160a01b038316602482015290516000935073cddb32b6bb2808d5b5115daab207479ce98d26369163861ddecc916044808301926020929190829003018186803b15801561299a57600080fd5b505af41580156129ae573d6000803e3d6000fd5b505050506040513d60208110156129c457600080fd5b505190506128db565b50505b9392505050565b60408051600160e11b636e8930a3028152600e60048201526001600160a01b0383166024820152905160009173ed4d05496c71e71cc2a8726af1242c22108d17619163dd12614691604480820192602092909190829003018186803b158015611f9757600080fd5b600654600954600a54600b5463ffffffff909316926001600160a01b039092169184565b600080612a6e61567d565b5050506001600160a01b039182166000908152602081815260408083209390941682529182528290208251808401909352600181015480845260029091015463ffffffff169290910182905291565b6060612ac88861235a565b612ad68787878787876125f4565b98975050505050505050565b600080612aed61567d565b5050506001600160a01b039182166000908152602081815260408083209390941682529182528290208251808401909352600381015480845260049091015463ffffffff169290910182905291565b60006001612b48612ee6565b0363ffffffff168263ffffffff161480156122535750603c612b686112c8565b101592915050565b60025460035461ffff1610612bcf5760408051600160e51b62461bcd02815260206004820152601260248201527f4d617820746f6b656e7320726561636865640000000000000000000000000000604482015290519081900360640190fd5b60035461ffff1615612c565760035460408051600160e01b634417f4db028152336004820152678ac7230489e8000060248201529051620100009092046001600160a01b031691634417f4db9160448082019260009290919082900301818387803b158015612c3d57600080fd5b505af1158015612c51573d6000803e3d6000fd5b505050505b60035460408051600160e01b639f3cb655028152600e600482015261ffff90921660248301526001600160a01b03831660448301525173ed4d05496c71e71cc2a8726af1242c22108d176191639f3cb655916064808301926020929190829003018186803b158015612cc757600080fd5b505af4158015612cdb573d6000803e3d6000fd5b505050506040513d6020811015612cf157600080fd5b5051612d475760408051600160e51b62461bcd02815260206004820152601860248201527f546f6b656e20616c726561647920726567697374657265640000000000000000604482015290519081900360640190fd5b600354604080516001600160a01b038416815261ffff909216602083015280517fe4b282c4351ffe36572a572de193a7de086edc47c9e62669fe6ab49fc53a33139281900390910190a1506003805461ffff8082166001011661ffff19909116179055565b6001600160a01b03808316600090815260208181526040808320938516835292905290812054612dda612ee6565b6001600160a01b038086166000908152602081815260408083209388168352929052206002015463ffffffff91821691161015612e4c576001600160a01b0380851660009081526020818152604080832093871683529290522060010154612e4990829063ffffffff6143c516565b90505b612e54612ee6565b6001600160a01b038086166000908152602081815260408083209388168352929052206004015463ffffffff918216911610156129d0576001600160a01b0380851660009081526020818152604080832093871683529290522060030154612ecd90612ec09083614b98565b829063ffffffff6147a616565b949350505050565b6103e881565b607081565b61012c81565b61012c420490565b6060825b6001600160a01b038516600090815260046020526040902054612f1b9061ffff86860116614b98565b8161ffff16101561305c5761305261304586612f8d88612f88600460008c6001600160a01b03166001600160a01b031681526020019081526020016000208761ffff1681548110612f6857fe5b600091825260209091206002909102015462010000900461ffff16611f34565b612dac565b6001600160a01b0389166000908152600460205260409020805461ffff8716908110612fb557fe5b60009182526020918290206040805160e0810182526002909302909101805461ffff8082168552620100008204169484019490945263ffffffff600160201b8504811692840192909252600160401b840490911660608301526001600160801b03600160601b909304831660808301526001015480831660a0830152600160801b900490911660c0820152614bae565b839063ffffffff61348916565b9150600101612ef2565b509392505050565b60408051600160e01b63b3129e1f028152600e60048201526001600160a01b0383166024820152905160009173ed4d05496c71e71cc2a8726af1242c22108d17619163b3129e1f91604480820192602092909190829003018186803b158015611f9757600080fd5b6130d633846131d6565b156130e5576130e53384613254565b6130ed612ee6565b63ffffffff168163ffffffff16101561313a57604051600160e51b62461bcd028152600401808060200182810382526022815260200180615abe6022913960400191505060405180910390fd5b60408051808201825283815263ffffffff83811660208084018281523360008181528084528781206001600160a01b038c16808352908552908890209651600388015591516004909601805463ffffffff19169690951695909517909355845187815290810191909152835191937f2c6245af506f0fc1089918c02c1d01bde9cc807609b334b3e7644d6dfb5a6c5e92918290030190a3505050565b60006131e0612ee6565b6001600160a01b038085166000908152602081815260408083209387168352929052206004015463ffffffff91821691161080156129d05750506001600160a01b0391821660009081526020818152604080832093909416825291909152206004015463ffffffff16151590565b60025481565b61325e8282614a75565b613266612ee6565b6001600160a01b038084166000908152602081815260408083209386168352929052206004015463ffffffff9182169116106132d657604051600160e51b62461bcd0281526004018080602001828103825260268152602001806159be6026913960400191505060405180910390fd5b6132de612ee6565b6001600160a01b0380841660009081526001602090815260408083209386168352929052205463ffffffff91821691161061334d57604051600160e51b62461bcd0281526004018080602001828103825260458152602001806158b86045913960600191505060405180910390fd5b6001600160a01b03828116600090815260208181526040808320938516835292905290812080546003909101546133849190614b98565b6001600160a01b03808516600090815260208181526040808320938716835292905220549091506133bb908263ffffffff6147a616565b6001600160a01b0384811660009081526020818152604080832093871683529290529081209182556003820155600401805463ffffffff19169055613401828483614dc0565b816001600160a01b0316836001600160a01b03167f9b1bfa7fa9ee420a16e124f794c35ac9f90472acc99140eb2f6447c714cad8eb836040518082815260200191505060405180910390a3505050565b601e81565b60006001613462612ee6565b60065463ffffffff908116929091031614156134815750600b546112d2565b5060006112d2565b6060806040519050835180825260208201818101602087015b818310156134ba5780518352602092830192016134a2565b50855184518101855292509050808201602086015b818310156134e75780518352602092830192016134cf565b508651929092011591909101601f01601f191660405250905092915050565b60408051600160e01b63ab8978b7028152600e600482015261ffff88166024820152905160009173ed4d05496c71e71cc2a8726af1242c22108d17619163ab8978b791604480820192602092909190829003018186803b15801561356957600080fd5b505af415801561357d573d6000803e3d6000fd5b505050506040513d602081101561359357600080fd5b50516135e95760408051600160e51b62461bcd02815260206004820152601860248201527f42757920746f6b656e206d757374206265206c69737465640000000000000000604482015290519081900360640190fd5b60408051600160e01b63ab8978b7028152600e600482015261ffff88166024820152905173ed4d05496c71e71cc2a8726af1242c22108d17619163ab8978b7916044808301926020929190829003018186803b15801561364857600080fd5b505af415801561365c573d6000803e3d6000fd5b505050506040513d602081101561367257600080fd5b50516136c85760408051600160e51b62461bcd02815260206004820152601960248201527f53656c6c20746f6b656e206d757374206265206c697374656400000000000000604482015290519081900360640190fd5b8561ffff168761ffff1614156137285760408051600160e51b62461bcd02815260206004820152601c60248201527f45786368616e676520746f6b656e73206e6f742064697374696e637400000000604482015290519081900360640190fd5b613730612ee6565b63ffffffff168563ffffffff16101561377d57604051600160e51b62461bcd0281526004018080602001828103825260228152602001806159296022913960400191505060405180910390fd5b60046000336001600160a01b03166001600160a01b031681526020019081526020016000206040518060e001604052808961ffff1681526020018861ffff1681526020018763ffffffff1681526020018663ffffffff168152602001856001600160801b03168152602001846001600160801b0316815260200160006001600160801b03168152509080600181540180825580915050906001820390600052602060002090600202016000909192909190915060008201518160000160006101000a81548161ffff021916908361ffff16021790555060208201518160000160026101000a81548161ffff021916908361ffff16021790555060408201518160000160046101000a81548163ffffffff021916908363ffffffff16021790555060608201518160000160086101000a81548163ffffffff021916908363ffffffff160217905550608082015181600001600c6101000a8154816001600160801b0302191690836001600160801b0316021790555060a08201518160010160006101000a8154816001600160801b0302191690836001600160801b0316021790555060c08201518160010160106101000a8154816001600160801b0302191690836001600160801b031602179055505050506000613984600160046000336001600160a01b03166001600160a01b031681526020019081526020016000208054905003614e15565b6040805161ffff808416825263ffffffff808b1660208401528916828401526001600160801b038089166060840152871660808301529151929350818a1692918b169133917fdecf6fde8243981299f7b7a776f29a9fc67a2c9848e25d77c50eb11fa58a7e21919081900360a00190a460408051600160e11b634743bd03028152600c6004820152336024820152905173cddb32b6bb2808d5b5115daab207479ce98d263691638e877a06916044808301926020929190829003018186803b158015613a4f57600080fd5b505af4158015613a63573d6000803e3d6000fd5b505050506040513d6020811015613a7957600080fd5b509098975050505050505050565b6000613aa26065613a96613456565b9063ffffffff61423616565b613ab383606463ffffffff61423616565b1192915050565b6000805b8251811015613b08576127106001600160801b0316838281518110613adf57fe5b60200260200101516001600160801b03161015613b00576000915050611fc6565b600101613abe565b50600192915050565b600060015b8251811015613b0857826001820381518110613b2e57fe5b602002602001015161ffff16838281518110613b4657fe5b602002602001015161ffff1611613b61576000915050611fc6565b600101613b16565b6000613b73614e5f565b613bf857600354600a5460408051600160e01b634417f4db028152306004820152602481019290925251620100009092046001600160a01b031691634417f4db9160448082019260009290919082900301818387803b158015613bd557600080fd5b505af1158015613be9573d6000803e3d6000fd5b5050600a5492506112d2915050565b50600090565b613c06614e5f565b15613fcb5760005b600754811015613d9b57600060066001018281548110613c2a57fe5b60009182526020822060029091020154600780546001600160a01b0390921693509084908110613c5657fe5b906000526020600020906002020160010160109054906101000a900461ffff169050613c80615641565b6001600160a01b0383166000908152600460205260409020805461ffff8416908110613ca857fe5b600091825260208083206040805160e0810182526002909402909101805461ffff8082168652620100008204169385019390935263ffffffff600160201b8404811692850192909252600160401b830490911660608401526001600160801b03600160601b909204821660808401526001015480821660a0840152600160801b90041660c082015260078054919350613d689187908110613d4557fe5b60009182526020909120600160029092020101546001600160801b031683614125565b915050613d8b84613d7c8460200151611f34565b836001600160801b0316614e81565b505060019092019150613c0e9050565b5060005b600754811015613fa857600060066001018281548110613dbb57fe5b60009182526020822060029091020154600780546001600160a01b0390921693509084908110613de757fe5b906000526020600020906002020160010160109054906101000a900461ffff169050613e11615641565b6001600160a01b0383166000908152600460205260409020805461ffff8416908110613e3957fe5b600091825260208083206040805160e0810182526002909402909101805461ffff8082168652620100008204169385019390935263ffffffff600160201b8404811692850192909252600160401b830490911660608401526001600160801b03600160601b909204821660808401526001015480821660a0840152600160801b90041660c0820152600780549193508291613efc919088908110613ed957fe5b60009182526020909120600160029092020101546001600160801b031684614125565b91509150613f0b858583614eee565b613f2b85613f1c8560000151611f34565b846001600160801b0316614f8e565b60208084015184516040805161ffff92831681526001600160801b0380871695820195909552938616848201525191811692908716916001600160a01b038916917fb7214f648cea2a7c47aaea7e7aafef610de8d04366d26f66879d076516964eae919081900360600190a4505060019093019250613d9f915050565b50600954613fcb906001600160a01b0316613fc36000611f34565b600a54614f8e565b565b60005b6008548110156140465760006005600060066002018481548110613ff057fe5b60009182526020808320601083040154600f9092166002026101000a90910461ffff168352820192909252604001902080546001600160801b0319166001600160801b0392909216919091179055600101613fd0565b5060005b81518110156125d45782818151811061405f57fe5b60200260200101516005600084848151811061407757fe5b60209081029190910181015161ffff16825281019190915260400160002080546001600160801b0319166001600160801b039290921691909117905560010161404a565b606081516001016040519080825280602002602001820160405280156140eb578160200160208202803883390190505b5092915050565b60008163ffffffff16836040015163ffffffff16111580156129d057505060609091015163ffffffff9182169116101590565b805161ffff9081166000908152600560209081526040808320549185015190931682529181205490918291829161416a9187916001600160801b039081169116614fcc565b94959350505050565b600061417f8685615003565b9050600061418d8686615003565b90506141be846001600160801b03168984815181106141a857fe5b60200260200101516150ec90919063ffffffff16565b8883815181106141ca57fe5b602002602001018181525050614205836001600160801b03168983815181106141ef57fe5b602002602001015161515490919063ffffffff16565b88828151811061421157fe5b6020026020010181815250505050505050505050565b60c081015160a0909101510390565b60008261424557506000612253565b8282028284828161425257fe5b04146129d057604051600160e51b62461bcd02815260040180806020018281038252602181526020018061597c6021913960400191505060405180910390fd5b6080810151815161ffff90811660009081526005602090815260408083205491860151909316825291812054909283926142f7926001600160801b03928316926142e29289929082169116614fcc565b6001600160801b03169063ffffffff61423616565b835161ffff1660009081526005602052604081205460a08601519293509091614352916001600160801b0390811691613a969161433c9187911663ffffffff6151bc16565b6001600160801b0389169063ffffffff6147a616565b60a085018051865161ffff166000908152600560205260408120549251939450926143a9926001600160801b039283169261439d9291811691613a969189911663ffffffff6151fe16565b9063ffffffff6151bc16565b90506143bb828263ffffffff6147a616565b9695505050505050565b6000828201838110156129d05760408051600160e51b62461bcd02815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6001600160a01b038316600090815260046020526040902080546144df919061ffff851690811061444f57fe5b60009182526020918290206040805160e0810182526002909302909101805461ffff8082168552620100008204169484019490945263ffffffff600160201b8504811692840192909252600160401b840490911660608301526001600160801b03600160601b909304831660808301526001015480831660a0830152600160801b900490911660c0820152615240565b156125d457614557614552826001600160801b031660046000876001600160a01b03166001600160a01b031681526020019081526020016000208561ffff168154811061452857fe5b6000918252602090912060029091020160010154600160801b90046001600160801b0316906143c5565b61527a565b6001600160a01b0384166000908152600460205260409020805461ffff851690811061457f57fe5b906000526020600020906002020160010160106101000a8154816001600160801b0302191690836001600160801b03160217905550505050565b6145c383836131d6565b1561460e576145d0612ee6565b6001600160a01b038481166000908152600160209081526040808320938716835292905220805463ffffffff191663ffffffff929092169190911790555b6125d4838383614e81565b6146238383612dac565b81111561467a5760408051600160e51b62461bcd02815260206004820152601e60248201527f416d6f756e742065786365656473207573657227732062616c616e63652e0000604482015290519081900360640190fd5b6125d4838383614f8e565b6000806146b361469485614227565b6001600160801b03166146ae85612f888860200151611f34565b614b98565b60a085015160208087015161ffff16600090815260059091526040812054929350916146f1916001600160801b03918216911663ffffffff61423616565b855161ffff166000908152600560205260408120546080880151929350909161473c916103e79161439d916103e891613a96916001600160801b03918216911663ffffffff61423616565b905060008183111561475b57614758838363ffffffff6147a616565b90505b60a087015161477d906001600160801b031661439d868463ffffffff61423616565b979650505050505050565b60008160008151811061479757fe5b60200260200101519050919050565b60006129d083836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506152c1565b6147f181613a87565b61482f57604051600160e51b62461bcd02815260040180806020018281038252603b815260200180615806603b913960400191505060405180910390fd5b600b55565b600a81905561484d336148476000611f34565b836145b9565b50565b60008160008151811061485f57fe5b6020026020010151136148a657604051600160e51b62461bcd028152600401808060200182810382526029815260200180615a236029913960400191505060405180910390fd5b60015b8151811015611fdd578181815181106148be57fe5b602002602001015160001461491d5760408051600160e51b62461bcd02815260206004820181905260248201527f546f6b656e20636f6e736572766174696f6e20646f6573206e6f7420686f6c64604482015290519081900360640190fd5b6001016148a9565b6006805463ffffffff191663ffffffff871617905560005b8451811015614a47576006600101604051806060016040528087848151811061496257fe5b60200260200101516001600160a01b0316815260200185848151811061498457fe5b60200260200101516001600160801b031681526020018684815181106149a657fe5b60209081029190910181015161ffff908116909252835460018082018655600095865294829020845160029092020180546001600160a01b0319166001600160a01b039092169190911781559083015190840180546040909401516001600160801b03199094166001600160801b039092169190911771ffff000000000000000000000000000000001916600160801b93909216929092021790550161493d565b508051614a5b906008906020840190615694565b5050600980546001600160a01b0319163317905550505050565b6001600160a01b038083166000908152602081815260408083209385168352929052206002015463ffffffff168015801590614abd5750614ab4612ee6565b63ffffffff1681105b156125d4576001600160a01b0383811660009081526020818152604080832093861683529290522060018101549054614af5916143c5565b6001600160a01b0393841660009081526020818152604080832095909616825293909352928220928355506001820155600201805463ffffffff19169055565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b0316600160e01b6323b872dd02179052614b9290859061535b565b50505050565b6000818310614ba757816129d0565b5090919050565b604080516001600160a01b03851660601b6020820152815180820360140181526034820183526054808301869052835180840390910181526074909201909252614bff90829063ffffffff61348916565b82516040805161ffff90921660f01b60208301528051808303600201815260229092019052909150614c3890829063ffffffff61348916565b9050614c7b8260200151604051602001808261ffff1661ffff1660f01b81526002019150506040516020818303038152906040528261348990919063ffffffff16565b604080840151815163ffffffff91821660e01b6020820152825180820360040181526024909101909252919250614cb59183919061348916565b60608301516040805163ffffffff92831660e01b6020820152815180820360040181526024909101909152919250614cef91839161348916565b9050614d3c826080015160405160200180826001600160801b03166001600160801b031660801b81526010019150506040516020818303038152906040528261348990919063ffffffff16565b60a0830151604080516001600160801b0390921660801b60208301528051808303601001815260309092019052909150614d7d90829063ffffffff61348916565b9050612ecd614d8b83614227565b604080516001600160801b0390921660801b60208301528051808303601001815260309092019052829063ffffffff61348916565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b0316600160e01b63a9059cbb021790526125d490849061535b565b6000620100008210614e5b57604051600160e51b62461bcd02815260040180806020018281038252602681526020018061579a6026913960400191505060405180910390fd5b5090565b60006001614e6b612ee6565b60065463ffffffff908116929091031614905090565b614e8b8383614a75565b6001600160a01b0380841660009081526020818152604080832093861683529290522054614ebf908263ffffffff6143c516565b6001600160a01b0393841660009081526020818152604080832095909616825293909352929091209190915550565b6001600160a01b03831660009081526004602052604090208054614f1b919061ffff851690811061444f57fe5b156125d457614557614552826001600160801b031660046000876001600160a01b03166001600160a01b031681526020019081526020016000208561ffff1681548110614f6457fe5b6000918252602090912060029091020160010154600160801b90046001600160801b0316906147a6565b614f988383614a75565b6001600160a01b0380841660009081526020818152604080832093861683529290522054614ebf908263ffffffff6147a616565b6000612ecd6145526001600160801b038085169061439d906103e890613a96906103e79084908c8116908c1663ffffffff61423616565b600061ffff831661501657506000612253565b8151600090600019015b81811061509c57600060028383010490508561ffff1685828151811061504257fe5b602002602001015161ffff161415615061576001019250612253915050565b8561ffff1685828151811061507257fe5b602002602001015161ffff16101561508f57806001019250615096565b6001810391505b50615020565b60408051600160e51b62461bcd02815260206004820152601c60248201527f5072696365206e6f742070726f766964656420666f7220746f6b656e00000000604482015290519081900360640190fd5b60008183038183128015906151015750838113155b80615116575060008312801561511657508381135b6129d057604051600160e51b62461bcd028152600401808060200182810382526024815260200180615a706024913960400191505060405180910390fd5b60008282018183128015906151695750838112155b8061517e575060008312801561517e57508381125b6129d057604051600160e51b62461bcd0281526004018080602001828103825260218152602001806158686021913960400191505060405180910390fd5b60006129d083836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061551c565b60006129d083836040518060400160405280601881526020017f536166654d6174683a206d6f64756c6f206279207a65726f0000000000000000815250615584565b60006000196001600160801b031682608001516001600160801b03161415801561225357505060a001516001600160801b03908116141590565b6000600160801b8210614e5b57604051600160e51b62461bcd0281526004018080602001828103825260278152602001806158416027913960400191505060405180910390fd5b6000818484111561535357604051600160e51b62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360005b83811015615318578181015183820152602001615300565b50505050905090810190601f1680156153455780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b61536d826001600160a01b03166155e9565b6153c15760408051600160e51b62461bcd02815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b602083106153ff5780518252601f1990920191602091820191016153e0565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114615461576040519150601f19603f3d011682016040523d82523d6000602084013e615466565b606091505b5091509150816154c05760408051600160e51b62461bcd02815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115614b92578080602001905160208110156154dc57600080fd5b5051614b9257604051600160e51b62461bcd02815260040180806020018281038252602a815260200180615a94602a913960400191505060405180910390fd5b6000818361556e57604051600160e51b62461bcd028152602060048201818152835160248401528351909283926044909101919085019080838360008315615318578181015183820152602001615300565b50600083858161557a57fe5b0495945050505050565b600081836155d657604051600160e51b62461bcd028152602060048201818152835160248401528351909283926044909101919085019080838360008315615318578181015183820152602001615300565b508284816155e057fe5b06949350505050565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708115801590612ecd5750141592915050565b508054600082556002029060005260206000209081019061484d9190615739565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565b604080518082019091526000808252602082015290565b82805482825590600052602060002090600f0160109004810192821561572d5791602002820160005b838211156156fd57835183826101000a81548161ffff021916908361ffff16021790555092602001926002016020816001010492830192600103026156bd565b801561572b5782816101000a81549061ffff02191690556002016020816001010492830192600103026156fd565b505b50614e5b92915061577a565b6112d291905b80821115614e5b5780546001600160a01b031916815560018101805471ffffffffffffffffffffffffffffffffffff1916905560020161573f565b6112d291905b80821115614e5b57805461ffff1916815560010161578056fe53616665436173743a2076616c756520646f65736e27742066697420696e203136206269747362757920616d6f756e74206c657373207468616e20414d4f554e545f4d494e494d554d536f6c7574696f6e2065786365656473204d41585f544f55434845445f4f52444552534e6577206f626a65637469766520646f65736e27742073756666696369656e746c7920696d70726f76652063757272656e7420736f6c7574696f6e53616665436173743a2076616c756520646f65736e27742066697420696e2031323820626974735369676e6564536166654d6174683a206164646974696f6e206f766572666c6f77536f6c7574696f6e7320617265206e6f206c6f6e67657220616363657074656420666f7220746869732062617463685769746864726177206e6f7420706f737369626c6520666f7220746f6b656e20746861742069732074726164656420696e207468652063757272656e742061756374696f6e4174206c65617374206f6e65207072696365206c6f776572207468616e20414d4f554e545f4d494e494d554d4f72646572732063616e277420626520706c6163656420696e207468652070617374657865637574656453656c6c416d6f756e7420626967676572207468616e2073706563696669656420696e206f72646572536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7770726963657320617265206e6f74206f72646572656420627920746f6b656e4964776974686472617720776173206e6f7420726567697374657265642070726576696f75736c79436c61696d6564206f626a65637469766520646f65736e27742073756666696369656e746c7920696d70726f76652063757272656e7420736f6c7574696f6e546f6b656e20636f6e736572766174696f6e2061742030206d75737420626520706f7369746976652e73656c6c20616d6f756e74206c657373207468616e20414d4f554e545f4d494e494d554d5369676e6564536166654d6174683a207375627472616374696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564526571756573742063616e6e6f74206265206d61646520696e207468652070617374a165627a7a7230582073ef6c40df46039e6be6d195731df39de7c70e851409dcab570af49ff8ec1aca0029

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

000000000000000000000000000000000000000000000000000000000000ffff0000000000000000000000001a5f9352af8af974bfc03399e3767df6370d82e4

-----Decoded View---------------
Arg [0] : maxTokens (uint256): 65535
Arg [1] : _feeToken (address): 0x1A5F9352Af8aF974bFC03399e3767DF6370d82e4

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000000000000000000000000000000000000000ffff
Arg [1] : 0000000000000000000000001a5f9352af8af974bfc03399e3767df6370d82e4


Libraries Used

IterableAppendOnlySet : 0xcddb32b6bb2808d5b5115daab207479ce98d2636Unverified

Swarm Source

bzzr://73ef6c40df46039e6be6d195731df39de7c70e851409dcab570af49ff8ec1aca

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
0x6F400810b62df8E13fded51bE75fF5393eaa841F
Chain Token Portfolio % Price Amount Value
ETH66.88%$3,617.2300.6069$1,087,354.03
ETH22.82%$0.999798371,055.5461$370,980.59
ETH2.33%$0.98685738,318.9529$37,815.33
ETH2.01%$1.0132,392.2476$32,683.78
ETH1.85%$130,015.4277$30,045.44
ETH1.17%$0.99844119,089.3439$19,059.58
ETH0.97%$0.68223823,166.2599$15,804.9
ETH0.94%$0.99979315,319.8574$15,316.69
ETH0.52%$0.48715317,241.6581$8,399.33
ETH0.23%$24.31154.5663$3,757.51
ETH0.15%$106,1030.0223$2,370.8
ETH0.05%$0.1289956,638.904$856.39
ETH0.02%$126.122.7425$345.88
ETH0.02%$1.4205.394$287.55
ETH0.01%$1,315.180.1725$226.82
ETH<0.01%$0.999804158.8903$158.86
ETH<0.01%$0.999302131.79$131.7
ETH<0.01%$0.88786174.5296$66.17
ETH<0.01%$0.00111358,171.0662$64.75
ETH<0.01%$4,077.640.0085489$34.86
ETH<0.01%$0.032029925.6054$29.65
ETH<0.01%$0.0016067,347.0374$11.8
ETH<0.01%$35.050.2669$9.35
ETH<0.01%$0.000652,982.132$1.94
ETH<0.01%$0.007914100.0015$0.7914
ETH<0.01%$0.00618620$0.1237
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ 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.