Latest 25 from a total of 174,694 transactions
| Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Withdraw | 23704151 | 9 days ago | IN | 0 ETH | 0.00017449 | ||||
| Request Withdraw | 23704145 | 9 days ago | IN | 0 ETH | 0.00014496 | ||||
| Withdraw | 23685123 | 11 days ago | IN | 0 ETH | 0.00005661 | ||||
| Request Withdraw | 23685066 | 11 days ago | IN | 0 ETH | 0.00011662 | ||||
| Withdraw | 23685061 | 11 days ago | IN | 0 ETH | 0.00010042 | ||||
| Withdraw | 23685052 | 11 days ago | IN | 0 ETH | 0.00007734 | ||||
| Withdraw | 23685046 | 11 days ago | IN | 0 ETH | 0.00012468 | ||||
| Withdraw | 23685037 | 11 days ago | IN | 0 ETH | 0.00007781 | ||||
| Request Withdraw | 23684919 | 11 days ago | IN | 0 ETH | 0.00024678 | ||||
| Request Withdraw | 23684872 | 11 days ago | IN | 0 ETH | 0.00004742 | ||||
| Request Withdraw | 23684857 | 11 days ago | IN | 0 ETH | 0.00003744 | ||||
| Request Withdraw | 23684848 | 11 days ago | IN | 0 ETH | 0.00003473 | ||||
| Withdraw | 23684130 | 11 days ago | IN | 0 ETH | 0.00004818 | ||||
| Request Withdraw | 23684101 | 11 days ago | IN | 0 ETH | 0.00005296 | ||||
| Withdraw | 23684092 | 11 days ago | IN | 0 ETH | 0.00003781 | ||||
| Withdraw | 23625083 | 20 days ago | IN | 0 ETH | 0.00000729 | ||||
| Request Withdraw | 23625065 | 20 days ago | IN | 0 ETH | 0.00000748 | ||||
| Submit Solution | 23625059 | 20 days ago | IN | 0 ETH | 0.00004758 | ||||
| Place Order | 23625045 | 20 days ago | IN | 0 ETH | 0.00001026 | ||||
| Place Order | 23625002 | 20 days ago | IN | 0 ETH | 0.00000992 | ||||
| Place Order | 23625002 | 20 days ago | IN | 0 ETH | 0.00000992 | ||||
| Deposit | 23624555 | 20 days ago | IN | 0 ETH | 0.00001016 | ||||
| Withdraw | 23624485 | 20 days ago | IN | 0 ETH | 0.00000973 | ||||
| Request Withdraw | 23624455 | 20 days ago | IN | 0 ETH | 0.00000816 | ||||
| Submit Solution | 23624455 | 20 days ago | IN | 0 ETH | 0.00004774 |
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
To
|
|||
|---|---|---|---|---|---|---|---|
| - | 10587158 | 1924 days ago | 0.1 ETH |
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
This contract contains unverified libraries: IterableAppendOnlySet
Contract Name:
BatchExchange
Compiler Version
v0.5.6+commit.b259423e
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity)
/**
*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
- No Contract Security Audit Submitted- Submit Audit Here
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"}]Contract Creation Code
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
IdToAddressBiMap : 0xed4d05496c71e71cc2a8726af1242c22108d1761IterableAppendOnlySet : 0xcddb32b6bb2808d5b5115daab207479ce98d2636Unverified
Swarm Source
bzzr://73ef6c40df46039e6be6d195731df39de7c70e851409dcab570af49ff8ec1aca
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| ETH | 66.88% | $3,617.2 | 300.6069 | $1,087,354.03 | |
| ETH | 22.82% | $0.999798 | 371,055.5461 | $370,980.59 | |
| ETH | 2.33% | $0.986857 | 38,318.9529 | $37,815.33 | |
| ETH | 2.01% | $1.01 | 32,392.2476 | $32,683.78 | |
| ETH | 1.85% | $1 | 30,015.4277 | $30,045.44 | |
| ETH | 1.17% | $0.998441 | 19,089.3439 | $19,059.58 | |
| ETH | 0.97% | $0.682238 | 23,166.2599 | $15,804.9 | |
| ETH | 0.94% | $0.999793 | 15,319.8574 | $15,316.69 | |
| ETH | 0.52% | $0.487153 | 17,241.6581 | $8,399.33 | |
| ETH | 0.23% | $24.31 | 154.5663 | $3,757.51 | |
| ETH | 0.15% | $106,103 | 0.0223 | $2,370.8 | |
| ETH | 0.05% | $0.128995 | 6,638.904 | $856.39 | |
| ETH | 0.02% | $126.12 | 2.7425 | $345.88 | |
| ETH | 0.02% | $1.4 | 205.394 | $287.55 | |
| ETH | 0.01% | $1,315.18 | 0.1725 | $226.82 | |
| ETH | <0.01% | $0.999804 | 158.8903 | $158.86 | |
| ETH | <0.01% | $0.999302 | 131.79 | $131.7 | |
| ETH | <0.01% | $0.887861 | 74.5296 | $66.17 | |
| ETH | <0.01% | $0.001113 | 58,171.0662 | $64.75 | |
| ETH | <0.01% | $4,077.64 | 0.0085489 | $34.86 | |
| ETH | <0.01% | $0.032029 | 925.6054 | $29.65 | |
| ETH | <0.01% | $0.001606 | 7,347.0374 | $11.8 | |
| ETH | <0.01% | $35.05 | 0.2669 | $9.35 | |
| ETH | <0.01% | $0.00065 | 2,982.132 | $1.94 | |
| ETH | <0.01% | $0.007914 | 100.0015 | $0.7914 | |
| ETH | <0.01% | $0.006186 | 20 | $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.