Overview
ETH Balance
0.2519999999999994 ETH
Eth Value
$976.99 (@ $3,876.94/ETH)More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 596 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Set Max Supply11... | 15181347 | 878 days ago | IN | 0 ETH | 0.00184869 | ||||
Batch Mint ERC11... | 14908103 | 923 days ago | IN | 0 ETH | 0.00271456 | ||||
Batch Mint ERC11... | 14708527 | 956 days ago | IN | 0.126 ETH | 0.01121891 | ||||
Batch Mint ERC11... | 14707835 | 956 days ago | IN | 0 ETH | 0.0121787 | ||||
Batch Mint ERC11... | 14707826 | 956 days ago | IN | 0.126 ETH | 0.00772691 | ||||
Batch Mint ERC11... | 14707358 | 956 days ago | IN | 0.126 ETH | 0.00760049 | ||||
Batch Mint ERC11... | 14707287 | 956 days ago | IN | 0.126 ETH | 0.00859171 | ||||
Batch Mint ERC11... | 14706965 | 956 days ago | IN | 0.126 ETH | 0.0118269 | ||||
Batch Mint ERC11... | 14706903 | 956 days ago | IN | 0.126 ETH | 0.00965421 | ||||
Batch Mint ERC11... | 14706757 | 956 days ago | IN | 0.7 ETH | 0.01263079 | ||||
Batch Mint ERC11... | 14706578 | 956 days ago | IN | 0.7 ETH | 0.00963927 | ||||
Batch Mint ERC11... | 14706532 | 956 days ago | IN | 0.7 ETH | 0.00884935 | ||||
Batch Mint ERC11... | 14706137 | 956 days ago | IN | 0.126 ETH | 0.01623701 | ||||
Batch Mint ERC11... | 14705913 | 956 days ago | IN | 0.126 ETH | 0.00844959 | ||||
Batch Mint ERC11... | 14705035 | 956 days ago | IN | 0.826 ETH | 0.01441164 | ||||
Batch Mint ERC11... | 14704679 | 956 days ago | IN | 0 ETH | 0.00765593 | ||||
Batch Mint ERC11... | 14704672 | 956 days ago | IN | 0.826 ETH | 0.01329265 | ||||
Batch Mint ERC11... | 14704311 | 956 days ago | IN | 0.126 ETH | 0.01098748 | ||||
Batch Mint ERC11... | 14704280 | 956 days ago | IN | 0.126 ETH | 0.01098748 | ||||
Batch Mint ERC11... | 14704242 | 956 days ago | IN | 0.126 ETH | 0.00771101 | ||||
Batch Mint ERC11... | 14703984 | 956 days ago | IN | 0.126 ETH | 0.00812634 | ||||
Batch Mint ERC11... | 14703826 | 956 days ago | IN | 0.126 ETH | 0.00960899 | ||||
Batch Mint ERC11... | 14703780 | 956 days ago | IN | 0.126 ETH | 0.01370432 | ||||
Batch Mint ERC11... | 14703160 | 957 days ago | IN | 0.126 ETH | 0.00816843 | ||||
Batch Mint ERC11... | 14701596 | 957 days ago | IN | 0.126 ETH | 0.00670694 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
14708527 | 956 days ago | 0.08442 ETH | ||||
14708527 | 956 days ago | 0.04158 ETH | ||||
14707826 | 956 days ago | 0.08442 ETH | ||||
14707826 | 956 days ago | 0.04158 ETH | ||||
14707358 | 956 days ago | 0.08442 ETH | ||||
14707358 | 956 days ago | 0.04158 ETH | ||||
14707287 | 956 days ago | 0.08442 ETH | ||||
14707287 | 956 days ago | 0.04158 ETH | ||||
14706965 | 956 days ago | 0.08442 ETH | ||||
14706965 | 956 days ago | 0.04158 ETH | ||||
14706903 | 956 days ago | 0.08442 ETH | ||||
14706903 | 956 days ago | 0.04158 ETH | ||||
14706757 | 956 days ago | 0.469 ETH | ||||
14706757 | 956 days ago | 0.231 ETH | ||||
14706578 | 956 days ago | 0.469 ETH | ||||
14706578 | 956 days ago | 0.231 ETH | ||||
14706532 | 956 days ago | 0.469 ETH | ||||
14706532 | 956 days ago | 0.231 ETH | ||||
14706137 | 956 days ago | 0.08442 ETH | ||||
14706137 | 956 days ago | 0.04158 ETH | ||||
14705913 | 956 days ago | 0.08442 ETH | ||||
14705913 | 956 days ago | 0.04158 ETH | ||||
14705035 | 956 days ago | 0.08442 ETH | ||||
14705035 | 956 days ago | 0.04158 ETH | ||||
14705035 | 956 days ago | 0.469 ETH |
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
OdysseyRouter
Compiler Version
v0.8.12+commit.f00d7308
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity)
/** *Submitted for verification at Etherscan.io on 2022-04-29 */ // SPDX-License-Identifier: Unlicense pragma solidity >=0.8.12; /// @notice Gas optimized reentrancy protection for smart contracts. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/ReentrancyGuard.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol) abstract contract ReentrancyGuard { uint256 private locked = 1; modifier nonReentrant() { require(locked == 1, "REENTRANCY"); locked = 2; _; locked = 1; } } /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. abstract contract ERC20 { /*/////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer(address indexed from, address indexed to, uint256 amount); event Approval( address indexed owner, address indexed spender, uint256 amount ); /*/////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ string public name; string public symbol; uint8 public immutable decimals; /*/////////////////////////////////////////////////////////////// ERC20 STORAGE //////////////////////////////////////////////////////////////*/ uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; /*/////////////////////////////////////////////////////////////// EIP-2612 STORAGE //////////////////////////////////////////////////////////////*/ bytes32 public constant PERMIT_TYPEHASH = keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ); uint256 internal immutable INITIAL_CHAIN_ID; bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; mapping(address => uint256) public nonces; /*/////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor( string memory _name, string memory _symbol, uint8 _decimals ) { name = _name; symbol = _symbol; decimals = _decimals; INITIAL_CHAIN_ID = block.chainid; INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); } /*/////////////////////////////////////////////////////////////// ERC20 LOGIC //////////////////////////////////////////////////////////////*/ function approve(address spender, uint256 amount) public virtual returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function transfer(address to, uint256 amount) public virtual returns (bool) { balanceOf[msg.sender] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(msg.sender, to, amount); return true; } function transferFrom( address from, address to, uint256 amount ) public virtual returns (bool) { uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; balanceOf[from] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(from, to, amount); return true; } /*/////////////////////////////////////////////////////////////// EIP-2612 LOGIC //////////////////////////////////////////////////////////////*/ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); // Unchecked because the only math done is incrementing // the owner's nonce which cannot realistically overflow. unchecked { bytes32 digest = keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline ) ) ) ); address recoveredAddress = ecrecover(digest, v, r, s); require( recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER" ); allowance[recoveredAddress][spender] = value; } emit Approval(owner, spender, value); } function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); } function computeDomainSeparator() internal view virtual returns (bytes32) { return keccak256( abi.encode( keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ), keccak256(bytes(name)), keccak256("1"), block.chainid, address(this) ) ); } /*/////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 amount) internal virtual { totalSupply += amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(address(0), to, amount); } function _burn(address from, uint256 amount) internal virtual { balanceOf[from] -= amount; // Cannot underflow because a user's balance // will never be larger than the total supply. unchecked { totalSupply -= amount; } emit Transfer(from, address(0), amount); } } // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol) /** * @dev String operations. */ library Strings { bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { // Inspired by OraclizeAPI's implementation - MIT licence // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol if (value == 0) { return "0"; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); while (value != 0) { digits -= 1; buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); value /= 10; } return string(buffer); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { if (value == 0) { return "0x00"; } uint256 temp = value; uint256 length = 0; while (temp != 0) { length++; temp >>= 8; } return toHexString(value, length); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _HEX_SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } } /// @title Interface for verifying contract-based account signatures /// @notice Interface that verifies provided signature for the data /// @dev Interface defined by EIP-1271 interface IERC1271 { /// @notice Returns whether the provided signature is valid for the provided data /// @dev MUST return the bytes4 magic value 0x1626ba7e when function passes. /// MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5). /// MUST allow external calls. /// @param hash Hash of the data to be signed /// @param signature Signature byte array associated with _data /// @return magicValue The bytes4 magic value 0x1626ba7e function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue); } // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol) /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require( address(this).balance >= amount, "Address: insufficient balance" ); (bool success, ) = recipient.call{value: amount}(""); require( success, "Address: unable to send value, recipient may have reverted" ); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue( target, data, value, "Address: low-level call with value failed" ); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require( address(this).balance >= value, "Address: insufficient balance for call" ); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}( data ); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall( target, data, "Address: low-level static call failed" ); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall( target, data, "Address: low-level delegate call failed" ); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } } library Signature { function recover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. require( uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ODYSSEY: INVALID_SIGNATURE_S_VALUE" ); require(v == 27 || v == 28, "ODYSSEY: INVALID_SIGNATURE_V_VALUE"); // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); require(signer != address(0), "ODYSSEY: INVALID_SIGNATURE"); return signer; } function verify( bytes32 hash, address signer, uint8 v, bytes32 r, bytes32 s, bytes32 domainSeparator ) internal view { bytes32 digest = keccak256( abi.encodePacked("\x19\x01", domainSeparator, hash) ); if (Address.isContract(signer)) { require( IERC1271(signer).isValidSignature( digest, abi.encodePacked(r, s, v) ) == 0x1626ba7e, "ODYSSEY: UNAUTHORIZED" ); } else { require( recover(digest, v, r, s) == signer, "ODYSSEY: UNAUTHORIZED" ); } } } // OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/MerkleProof.sol) /** * @dev These functions deal with verification of Merkle Trees proofs. * * The proofs can be generated using the JavaScript library * https://github.com/miguelmota/merkletreejs[merkletreejs]. * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled. * * See `test/utils/cryptography/MerkleProof.test.js` for some examples. * * WARNING: You should avoid using leaf values that are 64 bytes long prior to * hashing, or use a hash function other than keccak256 for hashing leaves. * This is because the concatenation of a sorted pair of internal nodes in * the merkle tree could be reinterpreted as a leaf value. */ library MerkleProof { /** * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree * defined by `root`. For this, a `proof` must be provided, containing * sibling hashes on the branch from the leaf to the root of the tree. Each * pair of leaves and each pair of pre-images are assumed to be sorted. */ function verify( bytes32[] memory proof, bytes32 root, bytes32 leaf ) internal pure returns (bool) { return processProof(proof, leaf) == root; } /** * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt * hash matches the root of the tree. When processing the proof, the pairs * of leafs & pre-images are assumed to be sorted. * * _Available since v4.4._ */ function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { bytes32 proofElement = proof[i]; if (computedHash <= proofElement) { // Hash(current computed hash + current element of the proof) computedHash = _efficientHash(computedHash, proofElement); } else { // Hash(current element of the proof + current computed hash) computedHash = _efficientHash(proofElement, computedHash); } } return computedHash; } function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) { assembly { mstore(0x00, a) mstore(0x20, b) value := keccak256(0x00, 0x40) } } } library MerkleWhiteList { function verify( address sender, bytes32[] calldata merkleProof, bytes32 merkleRoot ) internal pure { // Verify whitelist require(address(0) != sender); bytes32 leaf = keccak256(abi.encodePacked(sender)); require( MerkleProof.verify(merkleProof, merkleRoot, leaf), "Not whitelisted" ); } } /// @notice Modern, minimalist, and gas efficient ERC-721 implementation. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol) /// @dev Note that balanceOf does not revert if passed the zero address, in defiance of the ERC. abstract contract ERC721 { /*/////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer( address indexed from, address indexed to, uint256 indexed id ); event Approval( address indexed owner, address indexed spender, uint256 indexed id ); event ApprovalForAll( address indexed owner, address indexed operator, bool approved ); /*/////////////////////////////////////////////////////////////// METADATA STORAGE/LOGIC //////////////////////////////////////////////////////////////*/ string public name; string public symbol; function tokenURI(uint256 id) public view virtual returns (string memory); /*/////////////////////////////////////////////////////////////// ERC721 STORAGE //////////////////////////////////////////////////////////////*/ mapping(address => uint256) public balanceOf; mapping(uint256 => address) public ownerOf; mapping(uint256 => address) public getApproved; mapping(address => mapping(address => bool)) public isApprovedForAll; /*/////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor(string memory _name, string memory _symbol) { name = _name; symbol = _symbol; } /*/////////////////////////////////////////////////////////////// ERC721 LOGIC //////////////////////////////////////////////////////////////*/ function approve(address spender, uint256 id) public virtual { address owner = ownerOf[id]; require( msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED" ); getApproved[id] = spender; emit Approval(owner, spender, id); } function setApprovalForAll(address operator, bool approved) public virtual { isApprovedForAll[msg.sender][operator] = approved; emit ApprovalForAll(msg.sender, operator, approved); } function transferFrom( address from, address to, uint256 id ) public virtual { require(from == ownerOf[id], "WRONG_FROM"); require(to != address(0), "INVALID_RECIPIENT"); require( msg.sender == from || msg.sender == getApproved[id] || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED" ); // Underflow of the sender's balance is impossible because we check for // ownership above and the recipient's balance can't realistically overflow. unchecked { balanceOf[from]--; balanceOf[to]++; } ownerOf[id] = to; delete getApproved[id]; emit Transfer(from, to, id); } function safeTransferFrom( address from, address to, uint256 id ) public virtual { transferFrom(from, to, id); require( to.code.length == 0 || ERC721TokenReceiver(to).onERC721Received( msg.sender, from, id, "" ) == ERC721TokenReceiver.onERC721Received.selector, "UNSAFE_RECIPIENT" ); } function safeTransferFrom( address from, address to, uint256 id, bytes memory data ) public virtual { transferFrom(from, to, id); require( to.code.length == 0 || ERC721TokenReceiver(to).onERC721Received( msg.sender, from, id, data ) == ERC721TokenReceiver.onERC721Received.selector, "UNSAFE_RECIPIENT" ); } /*/////////////////////////////////////////////////////////////// ERC165 LOGIC //////////////////////////////////////////////////////////////*/ function supportsInterface(bytes4 interfaceId) public pure virtual returns (bool) { return interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721 interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata } /*/////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 id) internal virtual { require(to != address(0), "INVALID_RECIPIENT"); require(ownerOf[id] == address(0), "ALREADY_MINTED"); // Counter overflow is incredibly unrealistic. unchecked { balanceOf[to]++; } ownerOf[id] = to; emit Transfer(address(0), to, id); } function _burn(uint256 id) internal virtual { address owner = ownerOf[id]; require(ownerOf[id] != address(0), "NOT_MINTED"); // Ownership check above ensures no underflow. unchecked { balanceOf[owner]--; } delete ownerOf[id]; delete getApproved[id]; emit Transfer(owner, address(0), id); } /*/////////////////////////////////////////////////////////////// INTERNAL SAFE MINT LOGIC //////////////////////////////////////////////////////////////*/ function _safeMint(address to, uint256 id) internal virtual { _mint(to, id); require( to.code.length == 0 || ERC721TokenReceiver(to).onERC721Received( msg.sender, address(0), id, "" ) == ERC721TokenReceiver.onERC721Received.selector, "UNSAFE_RECIPIENT" ); } function _safeMint( address to, uint256 id, bytes memory data ) internal virtual { _mint(to, id); require( to.code.length == 0 || ERC721TokenReceiver(to).onERC721Received( msg.sender, address(0), id, data ) == ERC721TokenReceiver.onERC721Received.selector, "UNSAFE_RECIPIENT" ); } } /// @notice A generic interface for a contract which properly accepts ERC721 tokens. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol) interface ERC721TokenReceiver { function onERC721Received( address operator, address from, uint256 id, bytes calldata data ) external returns (bytes4); } library UInt2Str { function uint2str(uint256 _i) internal pure returns (string memory _uintAsString) { if (_i == 0) { return "0"; } uint256 j = _i; uint256 len; while (j != 0) { len++; j /= 10; } bytes memory bstr = new bytes(len); uint256 k = len; while (_i != 0) { k = k - 1; uint8 temp = (48 + uint8(_i - (_i / 10) * 10)); bytes1 b1 = bytes1(temp); bstr[k] = b1; _i /= 10; } return string(bstr); } } contract OdysseyERC721 is ERC721("", "") { using UInt2Str for uint256; /*/////////////////////////////////////////////////////////////// CUSTOM ERRORS //////////////////////////////////////////////////////////////*/ error OdysseyERC721_AlreadyInit(); error OdysseyERC721_Unauthorized(); error OdysseyERC721_BadAddress(); /*/////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ address launcher; address public owner; bool initialized; string public baseURI; uint256 public royaltyFeeInBips; // 1% = 100 address public royaltyReceiver; string public contractURI; /*/////////////////////////////////////////////////////////////// METADATA LOGIC //////////////////////////////////////////////////////////////*/ function tokenURI(uint256 id) public view virtual override returns (string memory) { return string(abi.encodePacked(baseURI, id.uint2str())); } /*/////////////////////////////////////////////////////////////// FACTORY LOGIC //////////////////////////////////////////////////////////////*/ function initialize( address _launcher, address _owner, string calldata _name, string calldata _symbol, string calldata _baseURI ) external { if (initialized) { revert OdysseyERC721_AlreadyInit(); } initialized = true; launcher = _launcher; owner = _owner; name = _name; symbol = _symbol; baseURI = _baseURI; } /*/////////////////////////////////////////////////////////////// ERC721 LOGIC //////////////////////////////////////////////////////////////*/ function transferOwnership(address newOwner) public virtual { if (newOwner == address(0)) { revert OdysseyERC721_BadAddress(); } if (msg.sender != owner) { revert OdysseyERC721_Unauthorized(); } owner = newOwner; } function mint(address user, uint256 id) external { if (msg.sender != launcher) { revert OdysseyERC721_Unauthorized(); } _mint(user, id); } /*/////////////////////////////////////////////////////////////// EIP2981 LOGIC //////////////////////////////////////////////////////////////*/ function royaltyInfo(uint256, uint256 _salePrice) external view returns (address receiver, uint256 royaltyAmount) { return (royaltyReceiver, (_salePrice / 10000) * royaltyFeeInBips); } function setRoyaltyInfo(address _royaltyReceiver, uint256 _royaltyFeeInBips) external { if (_royaltyReceiver == address(0)) { revert OdysseyERC721_BadAddress(); } if (msg.sender != owner) { revert OdysseyERC721_Unauthorized(); } royaltyReceiver = _royaltyReceiver; royaltyFeeInBips = _royaltyFeeInBips; } function setContractURI(string memory _uri) public { if (msg.sender != owner) { revert OdysseyERC721_Unauthorized(); } contractURI = _uri; } /*/////////////////////////////////////////////////////////////// ERC165 LOGIC //////////////////////////////////////////////////////////////*/ function supportsInterface(bytes4 interfaceID) public pure override(ERC721) returns (bool) { return bytes4(keccak256("royaltyInfo(uint256,uint256)")) == interfaceID || super.supportsInterface(interfaceID); } } /// @notice Minimalist and gas efficient standard ERC1155 implementation. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC1155.sol) abstract contract ERC1155 { /*/////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event TransferSingle( address indexed operator, address indexed from, address indexed to, uint256 id, uint256 amount ); event TransferBatch( address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] amounts ); event ApprovalForAll( address indexed owner, address indexed operator, bool approved ); event URI(string value, uint256 indexed id); /*/////////////////////////////////////////////////////////////// ERC1155 STORAGE //////////////////////////////////////////////////////////////*/ mapping(address => mapping(uint256 => uint256)) public balanceOf; mapping(address => mapping(address => bool)) public isApprovedForAll; /*/////////////////////////////////////////////////////////////// METADATA LOGIC //////////////////////////////////////////////////////////////*/ function uri(uint256 id) public view virtual returns (string memory); /*/////////////////////////////////////////////////////////////// ERC1155 LOGIC //////////////////////////////////////////////////////////////*/ function setApprovalForAll(address operator, bool approved) public virtual { isApprovedForAll[msg.sender][operator] = approved; emit ApprovalForAll(msg.sender, operator, approved); } function safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes memory data ) public virtual { require( msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED" ); balanceOf[from][id] -= amount; balanceOf[to][id] += amount; emit TransferSingle(msg.sender, from, to, id, amount); require( to.code.length == 0 ? to != address(0) : ERC1155TokenReceiver(to).onERC1155Received( msg.sender, from, id, amount, data ) == ERC1155TokenReceiver.onERC1155Received.selector, "UNSAFE_RECIPIENT" ); } function safeBatchTransferFrom( address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) public virtual { uint256 idsLength = ids.length; // Saves MLOADs. require(idsLength == amounts.length, "LENGTH_MISMATCH"); require( msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED" ); for (uint256 i = 0; i < idsLength; ) { uint256 id = ids[i]; uint256 amount = amounts[i]; balanceOf[from][id] -= amount; balanceOf[to][id] += amount; // An array can't have a total length // larger than the max uint256 value. unchecked { i++; } } emit TransferBatch(msg.sender, from, to, ids, amounts); require( to.code.length == 0 ? to != address(0) : ERC1155TokenReceiver(to).onERC1155BatchReceived( msg.sender, from, ids, amounts, data ) == ERC1155TokenReceiver.onERC1155BatchReceived.selector, "UNSAFE_RECIPIENT" ); } function balanceOfBatch(address[] memory owners, uint256[] memory ids) public view virtual returns (uint256[] memory balances) { uint256 ownersLength = owners.length; // Saves MLOADs. require(ownersLength == ids.length, "LENGTH_MISMATCH"); balances = new uint256[](owners.length); // Unchecked because the only math done is incrementing // the array index counter which cannot possibly overflow. unchecked { for (uint256 i = 0; i < ownersLength; i++) { balances[i] = balanceOf[owners[i]][ids[i]]; } } } /*/////////////////////////////////////////////////////////////// ERC165 LOGIC //////////////////////////////////////////////////////////////*/ function supportsInterface(bytes4 interfaceId) public pure virtual returns (bool) { return interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155 interfaceId == 0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI } /*/////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint( address to, uint256 id, uint256 amount, bytes memory data ) internal { balanceOf[to][id] += amount; emit TransferSingle(msg.sender, address(0), to, id, amount); require( to.code.length == 0 ? to != address(0) : ERC1155TokenReceiver(to).onERC1155Received( msg.sender, address(0), id, amount, data ) == ERC1155TokenReceiver.onERC1155Received.selector, "UNSAFE_RECIPIENT" ); } function _batchMint( address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal { uint256 idsLength = ids.length; // Saves MLOADs. require(idsLength == amounts.length, "LENGTH_MISMATCH"); for (uint256 i = 0; i < idsLength; ) { balanceOf[to][ids[i]] += amounts[i]; // An array can't have a total length // larger than the max uint256 value. unchecked { i++; } } emit TransferBatch(msg.sender, address(0), to, ids, amounts); require( to.code.length == 0 ? to != address(0) : ERC1155TokenReceiver(to).onERC1155BatchReceived( msg.sender, address(0), ids, amounts, data ) == ERC1155TokenReceiver.onERC1155BatchReceived.selector, "UNSAFE_RECIPIENT" ); } function _batchBurn( address from, uint256[] memory ids, uint256[] memory amounts ) internal { uint256 idsLength = ids.length; // Saves MLOADs. require(idsLength == amounts.length, "LENGTH_MISMATCH"); for (uint256 i = 0; i < idsLength; ) { balanceOf[from][ids[i]] -= amounts[i]; // An array can't have a total length // larger than the max uint256 value. unchecked { i++; } } emit TransferBatch(msg.sender, from, address(0), ids, amounts); } function _burn( address from, uint256 id, uint256 amount ) internal { balanceOf[from][id] -= amount; emit TransferSingle(msg.sender, from, address(0), id, amount); } } /// @notice A generic interface for a contract which properly accepts ERC1155 tokens. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC1155.sol) interface ERC1155TokenReceiver { function onERC1155Received( address operator, address from, uint256 id, uint256 amount, bytes calldata data ) external returns (bytes4); function onERC1155BatchReceived( address operator, address from, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data ) external returns (bytes4); } contract OdysseyERC1155 is ERC1155 { using UInt2Str for uint256; /*/////////////////////////////////////////////////////////////// CUSTOM ERRORS //////////////////////////////////////////////////////////////*/ error OdysseyERC1155_AlreadyInit(); error OdysseyERC1155_Unauthorized(); error OdysseyERC1155_BadAddress(); /*/////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ address launcher; address public owner; string public name; string public symbol; string public baseURI; bool initialized; uint256 public royaltyFeeInBips; // 1% = 100 address public royaltyReceiver; string public contractURI; /*/////////////////////////////////////////////////////////////// METADATA LOGIC //////////////////////////////////////////////////////////////*/ function uri(uint256 id) public view virtual override returns (string memory) { return string(abi.encodePacked(baseURI, id.uint2str())); } /*/////////////////////////////////////////////////////////////// FACTORY LOGIC //////////////////////////////////////////////////////////////*/ function initialize( address _launcher, address _owner, string calldata _name, string calldata _symbol, string calldata _baseURI ) external { if (isInit()) { revert OdysseyERC1155_AlreadyInit(); } initialized = true; launcher = _launcher; owner = _owner; name = _name; symbol = _symbol; baseURI = _baseURI; } function isInit() internal view returns (bool) { return initialized; } /*/////////////////////////////////////////////////////////////// ERC1155 LOGIC //////////////////////////////////////////////////////////////*/ function transferOwnership(address newOwner) public virtual { if (newOwner == address(0)) { revert OdysseyERC1155_BadAddress(); } if (msg.sender != owner) { revert OdysseyERC1155_Unauthorized(); } owner = newOwner; } function mint(address user, uint256 id) external { if (msg.sender != launcher) { revert OdysseyERC1155_Unauthorized(); } _mint(user, id, 1, ""); } function mintBatch( address user, uint256 id, uint256 amount ) external { if (msg.sender != launcher) { revert OdysseyERC1155_Unauthorized(); } _mint(user, id, amount, ""); } /*/////////////////////////////////////////////////////////////// EIP2981 LOGIC //////////////////////////////////////////////////////////////*/ function royaltyInfo(uint256, uint256 _salePrice) external view returns (address receiver, uint256 royaltyAmount) { return (royaltyReceiver, (_salePrice / 10000) * royaltyFeeInBips); } function setRoyaltyInfo(address _royaltyReceiver, uint256 _royaltyFeeInBips) external { if (_royaltyReceiver == address(0)) { revert OdysseyERC1155_BadAddress(); } if (msg.sender != owner) { revert OdysseyERC1155_Unauthorized(); } royaltyReceiver = _royaltyReceiver; royaltyFeeInBips = _royaltyFeeInBips; } function setContractURI(string memory _uri) public { if (msg.sender != owner) { revert OdysseyERC1155_Unauthorized(); } contractURI = _uri; } /*/////////////////////////////////////////////////////////////// ERC165 LOGIC //////////////////////////////////////////////////////////////*/ function supportsInterface(bytes4 interfaceID) public pure override(ERC1155) returns (bool) { return bytes4(keccak256("royaltyInfo(uint256,uint256)")) == interfaceID || super.supportsInterface(interfaceID); } } contract OdysseyTokenFactory { /*/////////////////////////////////////////////////////////////// CUSTOM ERRORS //////////////////////////////////////////////////////////////*/ error OdysseyTokenFactory_TokenAlreadyExists(); /*/////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event TokenCreated( string indexed name, string indexed symbol, address addr, bool isERC721, uint256 length ); /*/////////////////////////////////////////////////////////////// FACTORY STORAGE //////////////////////////////////////////////////////////////*/ mapping(string => mapping(string => address)) public getToken; mapping(address => uint256) public tokenExists; address[] public allTokens; /*/////////////////////////////////////////////////////////////// FACTORY LOGIC //////////////////////////////////////////////////////////////*/ function allTokensLength() external view returns (uint256) { return allTokens.length; } function create1155( address owner, string calldata name, string calldata symbol, string calldata baseURI ) external returns (address token) { if (getToken[name][symbol] != address(0)) { revert OdysseyTokenFactory_TokenAlreadyExists(); } bytes memory bytecode = type(OdysseyERC1155).creationCode; bytes32 salt = keccak256(abi.encodePacked(name, symbol)); assembly { token := create2(0, add(bytecode, 32), mload(bytecode), salt) } getToken[name][symbol] = token; tokenExists[token] = 1; // Run the proper initialize function OdysseyERC1155(token).initialize( msg.sender, owner, name, symbol, string( abi.encodePacked( baseURI, Strings.toString(block.chainid), "/", Strings.toHexString(uint160(token)), "/" ) ) ); emit TokenCreated(name, symbol, token, false, allTokens.length); return token; } function create721( address owner, string calldata name, string calldata symbol, string calldata baseURI ) external returns (address token) { if (getToken[name][symbol] != address(0)) { revert OdysseyTokenFactory_TokenAlreadyExists(); } bytes memory bytecode = type(OdysseyERC721).creationCode; bytes32 salt = keccak256(abi.encodePacked(name, symbol)); assembly { token := create2(0, add(bytecode, 32), mload(bytecode), salt) } getToken[name][symbol] = token; tokenExists[token] = 1; // Run the proper initialize function OdysseyERC721(token).initialize( msg.sender, owner, name, symbol, string( abi.encodePacked( baseURI, Strings.toString(block.chainid), "/", Strings.toHexString(uint160(token)), "/" ) ) ); emit TokenCreated(name, symbol, token, true, allTokens.length); } } library OdysseyLib { struct Odyssey1155Info { uint256[] maxSupply; uint256[] tokenIds; uint256[] reserveAmounts; } struct BatchMint { bytes32[][] merkleProof; bytes32[] merkleRoot; uint256[] minPrice; uint256[] mintsPerUser; uint256[] tokenId; address[] tokenAddress; address[] currency; uint8[] v; bytes32[] r; bytes32[] s; } struct Percentage { uint256 numerator; uint256 denominator; } function compareDefaultPercentage(OdysseyLib.Percentage calldata percent) internal pure returns (bool result) { if (percent.numerator > percent.denominator) { // Can't have a percent greater than 100 return false; } if (percent.numerator == 0 || percent.denominator == 0) { // Can't use 0 in percentage return false; } //Check cross multiplication of 3/100 uint256 crossMultiple1 = percent.numerator * 100; uint256 crossMultiple2 = percent.denominator * 3; if (crossMultiple1 < crossMultiple2) { return false; } return true; } } abstract contract OdysseyDatabase { // Custom Errors error OdysseyLaunchPlatform_TokenDoesNotExist(); error OdysseyLaunchPlatform_AlreadyClaimed(); error OdysseyLaunchPlatform_MaxSupplyCap(); error OdysseyLaunchPlatform_InsufficientFunds(); error OdysseyLaunchPlatform_TreasuryPayFailure(); error OdysseyLaunchPlatform_FailedToPayEther(); error OdysseyLaunchPlatform_FailedToPayERC20(); error OdysseyLaunchPlatform_ReservedOrClaimedMax(); // Constants // keccak256("whitelistMint721(bytes32 merkleRoot,uint256 minPrice,uint256 mintsPerUser,address tokenAddress,address currency)").toString('hex') bytes32 public constant MERKLE_TREE_ROOT_ERC721_TYPEHASH = 0xf0f6f256599682b9387f45fc268ed696625f835d98d64b8967134239e103fc6c; // keccak256("whitelistMint1155(bytes32 merkleRoot,uint256 minPrice,uint256 mintsPerUser,uint256 tokenId,address tokenAddress,address currency)").toString('hex') bytes32 public constant MERKLE_TREE_ROOT_ERC1155_TYPEHASH = 0x0a52f6e0133eadd055cc5703844e676242c3b461d85fb7ce7f74becd7e40edd1; // Def understand this before writing code: // https://docs.soliditylang.org/en/v0.8.12/internals/layout_in_storage.html //--------------------------------------------------------------------------------// // Slot | Type | Description // //--------------------------------------------------------------------------------// // 0x00 | address | OdysseyLaunchPlatform.sol // // 0x01 | address | OdysseyFactory.sol // // 0x02 | address | Treasury Multisig // // 0x03 | address | Admin Address // // 0x04 | address | OdysseyXp.sol // //--------------------------------------------------------------------------------// // Slot storage address launchPlatform; // slot 0 address factory; // slot 1 address treasury; // slot 2 address admin; //slot 3 address xp; //slot 4 // Common Storage mapping(address => bytes32) public domainSeparator; mapping(address => uint256) public whitelistActive; mapping(address => address) public ownerOf; mapping(address => address) public royaltyRecipient; mapping(address => OdysseyLib.Percentage) public treasuryCommission; mapping(address => uint256) public ohmFamilyCurrencies; // ERC721 Storage mapping(address => mapping(address => uint256)) public whitelistClaimed721; mapping(address => mapping(address => uint256)) public isReserved721; mapping(address => uint256) public cumulativeSupply721; mapping(address => uint256) public mintedSupply721; mapping(address => uint256) public maxSupply721; // ERC1155 Storage mapping(address => mapping(address => mapping(uint256 => uint256))) public whitelistClaimed1155; mapping(address => mapping(address => mapping(uint256 => uint256))) public isReserved1155; mapping(address => mapping(uint256 => uint256)) public cumulativeSupply1155; mapping(address => mapping(uint256 => uint256)) public maxSupply1155; function readSlotAsAddress(uint256 slot) public view returns (address data) { assembly { data := sload(slot) } } } /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol) /// @author Modified from Gnosis (https://github.com/gnosis/gp-v2-contracts/blob/main/src/contracts/libraries/GPv2SafeERC20.sol) /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. library SafeTransferLib { /*/////////////////////////////////////////////////////////////// ETH OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferETH(address to, uint256 amount) internal { bool callStatus; assembly { // Transfer the ETH and store if it succeeded or not. callStatus := call(gas(), to, amount, 0, 0, 0, 0) } require(callStatus, "ETH_TRANSFER_FAILED"); } /*/////////////////////////////////////////////////////////////// ERC20 OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferFrom( ERC20 token, address from, address to, uint256 amount ) internal { bool callStatus; assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata to memory piece by piece: mstore( freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000 ) // Begin with the function selector. mstore( add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff) ) // Mask and append the "from" argument. mstore( add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff) ) // Mask and append the "to" argument. mstore(add(freeMemoryPointer, 68), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value. // Call the token and store if it succeeded or not. // We use 100 because the calldata length is 4 + 32 * 3. callStatus := call(gas(), token, 0, freeMemoryPointer, 100, 0, 0) } require( didLastOptionalReturnCallSucceed(callStatus), "TRANSFER_FROM_FAILED" ); } function safeTransfer( ERC20 token, address to, uint256 amount ) internal { bool callStatus; assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata to memory piece by piece: mstore( freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000 ) // Begin with the function selector. mstore( add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff) ) // Mask and append the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value. // Call the token and store if it succeeded or not. // We use 68 because the calldata length is 4 + 32 * 2. callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0) } require( didLastOptionalReturnCallSucceed(callStatus), "TRANSFER_FAILED" ); } function safeApprove( ERC20 token, address to, uint256 amount ) internal { bool callStatus; assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata to memory piece by piece: mstore( freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000 ) // Begin with the function selector. mstore( add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff) ) // Mask and append the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value. // Call the token and store if it succeeded or not. // We use 68 because the calldata length is 4 + 32 * 2. callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0) } require(didLastOptionalReturnCallSucceed(callStatus), "APPROVE_FAILED"); } /*/////////////////////////////////////////////////////////////// INTERNAL HELPER LOGIC //////////////////////////////////////////////////////////////*/ function didLastOptionalReturnCallSucceed(bool callStatus) private pure returns (bool success) { assembly { // Get how many bytes the call returned. let returnDataSize := returndatasize() // If the call reverted: if iszero(callStatus) { // Copy the revert message into memory. returndatacopy(0, 0, returnDataSize) // Revert with the same message. revert(0, returnDataSize) } switch returnDataSize case 32 { // Copy the return data into memory. returndatacopy(0, 0, returnDataSize) // Set success to whether it returned true. success := iszero(iszero(mload(0))) } case 0 { // There was no return data. success := 1 } default { // It returned some malformed input. success := 0 } } } } /// @notice Arithmetic library with operations for fixed-point numbers. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/FixedPointMathLib.sol) /// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol) library FixedPointMathLib { /*////////////////////////////////////////////////////////////// SIMPLIFIED FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s. function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down. } function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up. } function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down. } function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up. } /*////////////////////////////////////////////////////////////// LOW LEVEL FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ function mulDivDown( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { assembly { // Store x * y in z for now. z := mul(x, y) // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y)) if iszero( and( iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)) ) ) { revert(0, 0) } // Divide z by the denominator. z := div(z, denominator) } } function mulDivUp( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { assembly { // Store x * y in z for now. z := mul(x, y) // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y)) if iszero( and( iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)) ) ) { revert(0, 0) } // First, divide z - 1 by the denominator and add 1. // We allow z - 1 to underflow if z is 0, because we multiply the // end result by 0 if z is zero, ensuring we return 0 if z is zero. z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1)) } } function rpow( uint256 x, uint256 n, uint256 scalar ) internal pure returns (uint256 z) { assembly { switch x case 0 { switch n case 0 { // 0 ** 0 = 1 z := scalar } default { // 0 ** n = 0 z := 0 } } default { switch mod(n, 2) case 0 { // If n is even, store scalar in z for now. z := scalar } default { // If n is odd, store x in z for now. z := x } // Shifting right by 1 is like dividing by 2. let half := shr(1, scalar) for { // Shift n right by 1 before looping to halve it. n := shr(1, n) } n { // Shift n right by 1 each iteration to halve it. n := shr(1, n) } { // Revert immediately if x ** 2 would overflow. // Equivalent to iszero(eq(div(xx, x), x)) here. if shr(128, x) { revert(0, 0) } // Store x squared. let xx := mul(x, x) // Round to the nearest number. let xxRound := add(xx, half) // Revert if xx + half overflowed. if lt(xxRound, xx) { revert(0, 0) } // Set x to scaled xxRound. x := div(xxRound, scalar) // If n is even: if mod(n, 2) { // Compute z * x. let zx := mul(z, x) // If z * x overflowed: if iszero(eq(div(zx, x), z)) { // Revert if x is non-zero. if iszero(iszero(x)) { revert(0, 0) } } // Round to the nearest number. let zxRound := add(zx, half) // Revert if zx + half overflowed. if lt(zxRound, zx) { revert(0, 0) } // Return properly scaled zxRound. z := div(zxRound, scalar) } } } } } /*////////////////////////////////////////////////////////////// GENERAL NUMBER UTILITIES //////////////////////////////////////////////////////////////*/ function sqrt(uint256 x) internal pure returns (uint256 z) { assembly { // Start off with z at 1. z := 1 // Used below to help find a nearby power of 2. let y := x // Find the lowest power of 2 that is at least sqrt(x). if iszero(lt(y, 0x100000000000000000000000000000000)) { y := shr(128, y) // Like dividing by 2 ** 128. z := shl(64, z) // Like multiplying by 2 ** 64. } if iszero(lt(y, 0x10000000000000000)) { y := shr(64, y) // Like dividing by 2 ** 64. z := shl(32, z) // Like multiplying by 2 ** 32. } if iszero(lt(y, 0x100000000)) { y := shr(32, y) // Like dividing by 2 ** 32. z := shl(16, z) // Like multiplying by 2 ** 16. } if iszero(lt(y, 0x10000)) { y := shr(16, y) // Like dividing by 2 ** 16. z := shl(8, z) // Like multiplying by 2 ** 8. } if iszero(lt(y, 0x100)) { y := shr(8, y) // Like dividing by 2 ** 8. z := shl(4, z) // Like multiplying by 2 ** 4. } if iszero(lt(y, 0x10)) { y := shr(4, y) // Like dividing by 2 ** 4. z := shl(2, z) // Like multiplying by 2 ** 2. } if iszero(lt(y, 0x8)) { // Equivalent to 2 ** z. z := shl(1, z) } // Shifting right by 1 is like dividing by 2. z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) // Compute a rounded down version of z. let zRoundDown := div(x, z) // If zRoundDown is smaller, use it. if lt(zRoundDown, z) { z := zRoundDown } } } } // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165Checker.sol) // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); } /** * @dev Library used to query support of an interface declared via {IERC165}. * * Note that these functions return the actual result of the query: they do not * `revert` if an interface is not supported. It is up to the caller to decide * what to do in these cases. */ library ERC165Checker { // As per the EIP-165 spec, no interface should ever match 0xffffffff bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff; /** * @dev Returns true if `account` supports the {IERC165} interface, */ function supportsERC165(address account) internal view returns (bool) { // Any contract that implements ERC165 must explicitly indicate support of // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid return _supportsERC165Interface(account, type(IERC165).interfaceId) && !_supportsERC165Interface(account, _INTERFACE_ID_INVALID); } /** * @dev Returns true if `account` supports the interface defined by * `interfaceId`. Support for {IERC165} itself is queried automatically. * * See {IERC165-supportsInterface}. */ function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) { // query support of both ERC165 as per the spec and support of _interfaceId return supportsERC165(account) && _supportsERC165Interface(account, interfaceId); } /** * @dev Returns a boolean array where each value corresponds to the * interfaces passed in and whether they're supported or not. This allows * you to batch check interfaces for a contract where your expectation * is that some interfaces may not be supported. * * See {IERC165-supportsInterface}. * * _Available since v3.4._ */ function getSupportedInterfaces( address account, bytes4[] memory interfaceIds ) internal view returns (bool[] memory) { // an array of booleans corresponding to interfaceIds and whether they're supported or not bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length); // query support of ERC165 itself if (supportsERC165(account)) { // query support of each interface in interfaceIds for (uint256 i = 0; i < interfaceIds.length; i++) { interfaceIdsSupported[i] = _supportsERC165Interface( account, interfaceIds[i] ); } } return interfaceIdsSupported; } /** * @dev Returns true if `account` supports all the interfaces defined in * `interfaceIds`. Support for {IERC165} itself is queried automatically. * * Batch-querying can lead to gas savings by skipping repeated checks for * {IERC165} support. * * See {IERC165-supportsInterface}. */ function supportsAllInterfaces( address account, bytes4[] memory interfaceIds ) internal view returns (bool) { // query support of ERC165 itself if (!supportsERC165(account)) { return false; } // query support of each interface in _interfaceIds for (uint256 i = 0; i < interfaceIds.length; i++) { if (!_supportsERC165Interface(account, interfaceIds[i])) { return false; } } // all interfaces supported return true; } /** * @notice Query if a contract implements an interface, does not check ERC165 support * @param account The address of the contract to query for support of an interface * @param interfaceId The interface identifier, as specified in ERC-165 * @return true if the contract at account indicates support of the interface with * identifier interfaceId, false otherwise * @dev Assumes that account contains a contract that supports ERC165, otherwise * the behavior of this method is undefined. This precondition can be checked * with {supportsERC165}. * Interface identification is specified in ERC-165. */ function _supportsERC165Interface(address account, bytes4 interfaceId) private view returns (bool) { bytes memory encodedParams = abi.encodeWithSelector( IERC165.supportsInterface.selector, interfaceId ); (bool success, bytes memory result) = account.staticcall{gas: 30000}( encodedParams ); if (result.length < 32) return false; return success && abi.decode(result, (bool)); } } struct Rewards { uint256 sale; uint256 purchase; uint256 mint; uint256 ohmPurchase; uint256 ohmMint; uint256 multiplier; } struct NFT { address contractAddress; uint256 id; } enum NftType { ERC721, ERC1155 } error OdysseyXpDirectory_Unauthorized(); contract OdysseyXpDirectory { using ERC165Checker for address; Rewards public defaultRewards; mapping(address => Rewards) public erc721rewards; mapping(address => mapping(uint256 => Rewards)) public erc1155rewards; NFT[] public customRewardTokens; address public owner; constructor() { owner = msg.sender; } // modifier substitute function notOwner() internal view returns (bool) { return msg.sender != owner; } function transferOwnership(address newOwner) external { if (notOwner()) revert OdysseyXpDirectory_Unauthorized(); owner = newOwner; } /*/////////////////////////////////////////////////////////////// Reward Setters //////////////////////////////////////////////////////////////*/ /// @notice Set default rewards for contracts without a custom reward set /// @param sale XP reward for selling an NFT /// @param purchase XP reward for purchasing an NFT /// @param mint XP reward for minting an NFT /// @param ohmPurchase XP reward for purchasing an NFT with OHM /// @param ohmMint XP reward for minting an NFT with OHM /// @param multiplier XP reward multiplier for wallets holding an NFT function setDefaultRewards( uint256 sale, uint256 purchase, uint256 mint, uint256 ohmPurchase, uint256 ohmMint, uint256 multiplier ) public { if (notOwner()) revert OdysseyXpDirectory_Unauthorized(); defaultRewards = Rewards( sale, purchase, mint, ohmPurchase, ohmMint, multiplier ); } /// @notice Set custom rewards for an ERC721 contract /// @param sale XP reward for selling this NFT /// @param purchase XP reward for purchasing this NFT /// @param mint XP reward for minting this NFT /// @param ohmPurchase XP reward for purchasing this NFT with OHM /// @param ohmMint XP reward for minting this NFT with OHM /// @param multiplier XP reward multiplier for wallets holding this NFT function setErc721CustomRewards( address tokenAddress, uint256 sale, uint256 purchase, uint256 mint, uint256 ohmPurchase, uint256 ohmMint, uint256 multiplier ) public { if (notOwner()) revert OdysseyXpDirectory_Unauthorized(); customRewardTokens.push(NFT(tokenAddress, 0)); erc721rewards[tokenAddress] = Rewards( sale, purchase, mint, ohmPurchase, ohmMint, multiplier ); } /// @notice Set custom rewards for an ERC1155 contract and token ID /// @param sale XP reward for selling this NFT /// @param purchase XP reward for purchasing this NFT /// @param mint XP reward for minting this NFT /// @param ohmPurchase XP reward for purchasing this NFT with OHM /// @param ohmMint XP reward for minting this NFT with OHM /// @param multiplier XP reward multiplier for wallets holding this NFT function setErc1155CustomRewards( address tokenAddress, uint256 tokenId, uint256 sale, uint256 purchase, uint256 mint, uint256 ohmPurchase, uint256 ohmMint, uint256 multiplier ) public { if (notOwner()) revert OdysseyXpDirectory_Unauthorized(); customRewardTokens.push(NFT(tokenAddress, tokenId)); erc1155rewards[tokenAddress][tokenId] = Rewards( sale, purchase, mint, ohmPurchase, ohmMint, multiplier ); } /*/////////////////////////////////////////////////////////////// Reward Getters //////////////////////////////////////////////////////////////*/ /// @notice Get the XP reward for selling an NFT /// @param seller Seller of the NFT /// @param contractAddress Address of the NFT being sold /// @param tokenId ID of the NFT being sold function getSaleReward( address seller, address contractAddress, uint256 tokenId ) public view returns (uint256) { ( bool isCustomErc721, bool isCustomErc1155, uint256 multiplier ) = _getRewardDetails(seller, contractAddress, tokenId); if (isCustomErc721) { return erc721rewards[contractAddress].sale * multiplier; } else if (isCustomErc1155) { return erc1155rewards[contractAddress][tokenId].sale * multiplier; } else { return defaultRewards.sale * multiplier; } } /// @notice Get the XP reward for buying an NFT /// @param buyer Buyer of the NFT /// @param contractAddress Address of the NFT being sold /// @param tokenId ID of the NFT being sold function getPurchaseReward( address buyer, address contractAddress, uint256 tokenId ) public view returns (uint256) { ( bool isCustomErc721, bool isCustomErc1155, uint256 multiplier ) = _getRewardDetails(buyer, contractAddress, tokenId); if (isCustomErc721) { return erc721rewards[contractAddress].purchase * multiplier; } else if (isCustomErc1155) { return erc1155rewards[contractAddress][tokenId].purchase * multiplier; } else { return defaultRewards.purchase * multiplier; } } /// @notice Get the XP reward for minting an NFT /// @param buyer Buyer of the NFT /// @param contractAddress Address of the NFT being sold /// @param tokenId ID of the NFT being sold function getMintReward( address buyer, address contractAddress, uint256 tokenId ) public view returns (uint256) { ( bool isCustomErc721, bool isCustomErc1155, uint256 multiplier ) = _getRewardDetails(buyer, contractAddress, tokenId); if (isCustomErc721) { return erc721rewards[contractAddress].mint * multiplier; } else if (isCustomErc1155) { return erc1155rewards[contractAddress][tokenId].mint * multiplier; } else { return defaultRewards.mint * multiplier; } } /// @notice Get the XP reward for buying an NFT with OHM /// @param buyer Buyer of the NFT /// @param contractAddress Address of the NFT being sold /// @param tokenId ID of the NFT being sold function getOhmPurchaseReward( address buyer, address contractAddress, uint256 tokenId ) public view returns (uint256) { ( bool isCustomErc721, bool isCustomErc1155, uint256 multiplier ) = _getRewardDetails(buyer, contractAddress, tokenId); if (isCustomErc721) { return erc721rewards[contractAddress].ohmPurchase * multiplier; } else if (isCustomErc1155) { return erc1155rewards[contractAddress][tokenId].ohmPurchase * multiplier; } else { return defaultRewards.ohmPurchase * multiplier; } } /// @notice Get the XP reward for minting an NFT with OHM /// @param buyer Buyer of the NFT /// @param contractAddress Address of the NFT being sold /// @param tokenId ID of the NFT being sold function getOhmMintReward( address buyer, address contractAddress, uint256 tokenId ) public view returns (uint256) { ( bool isCustomErc721, bool isCustomErc1155, uint256 multiplier ) = _getRewardDetails(buyer, contractAddress, tokenId); if (isCustomErc721) { return erc721rewards[contractAddress].ohmMint * multiplier; } else if (isCustomErc1155) { return erc1155rewards[contractAddress][tokenId].ohmMint * multiplier; } else { return defaultRewards.ohmMint * multiplier; } } /// @notice Determine if an NFT has custom rewards and any multiplier based on the user's held NFTs /// @dev The multiplier and custom rewards are determined simultaneously to save on gas costs of iteration /// @param user Wallet address with potential multiplier NFTs /// @param contractAddress Address of the NFT being sold /// @param tokenId ID of the NFT being sold function _getRewardDetails( address user, address contractAddress, uint256 tokenId ) internal view returns ( bool isCustomErc721, bool isCustomErc1155, uint256 multiplier ) { NFT[] memory _customRewardTokens = customRewardTokens; // save an SLOAD from length reading for (uint256 i = 0; i < _customRewardTokens.length; i++) { NFT memory token = _customRewardTokens[i]; if (token.contractAddress.supportsInterface(0x80ac58cd)) { // is ERC721 if (OdysseyERC721(token.contractAddress).balanceOf(user) > 0) { uint256 reward = erc721rewards[token.contractAddress] .multiplier; multiplier = reward > 1 ? multiplier + reward : multiplier; // only increment if multiplier is non-one } if (contractAddress == token.contractAddress) { isCustomErc721 = true; } } else if (token.contractAddress.supportsInterface(0xd9b67a26)) { // is isERC1155 if ( OdysseyERC1155(token.contractAddress).balanceOf( user, token.id ) > 0 ) { uint256 reward = erc1155rewards[token.contractAddress][ token.id ].multiplier; multiplier = reward > 1 ? multiplier + reward : multiplier; // only increment if multiplier is non-one if ( contractAddress == token.contractAddress && tokenId == token.id ) { isCustomErc1155 = true; } } } } multiplier = multiplier == 0 ? defaultRewards.multiplier : multiplier; // if no custom multiplier, use default multiplier = multiplier > 4 ? 4 : multiplier; // multiplier caps at 4 } } error OdysseyXp_Unauthorized(); error OdysseyXp_NonTransferable(); error OdysseyXp_ZeroAssets(); contract OdysseyXp is ERC20 { using SafeTransferLib for ERC20; using FixedPointMathLib for uint256; struct UserHistory { uint256 balanceAtLastRedeem; uint256 globallyWithdrawnAtLastRedeem; } /*/////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Mint(address indexed owner, uint256 assets, uint256 xp); event Redeem(address indexed owner, uint256 assets, uint256 xp); /*/////////////////////////////////////////////////////////////// STATE VARIABLES //////////////////////////////////////////////////////////////*/ address public router; address public exchange; address public owner; uint256 public globallyWithdrawn; ERC20 public immutable asset; OdysseyXpDirectory public directory; mapping(address => UserHistory) public userHistories; constructor( ERC20 _asset, OdysseyXpDirectory _directory, address _router, address _exchange, address _owner ) ERC20("Odyssey XP", "XP", 0) { asset = _asset; directory = _directory; router = _router; exchange = _exchange; owner = _owner; } /*/////////////////////////////////////////////////////////////// MODIFIERS //////////////////////////////////////////////////////////////*/ function notOwner() internal view returns (bool) { return msg.sender != owner; } function notRouter() internal view returns (bool) { return msg.sender != router; } function notExchange() internal view returns (bool) { return msg.sender != exchange; } /*/////////////////////////////////////////////////////////////// RESTRICTED SETTERS //////////////////////////////////////////////////////////////*/ function setExchange(address _exchange) external { if (notOwner()) revert OdysseyXp_Unauthorized(); exchange = _exchange; } function setRouter(address _router) external { if (notOwner()) revert OdysseyXp_Unauthorized(); router = _router; } function setDirectory(address _directory) external { if (notOwner()) revert OdysseyXp_Unauthorized(); directory = OdysseyXpDirectory(_directory); } function transferOwnership(address _newOwner) external { if (notOwner()) revert OdysseyXp_Unauthorized(); owner = _newOwner; } /*/////////////////////////////////////////////////////////////// XP Granting Methods //////////////////////////////////////////////////////////////*/ function saleReward( address seller, address contractAddress, uint256 tokenId ) external { if (notExchange()) revert OdysseyXp_Unauthorized(); _grantXP( seller, directory.getSaleReward(seller, contractAddress, tokenId) ); } function purchaseReward( address buyer, address contractAddress, uint256 tokenId ) external { if (notExchange()) revert OdysseyXp_Unauthorized(); _grantXP( buyer, directory.getPurchaseReward(buyer, contractAddress, tokenId) ); } function mintReward( address buyer, address contractAddress, uint256 tokenId ) external { if (notRouter()) revert OdysseyXp_Unauthorized(); _grantXP( buyer, directory.getMintReward(buyer, contractAddress, tokenId) ); } function ohmPurchaseReward( address buyer, address contractAddress, uint256 tokenId ) external { if (notExchange()) revert OdysseyXp_Unauthorized(); _grantXP( buyer, directory.getOhmPurchaseReward(buyer, contractAddress, tokenId) ); } function ohmMintReward( address buyer, address contractAddress, uint256 tokenId ) external { if (notRouter()) revert OdysseyXp_Unauthorized(); _grantXP( buyer, directory.getOhmMintReward(buyer, contractAddress, tokenId) ); } /*/////////////////////////////////////////////////////////////// MINT LOGIC //////////////////////////////////////////////////////////////*/ /// @notice Grants the receiver the given amount of XP /// @dev Forces the receiver to redeem if they have rewards available /// @param receiver The address to grant XP to /// @param xp The amount of XP to grant function _grantXP(address receiver, uint256 xp) internal returns (uint256 assets) { uint256 currentXp = balanceOf[receiver]; if ((assets = previewRedeem(receiver, currentXp)) > 0) _redeem(receiver, assets, currentXp); // force redeeming to keep portions in line else if (currentXp == 0) userHistories[receiver] .globallyWithdrawnAtLastRedeem = globallyWithdrawn; // if a new user, adjust their history to calculate withdrawn at their first redeem _mint(receiver, xp); emit Mint(msg.sender, assets, xp); afterMint(assets, xp); } /*/////////////////////////////////////////////////////////////// REDEEM LOGIC //////////////////////////////////////////////////////////////*/ /// @notice external redeem method /// @dev will revert if there is nothing to redeem function redeem() public returns (uint256 assets) { uint256 xp = balanceOf[msg.sender]; if ((assets = previewRedeem(msg.sender, xp)) == 0) revert OdysseyXp_ZeroAssets(); _redeem(msg.sender, assets, xp); } /// @notice Internal logic for redeeming rewards /// @param receiver The receiver of rewards /// @param assets The amount of assets to grant /// @param xp The amount of XP the user is redeeming with function _redeem( address receiver, uint256 assets, uint256 xp ) internal virtual { beforeRedeem(assets, xp); userHistories[receiver].balanceAtLastRedeem = asset.balanceOf(address(this)) - assets; userHistories[receiver].globallyWithdrawnAtLastRedeem = globallyWithdrawn + assets; globallyWithdrawn += assets; asset.safeTransfer(receiver, assets); emit Redeem(receiver, assets, xp); } /*/////////////////////////////////////////////////////////////// ACCOUNTING LOGIC //////////////////////////////////////////////////////////////*/ /// @notice Preview the result of a redeem for the given user with the given XP amount /// @param recipient The user to check potential rewards for /// @param xp The amount of XP the user is previewing a redeem for function previewRedeem(address recipient, uint256 xp) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 || xp == 0 ? 0 : xp.mulDivDown(totalAssets(recipient), supply); } /// @notice The total amount of available assets for the user, adjusted based on their history /// @param user The user to check assets for function totalAssets(address user) internal view returns (uint256) { uint256 balance = asset.balanceOf(address(this)); // Saves an extra SLOAD if balance is non-zero. return balance + (globallyWithdrawn - userHistories[user].globallyWithdrawnAtLastRedeem) - userHistories[user].balanceAtLastRedeem; } /*/////////////////////////////////////////////////////////////// OVERRIDE TRANSFERABILITY //////////////////////////////////////////////////////////////*/ function transfer(address to, uint256 amount) public override returns (bool) { revert OdysseyXp_NonTransferable(); } function transferFrom( address from, address to, uint256 amount ) public override returns (bool) { revert OdysseyXp_NonTransferable(); } /*/////////////////////////////////////////////////////////////// INTERNAL HOOKS LOGIC //////////////////////////////////////////////////////////////*/ function beforeRedeem(uint256 assets, uint256 xp) internal virtual {} function afterMint(uint256 assets, uint256 xp) internal virtual {} } contract OdysseyLaunchPlatform is OdysseyDatabase, ReentrancyGuard { /*/////////////////////////////////////////////////////////////// ACTIONS //////////////////////////////////////////////////////////////*/ function mintERC721( bytes32[] calldata merkleProof, bytes32 merkleRoot, uint256 minPrice, uint256 mintsPerUser, address tokenAddress, address currency, uint8 v, bytes32 r, bytes32 s ) external payable nonReentrant { if (OdysseyTokenFactory(factory).tokenExists(tokenAddress) == 0) { revert OdysseyLaunchPlatform_TokenDoesNotExist(); } if (whitelistClaimed721[tokenAddress][msg.sender] >= mintsPerUser) { revert OdysseyLaunchPlatform_AlreadyClaimed(); } // Check if user is already reserved + paid if (isReserved721[tokenAddress][msg.sender] == 0) { if ( cumulativeSupply721[tokenAddress] >= maxSupply721[tokenAddress] ) { revert OdysseyLaunchPlatform_MaxSupplyCap(); } { // Verify merkle root and minPrice signed by owner (all id's have same min price) bytes32 hash = keccak256( abi.encode( MERKLE_TREE_ROOT_ERC721_TYPEHASH, merkleRoot, minPrice, mintsPerUser, tokenAddress, currency ) ); Signature.verify( hash, ownerOf[tokenAddress], v, r, s, domainSeparator[tokenAddress] ); } if (whitelistActive[tokenAddress] == 1) { // Verify user whitelisted MerkleWhiteList.verify(msg.sender, merkleProof, merkleRoot); } cumulativeSupply721[tokenAddress]++; OdysseyLib.Percentage storage percent = treasuryCommission[ tokenAddress ]; uint256 commission = (minPrice * percent.numerator) / percent.denominator; if (currency == address(0)) { if (msg.value < minPrice) { revert OdysseyLaunchPlatform_InsufficientFunds(); } (bool treasurySuccess, ) = treasury.call{value: commission}(""); if (!treasurySuccess) { revert OdysseyLaunchPlatform_TreasuryPayFailure(); } (bool success, ) = royaltyRecipient[tokenAddress].call{ value: minPrice - commission }(""); if (!success) { revert OdysseyLaunchPlatform_FailedToPayEther(); } } else { if ( ERC20(currency).allowance(msg.sender, address(this)) < minPrice ) { revert OdysseyLaunchPlatform_InsufficientFunds(); } bool result = ERC20(currency).transferFrom( msg.sender, treasury, commission ); if (!result) { revert OdysseyLaunchPlatform_TreasuryPayFailure(); } result = ERC20(currency).transferFrom( msg.sender, royaltyRecipient[tokenAddress], minPrice - commission ); if (!result) { revert OdysseyLaunchPlatform_FailedToPayERC20(); } if (ohmFamilyCurrencies[currency] == 1) { OdysseyXp(xp).ohmMintReward(msg.sender, tokenAddress, 0); } } } else { isReserved721[tokenAddress][msg.sender]--; } // Update State whitelistClaimed721[tokenAddress][msg.sender]++; OdysseyERC721(tokenAddress).mint( msg.sender, mintedSupply721[tokenAddress]++ ); } function reserveERC721( bytes32[] calldata merkleProof, bytes32 merkleRoot, uint256 minPrice, uint256 mintsPerUser, address tokenAddress, address currency, uint8 v, bytes32 r, bytes32 s ) external payable nonReentrant { if (OdysseyTokenFactory(factory).tokenExists(tokenAddress) == 0) { revert OdysseyLaunchPlatform_TokenDoesNotExist(); } if (cumulativeSupply721[tokenAddress] >= maxSupply721[tokenAddress]) { revert OdysseyLaunchPlatform_MaxSupplyCap(); } if ( isReserved721[tokenAddress][msg.sender] + whitelistClaimed721[tokenAddress][msg.sender] >= mintsPerUser ) { revert OdysseyLaunchPlatform_ReservedOrClaimedMax(); } { // Verify merkle root and minPrice signed by owner (all id's have same min price) bytes32 hash = keccak256( abi.encode( MERKLE_TREE_ROOT_ERC721_TYPEHASH, merkleRoot, minPrice, mintsPerUser, tokenAddress, currency ) ); Signature.verify( hash, ownerOf[tokenAddress], v, r, s, domainSeparator[tokenAddress] ); } if (whitelistActive[tokenAddress] == 1) { // Verify user whitelisted MerkleWhiteList.verify(msg.sender, merkleProof, merkleRoot); } // Set user is reserved isReserved721[tokenAddress][msg.sender]++; // Increate Reserved + minted supply cumulativeSupply721[tokenAddress]++; OdysseyLib.Percentage storage percent = treasuryCommission[ tokenAddress ]; uint256 commission = (minPrice * percent.numerator) / percent.denominator; if (currency == address(0)) { if (msg.value < minPrice) { revert OdysseyLaunchPlatform_InsufficientFunds(); } (bool treasurySuccess, ) = treasury.call{value: commission}(""); if (!treasurySuccess) { revert OdysseyLaunchPlatform_TreasuryPayFailure(); } (bool success, ) = royaltyRecipient[tokenAddress].call{ value: minPrice - commission }(""); if (!success) { revert OdysseyLaunchPlatform_FailedToPayEther(); } } else { if ( ERC20(currency).allowance(msg.sender, address(this)) < minPrice ) { revert OdysseyLaunchPlatform_InsufficientFunds(); } bool result = ERC20(currency).transferFrom( msg.sender, treasury, commission ); if (!result) { revert OdysseyLaunchPlatform_TreasuryPayFailure(); } result = ERC20(currency).transferFrom( msg.sender, royaltyRecipient[tokenAddress], minPrice - commission ); if (!result) { revert OdysseyLaunchPlatform_FailedToPayERC20(); } if (ohmFamilyCurrencies[currency] == 1) { OdysseyXp(xp).ohmMintReward(msg.sender, tokenAddress, 0); } } } function mintERC1155( bytes32[] calldata merkleProof, bytes32 merkleRoot, uint256 minPrice, uint256 mintsPerUser, uint256 tokenId, address tokenAddress, address currency, uint8 v, bytes32 r, bytes32 s ) external payable nonReentrant { if (OdysseyTokenFactory(factory).tokenExists(tokenAddress) == 0) { revert OdysseyLaunchPlatform_TokenDoesNotExist(); } if ( whitelistClaimed1155[tokenAddress][msg.sender][tokenId] >= mintsPerUser ) { revert OdysseyLaunchPlatform_AlreadyClaimed(); } // Check if user is already reserved + paid if (isReserved1155[tokenAddress][msg.sender][tokenId] == 0) { if ( cumulativeSupply1155[tokenAddress][tokenId] >= maxSupply1155[tokenAddress][tokenId] ) { revert OdysseyLaunchPlatform_MaxSupplyCap(); } { // Verify merkle root and minPrice signed by owner (all id's have same min price) bytes32 hash = keccak256( abi.encode( MERKLE_TREE_ROOT_ERC1155_TYPEHASH, merkleRoot, minPrice, mintsPerUser, tokenId, tokenAddress, currency ) ); Signature.verify( hash, ownerOf[tokenAddress], v, r, s, domainSeparator[tokenAddress] ); } if (whitelistActive[tokenAddress] == 1) { // Verify user whitelisted MerkleWhiteList.verify(msg.sender, merkleProof, merkleRoot); } cumulativeSupply1155[tokenAddress][tokenId]++; OdysseyLib.Percentage storage percent = treasuryCommission[ tokenAddress ]; uint256 commission = (minPrice * percent.numerator) / percent.denominator; if (currency == address(0)) { if (msg.value < minPrice) { revert OdysseyLaunchPlatform_InsufficientFunds(); } (bool treasurySuccess, ) = treasury.call{value: commission}(""); if (!treasurySuccess) { revert OdysseyLaunchPlatform_TreasuryPayFailure(); } (bool success, ) = royaltyRecipient[tokenAddress].call{ value: minPrice - commission }(""); if (!success) { revert OdysseyLaunchPlatform_FailedToPayEther(); } } else { if ( ERC20(currency).allowance(msg.sender, address(this)) < minPrice ) { revert OdysseyLaunchPlatform_InsufficientFunds(); } bool result = ERC20(currency).transferFrom( msg.sender, treasury, commission ); if (!result) { revert OdysseyLaunchPlatform_TreasuryPayFailure(); } result = ERC20(currency).transferFrom( msg.sender, royaltyRecipient[tokenAddress], minPrice - commission ); if (!result) { revert OdysseyLaunchPlatform_FailedToPayERC20(); } if (ohmFamilyCurrencies[currency] == 1) { OdysseyXp(xp).ohmMintReward( msg.sender, tokenAddress, tokenId ); } } } else { isReserved1155[tokenAddress][msg.sender][tokenId]--; } // Update State whitelistClaimed1155[tokenAddress][msg.sender][tokenId]++; OdysseyERC1155(tokenAddress).mint(msg.sender, tokenId); } function reserveERC1155( bytes32[] calldata merkleProof, bytes32 merkleRoot, uint256 minPrice, uint256 mintsPerUser, uint256 tokenId, address tokenAddress, address currency, uint8 v, bytes32 r, bytes32 s ) external payable nonReentrant { if (OdysseyTokenFactory(factory).tokenExists(tokenAddress) == 0) { revert OdysseyLaunchPlatform_TokenDoesNotExist(); } if ( cumulativeSupply1155[tokenAddress][tokenId] >= maxSupply1155[tokenAddress][tokenId] ) { revert OdysseyLaunchPlatform_MaxSupplyCap(); } if ( isReserved1155[tokenAddress][msg.sender][tokenId] + whitelistClaimed1155[tokenAddress][msg.sender][tokenId] >= mintsPerUser ) { revert OdysseyLaunchPlatform_ReservedOrClaimedMax(); } { // Verify merkle root and minPrice signed by owner (all id's have same min price) bytes32 hash = keccak256( abi.encode( MERKLE_TREE_ROOT_ERC1155_TYPEHASH, merkleRoot, minPrice, mintsPerUser, tokenId, tokenAddress, currency ) ); Signature.verify( hash, ownerOf[tokenAddress], v, r, s, domainSeparator[tokenAddress] ); } if (whitelistActive[tokenAddress] == 1) { // Verify user whitelisted MerkleWhiteList.verify(msg.sender, merkleProof, merkleRoot); } // Set user is reserved isReserved1155[tokenAddress][msg.sender][tokenId]++; // Increase Reserved + minted supply cumulativeSupply1155[tokenAddress][tokenId]++; OdysseyLib.Percentage storage percent = treasuryCommission[ tokenAddress ]; uint256 commission = (minPrice * percent.numerator) / percent.denominator; if (currency == address(0)) { if (msg.value < minPrice) { revert OdysseyLaunchPlatform_InsufficientFunds(); } (bool treasurySuccess, ) = treasury.call{value: commission}(""); if (!treasurySuccess) { revert OdysseyLaunchPlatform_TreasuryPayFailure(); } (bool success, ) = royaltyRecipient[tokenAddress].call{ value: minPrice - commission }(""); if (!success) { revert OdysseyLaunchPlatform_FailedToPayEther(); } } else { if ( ERC20(currency).allowance(msg.sender, address(this)) < minPrice ) { revert OdysseyLaunchPlatform_InsufficientFunds(); } bool result = ERC20(currency).transferFrom( msg.sender, treasury, commission ); if (!result) { revert OdysseyLaunchPlatform_TreasuryPayFailure(); } result = ERC20(currency).transferFrom( msg.sender, royaltyRecipient[tokenAddress], minPrice - commission ); if (!result) { revert OdysseyLaunchPlatform_FailedToPayERC20(); } if (ohmFamilyCurrencies[currency] == 1) { OdysseyXp(xp).ohmMintReward(msg.sender, tokenAddress, tokenId); } } } function setWhitelistStatus(address addr, bool active) external nonReentrant { if (OdysseyTokenFactory(factory).tokenExists(addr) == 0) { revert OdysseyLaunchPlatform_TokenDoesNotExist(); } whitelistActive[addr] = active ? 1 : 0; } function mint721OnCreate(uint256 amount, address token) external nonReentrant { cumulativeSupply721[token] = amount; mintedSupply721[token] = amount; uint256 i; for (; i < amount; ++i) { OdysseyERC721(token).mint(msg.sender, i); } } function mint1155OnCreate( uint256[] calldata tokenIds, uint256[] calldata amounts, address token ) external nonReentrant { uint256 i; for (; i < tokenIds.length; ++i) { cumulativeSupply1155[token][tokenIds[i]] = amounts[i]; OdysseyERC1155(token).mintBatch( msg.sender, tokenIds[i], amounts[i] ); } } function ownerMint721(address token, address to) external nonReentrant { if (cumulativeSupply721[token] >= maxSupply721[token]) { revert OdysseyLaunchPlatform_MaxSupplyCap(); } cumulativeSupply721[token]++; OdysseyERC721(token).mint(to, mintedSupply721[token]++); } function ownerMint1155( uint256 id, uint256 amount, address token, address to ) external nonReentrant { if ( cumulativeSupply1155[token][id] + amount > maxSupply1155[token][id] ) { revert OdysseyLaunchPlatform_MaxSupplyCap(); } cumulativeSupply1155[token][id] += amount; OdysseyERC1155(token).mintBatch(to, id, amount); } } contract OdysseyRouter is OdysseyDatabase, ReentrancyGuard { error OdysseyRouter_TokenIDSupplyMismatch(); error OdysseyRouter_WhitelistUpdateFail(); error OdysseyRouter_Unauthorized(); error OdysseyRouter_OwnerMintFailure(); error OdysseyRouter_BadTokenAddress(); error OdysseyRouter_BadOwnerAddress(); error OdysseyRouter_BadSenderAddress(); error OdysseyRouter_BadRecipientAddress(); error OdysseyRouter_BadTreasuryAddress(); error OdysseyRouter_BadAdminAddress(); constructor( address treasury_, address xpDirectory_, address xp_, address[] memory ohmCurrencies_ ) { launchPlatform = address(new OdysseyLaunchPlatform()); factory = address(new OdysseyTokenFactory()); treasury = treasury_; admin = msg.sender; uint256 i; for (; i < ohmCurrencies_.length; i++) { ohmFamilyCurrencies[ohmCurrencies_[i]] = 1; } if (xp_ == address(0)) { if (xpDirectory_ == address(0)) { xpDirectory_ = address(new OdysseyXpDirectory()); OdysseyXpDirectory(xpDirectory_).setDefaultRewards( 1, 1, 1, 3, 3, 1 ); OdysseyXpDirectory(xpDirectory_).transferOwnership(admin); } xp_ = address( new OdysseyXp( ERC20(ohmCurrencies_[0]), OdysseyXpDirectory(xpDirectory_), address(this), address(this), admin ) ); } xp = xp_; } function Factory() public view returns (OdysseyTokenFactory) { return OdysseyTokenFactory(readSlotAsAddress(1)); } function create1155( string calldata name, string calldata symbol, string calldata baseURI, OdysseyLib.Odyssey1155Info calldata info, OdysseyLib.Percentage calldata treasuryPercentage, address royaltyReceiver, bool whitelist ) external returns (address token) { if (info.maxSupply.length != info.tokenIds.length) { revert OdysseyRouter_TokenIDSupplyMismatch(); } token = Factory().create1155(msg.sender, name, symbol, baseURI); ownerOf[token] = msg.sender; whitelistActive[token] = whitelist ? 1 : 0; royaltyRecipient[token] = royaltyReceiver; uint256 i; for (; i < info.tokenIds.length; ++i) { maxSupply1155[token][info.tokenIds[i]] = (info.maxSupply[i] == 0) ? type(uint256).max : info.maxSupply[i]; } domainSeparator[token] = keccak256( abi.encode( // keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)') 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f, keccak256(bytes(Strings.toHexString(uint160(token)))), 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6, // keccak256(bytes("1")) block.chainid, token ) ); if (OdysseyLib.compareDefaultPercentage(treasuryPercentage)) { // Treasury % was greater than 3/100 treasuryCommission[token] = treasuryPercentage; } else { // Treasury % was less than 3/100, using 3/100 as default treasuryCommission[token] = OdysseyLib.Percentage(3, 100); } if (info.reserveAmounts.length > 0) { (bool success, bytes memory data) = launchPlatform.delegatecall( abi.encodeWithSignature( "mint1155OnCreate(uint256[],uint256[],address)", info.tokenIds, info.reserveAmounts, token ) ); if (!success) { if (data.length == 0) revert(); assembly { revert(add(32, data), mload(data)) } } } return token; } function create721( string calldata name, string calldata symbol, string calldata baseURI, uint256 maxSupply, uint256 reserveAmount, OdysseyLib.Percentage calldata treasuryPercentage, address royaltyReceiver, bool whitelist ) external returns (address token) { token = Factory().create721(msg.sender, name, symbol, baseURI); ownerOf[token] = msg.sender; maxSupply721[token] = (maxSupply == 0) ? type(uint256).max : maxSupply; whitelistActive[token] = whitelist ? 1 : 0; royaltyRecipient[token] = royaltyReceiver; domainSeparator[token] = keccak256( abi.encode( // keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)') 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f, keccak256(bytes(Strings.toHexString(uint160(token)))), 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6, // keccak256(bytes("1")) block.chainid, token ) ); if (OdysseyLib.compareDefaultPercentage(treasuryPercentage)) { // Treasury % was greater than 3/100 treasuryCommission[token] = treasuryPercentage; } else { // Treasury % was less than 3/100, using 3/100 as default treasuryCommission[token] = OdysseyLib.Percentage(3, 100); } if (reserveAmount > 0) { (bool success, bytes memory data) = launchPlatform.delegatecall( abi.encodeWithSignature( "mint721OnCreate(uint256,address)", reserveAmount, token ) ); if (!success) { if (data.length == 0) revert(); assembly { revert(add(32, data), mload(data)) } } } return token; } function mintERC721( bytes32[] calldata merkleProof, bytes32 merkleRoot, uint256 minPrice, uint256 mintsPerUser, address tokenAddress, address currency, uint8 v, bytes32 r, bytes32 s ) public payable { (bool success, bytes memory data) = launchPlatform.delegatecall( abi.encodeWithSignature( "mintERC721(bytes32[],bytes32,uint256,uint256,address,address,uint8,bytes32,bytes32)", merkleProof, merkleRoot, minPrice, mintsPerUser, tokenAddress, currency, v, r, s ) ); if (!success) { if (data.length == 0) revert(); assembly { revert(add(32, data), mload(data)) } } } function batchMintERC721(OdysseyLib.BatchMint calldata batch) public payable { for (uint256 i = 0; i < batch.tokenAddress.length; i++) { (bool success, bytes memory data) = launchPlatform.delegatecall( abi.encodeWithSignature( "mintERC721(bytes32[],bytes32,uint256,uint256,address,address,uint8,bytes32,bytes32)", batch.merkleProof[i], batch.merkleRoot[i], batch.minPrice[i], batch.mintsPerUser[i], batch.tokenAddress[i], batch.currency[i], batch.v[i], batch.r[i], batch.s[i] ) ); if (!success) { if (data.length == 0) revert(); assembly { revert(add(32, data), mload(data)) } } } } function reserveERC721( bytes32[] calldata merkleProof, bytes32 merkleRoot, uint256 minPrice, uint256 mintsPerUser, address tokenAddress, address currency, uint8 v, bytes32 r, bytes32 s ) public payable { (bool success, bytes memory data) = launchPlatform.delegatecall( abi.encodeWithSignature( "reserveERC721(bytes32[],bytes32,uint256,uint256,address,address,uint8,bytes32,bytes32)", merkleProof, merkleRoot, minPrice, mintsPerUser, tokenAddress, currency, v, r, s ) ); if (!success) { if (data.length == 0) revert(); assembly { revert(add(32, data), mload(data)) } } } function batchReserveERC721(OdysseyLib.BatchMint calldata batch) public payable { for (uint256 i = 0; i < batch.tokenAddress.length; i++) { (bool success, bytes memory data) = launchPlatform.delegatecall( abi.encodeWithSignature( "reserveERC721(bytes32[],bytes32,uint256,uint256,address,address,uint8,bytes32,bytes32)", batch.merkleProof[i], batch.merkleRoot[i], batch.minPrice[i], batch.mintsPerUser[i], batch.tokenAddress[i], batch.currency[i], batch.v[i], batch.r[i], batch.s[i] ) ); if (!success) { if (data.length == 0) revert(); assembly { revert(add(32, data), mload(data)) } } } } function mintERC1155( bytes32[] calldata merkleProof, bytes32 merkleRoot, uint256 minPrice, uint256 mintsPerUser, uint256 tokenId, address tokenAddress, address currency, uint8 v, bytes32 r, bytes32 s ) public payable { (bool success, bytes memory data) = launchPlatform.delegatecall( abi.encodeWithSignature( "mintERC1155(bytes32[],bytes32,uint256,uint256,uint256,address,address,uint8,bytes32,bytes32)", merkleProof, merkleRoot, minPrice, mintsPerUser, tokenId, tokenAddress, currency, v, r, s ) ); if (!success) { if (data.length == 0) revert(); assembly { revert(add(32, data), mload(data)) } } } function batchMintERC1155(OdysseyLib.BatchMint calldata batch) public payable { for (uint256 i = 0; i < batch.tokenAddress.length; i++) { (bool success, bytes memory data) = launchPlatform.delegatecall( abi.encodeWithSignature( "mintERC1155(bytes32[],bytes32,uint256,uint256,uint256,address,address,uint8,bytes32,bytes32)", batch.merkleProof[i], batch.merkleRoot[i], batch.minPrice[i], batch.mintsPerUser[i], batch.tokenId[i], batch.tokenAddress[i], batch.currency[i], batch.v[i], batch.r[i], batch.s[i] ) ); if (!success) { if (data.length == 0) revert(); assembly { revert(add(32, data), mload(data)) } } } } function reserveERC1155( bytes32[] calldata merkleProof, bytes32 merkleRoot, uint256 minPrice, uint256 mintsPerUser, uint256 tokenId, address tokenAddress, address currency, uint8 v, bytes32 r, bytes32 s ) public payable { (bool success, bytes memory data) = launchPlatform.delegatecall( abi.encodeWithSignature( "reserveERC1155(bytes32[],bytes32,uint256,uint256,uint256,address,address,uint8,bytes32,bytes32)", merkleProof, merkleRoot, minPrice, mintsPerUser, tokenId, tokenAddress, currency, v, r, s ) ); if (!success) { if (data.length == 0) revert(); assembly { revert(add(32, data), mload(data)) } } } function batchReserveERC1155(OdysseyLib.BatchMint calldata batch) public payable { for (uint256 i = 0; i < batch.tokenAddress.length; i++) { (bool success, bytes memory data) = launchPlatform.delegatecall( abi.encodeWithSignature( "reserveERC1155(bytes32[],bytes32,uint256,uint256,uint256,address,address,uint8,bytes32,bytes32)", batch.merkleProof[i], batch.merkleRoot[i], batch.minPrice[i], batch.mintsPerUser[i], batch.tokenId[i], batch.tokenAddress[i], batch.currency[i], batch.v[i], batch.r[i], batch.s[i] ) ); if (!success) { if (data.length == 0) revert(); assembly { revert(add(32, data), mload(data)) } } } } function setWhitelistStatus(address addr, bool active) public { if (msg.sender != ownerOf[addr]) { revert OdysseyRouter_Unauthorized(); } (bool success, ) = launchPlatform.delegatecall( abi.encodeWithSignature( "setWhitelistStatus(address,bool)", addr, active ) ); if (!success) { revert OdysseyRouter_WhitelistUpdateFail(); } } function ownerMint721(address token, address to) public { if (ownerOf[token] != msg.sender) { revert OdysseyRouter_Unauthorized(); } (bool success, ) = launchPlatform.delegatecall( abi.encodeWithSignature("ownerMint721(address,address)", token, to) ); if (!success) { revert OdysseyRouter_OwnerMintFailure(); } } function ownerMint1155( uint256 id, uint256 amount, address token, address to ) public { if (ownerOf[token] != msg.sender) { revert OdysseyRouter_Unauthorized(); } (bool success, ) = launchPlatform.delegatecall( abi.encodeWithSignature( "ownerMint1155(uint256,uint256,address,address)", id, amount, token, to ) ); if (!success) { revert OdysseyRouter_OwnerMintFailure(); } } function setOwnerShip(address token, address newOwner) public { if (token == address(0)) { revert OdysseyRouter_BadTokenAddress(); } if (newOwner == address(0)) { revert OdysseyRouter_BadOwnerAddress(); } if (msg.sender == address(0)) { revert OdysseyRouter_BadSenderAddress(); } if (ownerOf[token] != msg.sender) { revert OdysseyRouter_Unauthorized(); } ownerOf[token] = newOwner; } function setRoyaltyRecipient(address token, address recipient) public { if (token == address(0)) { revert OdysseyRouter_BadTokenAddress(); } if (recipient == address(0)) { revert OdysseyRouter_BadRecipientAddress(); } if (msg.sender == address(0)) { revert OdysseyRouter_BadSenderAddress(); } if (ownerOf[token] != msg.sender) { revert OdysseyRouter_Unauthorized(); } royaltyRecipient[token] = recipient; } function setTreasury(address newTreasury) public { if (msg.sender != admin) { revert OdysseyRouter_Unauthorized(); } if (msg.sender == address(0)) { revert OdysseyRouter_BadSenderAddress(); } if (newTreasury == address(0)) { revert OdysseyRouter_BadTreasuryAddress(); } treasury = newTreasury; } function setXP(address newXp) public { if (msg.sender != admin) { revert OdysseyRouter_Unauthorized(); } if (msg.sender == address(0)) { revert OdysseyRouter_BadSenderAddress(); } if (newXp == address(0)) { revert OdysseyRouter_BadTokenAddress(); } xp = newXp; } function setAdmin(address newAdmin) public { if (msg.sender != admin) { revert OdysseyRouter_Unauthorized(); } if (msg.sender == address(0)) { revert OdysseyRouter_BadSenderAddress(); } if (newAdmin == address(0)) { revert OdysseyRouter_BadAdminAddress(); } admin = newAdmin; } function setMaxSupply721(address token, uint256 amount) public { if (ownerOf[token] != msg.sender) { revert OdysseyRouter_Unauthorized(); } maxSupply721[token] = amount; } function setMaxSupply1155( address token, uint256[] calldata tokenIds, uint256[] calldata amounts ) public { if (ownerOf[token] != msg.sender) { revert OdysseyRouter_Unauthorized(); } uint256 i; for (; i < tokenIds.length; ++i) { maxSupply1155[token][tokenIds[i]] = amounts[i]; } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"treasury_","type":"address"},{"internalType":"address","name":"xpDirectory_","type":"address"},{"internalType":"address","name":"xp_","type":"address"},{"internalType":"address[]","name":"ohmCurrencies_","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"OdysseyLaunchPlatform_AlreadyClaimed","type":"error"},{"inputs":[],"name":"OdysseyLaunchPlatform_FailedToPayERC20","type":"error"},{"inputs":[],"name":"OdysseyLaunchPlatform_FailedToPayEther","type":"error"},{"inputs":[],"name":"OdysseyLaunchPlatform_InsufficientFunds","type":"error"},{"inputs":[],"name":"OdysseyLaunchPlatform_MaxSupplyCap","type":"error"},{"inputs":[],"name":"OdysseyLaunchPlatform_ReservedOrClaimedMax","type":"error"},{"inputs":[],"name":"OdysseyLaunchPlatform_TokenDoesNotExist","type":"error"},{"inputs":[],"name":"OdysseyLaunchPlatform_TreasuryPayFailure","type":"error"},{"inputs":[],"name":"OdysseyRouter_BadAdminAddress","type":"error"},{"inputs":[],"name":"OdysseyRouter_BadOwnerAddress","type":"error"},{"inputs":[],"name":"OdysseyRouter_BadRecipientAddress","type":"error"},{"inputs":[],"name":"OdysseyRouter_BadSenderAddress","type":"error"},{"inputs":[],"name":"OdysseyRouter_BadTokenAddress","type":"error"},{"inputs":[],"name":"OdysseyRouter_BadTreasuryAddress","type":"error"},{"inputs":[],"name":"OdysseyRouter_OwnerMintFailure","type":"error"},{"inputs":[],"name":"OdysseyRouter_TokenIDSupplyMismatch","type":"error"},{"inputs":[],"name":"OdysseyRouter_Unauthorized","type":"error"},{"inputs":[],"name":"OdysseyRouter_WhitelistUpdateFail","type":"error"},{"inputs":[],"name":"Factory","outputs":[{"internalType":"contract OdysseyTokenFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MERKLE_TREE_ROOT_ERC1155_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MERKLE_TREE_ROOT_ERC721_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32[][]","name":"merkleProof","type":"bytes32[][]"},{"internalType":"bytes32[]","name":"merkleRoot","type":"bytes32[]"},{"internalType":"uint256[]","name":"minPrice","type":"uint256[]"},{"internalType":"uint256[]","name":"mintsPerUser","type":"uint256[]"},{"internalType":"uint256[]","name":"tokenId","type":"uint256[]"},{"internalType":"address[]","name":"tokenAddress","type":"address[]"},{"internalType":"address[]","name":"currency","type":"address[]"},{"internalType":"uint8[]","name":"v","type":"uint8[]"},{"internalType":"bytes32[]","name":"r","type":"bytes32[]"},{"internalType":"bytes32[]","name":"s","type":"bytes32[]"}],"internalType":"struct OdysseyLib.BatchMint","name":"batch","type":"tuple"}],"name":"batchMintERC1155","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32[][]","name":"merkleProof","type":"bytes32[][]"},{"internalType":"bytes32[]","name":"merkleRoot","type":"bytes32[]"},{"internalType":"uint256[]","name":"minPrice","type":"uint256[]"},{"internalType":"uint256[]","name":"mintsPerUser","type":"uint256[]"},{"internalType":"uint256[]","name":"tokenId","type":"uint256[]"},{"internalType":"address[]","name":"tokenAddress","type":"address[]"},{"internalType":"address[]","name":"currency","type":"address[]"},{"internalType":"uint8[]","name":"v","type":"uint8[]"},{"internalType":"bytes32[]","name":"r","type":"bytes32[]"},{"internalType":"bytes32[]","name":"s","type":"bytes32[]"}],"internalType":"struct OdysseyLib.BatchMint","name":"batch","type":"tuple"}],"name":"batchMintERC721","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32[][]","name":"merkleProof","type":"bytes32[][]"},{"internalType":"bytes32[]","name":"merkleRoot","type":"bytes32[]"},{"internalType":"uint256[]","name":"minPrice","type":"uint256[]"},{"internalType":"uint256[]","name":"mintsPerUser","type":"uint256[]"},{"internalType":"uint256[]","name":"tokenId","type":"uint256[]"},{"internalType":"address[]","name":"tokenAddress","type":"address[]"},{"internalType":"address[]","name":"currency","type":"address[]"},{"internalType":"uint8[]","name":"v","type":"uint8[]"},{"internalType":"bytes32[]","name":"r","type":"bytes32[]"},{"internalType":"bytes32[]","name":"s","type":"bytes32[]"}],"internalType":"struct OdysseyLib.BatchMint","name":"batch","type":"tuple"}],"name":"batchReserveERC1155","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32[][]","name":"merkleProof","type":"bytes32[][]"},{"internalType":"bytes32[]","name":"merkleRoot","type":"bytes32[]"},{"internalType":"uint256[]","name":"minPrice","type":"uint256[]"},{"internalType":"uint256[]","name":"mintsPerUser","type":"uint256[]"},{"internalType":"uint256[]","name":"tokenId","type":"uint256[]"},{"internalType":"address[]","name":"tokenAddress","type":"address[]"},{"internalType":"address[]","name":"currency","type":"address[]"},{"internalType":"uint8[]","name":"v","type":"uint8[]"},{"internalType":"bytes32[]","name":"r","type":"bytes32[]"},{"internalType":"bytes32[]","name":"s","type":"bytes32[]"}],"internalType":"struct OdysseyLib.BatchMint","name":"batch","type":"tuple"}],"name":"batchReserveERC721","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"string","name":"baseURI","type":"string"},{"components":[{"internalType":"uint256[]","name":"maxSupply","type":"uint256[]"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"uint256[]","name":"reserveAmounts","type":"uint256[]"}],"internalType":"struct OdysseyLib.Odyssey1155Info","name":"info","type":"tuple"},{"components":[{"internalType":"uint256","name":"numerator","type":"uint256"},{"internalType":"uint256","name":"denominator","type":"uint256"}],"internalType":"struct OdysseyLib.Percentage","name":"treasuryPercentage","type":"tuple"},{"internalType":"address","name":"royaltyReceiver","type":"address"},{"internalType":"bool","name":"whitelist","type":"bool"}],"name":"create1155","outputs":[{"internalType":"address","name":"token","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"string","name":"baseURI","type":"string"},{"internalType":"uint256","name":"maxSupply","type":"uint256"},{"internalType":"uint256","name":"reserveAmount","type":"uint256"},{"components":[{"internalType":"uint256","name":"numerator","type":"uint256"},{"internalType":"uint256","name":"denominator","type":"uint256"}],"internalType":"struct OdysseyLib.Percentage","name":"treasuryPercentage","type":"tuple"},{"internalType":"address","name":"royaltyReceiver","type":"address"},{"internalType":"bool","name":"whitelist","type":"bool"}],"name":"create721","outputs":[{"internalType":"address","name":"token","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"cumulativeSupply1155","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"cumulativeSupply721","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"domainSeparator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"isReserved1155","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isReserved721","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"maxSupply1155","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxSupply721","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"},{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"},{"internalType":"uint256","name":"minPrice","type":"uint256"},{"internalType":"uint256","name":"mintsPerUser","type":"uint256"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"address","name":"currency","type":"address"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"mintERC1155","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"},{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"},{"internalType":"uint256","name":"minPrice","type":"uint256"},{"internalType":"uint256","name":"mintsPerUser","type":"uint256"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"address","name":"currency","type":"address"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"mintERC721","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"mintedSupply721","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"ohmFamilyCurrencies","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"ownerMint1155","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"ownerMint721","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"slot","type":"uint256"}],"name":"readSlotAsAddress","outputs":[{"internalType":"address","name":"data","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"},{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"},{"internalType":"uint256","name":"minPrice","type":"uint256"},{"internalType":"uint256","name":"mintsPerUser","type":"uint256"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"address","name":"currency","type":"address"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"reserveERC1155","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"},{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"},{"internalType":"uint256","name":"minPrice","type":"uint256"},{"internalType":"uint256","name":"mintsPerUser","type":"uint256"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"address","name":"currency","type":"address"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"reserveERC721","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"royaltyRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"setMaxSupply1155","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"setMaxSupply721","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"newOwner","type":"address"}],"name":"setOwnerShip","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"recipient","type":"address"}],"name":"setRoyaltyRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newTreasury","type":"address"}],"name":"setTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"bool","name":"active","type":"bool"}],"name":"setWhitelistStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newXp","type":"address"}],"name":"setXP","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"treasuryCommission","outputs":[{"internalType":"uint256","name":"numerator","type":"uint256"},{"internalType":"uint256","name":"denominator","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelistActive","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"whitelistClaimed1155","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"whitelistClaimed721","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
608060405260016014553480156200001657600080fd5b506040516200d1cd3803806200d1cd8339810160408190526200003991620003af565b604051620000479062000344565b604051809103906000f08015801562000064573d6000803e3d6000fd5b50600080546001600160a01b0319166001600160a01b0392909216919091179055604051620000939062000352565b604051809103906000f080158015620000b0573d6000803e3d6000fd5b50600180546001600160a01b039283166001600160a01b0319918216179091556002805492871692821692909217909155600380549091163317905560005b815181101562000156576001600a6000848481518110620001145762000114620004ba565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000208190555080806200014d90620004d0565b915050620000ef565b6001600160a01b03831662000319576001600160a01b0384166200028457604051620001829062000360565b604051809103906000f0801580156200019f573d6000803e3d6000fd5b5060405163ee2f70a960e01b81526001600482018190526024820181905260448201819052600360648301819052608483015260a48201529094506001600160a01b0385169063ee2f70a99060c401600060405180830381600087803b1580156200020957600080fd5b505af11580156200021e573d6000803e3d6000fd5b505060035460405163f2fde38b60e01b81526001600160a01b039182166004820152908716925063f2fde38b9150602401600060405180830381600087803b1580156200026a57600080fd5b505af11580156200027f573d6000803e3d6000fd5b505050505b816000815181106200029a576200029a620004ba565b6020026020010151843030600360009054906101000a90046001600160a01b0316604051620002c9906200036e565b6001600160a01b0395861681529385166020850152918416604084015283166060830152909116608082015260a001604051809103906000f08015801562000315573d6000803e3d6000fd5b5092505b5050600480546001600160a01b0319166001600160a01b039290921691909117905550620004fa9050565b61329e806200348a83390190565b614155806200672883390190565b611082806200a87d83390190565b6118ce806200b8ff83390190565b80516001600160a01b03811681146200039457600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60008060008060808587031215620003c657600080fd5b620003d1856200037c565b93506020620003e28187016200037c565b9350620003f2604087016200037c565b60608701519093506001600160401b03808211156200041057600080fd5b818801915088601f8301126200042557600080fd5b8151818111156200043a576200043a62000399565b8060051b604051601f19603f8301168101818110858211171562000462576200046262000399565b60405291825284820192508381018501918b8311156200048157600080fd5b938501935b82851015620004aa576200049a856200037c565b8452938501939285019262000486565b989b979a50959850505050505050565b634e487b7160e01b600052603260045260246000fd5b6000600019821415620004f357634e487b7160e01b600052601160045260246000fd5b5060010190565b612f80806200050a6000396000f3fe6080604052600436106102305760003560e01c8063a41a964a1161012e578063c5936954116100ab578063d425f3cf1161006f578063d425f3cf1461077b578063dd55ebdc146107a8578063de8f21db146107d5578063e3d9a96d146107e8578063f0f442601461081557600080fd5b8063c5936954146106c1578063c67596f2146106e1578063c6aa2fb614610719578063c83dd23114610746578063cf6344391461075b57600080fd5b8063af91e8c9116100f2578063af91e8c91461063b578063b82263ca1461065b578063b98a44521461066e578063c188e3951461069b578063c4dd9ab7146106ae57600080fd5b8063a41a964a1461053d578063a51ffa4c14610571578063a88e6ce9146105ba578063aaa91a9f146105cd578063ac6390a41461060557600080fd5b80635f90e51a116101bc578063809a9f2f11610180578063809a9f2f1461048657806384f6f617146104a65780638bbdc1dd146104c55780638eb346c1146104d857806390135fe41461051057600080fd5b80635f90e51a146103b05780636dc05dbc146103d05780636e6e13ac146103f0578063704b6c02146104285780637823d3151461044857600080fd5b80633bf95ee8116102035780633bf95ee8146102ea578063474361331461032c5780634d32ce2c1461033f5780634d9e7059146103525780635b8540611461037257600080fd5b80630c424284146102355780630f7c4bfc1461025757806314afd79e146102945780632157e77e146102ca575b600080fd5b34801561024157600080fd5b50610255610250366004612604565b610835565b005b34801561026357600080fd5b5061027761027236600461269a565b610931565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156102a057600080fd5b506102776102af36600461277c565b6007602052600090815260409020546001600160a01b031681565b3480156102d657600080fd5b506102776102e53660046127ab565b610c52565b3480156102f657600080fd5b5061031e7f0a52f6e0133eadd055cc5703844e676242c3b461d85fb7ce7f74becd7e40edd181565b60405190815260200161028b565b61025561033a36600461289c565b611077565b61025561034d36600461289c565b611307565b34801561035e57600080fd5b5061025561036d36600461291d565b61156b565b34801561037e57600080fd5b5061031e61038d3660046129a0565b601060209081526000938452604080852082529284528284209052825290205481565b3480156103bc57600080fd5b506102556103cb3660046129e1565b611634565b3480156103dc57600080fd5b506102556103eb36600461277c565b61168a565b3480156103fc57600080fd5b5061031e61040b3660046129e1565b601360209081526000928352604080842090915290825290205481565b34801561043457600080fd5b5061025561044336600461277c565b61171c565b34801561045457600080fd5b5061031e6104633660046129a0565b601160209081526000938452604080852082529284528284209052825290205481565b34801561049257600080fd5b506102556104a1366004612a0d565b6117ae565b3480156104b257600080fd5b506102776104c1366004612a46565b5490565b6102556104d336600461289c565b6118a5565b3480156104e457600080fd5b5061031e6104f3366004612a0d565b600b60209081526000928352604080842090915290825290205481565b34801561051c57600080fd5b5061031e61052b36600461277c565b60056020526000908152604090205481565b34801561054957600080fd5b5061031e7ff0f6f256599682b9387f45fc268ed696625f835d98d64b8967134239e103fc6c81565b34801561057d57600080fd5b506105a561058c36600461277c565b6009602052600090815260409020805460019091015482565b6040805192835260208301919091520161028b565b6102556105c8366004612a70565b611b09565b3480156105d957600080fd5b5061031e6105e83660046129e1565b601260209081526000928352604080842090915290825290205481565b34801561061157600080fd5b5061027761062036600461277c565b6008602052600090815260409020546001600160a01b031681565b34801561064757600080fd5b50610255610656366004612b1a565b611bd4565b61025561066936600461289c565b611ce0565b34801561067a57600080fd5b5061031e61068936600461277c565b600e6020526000908152604090205481565b6102556106a9366004612a70565b611f6c565b6102556106bc366004612b64565b611fd4565b3480156106cd57600080fd5b506102556106dc366004612a0d565b6120a2565b3480156106ed57600080fd5b5061031e6106fc366004612a0d565b600c60209081526000928352604080842090915290825290205481565b34801561072557600080fd5b5061031e61073436600461277c565b60066020526000908152604090205481565b34801561075257600080fd5b50610277612176565b34801561076757600080fd5b50610255610776366004612a0d565b612186565b34801561078757600080fd5b5061031e61079636600461277c565b600d6020526000908152604090205481565b3480156107b457600080fd5b5061031e6107c336600461277c565b600f6020526000908152604090205481565b6102556107e3366004612b64565b61225a565b3480156107f457600080fd5b5061031e61080336600461277c565b600a6020526000908152604090205481565b34801561082157600080fd5b5061025561083036600461277c565b6122c4565b6001600160a01b0382811660009081526007602052604090205416331461086f57604051630862a88160e21b815260040160405180910390fd5b600080546040516001600160a01b03858116602483015284151560448301529091169060640160408051601f198184030181529181526020820180516001600160e01b031663031090a160e21b179052516108ca9190612c19565b600060405180830381855af49150503d8060008114610905576040519150601f19603f3d011682016040523d82523d6000602084013e61090a565b606091505b505090508061092c57604051637f77015160e01b815260040160405180910390fd5b505050565b600061093b612176565b6001600160a01b031663c8c43967338e8e8e8e8e8e6040518863ffffffff1660e01b81526004016109729796959493929190612c7d565b6020604051808303816000875af1158015610991573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109b59190612cd8565b6001600160a01b038116600090815260076020526040902080546001600160a01b03191633179055905085156109eb57856109ef565b6000195b6001600160a01b0382166000908152600f602052604090205581610a14576000610a17565b60015b6001600160a01b03828116600081815260066020908152604080832060ff96909616909555600890529290922080546001600160a01b0319169186169190911790557f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f90610a8490612356565b80516020918201206040805192830193909352918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201526001600160a01b03821660a082015260c00160408051601f1981840301815291815281516020928301206001600160a01b03841660009081526005909352912055610b14846123b2565b15610b47576001600160a01b03811660009081526009602090815260409091208535815590850135600182015550610b83565b60408051808201825260038152606460208083019182526001600160a01b03851660009081526009909152929092209051815590516001909101555b8415610c435760008054604051602481018890526001600160a01b0384811660448301528392169060640160408051601f198184030181529181526020820180516001600160e01b0316630bc2051f60e41b17905251610be39190612c19565b600060405180830381855af49150503d8060008114610c1e576040519150601f19603f3d011682016040523d82523d6000602084013e610c23565b606091505b509150915081610c40578051610c3857600080fd5b805181602001fd5b50505b9b9a5050505050505050505050565b6000610c616020860186612cf5565b9050610c6d8680612cf5565b905014610c8d5760405163161b1f5b60e01b815260040160405180910390fd5b610c95612176565b6001600160a01b03166360596ba0338d8d8d8d8d8d6040518863ffffffff1660e01b8152600401610ccc9796959493929190612c7d565b6020604051808303816000875af1158015610ceb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d0f9190612cd8565b6001600160a01b038116600090815260076020526040902080546001600160a01b03191633179055905081610d45576000610d48565b60015b6001600160a01b03828116600090815260066020908152604080832060ff959095169094556008905291822080546001600160a01b0319169186169190911790555b610d976020870187612cf5565b9050811015610e5457610daa8680612cf5565b82818110610dba57610dba612d3f565b90506020020135600014610dee57610dd28680612cf5565b82818110610de257610de2612d3f565b90506020020135610df2565b6000195b6001600160a01b038316600090815260136020908152604082209190610e1a908a018a612cf5565b85818110610e2a57610e2a612d3f565b9050602002013581526020019081526020016000208190555080610e4d90612d6b565b9050610d8a565b7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f610e87836001600160a01b0316612356565b80516020918201206040805192830193909352918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201526001600160a01b03831660a082015260c00160408051601f1981840301815291815281516020928301206001600160a01b03851660009081526005909352912055610f17856123b2565b15610f4a576001600160a01b03821660009081526009602090815260409091208635815590860135600182015550610f86565b60408051808201825260038152606460208083019182526001600160a01b03861660009081526009909152929092209051815590516001909101555b6000610f956040880188612cf5565b90501115611068576000805481906001600160a01b0316610fb960208a018a612cf5565b610fc660408c018c612cf5565b88604051602401610fdb959493929190612dbc565b60408051601f198184030181529181526020820180516001600160e01b031663b5afec9560e01b179052516110109190612c19565b600060405180830381855af49150503d806000811461104b576040519150601f19603f3d011682016040523d82523d6000602084013e611050565b606091505b509150915081611065578051610c3857600080fd5b50505b509a9950505050505050505050565b60005b61108760a0830183612cf5565b9050811015611303576000805481906001600160a01b03166110a98580612cf5565b858181106110b9576110b9612d3f565b90506020028101906110cb9190612cf5565b6110d86020880188612cf5565b878181106110e8576110e8612d3f565b905060200201358780604001906110ff9190612cf5565b8881811061110f5761110f612d3f565b905060200201358880606001906111269190612cf5565b8981811061113657611136612d3f565b9050602002013589806080019061114d9190612cf5565b8a81811061115d5761115d612d3f565b905060200201358a8060a001906111749190612cf5565b8b81811061118457611184612d3f565b9050602002016020810190611199919061277c565b6111a660c08d018d612cf5565b8c8181106111b6576111b6612d3f565b90506020020160208101906111cb919061277c565b6111d860e08e018e612cf5565b8d8181106111e8576111e8612d3f565b90506020020160208101906111fd9190612dfe565b61120b6101008f018f612cf5565b8e81811061121b5761121b612d3f565b905060200201358e8061012001906112339190612cf5565b8f81811061124357611243612d3f565b905060200201356040516024016112649b9a99989796959493929190612e19565b60408051601f198184030181529181526020820180516001600160e01b031663de8f21db60e01b179052516112999190612c19565b600060405180830381855af49150503d80600081146112d4576040519150601f19603f3d011682016040523d82523d6000602084013e6112d9565b606091505b5091509150816112ee578051610c3857600080fd5b505080806112fb90612d6b565b91505061107a565b5050565b60005b61131760a0830183612cf5565b9050811015611303576000805481906001600160a01b03166113398580612cf5565b8581811061134957611349612d3f565b905060200281019061135b9190612cf5565b6113686020880188612cf5565b8781811061137857611378612d3f565b9050602002013587806040019061138f9190612cf5565b8881811061139f5761139f612d3f565b905060200201358880606001906113b69190612cf5565b898181106113c6576113c6612d3f565b90506020020135898060a001906113dd9190612cf5565b8a8181106113ed576113ed612d3f565b9050602002016020810190611402919061277c565b61140f60c08c018c612cf5565b8b81811061141f5761141f612d3f565b9050602002016020810190611434919061277c565b61144160e08d018d612cf5565b8c81811061145157611451612d3f565b90506020020160208101906114669190612dfe565b6114746101008e018e612cf5565b8d81811061148457611484612d3f565b905060200201358d80610120019061149c9190612cf5565b8e8181106114ac576114ac612d3f565b905060200201356040516024016114cc9a99989796959493929190612e82565b60408051601f198184030181529181526020820180516001600160e01b031663a88e6ce960e01b179052516115019190612c19565b600060405180830381855af49150503d806000811461153c576040519150601f19603f3d011682016040523d82523d6000602084013e611541565b606091505b509150915081611556578051610c3857600080fd5b5050808061156390612d6b565b91505061130a565b6001600160a01b038581166000908152600760205260409020541633146115a557604051630862a88160e21b815260040160405180910390fd5b60005b8381101561162c578282828181106115c2576115c2612d3f565b9050602002013560136000886001600160a01b03166001600160a01b03168152602001908152602001600020600087878581811061160257611602612d3f565b905060200201358152602001908152602001600020819055508061162590612d6b565b90506115a8565b505050505050565b6001600160a01b0382811660009081526007602052604090205416331461166e57604051630862a88160e21b815260040160405180910390fd5b6001600160a01b039091166000908152600f6020526040902055565b6003546001600160a01b031633146116b557604051630862a88160e21b815260040160405180910390fd5b336116d357604051633c0a853160e11b815260040160405180910390fd5b6001600160a01b0381166116fa57604051637a381aaf60e01b815260040160405180910390fd5b600480546001600160a01b0319166001600160a01b0392909216919091179055565b6003546001600160a01b0316331461174757604051630862a88160e21b815260040160405180910390fd5b3361176557604051633c0a853160e11b815260040160405180910390fd5b6001600160a01b03811661178c5760405163035745df60e51b815260040160405180910390fd5b600380546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b038281166000908152600760205260409020541633146117e857604051630862a88160e21b815260040160405180910390fd5b600080546040516001600160a01b03858116602483015284811660448301529091169060640160408051601f198184030181529181526020820180516001600160e01b031663809a9f2f60e01b179052516118439190612c19565b600060405180830381855af49150503d806000811461187e576040519150601f19603f3d011682016040523d82523d6000602084013e611883565b606091505b505090508061092c5760405163786fb38760e01b815260040160405180910390fd5b60005b6118b560a0830183612cf5565b9050811015611303576000805481906001600160a01b03166118d78580612cf5565b858181106118e7576118e7612d3f565b90506020028101906118f99190612cf5565b6119066020880188612cf5565b8781811061191657611916612d3f565b9050602002013587806040019061192d9190612cf5565b8881811061193d5761193d612d3f565b905060200201358880606001906119549190612cf5565b8981811061196457611964612d3f565b90506020020135898060a0019061197b9190612cf5565b8a81811061198b5761198b612d3f565b90506020020160208101906119a0919061277c565b6119ad60c08c018c612cf5565b8b8181106119bd576119bd612d3f565b90506020020160208101906119d2919061277c565b6119df60e08d018d612cf5565b8c8181106119ef576119ef612d3f565b9050602002016020810190611a049190612dfe565b611a126101008e018e612cf5565b8d818110611a2257611a22612d3f565b905060200201358d806101200190611a3a9190612cf5565b8e818110611a4a57611a4a612d3f565b90506020020135604051602401611a6a9a99989796959493929190612e82565b60408051601f198184030181529181526020820180516001600160e01b031663c188e39560e01b17905251611a9f9190612c19565b600060405180830381855af49150503d8060008114611ada576040519150601f19603f3d011682016040523d82523d6000602084013e611adf565b606091505b509150915081611af4578051610c3857600080fd5b50508080611b0190612d6b565b9150506118a8565b6000805460405182916001600160a01b031690611b3c908e908e908e908e908e908e908e908e908e908e90602401612e82565b60408051601f198184030181529181526020820180516001600160e01b031663a88e6ce960e01b17905251611b719190612c19565b600060405180830381855af49150503d8060008114611bac576040519150601f19603f3d011682016040523d82523d6000602084013e611bb1565b606091505b509150915081611bc6578051610c3857600080fd5b505050505050505050505050565b6001600160a01b03828116600090815260076020526040902054163314611c0e57604051630862a88160e21b815260040160405180910390fd5b6000805460405160248101879052604481018690526001600160a01b03858116606483015284811660848301529091169060a40160408051601f198184030181529181526020820180516001600160e01b031663af91e8c960e01b17905251611c779190612c19565b600060405180830381855af49150503d8060008114611cb2576040519150601f19603f3d011682016040523d82523d6000602084013e611cb7565b606091505b5050905080611cd95760405163786fb38760e01b815260040160405180910390fd5b5050505050565b60005b611cf060a0830183612cf5565b9050811015611303576000805481906001600160a01b0316611d128580612cf5565b85818110611d2257611d22612d3f565b9050602002810190611d349190612cf5565b611d416020880188612cf5565b87818110611d5157611d51612d3f565b90506020020135878060400190611d689190612cf5565b88818110611d7857611d78612d3f565b90506020020135888060600190611d8f9190612cf5565b89818110611d9f57611d9f612d3f565b90506020020135898060800190611db69190612cf5565b8a818110611dc657611dc6612d3f565b905060200201358a8060a00190611ddd9190612cf5565b8b818110611ded57611ded612d3f565b9050602002016020810190611e02919061277c565b611e0f60c08d018d612cf5565b8c818110611e1f57611e1f612d3f565b9050602002016020810190611e34919061277c565b611e4160e08e018e612cf5565b8d818110611e5157611e51612d3f565b9050602002016020810190611e669190612dfe565b611e746101008f018f612cf5565b8e818110611e8457611e84612d3f565b905060200201358e806101200190611e9c9190612cf5565b8f818110611eac57611eac612d3f565b90506020020135604051602401611ecd9b9a99989796959493929190612e19565b60408051601f198184030181529181526020820180516001600160e01b031663c4dd9ab760e01b17905251611f029190612c19565b600060405180830381855af49150503d8060008114611f3d576040519150601f19603f3d011682016040523d82523d6000602084013e611f42565b606091505b509150915081611f57578051610c3857600080fd5b50508080611f6490612d6b565b915050611ce3565b6000805460405182916001600160a01b031690611f9f908e908e908e908e908e908e908e908e908e908e90602401612e82565b60408051601f198184030181529181526020820180516001600160e01b031663c188e39560e01b17905251611b719190612c19565b6000805460405182916001600160a01b031690612009908f908f908f908f908f908f908f908f908f908f908f90602401612e19565b60408051601f198184030181529181526020820180516001600160e01b031663c4dd9ab760e01b1790525161203e9190612c19565b600060405180830381855af49150503d8060008114612079576040519150601f19603f3d011682016040523d82523d6000602084013e61207e565b606091505b509150915081612093578051610c3857600080fd5b50505050505050505050505050565b6001600160a01b0382166120c957604051637a381aaf60e01b815260040160405180910390fd5b6001600160a01b0381166120f057604051630572b44f60e41b815260040160405180910390fd5b3361210e57604051633c0a853160e11b815260040160405180910390fd5b6001600160a01b0382811660009081526007602052604090205416331461214857604051630862a88160e21b815260040160405180910390fd5b6001600160a01b03918216600090815260086020526040902080546001600160a01b03191691909216179055565b600061218160015490565b905090565b6001600160a01b0382166121ad57604051637a381aaf60e01b815260040160405180910390fd5b6001600160a01b0381166121d457604051630b07403f60e21b815260040160405180910390fd5b336121f257604051633c0a853160e11b815260040160405180910390fd5b6001600160a01b0382811660009081526007602052604090205416331461222c57604051630862a88160e21b815260040160405180910390fd5b6001600160a01b03918216600090815260076020526040902080546001600160a01b03191691909216179055565b6000805460405182916001600160a01b03169061228f908f908f908f908f908f908f908f908f908f908f908f90602401612e19565b60408051601f198184030181529181526020820180516001600160e01b031663de8f21db60e01b1790525161203e9190612c19565b6003546001600160a01b031633146122ef57604051630862a88160e21b815260040160405180910390fd5b3361230d57604051633c0a853160e11b815260040160405180910390fd5b6001600160a01b038116612334576040516322f85dbf60e01b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b0392909216919091179055565b60608161237d5750506040805180820190915260048152630307830360e41b602082015290565b8160005b81156123a0578061239181612d6b565b915050600882901c9150612381565b6123aa8482612425565b949350505050565b60006020820135823511156123c957506000919050565b813515806123d957506020820135155b156123e657506000919050565b60006123f483356064612ee6565b9050600061240760208501356003612ee6565b90508082101561241b575060009392505050565b5060019392505050565b60606000612434836002612ee6565b61243f906002612f05565b67ffffffffffffffff81111561245757612457612f1d565b6040519080825280601f01601f191660200182016040528015612481576020820181803683370190505b509050600360fc1b8160008151811061249c5761249c612d3f565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106124cb576124cb612d3f565b60200101906001600160f81b031916908160001a90535060006124ef846002612ee6565b6124fa906001612f05565b90505b6001811115612572576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061252e5761252e612d3f565b1a60f81b82828151811061254457612544612d3f565b60200101906001600160f81b031916908160001a90535060049490941c9361256b81612f33565b90506124fd565b5083156125c55760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015260640160405180910390fd5b9392505050565b6001600160a01b03811681146125e157600080fd5b50565b80356125ef816125cc565b919050565b803580151581146125ef57600080fd5b6000806040838503121561261757600080fd5b8235612622816125cc565b9150612630602084016125f4565b90509250929050565b60008083601f84011261264b57600080fd5b50813567ffffffffffffffff81111561266357600080fd5b60208301915083602082850101111561267b57600080fd5b9250929050565b60006040828403121561269457600080fd5b50919050565b60008060008060008060008060008060006101208c8e0312156126bc57600080fd5b67ffffffffffffffff808d3511156126d357600080fd5b6126e08e8e358f01612639565b909c509a5060208d01358110156126f657600080fd5b6127068e60208f01358f01612639565b909a50985060408d013581101561271c57600080fd5b5061272d8d60408e01358e01612639565b909750955060608c0135945060808c0135935061274d8d60a08e01612682565b925061275b60e08d016125e4565b915061276a6101008d016125f4565b90509295989b509295989b9093969950565b60006020828403121561278e57600080fd5b81356125c5816125cc565b60006060828403121561269457600080fd5b6000806000806000806000806000806101008b8d0312156127cb57600080fd5b8a3567ffffffffffffffff808211156127e357600080fd5b6127ef8e838f01612639565b909c509a5060208d013591508082111561280857600080fd5b6128148e838f01612639565b909a50985060408d013591508082111561282d57600080fd5b6128398e838f01612639565b909850965060608d013591508082111561285257600080fd5b5061285f8d828e01612799565b94505061286f8c60808d01612682565b925061287d60c08c016125e4565b915061288b60e08c016125f4565b90509295989b9194979a5092959850565b6000602082840312156128ae57600080fd5b813567ffffffffffffffff8111156128c557600080fd5b820161014081850312156125c557600080fd5b60008083601f8401126128ea57600080fd5b50813567ffffffffffffffff81111561290257600080fd5b6020830191508360208260051b850101111561267b57600080fd5b60008060008060006060868803121561293557600080fd5b8535612940816125cc565b9450602086013567ffffffffffffffff8082111561295d57600080fd5b61296989838a016128d8565b9096509450604088013591508082111561298257600080fd5b5061298f888289016128d8565b969995985093965092949392505050565b6000806000606084860312156129b557600080fd5b83356129c0816125cc565b925060208401356129d0816125cc565b929592945050506040919091013590565b600080604083850312156129f457600080fd5b82356129ff816125cc565b946020939093013593505050565b60008060408385031215612a2057600080fd5b8235612a2b816125cc565b91506020830135612a3b816125cc565b809150509250929050565b600060208284031215612a5857600080fd5b5035919050565b803560ff811681146125ef57600080fd5b6000806000806000806000806000806101208b8d031215612a9057600080fd5b8a3567ffffffffffffffff811115612aa757600080fd5b612ab38d828e016128d8565b909b5099505060208b0135975060408b0135965060608b0135955060808b0135612adc816125cc565b945060a08b0135612aec816125cc565b9350612afa60c08c01612a5f565b925060e08b013591506101008b013590509295989b9194979a5092959850565b60008060008060808587031215612b3057600080fd5b84359350602085013592506040850135612b49816125cc565b91506060850135612b59816125cc565b939692955090935050565b60008060008060008060008060008060006101408c8e031215612b8657600080fd5b8b3567ffffffffffffffff811115612b9d57600080fd5b612ba98e828f016128d8565b909c509a505060208c0135985060408c0135975060608c0135965060808c0135955060a08c0135612bd9816125cc565b945060c08c0135612be9816125cc565b9350612bf760e08d01612a5f565b92506101008c013591506101208c013590509295989b509295989b9093969950565b6000825160005b81811015612c3a5760208186018101518583015201612c20565b81811115612c49576000828501525b509190910192915050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6001600160a01b0388168152608060208201819052600090612ca2908301888a612c54565b8281036040840152612cb5818789612c54565b90508281036060840152612cca818587612c54565b9a9950505050505050505050565b600060208284031215612cea57600080fd5b81516125c5816125cc565b6000808335601e19843603018112612d0c57600080fd5b83018035915067ffffffffffffffff821115612d2757600080fd5b6020019150600581901b360382131561267b57600080fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600019821415612d7f57612d7f612d55565b5060010190565b81835260006001600160fb1b03831115612d9f57600080fd5b8260051b8083602087013760009401602001938452509192915050565b606081526000612dd0606083018789612d86565b8281036020840152612de3818688612d86565b91505060018060a01b03831660408301529695505050505050565b600060208284031215612e1057600080fd5b6125c582612a5f565b61014081526000612e2f61014083018d8f612d86565b602083019b909b52506040810198909852606088019690965260808701949094526001600160a01b0392831660a0870152911660c085015260ff1660e08401526101008301526101209091015292915050565b6000610120808352612e978184018d8f612d86565b602084019b909b525050604081019790975260608701959095526001600160a01b0393841660808701529190921660a085015260ff90911660c084015260e08301526101009091015292915050565b6000816000190483118215151615612f0057612f00612d55565b500290565b60008219821115612f1857612f18612d55565b500190565b634e487b7160e01b600052604160045260246000fd5b600081612f4257612f42612d55565b50600019019056fea264697066735822122023ed71495e38f66a1d455443cde842982c3b06bad6b09689e2facfe108ef187364736f6c634300080c00336080604052600160145534801561001557600080fd5b50613279806100256000396000f3fe60806040526004361061019c5760003560e01c8063aaa91a9f116100ec578063c4dd9ab71161008a578063d425f3cf11610064578063d425f3cf146105c6578063dd55ebdc146105f3578063de8f21db14610620578063e3d9a96d1461063357600080fd5b8063c4dd9ab71461054e578063c67596f214610561578063c6aa2fb61461059957600080fd5b8063b5afec95116100c6578063b5afec95146104ce578063b98a4452146104ee578063bc2051f01461051b578063c188e3951461053b57600080fd5b8063aaa91a9f14610440578063ac6390a414610478578063af91e8c9146104ae57600080fd5b8063809a9f2f1161015957806390135fe41161013357806390135fe414610383578063a41a964a146103b0578063a51ffa4c146103e4578063a88e6ce91461042d57600080fd5b8063809a9f2f1461030c57806384f6f6171461032c5780638eb346c11461034b57600080fd5b80630c424284146101a157806314afd79e146101c35780633bf95ee8146102165780635b854061146102585780636e6e13ac146102965780637823d315146102ce575b600080fd5b3480156101ad57600080fd5b506101c16101bc366004612c8b565b610660565b005b3480156101cf57600080fd5b506101f96101de366004612cc2565b6007602052600090815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561022257600080fd5b5061024a7f0a52f6e0133eadd055cc5703844e676242c3b461d85fb7ce7f74becd7e40edd181565b60405190815260200161020d565b34801561026457600080fd5b5061024a610273366004612ce4565b601060209081526000938452604080852082529284528284209052825290205481565b3480156102a257600080fd5b5061024a6102b1366004612d20565b601360209081526000928352604080842090915290825290205481565b3480156102da57600080fd5b5061024a6102e9366004612ce4565b601160209081526000938452604080852082529284528284209052825290205481565b34801561031857600080fd5b506101c1610327366004612d4a565b610754565b34801561033857600080fd5b506101f9610347366004612d7d565b5490565b34801561035757600080fd5b5061024a610366366004612d4a565b600b60209081526000928352604080842090915290825290205481565b34801561038f57600080fd5b5061024a61039e366004612cc2565b60056020526000908152604090205481565b3480156103bc57600080fd5b5061024a7ff0f6f256599682b9387f45fc268ed696625f835d98d64b8967134239e103fc6c81565b3480156103f057600080fd5b506104186103ff366004612cc2565b6009602052600090815260409020805460019091015482565b6040805192835260208301919091520161020d565b6101c161043b366004612df3565b61087e565b34801561044c57600080fd5b5061024a61045b366004612d20565b601260209081526000928352604080842090915290825290205481565b34801561048457600080fd5b506101f9610493366004612cc2565b6008602052600090815260409020546001600160a01b031681565b3480156104ba57600080fd5b506101c16104c9366004612e99565b610fc1565b3480156104da57600080fd5b506101c16104e9366004612edf565b6110f5565b3480156104fa57600080fd5b5061024a610509366004612cc2565b600e6020526000908152604090205481565b34801561052757600080fd5b506101c1610536366004612f60565b61124c565b6101c1610549366004612df3565b61131f565b6101c161055c366004612f83565b611991565b34801561056d57600080fd5b5061024a61057c366004612d4a565b600c60209081526000928352604080842090915290825290205481565b3480156105a557600080fd5b5061024a6105b4366004612cc2565b60066020526000908152604090205481565b3480156105d257600080fd5b5061024a6105e1366004612cc2565b600d6020526000908152604090205481565b3480156105ff57600080fd5b5061024a61060e366004612cc2565b600f6020526000908152604090205481565b6101c161062e366004612f83565b612041565b34801561063f57600080fd5b5061024a61064e366004612cc2565b600a6020526000908152604090205481565b60145460011461068b5760405162461bcd60e51b815260040161068290613034565b60405180910390fd5b600260145560015460405163599fbc6560e11b81526001600160a01b0384811660048301529091169063b33f78ca90602401602060405180830381865afa1580156106da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106fe9190613058565b61071b5760405163c043671760e01b815260040160405180910390fd5b8061072757600061072a565b60015b6001600160a01b03909216600090815260066020526040902060ff92909216909155506001601455565b6014546001146107765760405162461bcd60e51b815260040161068290613034565b60026014556001600160a01b0382166000908152600f6020908152604080832054600d90925290912054106107be57604051634e3e4dad60e11b815260040160405180910390fd5b6001600160a01b0382166000908152600d602052604081208054916107e283613087565b90915550506001600160a01b0382166000818152600e6020526040812080546340c10f199285929061081383613087565b909155506040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b15801561085d57600080fd5b505af1158015610871573d6000803e3d6000fd5b5050600160145550505050565b6014546001146108a05760405162461bcd60e51b815260040161068290613034565b600260145560015460405163599fbc6560e11b81526001600160a01b0387811660048301529091169063b33f78ca90602401602060405180830381865afa1580156108ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109139190613058565b6109305760405163c043671760e01b815260040160405180910390fd5b6001600160a01b0385166000908152600b6020908152604080832033845290915290205486116109735760405163ed39ba7f60e01b815260040160405180910390fd5b6001600160a01b0385166000908152600c60209081526040808320338452909152902054610eb9576001600160a01b0385166000908152600f6020908152604080832054600d90925290912054106109de57604051634e3e4dad60e11b815260040160405180910390fd5b604080517ff0f6f256599682b9387f45fc268ed696625f835d98d64b8967134239e103fc6c602082015290810189905260608101889052608081018790526001600160a01b0380871660a0830152851660c082015260009060e00160408051601f1981840301815291815281516020928301206001600160a01b03808a166000908152600785528381205460059095529290922054909350610a89928492169087908790879061279c565b506001600160a01b03851660009081526006602052604090205460011415610ab757610ab7338b8b8b612959565b6001600160a01b0385166000908152600d60205260408120805491610adb83613087565b90915550506001600160a01b038516600090815260096020526040812060018101548154919291610b0c908b6130a2565b610b1691906130c1565b90506001600160a01b038616610c4c5788341015610b4757604051633f943cc760e21b815260040160405180910390fd5b6002546040516000916001600160a01b03169083908381818185875af1925050503d8060008114610b94576040519150601f19603f3d011682016040523d82523d6000602084013e610b99565b606091505b5050905080610bbb57604051630303e72760e41b815260040160405180910390fd5b6001600160a01b03808916600090815260086020526040812054909116610be2848d6130e3565b604051600081818185875af1925050503d8060008114610c1e576040519150601f19603f3d011682016040523d82523d6000602084013e610c23565b606091505b5050905080610c455760405163799cbfed60e11b815260040160405180910390fd5b5050610eb2565b604051636eb1769f60e11b815233600482015230602482015289906001600160a01b0388169063dd62ed3e90604401602060405180830381865afa158015610c98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cbc9190613058565b1015610cdb57604051633f943cc760e21b815260040160405180910390fd5b6002546040516323b872dd60e01b81526000916001600160a01b03808a16926323b872dd92610d1392339291169087906004016130fa565b6020604051808303816000875af1158015610d32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d56919061311e565b905080610d7657604051630303e72760e41b815260040160405180910390fd5b6001600160a01b03888116600090815260086020526040902054818916916323b872dd91339116610da7868f6130e3565b6040518463ffffffff1660e01b8152600401610dc5939291906130fa565b6020604051808303816000875af1158015610de4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e08919061311e565b905080610e2857604051634191f0ed60e11b815260040160405180910390fd5b6001600160a01b0387166000908152600a602052604090205460011415610eb05760048054604051636da2661b60e01b81526001600160a01b0390911691636da2661b91610e7d9133918d91600091016130fa565b600060405180830381600087803b158015610e9757600080fd5b505af1158015610eab573d6000803e3d6000fd5b505050505b505b5050610eee565b6001600160a01b0385166000908152600c602090815260408083203384529091528120805491610ee88361313b565b91905055505b6001600160a01b0385166000908152600b602090815260408083203384529091528120805491610f1d83613087565b90915550506001600160a01b0385166000818152600e6020526040812080546340c10f1992339290610f4e83613087565b909155506040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b158015610f9857600080fd5b505af1158015610fac573d6000803e3d6000fd5b50506001601455505050505050505050505050565b601454600114610fe35760405162461bcd60e51b815260040161068290613034565b60026014556001600160a01b03821660008181526013602090815260408083208884528252808320549383526012825280832088845290915290205461102a908590613152565b111561104957604051634e3e4dad60e11b815260040160405180910390fd5b6001600160a01b03821660009081526012602090815260408083208784529091528120805485929061107c908490613152565b9091555050604051631740d57560e11b81526001600160a01b0382811660048301526024820186905260448201859052831690632e81aaea90606401600060405180830381600087803b1580156110d257600080fd5b505af11580156110e6573d6000803e3d6000fd5b50506001601455505050505050565b6014546001146111175760405162461bcd60e51b815260040161068290613034565b600260145560005b84811015610871578383828181106111395761113961316a565b9050602002013560126000846001600160a01b03166001600160a01b0316815260200190815260200160002060008888858181106111795761117961316a565b90506020020135815260200190815260200160002081905550816001600160a01b0316632e81aaea338888858181106111b4576111b461316a565b905060200201358787868181106111cd576111cd61316a565b6040516001600160e01b031960e088901b1681526001600160a01b039095166004860152602485019390935250602090910201356044820152606401600060405180830381600087803b15801561122357600080fd5b505af1158015611237573d6000803e3d6000fd5b505050508061124590613087565b905061111f565b60145460011461126e5760405162461bcd60e51b815260040161068290613034565b60026014556001600160a01b0381166000908152600d60209081526040808320859055600e90915281208390555b82811015611315576040516340c10f1960e01b8152336004820152602481018290526001600160a01b038316906340c10f1990604401600060405180830381600087803b1580156112ec57600080fd5b505af1158015611300573d6000803e3d6000fd5b505050508061130e90613087565b905061129c565b5050600160145550565b6014546001146113415760405162461bcd60e51b815260040161068290613034565b600260145560015460405163599fbc6560e11b81526001600160a01b0387811660048301529091169063b33f78ca90602401602060405180830381865afa158015611390573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113b49190613058565b6113d15760405163c043671760e01b815260040160405180910390fd5b6001600160a01b0385166000908152600f6020908152604080832054600d909252909120541061141457604051634e3e4dad60e11b815260040160405180910390fd5b6001600160a01b0385166000818152600b602090815260408083203380855290835281842054948452600c8352818420908452909152902054879161145891613152565b1061147657604051636e6d1a4560e01b815260040160405180910390fd5b604080517ff0f6f256599682b9387f45fc268ed696625f835d98d64b8967134239e103fc6c602082015290810189905260608101889052608081018790526001600160a01b0380871660a0830152851660c082015260009060e00160408051601f1981840301815291815281516020928301206001600160a01b03808a166000908152600785528381205460059095529290922054909350611521928492169087908790879061279c565b506001600160a01b0385166000908152600660205260409020546001141561154f5761154f338b8b8b612959565b6001600160a01b0385166000908152600c60209081526040808320338452909152812080549161157e83613087565b90915550506001600160a01b0385166000908152600d602052604081208054916115a783613087565b90915550506001600160a01b0385166000908152600960205260408120600181015481549192916115d8908b6130a2565b6115e291906130c1565b90506001600160a01b038616611718578834101561161357604051633f943cc760e21b815260040160405180910390fd5b6002546040516000916001600160a01b03169083908381818185875af1925050503d8060008114611660576040519150601f19603f3d011682016040523d82523d6000602084013e611665565b606091505b505090508061168757604051630303e72760e41b815260040160405180910390fd5b6001600160a01b038089166000908152600860205260408120549091166116ae848d6130e3565b604051600081818185875af1925050503d80600081146116ea576040519150601f19603f3d011682016040523d82523d6000602084013e6116ef565b606091505b50509050806117115760405163799cbfed60e11b815260040160405180910390fd5b505061197e565b604051636eb1769f60e11b815233600482015230602482015289906001600160a01b0388169063dd62ed3e90604401602060405180830381865afa158015611764573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117889190613058565b10156117a757604051633f943cc760e21b815260040160405180910390fd5b6002546040516323b872dd60e01b81526000916001600160a01b03808a16926323b872dd926117df92339291169087906004016130fa565b6020604051808303816000875af11580156117fe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611822919061311e565b90508061184257604051630303e72760e41b815260040160405180910390fd5b6001600160a01b03888116600090815260086020526040902054818916916323b872dd91339116611873868f6130e3565b6040518463ffffffff1660e01b8152600401611891939291906130fa565b6020604051808303816000875af11580156118b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118d4919061311e565b9050806118f457604051634191f0ed60e11b815260040160405180910390fd5b6001600160a01b0387166000908152600a60205260409020546001141561197c5760048054604051636da2661b60e01b81526001600160a01b0390911691636da2661b916119499133918d91600091016130fa565b600060405180830381600087803b15801561196357600080fd5b505af1158015611977573d6000803e3d6000fd5b505050505b505b5050600160145550505050505050505050565b6014546001146119b35760405162461bcd60e51b815260040161068290613034565b600260145560015460405163599fbc6560e11b81526001600160a01b0387811660048301529091169063b33f78ca90602401602060405180830381865afa158015611a02573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a269190613058565b611a435760405163c043671760e01b815260040160405180910390fd5b6001600160a01b03851660008181526013602090815260408083208a8452825280832054938352601282528083208a845290915290205410611a9857604051634e3e4dad60e11b815260040160405180910390fd5b6001600160a01b0385166000818152601060209081526040808320338085529083528184208b85528352818420549484526011835281842090845282528083208a84529091529020548891611aec91613152565b10611b0a57604051636e6d1a4560e01b815260040160405180910390fd5b604051600090611b4a907f0a52f6e0133eadd055cc5703844e676242c3b461d85fb7ce7f74becd7e40edd1908c908c908c908c908c908c90602001613180565b60408051601f1981840301815291815281516020928301206001600160a01b03808a166000908152600785528381205460059095529290922054909350611b9a928492169087908790879061279c565b506001600160a01b03851660009081526006602052604090205460011415611bc857611bc8338c8c8c612959565b6001600160a01b038516600090815260116020908152604080832033845282528083208984529091528120805491611bff83613087565b90915550506001600160a01b03851660009081526012602090815260408083208984529091528120805491611c3383613087565b90915550506001600160a01b038516600090815260096020526040812060018101548154919291611c64908c6130a2565b611c6e91906130c1565b90506001600160a01b038616611da45789341015611c9f57604051633f943cc760e21b815260040160405180910390fd5b6002546040516000916001600160a01b03169083908381818185875af1925050503d8060008114611cec576040519150601f19603f3d011682016040523d82523d6000602084013e611cf1565b606091505b5050905080611d1357604051630303e72760e41b815260040160405180910390fd5b6001600160a01b03808916600090815260086020526040812054909116611d3a848e6130e3565b604051600081818185875af1925050503d8060008114611d76576040519150601f19603f3d011682016040523d82523d6000602084013e611d7b565b606091505b5050905080611d9d5760405163799cbfed60e11b815260040160405180910390fd5b505061202d565b604051636eb1769f60e11b81523360048201523060248201528a906001600160a01b0388169063dd62ed3e90604401602060405180830381865afa158015611df0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e149190613058565b1015611e3357604051633f943cc760e21b815260040160405180910390fd5b6002546040516323b872dd60e01b81526000916001600160a01b03808a16926323b872dd92611e6b92339291169087906004016130fa565b6020604051808303816000875af1158015611e8a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611eae919061311e565b905080611ece57604051630303e72760e41b815260040160405180910390fd5b866001600160a01b03166323b872dd33600860008c6001600160a01b03166001600160a01b0316815260200190815260200160002060009054906101000a90046001600160a01b0316858f611f2391906130e3565b6040518463ffffffff1660e01b8152600401611f41939291906130fa565b6020604051808303816000875af1158015611f60573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f84919061311e565b905080611fa457604051634191f0ed60e11b815260040160405180910390fd5b6001600160a01b0387166000908152600a60205260409020546001141561202b5760048054604051636da2661b60e01b81526001600160a01b0390911691636da2661b91611ff89133918d918f91016130fa565b600060405180830381600087803b15801561201257600080fd5b505af1158015612026573d6000803e3d6000fd5b505050505b505b505060016014555050505050505050505050565b6014546001146120635760405162461bcd60e51b815260040161068290613034565b600260145560015460405163599fbc6560e11b81526001600160a01b0387811660048301529091169063b33f78ca90602401602060405180830381865afa1580156120b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120d69190613058565b6120f35760405163c043671760e01b815260040160405180910390fd5b6001600160a01b03851660009081526010602090815260408083203384528252808320898452909152902054871161213e5760405163ed39ba7f60e01b815260040160405180910390fd5b6001600160a01b038516600090815260116020908152604080832033845282528083208984529091529020546126b1576001600160a01b03851660008181526013602090815260408083208a8452825280832054938352601282528083208a8452909152902054106121c357604051634e3e4dad60e11b815260040160405180910390fd5b604051600090612203907f0a52f6e0133eadd055cc5703844e676242c3b461d85fb7ce7f74becd7e40edd1908c908c908c908c908c908c90602001613180565b60408051601f1981840301815291815281516020928301206001600160a01b03808a166000908152600785528381205460059095529290922054909350612253928492169087908790879061279c565b506001600160a01b0385166000908152600660205260409020546001141561228157612281338c8c8c612959565b6001600160a01b038516600090815260126020908152604080832089845290915281208054916122b083613087565b90915550506001600160a01b0385166000908152600960205260408120600181015481549192916122e1908c6130a2565b6122eb91906130c1565b90506001600160a01b038616612421578934101561231c57604051633f943cc760e21b815260040160405180910390fd5b6002546040516000916001600160a01b03169083908381818185875af1925050503d8060008114612369576040519150601f19603f3d011682016040523d82523d6000602084013e61236e565b606091505b505090508061239057604051630303e72760e41b815260040160405180910390fd5b6001600160a01b038089166000908152600860205260408120549091166123b7848e6130e3565b604051600081818185875af1925050503d80600081146123f3576040519150601f19603f3d011682016040523d82523d6000602084013e6123f8565b606091505b505090508061241a5760405163799cbfed60e11b815260040160405180910390fd5b50506126aa565b604051636eb1769f60e11b81523360048201523060248201528a906001600160a01b0388169063dd62ed3e90604401602060405180830381865afa15801561246d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124919190613058565b10156124b057604051633f943cc760e21b815260040160405180910390fd5b6002546040516323b872dd60e01b81526000916001600160a01b03808a16926323b872dd926124e892339291169087906004016130fa565b6020604051808303816000875af1158015612507573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061252b919061311e565b90508061254b57604051630303e72760e41b815260040160405180910390fd5b866001600160a01b03166323b872dd33600860008c6001600160a01b03166001600160a01b0316815260200190815260200160002060009054906101000a90046001600160a01b0316858f6125a091906130e3565b6040518463ffffffff1660e01b81526004016125be939291906130fa565b6020604051808303816000875af11580156125dd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612601919061311e565b90508061262157604051634191f0ed60e11b815260040160405180910390fd5b6001600160a01b0387166000908152600a6020526040902054600114156126a85760048054604051636da2661b60e01b81526001600160a01b0390911691636da2661b916126759133918d918f91016130fa565b600060405180830381600087803b15801561268f57600080fd5b505af11580156126a3573d6000803e3d6000fd5b505050505b505b50506126ee565b6001600160a01b0385166000908152601160209081526040808320338452825280832089845290915281208054916126e88361313b565b91905055505b6001600160a01b03851660009081526010602090815260408083203384528252808320898452909152812080549161272583613087565b90915550506040516340c10f1960e01b8152336004820152602481018790526001600160a01b038616906340c10f1990604401600060405180830381600087803b15801561277257600080fd5b505af1158015612786573d6000803e3d6000fd5b5050600160145550505050505050505050505050565b60405161190160f01b602082015260228101829052604281018790526000906062016040516020818303038152906040528051906020012090506127e9866001600160a01b03163b151590565b156128ec57604080516020810186905280820185905260f887901b6001600160f81b0319166060820152815160418183030181526061820192839052630b135d3f60e11b9092526001600160a01b03881691631626ba7e9161284f9185916065016131bc565b602060405180830381865afa15801561286c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128909190613219565b6001600160e01b031916631626ba7e60e01b146128e75760405162461bcd60e51b815260206004820152601560248201527413d11654d4d1564e8815539055551213d492569151605a1b6044820152606401610682565b612950565b856001600160a01b031661290282878787612a2b565b6001600160a01b0316146129505760405162461bcd60e51b815260206004820152601560248201527413d11654d4d1564e8815539055551213d492569151605a1b6044820152606401610682565b50505050505050565b6001600160a01b03841661296c57600080fd5b6040516bffffffffffffffffffffffff19606086901b1660208201526000906034016040516020818303038152906040528051906020012090506129e6848480806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250869250859150612bd49050565b612a245760405162461bcd60e51b815260206004820152600f60248201526e139bdd081dda1a5d195b1a5cdd1959608a1b6044820152606401610682565b5050505050565b60007f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0821115612aa85760405162461bcd60e51b815260206004820152602260248201527f4f4459535345593a20494e56414c49445f5349474e41545552455f535f56414c604482015261554560f01b6064820152608401610682565b8360ff16601b1480612abd57508360ff16601c145b612b145760405162461bcd60e51b815260206004820152602260248201527f4f4459535345593a20494e56414c49445f5349474e41545552455f565f56414c604482015261554560f01b6064820152608401610682565b6040805160008082526020820180845288905260ff871692820192909252606081018590526080810184905260019060a0016020604051602081039080840390855afa158015612b68573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116612bcb5760405162461bcd60e51b815260206004820152601a60248201527f4f4459535345593a20494e56414c49445f5349474e41545552450000000000006044820152606401610682565b95945050505050565b600082612be18584612bea565b14949350505050565b600081815b8451811015612c56576000858281518110612c0c57612c0c61316a565b60200260200101519050808311612c325760008381526020829052604090209250612c43565b600081815260208490526040902092505b5080612c4e81613087565b915050612bef565b509392505050565b80356001600160a01b0381168114612c7557600080fd5b919050565b8015158114612c8857600080fd5b50565b60008060408385031215612c9e57600080fd5b612ca783612c5e565b91506020830135612cb781612c7a565b809150509250929050565b600060208284031215612cd457600080fd5b612cdd82612c5e565b9392505050565b600080600060608486031215612cf957600080fd5b612d0284612c5e565b9250612d1060208501612c5e565b9150604084013590509250925092565b60008060408385031215612d3357600080fd5b612d3c83612c5e565b946020939093013593505050565b60008060408385031215612d5d57600080fd5b612d6683612c5e565b9150612d7460208401612c5e565b90509250929050565b600060208284031215612d8f57600080fd5b5035919050565b60008083601f840112612da857600080fd5b50813567ffffffffffffffff811115612dc057600080fd5b6020830191508360208260051b8501011115612ddb57600080fd5b9250929050565b803560ff81168114612c7557600080fd5b6000806000806000806000806000806101208b8d031215612e1357600080fd5b8a3567ffffffffffffffff811115612e2a57600080fd5b612e368d828e01612d96565b909b5099505060208b0135975060408b0135965060608b01359550612e5d60808c01612c5e565b9450612e6b60a08c01612c5e565b9350612e7960c08c01612de2565b925060e08b013591506101008b013590509295989b9194979a5092959850565b60008060008060808587031215612eaf57600080fd5b8435935060208501359250612ec660408601612c5e565b9150612ed460608601612c5e565b905092959194509250565b600080600080600060608688031215612ef757600080fd5b853567ffffffffffffffff80821115612f0f57600080fd5b612f1b89838a01612d96565b90975095506020880135915080821115612f3457600080fd5b50612f4188828901612d96565b9094509250612f54905060408701612c5e565b90509295509295909350565b60008060408385031215612f7357600080fd5b82359150612d7460208401612c5e565b60008060008060008060008060008060006101408c8e031215612fa557600080fd5b8b3567ffffffffffffffff811115612fbc57600080fd5b612fc88e828f01612d96565b909c509a505060208c0135985060408c0135975060608c0135965060808c01359550612ff660a08d01612c5e565b945061300460c08d01612c5e565b935061301260e08d01612de2565b92506101008c013591506101208c013590509295989b509295989b9093969950565b6020808252600a90820152695245454e5452414e435960b01b604082015260600190565b60006020828403121561306a57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b600060001982141561309b5761309b613071565b5060010190565b60008160001904831182151516156130bc576130bc613071565b500290565b6000826130de57634e487b7160e01b600052601260045260246000fd5b500490565b6000828210156130f5576130f5613071565b500390565b6001600160a01b039384168152919092166020820152604081019190915260600190565b60006020828403121561313057600080fd5b8151612cdd81612c7a565b60008161314a5761314a613071565b506000190190565b6000821982111561316557613165613071565b500190565b634e487b7160e01b600052603260045260246000fd5b96875260208701959095526040860193909352606085019190915260808401526001600160a01b0390811660a08401521660c082015260e00190565b82815260006020604081840152835180604085015260005b818110156131f0578581018301518582016060015282016131d4565b81811115613202576000606083870101525b50601f01601f191692909201606001949350505050565b60006020828403121561322b57600080fd5b81516001600160e01b031981168114612cdd57600080fdfea264697066735822122044c5f5e798bd7853471e45cc2e5a71b3d9322893f9a1acc7ad46a540c467698164736f6c634300080c0033608060405234801561001057600080fd5b50614135806100206000396000f3fe60806040523480156200001157600080fd5b50600436106200006a5760003560e01c806360596ba0146200006f578063634282af14620000a3578063b33f78ca14620000ba578063c8c4396714620000ec578063ce5539d21462000103578063dbb80e421462000157575b600080fd5b620000866200008036600462000a4d565b62000160565b6040516001600160a01b0390911681526020015b60405180910390f35b62000086620000b436600462000b04565b620003eb565b620000dd620000cb36600462000b1e565b60016020526000908152604090205481565b6040519081526020016200009a565b62000086620000fd36600462000a4d565b62000416565b620000866200011436600462000be7565b815160208184018101805160008252928201948201949094209190935281518083018401805192815290840192909301919091209152546001600160a01b031681565b600254620000dd565b6000806001600160a01b0316600088886040516200018092919062000c52565b90815260200160405180910390208686604051620001a092919062000c52565b908152604051908190036020019020546001600160a01b031614620001d8576040516359294bdd60e11b815260040160405180910390fd5b600060405180602001620001ec90620009c8565b6020820181038252601f19601f8201166040525090506000888888886040516020016200021d949392919062000c62565b604051602081830303815290604052805190602001209050808251602084016000f592508260008a8a6040516200025692919062000c52565b908152602001604051809103902088886040516200027692919062000c52565b908152604080516020928190038301902080546001600160a01b0319166001600160a01b0394851617905591851660008181526001928390529290922055633f2f5ee2338c8c8c8c8c8c8c620002cc4662000691565b620002e08e6001600160a01b0316620007af565b604051602001620002f5949392919062000cb7565b6040516020818303038152906040526040518863ffffffff1660e01b815260040162000328979695949392919062000d36565b600060405180830381600087803b1580156200034357600080fd5b505af115801562000358573d6000803e3d6000fd5b5050505086866040516200036e92919062000c52565b604051809103902089896040516200038892919062000c52565b604080519182900382206002546001600160a01b03881684526000602085015291830191909152907f4d436b5f0e18ba082306559a4bd1e56cf927a6b69716fa45737f88089d7e233c906060015b60405180910390a35050979650505050505050565b60028181548110620003fc57600080fd5b6000918252602090912001546001600160a01b0316905081565b6000806001600160a01b0316600088886040516200043692919062000c52565b908152602001604051809103902086866040516200045692919062000c52565b908152604051908190036020019020546001600160a01b0316146200048e576040516359294bdd60e11b815260040160405180910390fd5b600060405180602001620004a290620009d6565b6020820181038252601f19601f820116604052509050600088888888604051602001620004d3949392919062000c62565b604051602081830303815290604052805190602001209050808251602084016000f592508260008a8a6040516200050c92919062000c52565b908152602001604051809103902088886040516200052c92919062000c52565b908152604080516020928190038301902080546001600160a01b0319166001600160a01b0394851617905591851660008181526001928390529290922055633f2f5ee2338c8c8c8c8c8c8c620005824662000691565b620005968e6001600160a01b0316620007af565b604051602001620005ab949392919062000cb7565b6040516020818303038152906040526040518863ffffffff1660e01b8152600401620005de979695949392919062000d36565b600060405180830381600087803b158015620005f957600080fd5b505af11580156200060e573d6000803e3d6000fd5b5050505086866040516200062492919062000c52565b604051809103902089896040516200063e92919062000c52565b604080519182900382206002546001600160a01b03881684526001602085015291830191909152907f4d436b5f0e18ba082306559a4bd1e56cf927a6b69716fa45737f88089d7e233c90606001620003d6565b606081620006b65750506040805180820190915260018152600360fc1b602082015290565b8160005b8115620006e65780620006cd8162000dca565b9150620006de9050600a8362000dfe565b9150620006ba565b60008167ffffffffffffffff81111562000704576200070462000b3c565b6040519080825280601f01601f1916602001820160405280156200072f576020820181803683370190505b5090505b8415620007a7576200074760018362000e15565b915062000756600a8662000e2f565b6200076390603062000e46565b60f81b8183815181106200077b576200077b62000e61565b60200101906001600160f81b031916908160001a9053506200079f600a8662000dfe565b945062000733565b949350505050565b606081620007d75750506040805180820190915260048152630307830360e41b602082015290565b8160005b8115620007fe5780620007ee8162000dca565b915050600882901c9150620007db565b620007a78482606060006200081583600262000e77565b6200082290600262000e46565b67ffffffffffffffff8111156200083d576200083d62000b3c565b6040519080825280601f01601f19166020018201604052801562000868576020820181803683370190505b509050600360fc1b8160008151811062000886576200088662000e61565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110620008b857620008b862000e61565b60200101906001600160f81b031916908160001a9053506000620008de84600262000e77565b620008eb90600162000e46565b90505b60018111156200096d576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811062000923576200092362000e61565b1a60f81b8282815181106200093c576200093c62000e61565b60200101906001600160f81b031916908160001a90535060049490941c93620009658162000e99565b9050620008ee565b508315620009c15760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015260640160405180910390fd5b9392505050565b611ae88062000eb483390190565b611764806200299c83390190565b80356001600160a01b0381168114620009fc57600080fd5b919050565b60008083601f84011262000a1457600080fd5b50813567ffffffffffffffff81111562000a2d57600080fd5b60208301915083602082850101111562000a4657600080fd5b9250929050565b60008060008060008060006080888a03121562000a6957600080fd5b62000a7488620009e4565b9650602088013567ffffffffffffffff8082111562000a9257600080fd5b62000aa08b838c0162000a01565b909850965060408a013591508082111562000aba57600080fd5b62000ac88b838c0162000a01565b909650945060608a013591508082111562000ae257600080fd5b5062000af18a828b0162000a01565b989b979a50959850939692959293505050565b60006020828403121562000b1757600080fd5b5035919050565b60006020828403121562000b3157600080fd5b620009c182620009e4565b634e487b7160e01b600052604160045260246000fd5b600082601f83011262000b6457600080fd5b813567ffffffffffffffff8082111562000b825762000b8262000b3c565b604051601f8301601f19908116603f0116810190828211818310171562000bad5762000bad62000b3c565b8160405283815286602085880101111562000bc757600080fd5b836020870160208301376000602085830101528094505050505092915050565b6000806040838503121562000bfb57600080fd5b823567ffffffffffffffff8082111562000c1457600080fd5b62000c228683870162000b52565b9350602085013591508082111562000c3957600080fd5b5062000c488582860162000b52565b9150509250929050565b8183823760009101908152919050565b8385823760008482016000815283858237600093019283525090949350505050565b60005b8381101562000ca157818101518382015260200162000c87565b8381111562000cb1576000848401525b50505050565b83858237600084820160008152845162000cd681836020890162000c84565b602f60f81b9101818152845190919062000cf881600185016020890162000c84565b60019201918201526002019695505050505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6001600160a01b0388811682528716602082015260a06040820181905260009062000d65908301878962000d0d565b828103606084015262000d7a81868862000d0d565b90508281036080840152835180825262000d9c81602084016020880162000c84565b601f01601f1916016020019998505050505050505050565b634e487b7160e01b600052601160045260246000fd5b600060001982141562000de15762000de162000db4565b5060010190565b634e487b7160e01b600052601260045260246000fd5b60008262000e105762000e1062000de8565b500490565b60008282101562000e2a5762000e2a62000db4565b500390565b60008262000e415762000e4162000de8565b500690565b6000821982111562000e5c5762000e5c62000db4565b500190565b634e487b7160e01b600052603260045260246000fd5b600081600019048311821515161562000e945762000e9462000db4565b500290565b60008162000eab5762000eab62000db4565b50600019019056fe608060405234801561001057600080fd5b50611ac8806100206000396000f3fe608060405234801561001057600080fd5b50600436106101415760003560e01c80636c0360eb116100b8578063a22cb4651161007c578063a22cb465146102d6578063e2e784d5146102e9578063e8a3d485146102fc578063e985e9c514610304578063f242432a14610332578063f2fde38b1461034557600080fd5b80636c0360eb146102755780638da5cb5b1461027d578063938e3d7b146102a857806395d89b41146102bb5780639fbc8713146102c357600080fd5b80632e81aaea1161010a5780632e81aaea146101fe5780632eb2c2d6146102135780633f2f5ee21461022657806340c10f19146102395780634e1273f41461024c5780635df478381461026c57600080fd5b8062fdd58e1461014657806301ffc9a71461018157806306fdde03146101a45780630e89341c146101b95780632a55205a146101cc575b600080fd5b61016e610154366004611126565b600060208181529281526040808220909352908152205481565b6040519081526020015b60405180910390f35b61019461018f366004611169565b610358565b6040519015158152602001610178565b6101ac610383565b60405161017891906111e5565b6101ac6101c73660046111f8565b610411565b6101df6101da366004611211565b610445565b604080516001600160a01b039093168352602083019190915201610178565b61021161020c366004611233565b61047e565b005b6102116102213660046113b4565b6104c9565b6102116102343660046114a0565b610780565b610211610247366004611126565b610811565b61025f61025a36600461155b565b61085c565b6040516101789190611656565b61016e60085481565b6101ac61098a565b600354610290906001600160a01b031681565b6040516001600160a01b039091168152602001610178565b6102116102b6366004611669565b610997565b6101ac6109d5565b600954610290906001600160a01b031681565b6102116102e43660046116ba565b6109e2565b6102116102f7366004611126565b610a4e565b6101ac610ac6565b6101946103123660046116f6565b600160209081526000928352604080842090915290825290205460ff1681565b610211610340366004611729565b610ad3565b61021161035336600461178e565b610cca565b600063152a902d60e11b6001600160e01b03198316148061037d575061037d82610d3e565b92915050565b60048054610390906117a9565b80601f01602080910402602001604051908101604052809291908181526020018280546103bc906117a9565b80156104095780601f106103de57610100808354040283529160200191610409565b820191906000526020600020905b8154815290600101906020018083116103ec57829003601f168201915b505050505081565b6060600661041e83610d8c565b60405160200161042f929190611800565b6040516020818303038152906040529050919050565b60095460085460009182916001600160a01b0390911690610468612710866118bd565b61047291906118df565b915091505b9250929050565b6002546001600160a01b031633146104a95760405163154cdd6360e31b815260040160405180910390fd5b6104c483838360405180602001604052806000815250610eb5565b505050565b8251825181146105125760405162461bcd60e51b815260206004820152600f60248201526e0988a9c8ea890be9a92a69a82a8869608b1b60448201526064015b60405180910390fd5b336001600160a01b038716148061054c57506001600160a01b038616600090815260016020908152604080832033845290915290205460ff165b6105895760405162461bcd60e51b815260206004820152600e60248201526d1393d517d055551213d49256915160921b6044820152606401610509565b60005b8181101561065e5760008582815181106105a8576105a86118fe565b6020026020010151905060008583815181106105c6576105c66118fe565b60200260200101519050806000808b6001600160a01b03166001600160a01b03168152602001908152602001600020600084815260200190815260200160002060008282546106159190611914565b90915550506001600160a01b0388166000908152602081815260408083208584529091528120805483929061064b90849061192b565b90915550506001909201915061058c9050565b50846001600160a01b0316866001600160a01b0316336001600160a01b03167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb87876040516106ae929190611943565b60405180910390a46001600160a01b0385163b1561074f5760405163bc197c8160e01b808252906001600160a01b0387169063bc197c81906106fc9033908b908a908a908a90600401611968565b6020604051808303816000875af115801561071b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061073f91906119c6565b6001600160e01b0319161461075c565b6001600160a01b03851615155b6107785760405162461bcd60e51b8152600401610509906119e3565b505050505050565b60075460ff16156107a357604051620489d760e91b815260040160405180910390fd5b6007805460ff19166001179055600280546001600160a01b03808b166001600160a01b03199283161790925560038054928a16929091169190911790556107ec60048787610ffd565b506107f960058585610ffd565b5061080660068383610ffd565b505050505050505050565b6002546001600160a01b0316331461083c5760405163154cdd6360e31b815260040160405180910390fd5b6108588282600160405180602001604052806000815250610eb5565b5050565b815181516060919081146108a45760405162461bcd60e51b815260206004820152600f60248201526e0988a9c8ea890be9a92a69a82a8869608b1b6044820152606401610509565b835167ffffffffffffffff8111156108be576108be611266565b6040519080825280602002602001820160405280156108e7578160200160208202803683370190505b50915060005b818110156109825760008086838151811061090a5761090a6118fe565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000206000858381518110610946576109466118fe565b602002602001015181526020019081526020016000205483828151811061096f5761096f6118fe565b60209081029190910101526001016108ed565b505092915050565b60068054610390906117a9565b6003546001600160a01b031633146109c25760405163154cdd6360e31b815260040160405180910390fd5b805161085890600a906020840190611081565b60058054610390906117a9565b3360008181526001602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b6001600160a01b038216610a7557604051634ba7322360e11b815260040160405180910390fd5b6003546001600160a01b03163314610aa05760405163154cdd6360e31b815260040160405180910390fd5b600980546001600160a01b0319166001600160a01b039390931692909217909155600855565b600a8054610390906117a9565b336001600160a01b0386161480610b0d57506001600160a01b038516600090815260016020908152604080832033845290915290205460ff165b610b4a5760405162461bcd60e51b815260206004820152600e60248201526d1393d517d055551213d49256915160921b6044820152606401610509565b6001600160a01b03851660009081526020818152604080832086845290915281208054849290610b7b908490611914565b90915550506001600160a01b03841660009081526020818152604080832086845290915281208054849290610bb190849061192b565b909155505060408051848152602081018490526001600160a01b03808716929088169133917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62910160405180910390a46001600160a01b0384163b15610c9a5760405163f23a6e6160e01b808252906001600160a01b0386169063f23a6e6190610c479033908a90899089908990600401611a0d565b6020604051808303816000875af1158015610c66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c8a91906119c6565b6001600160e01b03191614610ca7565b6001600160a01b03841615155b610cc35760405162461bcd60e51b8152600401610509906119e3565b5050505050565b6001600160a01b038116610cf157604051634ba7322360e11b815260040160405180910390fd5b6003546001600160a01b03163314610d1c5760405163154cdd6360e31b815260040160405180910390fd5b600380546001600160a01b0319166001600160a01b0392909216919091179055565b60006301ffc9a760e01b6001600160e01b031983161480610d6f5750636cdb3d1360e11b6001600160e01b03198316145b8061037d5750506001600160e01b0319166303a24d0760e21b1490565b606081610db05750506040805180820190915260018152600360fc1b602082015290565b8160005b8115610dda5780610dc481611a52565b9150610dd39050600a836118bd565b9150610db4565b60008167ffffffffffffffff811115610df557610df5611266565b6040519080825280601f01601f191660200182016040528015610e1f576020820181803683370190505b509050815b8515610eac57610e35600182611914565b90506000610e44600a886118bd565b610e4f90600a6118df565b610e599088611914565b610e64906030611a6d565b905060008160f81b905080848481518110610e8157610e816118fe565b60200101906001600160f81b031916908160001a905350610ea3600a896118bd565b97505050610e24565b50949350505050565b6001600160a01b03841660009081526020818152604080832086845290915281208054849290610ee690849061192b565b909155505060408051848152602081018490526001600160a01b0386169160009133917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62910160405180910390a46001600160a01b0384163b15610fce5760405163f23a6e6160e01b808252906001600160a01b0386169063f23a6e6190610f7b903390600090899089908990600401611a0d565b6020604051808303816000875af1158015610f9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fbe91906119c6565b6001600160e01b03191614610fdb565b6001600160a01b03841615155b610ff75760405162461bcd60e51b8152600401610509906119e3565b50505050565b828054611009906117a9565b90600052602060002090601f01602090048101928261102b5760008555611071565b82601f106110445782800160ff19823516178555611071565b82800160010185558215611071579182015b82811115611071578235825591602001919060010190611056565b5061107d9291506110f5565b5090565b82805461108d906117a9565b90600052602060002090601f0160209004810192826110af5760008555611071565b82601f106110c857805160ff1916838001178555611071565b82800160010185558215611071579182015b828111156110715782518255916020019190600101906110da565b5b8082111561107d57600081556001016110f6565b80356001600160a01b038116811461112157600080fd5b919050565b6000806040838503121561113957600080fd5b6111428361110a565b946020939093013593505050565b6001600160e01b03198116811461116657600080fd5b50565b60006020828403121561117b57600080fd5b813561118681611150565b9392505050565b60005b838110156111a8578181015183820152602001611190565b83811115610ff75750506000910152565b600081518084526111d181602086016020860161118d565b601f01601f19169290920160200192915050565b60208152600061118660208301846111b9565b60006020828403121561120a57600080fd5b5035919050565b6000806040838503121561122457600080fd5b50508035926020909101359150565b60008060006060848603121561124857600080fd5b6112518461110a565b95602085013595506040909401359392505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156112a5576112a5611266565b604052919050565b600067ffffffffffffffff8211156112c7576112c7611266565b5060051b60200190565b600082601f8301126112e257600080fd5b813560206112f76112f2836112ad565b61127c565b82815260059290921b8401810191818101908684111561131657600080fd5b8286015b84811015611331578035835291830191830161131a565b509695505050505050565b600067ffffffffffffffff83111561135657611356611266565b611369601f8401601f191660200161127c565b905082815283838301111561137d57600080fd5b828260208301376000602084830101529392505050565b600082601f8301126113a557600080fd5b6111868383356020850161133c565b600080600080600060a086880312156113cc57600080fd5b6113d58661110a565b94506113e36020870161110a565b9350604086013567ffffffffffffffff8082111561140057600080fd5b61140c89838a016112d1565b9450606088013591508082111561142257600080fd5b61142e89838a016112d1565b9350608088013591508082111561144457600080fd5b5061145188828901611394565b9150509295509295909350565b60008083601f84011261147057600080fd5b50813567ffffffffffffffff81111561148857600080fd5b60208301915083602082850101111561047757600080fd5b60008060008060008060008060a0898b0312156114bc57600080fd5b6114c58961110a565b97506114d360208a0161110a565b9650604089013567ffffffffffffffff808211156114f057600080fd5b6114fc8c838d0161145e565b909850965060608b013591508082111561151557600080fd5b6115218c838d0161145e565b909650945060808b013591508082111561153a57600080fd5b506115478b828c0161145e565b999c989b5096995094979396929594505050565b6000806040838503121561156e57600080fd5b823567ffffffffffffffff8082111561158657600080fd5b818501915085601f83011261159a57600080fd5b813560206115aa6112f2836112ad565b82815260059290921b840181019181810190898411156115c957600080fd5b948201945b838610156115ee576115df8661110a565b825294820194908201906115ce565b9650508601359250508082111561160457600080fd5b50611611858286016112d1565b9150509250929050565b600081518084526020808501945080840160005b8381101561164b5781518752958201959082019060010161162f565b509495945050505050565b602081526000611186602083018461161b565b60006020828403121561167b57600080fd5b813567ffffffffffffffff81111561169257600080fd5b8201601f810184136116a357600080fd5b6116b28482356020840161133c565b949350505050565b600080604083850312156116cd57600080fd5b6116d68361110a565b9150602083013580151581146116eb57600080fd5b809150509250929050565b6000806040838503121561170957600080fd5b6117128361110a565b91506117206020840161110a565b90509250929050565b600080600080600060a0868803121561174157600080fd5b61174a8661110a565b94506117586020870161110a565b93506040860135925060608601359150608086013567ffffffffffffffff81111561178257600080fd5b61145188828901611394565b6000602082840312156117a057600080fd5b6111868261110a565b600181811c908216806117bd57607f821691505b602082108114156117de57634e487b7160e01b600052602260045260246000fd5b50919050565b600081516117f681856020860161118d565b9290920192915050565b600080845481600182811c91508083168061181c57607f831692505b602080841082141561183c57634e487b7160e01b86526022600452602486fd5b81801561185057600181146118615761188e565b60ff1986168952848901965061188e565b60008b81526020902060005b868110156118865781548b82015290850190830161186d565b505084890196505b50505050505061189e81856117e4565b95945050505050565b634e487b7160e01b600052601160045260246000fd5b6000826118da57634e487b7160e01b600052601260045260246000fd5b500490565b60008160001904831182151516156118f9576118f96118a7565b500290565b634e487b7160e01b600052603260045260246000fd5b600082821015611926576119266118a7565b500390565b6000821982111561193e5761193e6118a7565b500190565b604081526000611956604083018561161b565b828103602084015261189e818561161b565b6001600160a01b0386811682528516602082015260a0604082018190526000906119949083018661161b565b82810360608401526119a6818661161b565b905082810360808401526119ba81856111b9565b98975050505050505050565b6000602082840312156119d857600080fd5b815161118681611150565b60208082526010908201526f155394d0519157d49150d2541251539560821b604082015260600190565b6001600160a01b03868116825285166020820152604081018490526060810183905260a060808201819052600090611a47908301846111b9565b979650505050505050565b6000600019821415611a6657611a666118a7565b5060010190565b600060ff821660ff84168060ff03821115611a8a57611a8a6118a7565b01939250505056fea2646970667358221220b4968124272ff911d5ec7df4207835b0ca22b335b4cbed407e8b89ccef6260a364736f6c634300080c003360806040523480156200001157600080fd5b50604080516020808201808452600080845284519283019094528382528251929391926200004192919062000060565b5080516200005790600190602084019062000060565b50505062000143565b8280546200006e9062000106565b90600052602060002090601f016020900481019282620000925760008555620000dd565b82601f10620000ad57805160ff1916838001178555620000dd565b82800160010185558215620000dd579182015b82811115620000dd578251825591602001919060010190620000c0565b50620000eb929150620000ef565b5090565b5b80821115620000eb5760008155600101620000f0565b600181811c908216806200011b57607f821691505b602082108114156200013d57634e487b7160e01b600052602260045260246000fd5b50919050565b61161180620001536000396000f3fe608060405234801561001057600080fd5b50600436106101585760003560e01c806370a08231116100c3578063b88d4fde1161007c578063b88d4fde1461032a578063c87b56dd1461033d578063e2e784d514610350578063e8a3d48514610363578063e985e9c51461036b578063f2fde38b1461039957600080fd5b806370a08231146102b65780638da5cb5b146102d6578063938e3d7b146102e957806395d89b41146102fc5780639fbc871314610304578063a22cb4651461031757600080fd5b80633f2f5ee2116101155780633f2f5ee21461023557806340c10f191461024857806342842e0e1461025b5780635df478381461026e5780636352211e146102855780636c0360eb146102ae57600080fd5b806301ffc9a71461015d57806306fdde0314610185578063081812fc1461019a578063095ea7b3146101db57806323b872dd146101f05780632a55205a14610203575b600080fd5b61017061016b366004610f93565b6103ac565b60405190151581526020015b60405180910390f35b61018d6103d7565b60405161017c919061100f565b6101c36101a8366004611022565b6004602052600090815260409020546001600160a01b031681565b6040516001600160a01b03909116815260200161017c565b6101ee6101e9366004611057565b610465565b005b6101ee6101fe366004611081565b61054c565b6102166102113660046110bd565b610713565b604080516001600160a01b03909316835260208301919091520161017c565b6101ee610243366004611121565b61074c565b6101ee610256366004611057565b6107e4565b6101ee610269366004611081565b61081d565b61027760095481565b60405190815260200161017c565b6101c3610293366004611022565b6003602052600090815260409020546001600160a01b031681565b61018d610915565b6102776102c43660046111dc565b60026020526000908152604090205481565b6007546101c3906001600160a01b031681565b6101ee6102f7366004611283565b610922565b61018d610960565b600a546101c3906001600160a01b031681565b6101ee6103253660046112d4565b61096d565b6101ee610338366004611310565b6109d9565b61018d61034b366004611022565b610abe565b6101ee61035e366004611057565b610af2565b61018d610b6a565b61017061037936600461138c565b600560209081526000928352604080842090915290825290205460ff1681565b6101ee6103a73660046111dc565b610b77565b600063152a902d60e11b6001600160e01b0319831614806103d157506103d182610beb565b92915050565b600080546103e4906113bf565b80601f0160208091040260200160405190810160405280929190818152602001828054610410906113bf565b801561045d5780601f106104325761010080835404028352916020019161045d565b820191906000526020600020905b81548152906001019060200180831161044057829003601f168201915b505050505081565b6000818152600360205260409020546001600160a01b0316338114806104ae57506001600160a01b038116600090815260056020908152604080832033845290915290205460ff165b6104f05760405162461bcd60e51b815260206004820152600e60248201526d1393d517d055551213d49256915160921b60448201526064015b60405180910390fd5b60008281526004602052604080822080546001600160a01b0319166001600160a01b0387811691821790925591518593918516917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a4505050565b6000818152600360205260409020546001600160a01b038481169116146105a25760405162461bcd60e51b815260206004820152600a60248201526957524f4e475f46524f4d60b01b60448201526064016104e7565b6001600160a01b0382166105ec5760405162461bcd60e51b81526020600482015260116024820152701253959053125117d49150d25412515395607a1b60448201526064016104e7565b336001600160a01b038416148061061957506000818152600460205260409020546001600160a01b031633145b8061064757506001600160a01b038316600090815260056020908152604080832033845290915290205460ff165b6106845760405162461bcd60e51b815260206004820152600e60248201526d1393d517d055551213d49256915160921b60448201526064016104e7565b6001600160a01b0380841660008181526002602090815260408083208054600019019055938616808352848320805460010190558583526003825284832080546001600160a01b03199081168317909155600490925284832080549092169091559251849392917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b600a5460095460009182916001600160a01b039091169061073661271086611410565b6107409190611432565b915091505b9250929050565b600754600160a01b900460ff16156107775760405163017ea83f60e61b815260040160405180910390fd5b60078054600680546001600160a01b0319166001600160a01b038c811691909117909155600160a01b6001600160a81b0319909216908a16171790556107bf60008787610e6d565b506107cc60018585610e6d565b506107d960088383610e6d565b505050505050505050565b6006546001600160a01b0316331461080f5760405163661af03760e11b815260040160405180910390fd5b6108198282610c39565b5050565b61082883838361054c565b6001600160a01b0382163b15806108d15750604051630a85bd0160e11b8082523360048301526001600160a01b03858116602484015260448301849052608060648401526000608484015290919084169063150b7a029060a4016020604051808303816000875af11580156108a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108c59190611451565b6001600160e01b031916145b6109105760405162461bcd60e51b815260206004820152601060248201526f155394d0519157d49150d2541251539560821b60448201526064016104e7565b505050565b600880546103e4906113bf565b6007546001600160a01b0316331461094d5760405163661af03760e11b815260040160405180910390fd5b805161081990600b906020840190610ef1565b600180546103e4906113bf565b3360008181526005602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b6109e484848461054c565b6001600160a01b0383163b1580610a795750604051630a85bd0160e11b808252906001600160a01b0385169063150b7a0290610a2a90339089908890889060040161146e565b6020604051808303816000875af1158015610a49573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a6d9190611451565b6001600160e01b031916145b610ab85760405162461bcd60e51b815260206004820152601060248201526f155394d0519157d49150d2541251539560821b60448201526064016104e7565b50505050565b60606008610acb83610d44565b604051602001610adc9291906114c7565b6040516020818303038152906040529050919050565b6001600160a01b038216610b1957604051634cc66cdd60e11b815260040160405180910390fd5b6007546001600160a01b03163314610b445760405163661af03760e11b815260040160405180910390fd5b600a80546001600160a01b0319166001600160a01b039390931692909217909155600955565b600b80546103e4906113bf565b6001600160a01b038116610b9e57604051634cc66cdd60e11b815260040160405180910390fd5b6007546001600160a01b03163314610bc95760405163661af03760e11b815260040160405180910390fd5b600780546001600160a01b0319166001600160a01b0392909216919091179055565b60006301ffc9a760e01b6001600160e01b031983161480610c1c57506380ac58cd60e01b6001600160e01b03198316145b806103d15750506001600160e01b031916635b5e139f60e01b1490565b6001600160a01b038216610c835760405162461bcd60e51b81526020600482015260116024820152701253959053125117d49150d25412515395607a1b60448201526064016104e7565b6000818152600360205260409020546001600160a01b031615610cd95760405162461bcd60e51b815260206004820152600e60248201526d1053149150511657d3525395115160921b60448201526064016104e7565b6001600160a01b038216600081815260026020908152604080832080546001019055848352600390915280822080546001600160a01b0319168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b606081610d685750506040805180820190915260018152600360fc1b602082015290565b8160005b8115610d925780610d7c8161156e565b9150610d8b9050600a83611410565b9150610d6c565b60008167ffffffffffffffff811115610dad57610dad6111f7565b6040519080825280601f01601f191660200182016040528015610dd7576020820181803683370190505b509050815b8515610e6457610ded600182611589565b90506000610dfc600a88611410565b610e0790600a611432565b610e119088611589565b610e1c9060306115a0565b905060008160f81b905080848481518110610e3957610e396115c5565b60200101906001600160f81b031916908160001a905350610e5b600a89611410565b97505050610ddc565b50949350505050565b828054610e79906113bf565b90600052602060002090601f016020900481019282610e9b5760008555610ee1565b82601f10610eb45782800160ff19823516178555610ee1565b82800160010185558215610ee1579182015b82811115610ee1578235825591602001919060010190610ec6565b50610eed929150610f65565b5090565b828054610efd906113bf565b90600052602060002090601f016020900481019282610f1f5760008555610ee1565b82601f10610f3857805160ff1916838001178555610ee1565b82800160010185558215610ee1579182015b82811115610ee1578251825591602001919060010190610f4a565b5b80821115610eed5760008155600101610f66565b6001600160e01b031981168114610f9057600080fd5b50565b600060208284031215610fa557600080fd5b8135610fb081610f7a565b9392505050565b60005b83811015610fd2578181015183820152602001610fba565b83811115610ab85750506000910152565b60008151808452610ffb816020860160208601610fb7565b601f01601f19169290920160200192915050565b602081526000610fb06020830184610fe3565b60006020828403121561103457600080fd5b5035919050565b80356001600160a01b038116811461105257600080fd5b919050565b6000806040838503121561106a57600080fd5b6110738361103b565b946020939093013593505050565b60008060006060848603121561109657600080fd5b61109f8461103b565b92506110ad6020850161103b565b9150604084013590509250925092565b600080604083850312156110d057600080fd5b50508035926020909101359150565b60008083601f8401126110f157600080fd5b50813567ffffffffffffffff81111561110957600080fd5b60208301915083602082850101111561074557600080fd5b60008060008060008060008060a0898b03121561113d57600080fd5b6111468961103b565b975061115460208a0161103b565b9650604089013567ffffffffffffffff8082111561117157600080fd5b61117d8c838d016110df565b909850965060608b013591508082111561119657600080fd5b6111a28c838d016110df565b909650945060808b01359150808211156111bb57600080fd5b506111c88b828c016110df565b999c989b5096995094979396929594505050565b6000602082840312156111ee57600080fd5b610fb08261103b565b634e487b7160e01b600052604160045260246000fd5b600067ffffffffffffffff80841115611228576112286111f7565b604051601f8501601f19908116603f01168101908282118183101715611250576112506111f7565b8160405280935085815286868601111561126957600080fd5b858560208301376000602087830101525050509392505050565b60006020828403121561129557600080fd5b813567ffffffffffffffff8111156112ac57600080fd5b8201601f810184136112bd57600080fd5b6112cc8482356020840161120d565b949350505050565b600080604083850312156112e757600080fd5b6112f08361103b565b91506020830135801515811461130557600080fd5b809150509250929050565b6000806000806080858703121561132657600080fd5b61132f8561103b565b935061133d6020860161103b565b925060408501359150606085013567ffffffffffffffff81111561136057600080fd5b8501601f8101871361137157600080fd5b6113808782356020840161120d565b91505092959194509250565b6000806040838503121561139f57600080fd5b6113a88361103b565b91506113b66020840161103b565b90509250929050565b600181811c908216806113d357607f821691505b602082108114156113f457634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b60008261142d57634e487b7160e01b600052601260045260246000fd5b500490565b600081600019048311821515161561144c5761144c6113fa565b500290565b60006020828403121561146357600080fd5b8151610fb081610f7a565b6001600160a01b03858116825284166020820152604081018390526080606082018190526000906114a190830184610fe3565b9695505050505050565b600081516114bd818560208601610fb7565b9290920192915050565b600080845481600182811c9150808316806114e357607f831692505b602080841082141561150357634e487b7160e01b86526022600452602486fd5b818015611517576001811461152857611555565b60ff19861689528489019650611555565b60008b81526020902060005b8681101561154d5781548b820152908501908301611534565b505084890196505b50505050505061156581856114ab565b95945050505050565b6000600019821415611582576115826113fa565b5060010190565b60008282101561159b5761159b6113fa565b500390565b600060ff821660ff84168060ff038211156115bd576115bd6113fa565b019392505050565b634e487b7160e01b600052603260045260246000fdfea2646970667358221220656bcabb0c05365279531b6b2a8081bea28afcf6dca5cc18b1039fc9f513235964736f6c634300080c0033a2646970667358221220e3622b1f7a6e62df610252e2880c4e4d0ffa1828c6336a86a99b29c293e70e1664736f6c634300080c0033608060405234801561001057600080fd5b50600980546001600160a01b03191633179055611050806100326000396000f3fe608060405234801561001057600080fd5b50600436106100ea5760003560e01c8063a5a57d211161008c578063d20fea2511610066578063d20fea251461029c578063ee2f70a9146102af578063f2fde38b146102c2578063f60ba1b0146102d557600080fd5b8063a5a57d2114610214578063b03d554714610257578063d0c52c451461028957600080fd5b806326903838116100c8578063269038381461013b578063347f73a11461018657806360cf6c4c146101d45780638da5cb5b146101e957600080fd5b806304c15e50146100ef5780630db9a5d5146101155780631f4042a214610128575b600080fd5b6101026100fd366004610d9a565b6102e8565b6040519081526020015b60405180910390f35b610102610123366004610d9a565b61037d565b610102610136366004610d9a565b610407565b60005460015460025460035460045460055461015995949392919086565b604080519687526020870195909552938501929092526060840152608083015260a082015260c00161010c565b610159610194366004610dd6565b6007602090815260009283526040808420909152908252902080546001820154600283015460038401546004850154600590950154939492939192909186565b6101e76101e2366004610e00565b610491565b005b6009546101fc906001600160a01b031681565b6040516001600160a01b03909116815260200161010c565b610159610222366004610e5c565b600660205260009081526040902080546001820154600283015460038401546004850154600590950154939492939192909186565b61026a610265366004610e77565b6105ea565b604080516001600160a01b03909316835260208301919091520161010c565b6101e7610297366004610e90565b610622565b6101026102aa366004610d9a565b61076a565b6101e76102bd366004610ee3565b6107f4565b6101e76102d0366004610e5c565b61086a565b6101026102e3366004610d9a565b6108b7565b6000806000806102f9878787610941565b9250925092508215610333576001600160a01b038616600090815260066020526040902054610329908290610f3c565b9350505050610376565b8115610368576001600160a01b0386166000908152600760209081526040808320888452909152902054610329908290610f3c565b600054610329908290610f3c565b9392505050565b60008060008061038e878787610941565b92509250925082156103c1576001600160a01b038616600090815260066020526040902060030154610329908290610f3c565b81156103f9576001600160a01b0386166000908152600760209081526040808320888452909152902060030154610329908290610f3c565b600354610329908290610f3c565b600080600080610418878787610941565b925092509250821561044b576001600160a01b038616600090815260066020526040902060040154610329908290610f3c565b8115610483576001600160a01b0386166000908152600760209081526040808320888452909152902060040154610329908290610f3c565b600454610329908290610f3c565b6009546001600160a01b031633146104bc57604051633d5d2fc360e21b815260040160405180910390fd5b600860405180604001604052808a6001600160a01b0316815260200189815250908060018154018082558091505060019003906000526020600020906002020160009091909190915060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506020820151816001015550506040518060c0016040528087815260200186815260200185815260200184815260200183815260200182815250600760008a6001600160a01b03166001600160a01b031681526020019081526020016000206000898152602001908152602001600020600082015181600001556020820151816001015560408201518160020155606082015181600301556080820151816004015560a082015181600501559050505050505050505050565b600881815481106105fa57600080fd5b6000918252602090912060029091020180546001909101546001600160a01b03909116915082565b6009546001600160a01b0316331461064d57604051633d5d2fc360e21b815260040160405180910390fd5b60086040518060400160405280896001600160a01b031681526020016000815250908060018154018082558091505060019003906000526020600020906002020160009091909190915060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506020820151816001015550506040518060c001604052808781526020018681526020018581526020018481526020018381526020018281525060066000896001600160a01b03166001600160a01b03168152602001908152602001600020600082015181600001556020820151816001015560408201518160020155606082015181600301556080820151816004015560a0820151816005015590505050505050505050565b60008060008061077b878787610941565b92509250925082156107ae576001600160a01b038616600090815260066020526040902060010154610329908290610f3c565b81156107e6576001600160a01b0386166000908152600760209081526040808320888452909152902060010154610329908290610f3c565b600154610329908290610f3c565b6009546001600160a01b0316331461081f57604051633d5d2fc360e21b815260040160405180910390fd5b6040805160c08101825287815260208101879052908101859052606081018490526080810183905260a001819052600095909555600193909355600291909155600355600455600555565b6009546001600160a01b0316331461089557604051633d5d2fc360e21b815260040160405180910390fd5b600980546001600160a01b0319166001600160a01b0392909216919091179055565b6000806000806108c8878787610941565b92509250925082156108fb576001600160a01b038616600090815260066020526040902060020154610329908290610f3c565b8115610933576001600160a01b0386166000908152600760209081526040808320888452909152902060020154610329908290610f3c565b600254610329908290610f3c565b6000806000806008805480602002602001604051908101604052809291908181526020016000905b828210156109b1576000848152602090819020604080518082019091526002850290910180546001600160a01b03168252600190810154828401529083529092019101610969565b50505050905060005b8151811015610c0d5760008282815181106109d7576109d7610f5b565b60209081029190910101518051909150610a01906001600160a01b03166380ac58cd60e01b610c3d565b15610ad25780516040516370a0823160e01b81526001600160a01b038b8116600483015260009216906370a0823190602401602060405180830381865afa158015610a50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a749190610f71565b1115610ab35780516001600160a01b031660009081526006602052604090206005015460018111610aa55784610aaf565b610aaf8186610f8a565b9450505b80516001600160a01b0389811691161415610acd57600195505b610bfa565b8051610aee906001600160a01b0316636cdb3d1360e11b610c3d565b15610bfa5780516020820151604051627eeac760e11b81526000926001600160a01b03169162fdd58e91610b3a918e916004016001600160a01b03929092168252602082015260400190565b602060405180830381865afa158015610b57573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b7b9190610f71565b1115610bfa5780516001600160a01b0316600090815260076020908152604080832082850151845290915290206005015460018111610bba5784610bc4565b610bc48186610f8a565b945081600001516001600160a01b0316896001600160a01b0316148015610bee5750816020015188145b15610bf857600195505b505b5080610c0581610fa2565b9150506109ba565b508115610c1a5781610c1e565b6005545b915060048211610c2e5781610c31565b60045b91505093509350939050565b6000610c4883610c62565b8015610c595750610c598383610c95565b90505b92915050565b6000610c75826301ffc9a760e01b610c95565b8015610c5c5750610c8e826001600160e01b0319610c95565b1592915050565b604080516001600160e01b0319831660248083019190915282518083039091018152604490910182526020810180516001600160e01b03166301ffc9a760e01b179052905160009190829081906001600160a01b0387169061753090610cfc908690610fbd565b6000604051808303818686fa925050503d8060008114610d38576040519150601f19603f3d011682016040523d82523d6000602084013e610d3d565b606091505b5091509150602081511015610d585760009350505050610c5c565b818015610d74575080806020019051810190610d749190610ff8565b9695505050505050565b80356001600160a01b0381168114610d9557600080fd5b919050565b600080600060608486031215610daf57600080fd5b610db884610d7e565b9250610dc660208501610d7e565b9150604084013590509250925092565b60008060408385031215610de957600080fd5b610df283610d7e565b946020939093013593505050565b600080600080600080600080610100898b031215610e1d57600080fd5b610e2689610d7e565b9a60208a01359a5060408a013599606081013599506080810135985060a0810135975060c0810135965060e00135945092505050565b600060208284031215610e6e57600080fd5b610c5982610d7e565b600060208284031215610e8957600080fd5b5035919050565b600080600080600080600060e0888a031215610eab57600080fd5b610eb488610d7e565b9960208901359950604089013598606081013598506080810135975060a0810135965060c00135945092505050565b60008060008060008060c08789031215610efc57600080fd5b505084359660208601359650604086013595606081013595506080810135945060a0013592509050565b634e487b7160e01b600052601160045260246000fd5b6000816000190483118215151615610f5657610f56610f26565b500290565b634e487b7160e01b600052603260045260246000fd5b600060208284031215610f8357600080fd5b5051919050565b60008219821115610f9d57610f9d610f26565b500190565b6000600019821415610fb657610fb6610f26565b5060010190565b6000825160005b81811015610fde5760208186018101518583015201610fc4565b81811115610fed576000828501525b509190910192915050565b60006020828403121561100a57600080fd5b8151801515811461037657600080fdfea264697066735822122009a080f3be075aa0df4961e80e47ff436674980a2de4f97601da32398f8865ea64736f6c634300080c00336101006040523480156200001257600080fd5b50604051620018ce380380620018ce833981016040819052620000359162000269565b604080518082018252600a81526904f6479737365792058560b41b602080830191825283518085019094526002845261058560f41b9084015281519192916000916200008491839190620001aa565b5081516200009a906001906020850190620001aa565b5060ff81166080524660a052620000b06200010e565b60c0525050506001600160a01b0394851660e052600a80549486166001600160a01b031995861617905560068054938616938516939093179092556007805491851691841691909117905560088054919093169116179055620003ca565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f600060405162000142919062000326565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b828054620001b890620002e9565b90600052602060002090601f016020900481019282620001dc576000855562000227565b82601f10620001f757805160ff191683800117855562000227565b8280016001018555821562000227579182015b82811115620002275782518255916020019190600101906200020a565b506200023592915062000239565b5090565b5b808211156200023557600081556001016200023a565b6001600160a01b03811681146200026657600080fd5b50565b600080600080600060a086880312156200028257600080fd5b85516200028f8162000250565b6020870151909550620002a28162000250565b6040870151909450620002b58162000250565b6060870151909350620002c88162000250565b6080870151909250620002db8162000250565b809150509295509295909350565b600181811c90821680620002fe57607f821691505b602082108114156200032057634e487b7160e01b600052602260045260246000fd5b50919050565b600080835481600182811c9150808316806200034357607f831692505b60208084108214156200036457634e487b7160e01b86526022600452602486fd5b8180156200037b57600181146200038d57620003bc565b60ff19861689528489019650620003bc565b60008a81526020902060005b86811015620003b45781548b82015290850190830162000399565b505084890196505b509498975050505050505050565b60805160a05160c05160e0516114b562000419600039600081816102c701528181610e1201528181610efa0152610f8c01526000610632015260006105fd0152600061028601526114b56000f3fe608060405234801561001057600080fd5b50600436106101e55760003560e01c80637b626f2c1161010f578063c41c2f24116100a2578063dd62ed3e11610071578063dd62ed3e14610458578063f11a3fcb14610483578063f2fde38b146104bf578063f887ea40146104d257600080fd5b8063c41c2f241461040c578063cbe52ae31461041f578063d2f7265a14610432578063d505accf1461044557600080fd5b8063a9059cbb116100de578063a9059cbb146103d0578063be040fb0146103de578063be204492146103e6578063c0d78655146103f957600080fd5b80637b626f2c146103825780637ecebe00146103955780638da5cb5b146103b557806395d89b41146103c857600080fd5b80633644e5151161018757806367b1f5df1161015657806367b1f5df146103295780636da2661b1461033c5780636e91818a1461034f57806370a082311461036257600080fd5b80633644e515146102ba57806338d52e0f146102c257806343c60544146103015780634c50f2841461031657600080fd5b806318160ddd116101c357806318160ddd1461023e57806323b872dd1461024757806330adf81f1461025a578063313ce5671461028157600080fd5b8063041874c6146101ea57806306fdde0314610206578063095ea7b31461021b575b600080fd5b6101f360095481565b6040519081526020015b60405180910390f35b61020e6104e5565b6040516101fd919061118e565b61022e6102293660046111ff565b610573565b60405190151581526020016101fd565b6101f360025481565b61022e610255366004611229565b6105df565b6101f37f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b6102a87f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff90911681526020016101fd565b6101f36105f9565b6102e97f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016101fd565b61031461030f366004611229565b610654565b005b610314610324366004611229565b610705565b610314610337366004611265565b61076a565b61031461034a366004611229565b6107b7565b61031461035d366004611265565b61081c565b6101f3610370366004611265565b60036020526000908152604090205481565b610314610390366004611229565b610869565b6101f36103a3366004611265565b60056020526000908152604090205481565b6008546102e9906001600160a01b031681565b61020e6108cd565b61022e6102553660046111ff565b6101f36108da565b6103146103f4366004611229565b610925565b610314610407366004611265565b61098a565b600a546102e9906001600160a01b031681565b6101f361042d3660046111ff565b6109d7565b6007546102e9906001600160a01b031681565b610314610453366004611280565b610a10565b6101f36104663660046112f3565b600460209081526000928352604080842090915290825290205481565b6104aa610491366004611265565b600b602052600090815260409020805460019091015482565b604080519283526020830191909152016101fd565b6103146104cd366004611265565b610c66565b6006546102e9906001600160a01b031681565b600080546104f290611326565b80601f016020809104026020016040519081016040528092919081815260200182805461051e90611326565b801561056b5780601f106105405761010080835404028352916020019161056b565b820191906000526020600020905b81548152906001019060200180831161054e57829003601f168201915b505050505081565b3360008181526004602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906105ce9086815260200190565b60405180910390a350600192915050565b600060405162db810f60e11b815260040160405180910390fd5b60007f0000000000000000000000000000000000000000000000000000000000000000461461062f5761062a610cb3565b905090565b507f000000000000000000000000000000000000000000000000000000000000000090565b6006546001600160a01b0316331461067f57604051637f7f876960e01b815260040160405180910390fd5b600a54604051630f60ba1b60e41b81526106ff9185916001600160a01b039091169063f60ba1b0906106b990849088908890600401611361565b602060405180830381865afa1580156106d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106fa9190611385565b610d4d565b50505050565b6007546001600160a01b0316331461073057604051637f7f876960e01b815260040160405180910390fd5b600a5460405163d20fea2560e01b81526106ff9185916001600160a01b039091169063d20fea25906106b990849088908890600401611361565b6008546001600160a01b0316331461079557604051637f7f876960e01b815260040160405180910390fd5b600780546001600160a01b0319166001600160a01b0392909216919091179055565b6006546001600160a01b031633146107e257604051637f7f876960e01b815260040160405180910390fd5b600a54604051630fa0215160e11b81526106ff9185916001600160a01b0390911690631f4042a2906106b990849088908890600401611361565b6008546001600160a01b0316331461084757604051637f7f876960e01b815260040160405180910390fd5b600a80546001600160a01b0319166001600160a01b0392909216919091179055565b6007546001600160a01b0316331461089457604051637f7f876960e01b815260040160405180910390fd5b600a54604051624c15e560e41b81526106ff9185916001600160a01b03909116906304c15e50906106b990849088908890600401611361565b600180546104f290611326565b3360008181526003602052604081205490916108f690826109d7565b9150816109165760405163385f6b1160e11b815260040160405180910390fd5b610921338383610dfb565b5090565b6007546001600160a01b0316331461095057604051637f7f876960e01b815260040160405180910390fd5b600a54604051630db9a5d560e01b81526106ff9185916001600160a01b0390911690630db9a5d5906106b990849088908890600401611361565b6008546001600160a01b031633146109b557604051637f7f876960e01b815260040160405180910390fd5b600680546001600160a01b0319166001600160a01b0392909216919091179055565b6002546000908015806109e8575082155b610a0557610a006109f885610f6a565b849083611044565b610a08565b60005b949350505050565b42841015610a655760405162461bcd60e51b815260206004820152601760248201527f5045524d49545f444541444c494e455f4558504952454400000000000000000060448201526064015b60405180910390fd5b6000610a6f6105f9565b6001600160a01b0389811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938c166060840152608083018b905260a083019390935260c08083018a90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f198184030181528282528051602091820120600080855291840180845281905260ff88169284019290925260608301869052608083018590529092509060019060a0016020604051602081039080840390855afa158015610b88573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811615801590610bbe5750886001600160a01b0316816001600160a01b0316145b610bfb5760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152606401610a5c565b6001600160a01b0390811660009081526004602090815260408083208b8516808552908352928190208a905551898152919350918a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b6008546001600160a01b03163314610c9157604051637f7f876960e01b815260040160405180910390fd5b600880546001600160a01b0319166001600160a01b0392909216919091179055565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051610ce5919061139e565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6001600160a01b03821660009081526003602052604081205481610d7185836109d7565b9250821115610d8a57610d85848383610dfb565b610daf565b80610daf576009546001600160a01b0385166000908152600b60205260409020600101555b610db98484611063565b604080518381526020810185905233917f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f910160405180910390a25092915050565b6040516370a0823160e01b815230600482015282907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015610e61573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e859190611385565b610e8f9190611450565b6001600160a01b0384166000908152600b6020526040902055600954610eb6908390611467565b6001600160a01b0384166000908152600b602052604081206001019190915560098054849290610ee7908490611467565b90915550610f2190506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001684846110ce565b60408051838152602081018390526001600160a01b038516917fe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a929910160405180910390a2505050565b6040516370a0823160e01b815230600482015260009081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015610fd3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ff79190611385565b6001600160a01b0384166000908152600b60205260409020805460019091015460095492935090916110299190611450565b6110339083611467565b61103d9190611450565b9392505050565b82820281151584158583048514171661105c57600080fd5b0492915050565b80600260008282546110759190611467565b90915550506001600160a01b0382166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b600060405163a9059cbb60e01b81526001600160a01b03841660048201528260248201526000806044836000895af191505061110981611147565b6106ff5760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606401610a5c565b60003d8261115957806000803e806000fd5b80602081146111715780156111825760009250611187565b816000803e60005115159250611187565b600192505b5050919050565b600060208083528351808285015260005b818110156111bb5785810183015185820160400152820161119f565b818111156111cd576000604083870101525b50601f01601f1916929092016040019392505050565b80356001600160a01b03811681146111fa57600080fd5b919050565b6000806040838503121561121257600080fd5b61121b836111e3565b946020939093013593505050565b60008060006060848603121561123e57600080fd5b611247846111e3565b9250611255602085016111e3565b9150604084013590509250925092565b60006020828403121561127757600080fd5b61103d826111e3565b600080600080600080600060e0888a03121561129b57600080fd5b6112a4886111e3565b96506112b2602089016111e3565b95506040880135945060608801359350608088013560ff811681146112d657600080fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561130657600080fd5b61130f836111e3565b915061131d602084016111e3565b90509250929050565b600181811c9082168061133a57607f821691505b6020821081141561135b57634e487b7160e01b600052602260045260246000fd5b50919050565b6001600160a01b039384168152919092166020820152604081019190915260600190565b60006020828403121561139757600080fd5b5051919050565b600080835481600182811c9150808316806113ba57607f831692505b60208084108214156113da57634e487b7160e01b86526022600452602486fd5b8180156113ee57600181146113ff5761142c565b60ff1986168952848901965061142c565b60008a81526020902060005b868110156114245781548b82015290850190830161140b565b505084890196505b509498975050505050505050565b634e487b7160e01b600052601160045260246000fd5b6000828210156114625761146261143a565b500390565b6000821982111561147a5761147a61143a565b50019056fea2646970667358221220a19885a55e8b0c417655f6a05466c45748cc67f3b8912f000ff31a4443743de164736f6c634300080c0033000000000000000000000000cb7ec73fd2bca2596794804a6ef5475d0dc4f42700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f00000000000000000000000064aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d500000000000000000000000004906695d6d12cf5459975d7c3c03356e4ccd460
Deployed Bytecode
0x6080604052600436106102305760003560e01c8063a41a964a1161012e578063c5936954116100ab578063d425f3cf1161006f578063d425f3cf1461077b578063dd55ebdc146107a8578063de8f21db146107d5578063e3d9a96d146107e8578063f0f442601461081557600080fd5b8063c5936954146106c1578063c67596f2146106e1578063c6aa2fb614610719578063c83dd23114610746578063cf6344391461075b57600080fd5b8063af91e8c9116100f2578063af91e8c91461063b578063b82263ca1461065b578063b98a44521461066e578063c188e3951461069b578063c4dd9ab7146106ae57600080fd5b8063a41a964a1461053d578063a51ffa4c14610571578063a88e6ce9146105ba578063aaa91a9f146105cd578063ac6390a41461060557600080fd5b80635f90e51a116101bc578063809a9f2f11610180578063809a9f2f1461048657806384f6f617146104a65780638bbdc1dd146104c55780638eb346c1146104d857806390135fe41461051057600080fd5b80635f90e51a146103b05780636dc05dbc146103d05780636e6e13ac146103f0578063704b6c02146104285780637823d3151461044857600080fd5b80633bf95ee8116102035780633bf95ee8146102ea578063474361331461032c5780634d32ce2c1461033f5780634d9e7059146103525780635b8540611461037257600080fd5b80630c424284146102355780630f7c4bfc1461025757806314afd79e146102945780632157e77e146102ca575b600080fd5b34801561024157600080fd5b50610255610250366004612604565b610835565b005b34801561026357600080fd5b5061027761027236600461269a565b610931565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156102a057600080fd5b506102776102af36600461277c565b6007602052600090815260409020546001600160a01b031681565b3480156102d657600080fd5b506102776102e53660046127ab565b610c52565b3480156102f657600080fd5b5061031e7f0a52f6e0133eadd055cc5703844e676242c3b461d85fb7ce7f74becd7e40edd181565b60405190815260200161028b565b61025561033a36600461289c565b611077565b61025561034d36600461289c565b611307565b34801561035e57600080fd5b5061025561036d36600461291d565b61156b565b34801561037e57600080fd5b5061031e61038d3660046129a0565b601060209081526000938452604080852082529284528284209052825290205481565b3480156103bc57600080fd5b506102556103cb3660046129e1565b611634565b3480156103dc57600080fd5b506102556103eb36600461277c565b61168a565b3480156103fc57600080fd5b5061031e61040b3660046129e1565b601360209081526000928352604080842090915290825290205481565b34801561043457600080fd5b5061025561044336600461277c565b61171c565b34801561045457600080fd5b5061031e6104633660046129a0565b601160209081526000938452604080852082529284528284209052825290205481565b34801561049257600080fd5b506102556104a1366004612a0d565b6117ae565b3480156104b257600080fd5b506102776104c1366004612a46565b5490565b6102556104d336600461289c565b6118a5565b3480156104e457600080fd5b5061031e6104f3366004612a0d565b600b60209081526000928352604080842090915290825290205481565b34801561051c57600080fd5b5061031e61052b36600461277c565b60056020526000908152604090205481565b34801561054957600080fd5b5061031e7ff0f6f256599682b9387f45fc268ed696625f835d98d64b8967134239e103fc6c81565b34801561057d57600080fd5b506105a561058c36600461277c565b6009602052600090815260409020805460019091015482565b6040805192835260208301919091520161028b565b6102556105c8366004612a70565b611b09565b3480156105d957600080fd5b5061031e6105e83660046129e1565b601260209081526000928352604080842090915290825290205481565b34801561061157600080fd5b5061027761062036600461277c565b6008602052600090815260409020546001600160a01b031681565b34801561064757600080fd5b50610255610656366004612b1a565b611bd4565b61025561066936600461289c565b611ce0565b34801561067a57600080fd5b5061031e61068936600461277c565b600e6020526000908152604090205481565b6102556106a9366004612a70565b611f6c565b6102556106bc366004612b64565b611fd4565b3480156106cd57600080fd5b506102556106dc366004612a0d565b6120a2565b3480156106ed57600080fd5b5061031e6106fc366004612a0d565b600c60209081526000928352604080842090915290825290205481565b34801561072557600080fd5b5061031e61073436600461277c565b60066020526000908152604090205481565b34801561075257600080fd5b50610277612176565b34801561076757600080fd5b50610255610776366004612a0d565b612186565b34801561078757600080fd5b5061031e61079636600461277c565b600d6020526000908152604090205481565b3480156107b457600080fd5b5061031e6107c336600461277c565b600f6020526000908152604090205481565b6102556107e3366004612b64565b61225a565b3480156107f457600080fd5b5061031e61080336600461277c565b600a6020526000908152604090205481565b34801561082157600080fd5b5061025561083036600461277c565b6122c4565b6001600160a01b0382811660009081526007602052604090205416331461086f57604051630862a88160e21b815260040160405180910390fd5b600080546040516001600160a01b03858116602483015284151560448301529091169060640160408051601f198184030181529181526020820180516001600160e01b031663031090a160e21b179052516108ca9190612c19565b600060405180830381855af49150503d8060008114610905576040519150601f19603f3d011682016040523d82523d6000602084013e61090a565b606091505b505090508061092c57604051637f77015160e01b815260040160405180910390fd5b505050565b600061093b612176565b6001600160a01b031663c8c43967338e8e8e8e8e8e6040518863ffffffff1660e01b81526004016109729796959493929190612c7d565b6020604051808303816000875af1158015610991573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109b59190612cd8565b6001600160a01b038116600090815260076020526040902080546001600160a01b03191633179055905085156109eb57856109ef565b6000195b6001600160a01b0382166000908152600f602052604090205581610a14576000610a17565b60015b6001600160a01b03828116600081815260066020908152604080832060ff96909616909555600890529290922080546001600160a01b0319169186169190911790557f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f90610a8490612356565b80516020918201206040805192830193909352918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201526001600160a01b03821660a082015260c00160408051601f1981840301815291815281516020928301206001600160a01b03841660009081526005909352912055610b14846123b2565b15610b47576001600160a01b03811660009081526009602090815260409091208535815590850135600182015550610b83565b60408051808201825260038152606460208083019182526001600160a01b03851660009081526009909152929092209051815590516001909101555b8415610c435760008054604051602481018890526001600160a01b0384811660448301528392169060640160408051601f198184030181529181526020820180516001600160e01b0316630bc2051f60e41b17905251610be39190612c19565b600060405180830381855af49150503d8060008114610c1e576040519150601f19603f3d011682016040523d82523d6000602084013e610c23565b606091505b509150915081610c40578051610c3857600080fd5b805181602001fd5b50505b9b9a5050505050505050505050565b6000610c616020860186612cf5565b9050610c6d8680612cf5565b905014610c8d5760405163161b1f5b60e01b815260040160405180910390fd5b610c95612176565b6001600160a01b03166360596ba0338d8d8d8d8d8d6040518863ffffffff1660e01b8152600401610ccc9796959493929190612c7d565b6020604051808303816000875af1158015610ceb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d0f9190612cd8565b6001600160a01b038116600090815260076020526040902080546001600160a01b03191633179055905081610d45576000610d48565b60015b6001600160a01b03828116600090815260066020908152604080832060ff959095169094556008905291822080546001600160a01b0319169186169190911790555b610d976020870187612cf5565b9050811015610e5457610daa8680612cf5565b82818110610dba57610dba612d3f565b90506020020135600014610dee57610dd28680612cf5565b82818110610de257610de2612d3f565b90506020020135610df2565b6000195b6001600160a01b038316600090815260136020908152604082209190610e1a908a018a612cf5565b85818110610e2a57610e2a612d3f565b9050602002013581526020019081526020016000208190555080610e4d90612d6b565b9050610d8a565b7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f610e87836001600160a01b0316612356565b80516020918201206040805192830193909352918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201526001600160a01b03831660a082015260c00160408051601f1981840301815291815281516020928301206001600160a01b03851660009081526005909352912055610f17856123b2565b15610f4a576001600160a01b03821660009081526009602090815260409091208635815590860135600182015550610f86565b60408051808201825260038152606460208083019182526001600160a01b03861660009081526009909152929092209051815590516001909101555b6000610f956040880188612cf5565b90501115611068576000805481906001600160a01b0316610fb960208a018a612cf5565b610fc660408c018c612cf5565b88604051602401610fdb959493929190612dbc565b60408051601f198184030181529181526020820180516001600160e01b031663b5afec9560e01b179052516110109190612c19565b600060405180830381855af49150503d806000811461104b576040519150601f19603f3d011682016040523d82523d6000602084013e611050565b606091505b509150915081611065578051610c3857600080fd5b50505b509a9950505050505050505050565b60005b61108760a0830183612cf5565b9050811015611303576000805481906001600160a01b03166110a98580612cf5565b858181106110b9576110b9612d3f565b90506020028101906110cb9190612cf5565b6110d86020880188612cf5565b878181106110e8576110e8612d3f565b905060200201358780604001906110ff9190612cf5565b8881811061110f5761110f612d3f565b905060200201358880606001906111269190612cf5565b8981811061113657611136612d3f565b9050602002013589806080019061114d9190612cf5565b8a81811061115d5761115d612d3f565b905060200201358a8060a001906111749190612cf5565b8b81811061118457611184612d3f565b9050602002016020810190611199919061277c565b6111a660c08d018d612cf5565b8c8181106111b6576111b6612d3f565b90506020020160208101906111cb919061277c565b6111d860e08e018e612cf5565b8d8181106111e8576111e8612d3f565b90506020020160208101906111fd9190612dfe565b61120b6101008f018f612cf5565b8e81811061121b5761121b612d3f565b905060200201358e8061012001906112339190612cf5565b8f81811061124357611243612d3f565b905060200201356040516024016112649b9a99989796959493929190612e19565b60408051601f198184030181529181526020820180516001600160e01b031663de8f21db60e01b179052516112999190612c19565b600060405180830381855af49150503d80600081146112d4576040519150601f19603f3d011682016040523d82523d6000602084013e6112d9565b606091505b5091509150816112ee578051610c3857600080fd5b505080806112fb90612d6b565b91505061107a565b5050565b60005b61131760a0830183612cf5565b9050811015611303576000805481906001600160a01b03166113398580612cf5565b8581811061134957611349612d3f565b905060200281019061135b9190612cf5565b6113686020880188612cf5565b8781811061137857611378612d3f565b9050602002013587806040019061138f9190612cf5565b8881811061139f5761139f612d3f565b905060200201358880606001906113b69190612cf5565b898181106113c6576113c6612d3f565b90506020020135898060a001906113dd9190612cf5565b8a8181106113ed576113ed612d3f565b9050602002016020810190611402919061277c565b61140f60c08c018c612cf5565b8b81811061141f5761141f612d3f565b9050602002016020810190611434919061277c565b61144160e08d018d612cf5565b8c81811061145157611451612d3f565b90506020020160208101906114669190612dfe565b6114746101008e018e612cf5565b8d81811061148457611484612d3f565b905060200201358d80610120019061149c9190612cf5565b8e8181106114ac576114ac612d3f565b905060200201356040516024016114cc9a99989796959493929190612e82565b60408051601f198184030181529181526020820180516001600160e01b031663a88e6ce960e01b179052516115019190612c19565b600060405180830381855af49150503d806000811461153c576040519150601f19603f3d011682016040523d82523d6000602084013e611541565b606091505b509150915081611556578051610c3857600080fd5b5050808061156390612d6b565b91505061130a565b6001600160a01b038581166000908152600760205260409020541633146115a557604051630862a88160e21b815260040160405180910390fd5b60005b8381101561162c578282828181106115c2576115c2612d3f565b9050602002013560136000886001600160a01b03166001600160a01b03168152602001908152602001600020600087878581811061160257611602612d3f565b905060200201358152602001908152602001600020819055508061162590612d6b565b90506115a8565b505050505050565b6001600160a01b0382811660009081526007602052604090205416331461166e57604051630862a88160e21b815260040160405180910390fd5b6001600160a01b039091166000908152600f6020526040902055565b6003546001600160a01b031633146116b557604051630862a88160e21b815260040160405180910390fd5b336116d357604051633c0a853160e11b815260040160405180910390fd5b6001600160a01b0381166116fa57604051637a381aaf60e01b815260040160405180910390fd5b600480546001600160a01b0319166001600160a01b0392909216919091179055565b6003546001600160a01b0316331461174757604051630862a88160e21b815260040160405180910390fd5b3361176557604051633c0a853160e11b815260040160405180910390fd5b6001600160a01b03811661178c5760405163035745df60e51b815260040160405180910390fd5b600380546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b038281166000908152600760205260409020541633146117e857604051630862a88160e21b815260040160405180910390fd5b600080546040516001600160a01b03858116602483015284811660448301529091169060640160408051601f198184030181529181526020820180516001600160e01b031663809a9f2f60e01b179052516118439190612c19565b600060405180830381855af49150503d806000811461187e576040519150601f19603f3d011682016040523d82523d6000602084013e611883565b606091505b505090508061092c5760405163786fb38760e01b815260040160405180910390fd5b60005b6118b560a0830183612cf5565b9050811015611303576000805481906001600160a01b03166118d78580612cf5565b858181106118e7576118e7612d3f565b90506020028101906118f99190612cf5565b6119066020880188612cf5565b8781811061191657611916612d3f565b9050602002013587806040019061192d9190612cf5565b8881811061193d5761193d612d3f565b905060200201358880606001906119549190612cf5565b8981811061196457611964612d3f565b90506020020135898060a0019061197b9190612cf5565b8a81811061198b5761198b612d3f565b90506020020160208101906119a0919061277c565b6119ad60c08c018c612cf5565b8b8181106119bd576119bd612d3f565b90506020020160208101906119d2919061277c565b6119df60e08d018d612cf5565b8c8181106119ef576119ef612d3f565b9050602002016020810190611a049190612dfe565b611a126101008e018e612cf5565b8d818110611a2257611a22612d3f565b905060200201358d806101200190611a3a9190612cf5565b8e818110611a4a57611a4a612d3f565b90506020020135604051602401611a6a9a99989796959493929190612e82565b60408051601f198184030181529181526020820180516001600160e01b031663c188e39560e01b17905251611a9f9190612c19565b600060405180830381855af49150503d8060008114611ada576040519150601f19603f3d011682016040523d82523d6000602084013e611adf565b606091505b509150915081611af4578051610c3857600080fd5b50508080611b0190612d6b565b9150506118a8565b6000805460405182916001600160a01b031690611b3c908e908e908e908e908e908e908e908e908e908e90602401612e82565b60408051601f198184030181529181526020820180516001600160e01b031663a88e6ce960e01b17905251611b719190612c19565b600060405180830381855af49150503d8060008114611bac576040519150601f19603f3d011682016040523d82523d6000602084013e611bb1565b606091505b509150915081611bc6578051610c3857600080fd5b505050505050505050505050565b6001600160a01b03828116600090815260076020526040902054163314611c0e57604051630862a88160e21b815260040160405180910390fd5b6000805460405160248101879052604481018690526001600160a01b03858116606483015284811660848301529091169060a40160408051601f198184030181529181526020820180516001600160e01b031663af91e8c960e01b17905251611c779190612c19565b600060405180830381855af49150503d8060008114611cb2576040519150601f19603f3d011682016040523d82523d6000602084013e611cb7565b606091505b5050905080611cd95760405163786fb38760e01b815260040160405180910390fd5b5050505050565b60005b611cf060a0830183612cf5565b9050811015611303576000805481906001600160a01b0316611d128580612cf5565b85818110611d2257611d22612d3f565b9050602002810190611d349190612cf5565b611d416020880188612cf5565b87818110611d5157611d51612d3f565b90506020020135878060400190611d689190612cf5565b88818110611d7857611d78612d3f565b90506020020135888060600190611d8f9190612cf5565b89818110611d9f57611d9f612d3f565b90506020020135898060800190611db69190612cf5565b8a818110611dc657611dc6612d3f565b905060200201358a8060a00190611ddd9190612cf5565b8b818110611ded57611ded612d3f565b9050602002016020810190611e02919061277c565b611e0f60c08d018d612cf5565b8c818110611e1f57611e1f612d3f565b9050602002016020810190611e34919061277c565b611e4160e08e018e612cf5565b8d818110611e5157611e51612d3f565b9050602002016020810190611e669190612dfe565b611e746101008f018f612cf5565b8e818110611e8457611e84612d3f565b905060200201358e806101200190611e9c9190612cf5565b8f818110611eac57611eac612d3f565b90506020020135604051602401611ecd9b9a99989796959493929190612e19565b60408051601f198184030181529181526020820180516001600160e01b031663c4dd9ab760e01b17905251611f029190612c19565b600060405180830381855af49150503d8060008114611f3d576040519150601f19603f3d011682016040523d82523d6000602084013e611f42565b606091505b509150915081611f57578051610c3857600080fd5b50508080611f6490612d6b565b915050611ce3565b6000805460405182916001600160a01b031690611f9f908e908e908e908e908e908e908e908e908e908e90602401612e82565b60408051601f198184030181529181526020820180516001600160e01b031663c188e39560e01b17905251611b719190612c19565b6000805460405182916001600160a01b031690612009908f908f908f908f908f908f908f908f908f908f908f90602401612e19565b60408051601f198184030181529181526020820180516001600160e01b031663c4dd9ab760e01b1790525161203e9190612c19565b600060405180830381855af49150503d8060008114612079576040519150601f19603f3d011682016040523d82523d6000602084013e61207e565b606091505b509150915081612093578051610c3857600080fd5b50505050505050505050505050565b6001600160a01b0382166120c957604051637a381aaf60e01b815260040160405180910390fd5b6001600160a01b0381166120f057604051630572b44f60e41b815260040160405180910390fd5b3361210e57604051633c0a853160e11b815260040160405180910390fd5b6001600160a01b0382811660009081526007602052604090205416331461214857604051630862a88160e21b815260040160405180910390fd5b6001600160a01b03918216600090815260086020526040902080546001600160a01b03191691909216179055565b600061218160015490565b905090565b6001600160a01b0382166121ad57604051637a381aaf60e01b815260040160405180910390fd5b6001600160a01b0381166121d457604051630b07403f60e21b815260040160405180910390fd5b336121f257604051633c0a853160e11b815260040160405180910390fd5b6001600160a01b0382811660009081526007602052604090205416331461222c57604051630862a88160e21b815260040160405180910390fd5b6001600160a01b03918216600090815260076020526040902080546001600160a01b03191691909216179055565b6000805460405182916001600160a01b03169061228f908f908f908f908f908f908f908f908f908f908f908f90602401612e19565b60408051601f198184030181529181526020820180516001600160e01b031663de8f21db60e01b1790525161203e9190612c19565b6003546001600160a01b031633146122ef57604051630862a88160e21b815260040160405180910390fd5b3361230d57604051633c0a853160e11b815260040160405180910390fd5b6001600160a01b038116612334576040516322f85dbf60e01b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b0392909216919091179055565b60608161237d5750506040805180820190915260048152630307830360e41b602082015290565b8160005b81156123a0578061239181612d6b565b915050600882901c9150612381565b6123aa8482612425565b949350505050565b60006020820135823511156123c957506000919050565b813515806123d957506020820135155b156123e657506000919050565b60006123f483356064612ee6565b9050600061240760208501356003612ee6565b90508082101561241b575060009392505050565b5060019392505050565b60606000612434836002612ee6565b61243f906002612f05565b67ffffffffffffffff81111561245757612457612f1d565b6040519080825280601f01601f191660200182016040528015612481576020820181803683370190505b509050600360fc1b8160008151811061249c5761249c612d3f565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106124cb576124cb612d3f565b60200101906001600160f81b031916908160001a90535060006124ef846002612ee6565b6124fa906001612f05565b90505b6001811115612572576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061252e5761252e612d3f565b1a60f81b82828151811061254457612544612d3f565b60200101906001600160f81b031916908160001a90535060049490941c9361256b81612f33565b90506124fd565b5083156125c55760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015260640160405180910390fd5b9392505050565b6001600160a01b03811681146125e157600080fd5b50565b80356125ef816125cc565b919050565b803580151581146125ef57600080fd5b6000806040838503121561261757600080fd5b8235612622816125cc565b9150612630602084016125f4565b90509250929050565b60008083601f84011261264b57600080fd5b50813567ffffffffffffffff81111561266357600080fd5b60208301915083602082850101111561267b57600080fd5b9250929050565b60006040828403121561269457600080fd5b50919050565b60008060008060008060008060008060006101208c8e0312156126bc57600080fd5b67ffffffffffffffff808d3511156126d357600080fd5b6126e08e8e358f01612639565b909c509a5060208d01358110156126f657600080fd5b6127068e60208f01358f01612639565b909a50985060408d013581101561271c57600080fd5b5061272d8d60408e01358e01612639565b909750955060608c0135945060808c0135935061274d8d60a08e01612682565b925061275b60e08d016125e4565b915061276a6101008d016125f4565b90509295989b509295989b9093969950565b60006020828403121561278e57600080fd5b81356125c5816125cc565b60006060828403121561269457600080fd5b6000806000806000806000806000806101008b8d0312156127cb57600080fd5b8a3567ffffffffffffffff808211156127e357600080fd5b6127ef8e838f01612639565b909c509a5060208d013591508082111561280857600080fd5b6128148e838f01612639565b909a50985060408d013591508082111561282d57600080fd5b6128398e838f01612639565b909850965060608d013591508082111561285257600080fd5b5061285f8d828e01612799565b94505061286f8c60808d01612682565b925061287d60c08c016125e4565b915061288b60e08c016125f4565b90509295989b9194979a5092959850565b6000602082840312156128ae57600080fd5b813567ffffffffffffffff8111156128c557600080fd5b820161014081850312156125c557600080fd5b60008083601f8401126128ea57600080fd5b50813567ffffffffffffffff81111561290257600080fd5b6020830191508360208260051b850101111561267b57600080fd5b60008060008060006060868803121561293557600080fd5b8535612940816125cc565b9450602086013567ffffffffffffffff8082111561295d57600080fd5b61296989838a016128d8565b9096509450604088013591508082111561298257600080fd5b5061298f888289016128d8565b969995985093965092949392505050565b6000806000606084860312156129b557600080fd5b83356129c0816125cc565b925060208401356129d0816125cc565b929592945050506040919091013590565b600080604083850312156129f457600080fd5b82356129ff816125cc565b946020939093013593505050565b60008060408385031215612a2057600080fd5b8235612a2b816125cc565b91506020830135612a3b816125cc565b809150509250929050565b600060208284031215612a5857600080fd5b5035919050565b803560ff811681146125ef57600080fd5b6000806000806000806000806000806101208b8d031215612a9057600080fd5b8a3567ffffffffffffffff811115612aa757600080fd5b612ab38d828e016128d8565b909b5099505060208b0135975060408b0135965060608b0135955060808b0135612adc816125cc565b945060a08b0135612aec816125cc565b9350612afa60c08c01612a5f565b925060e08b013591506101008b013590509295989b9194979a5092959850565b60008060008060808587031215612b3057600080fd5b84359350602085013592506040850135612b49816125cc565b91506060850135612b59816125cc565b939692955090935050565b60008060008060008060008060008060006101408c8e031215612b8657600080fd5b8b3567ffffffffffffffff811115612b9d57600080fd5b612ba98e828f016128d8565b909c509a505060208c0135985060408c0135975060608c0135965060808c0135955060a08c0135612bd9816125cc565b945060c08c0135612be9816125cc565b9350612bf760e08d01612a5f565b92506101008c013591506101208c013590509295989b509295989b9093969950565b6000825160005b81811015612c3a5760208186018101518583015201612c20565b81811115612c49576000828501525b509190910192915050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6001600160a01b0388168152608060208201819052600090612ca2908301888a612c54565b8281036040840152612cb5818789612c54565b90508281036060840152612cca818587612c54565b9a9950505050505050505050565b600060208284031215612cea57600080fd5b81516125c5816125cc565b6000808335601e19843603018112612d0c57600080fd5b83018035915067ffffffffffffffff821115612d2757600080fd5b6020019150600581901b360382131561267b57600080fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600019821415612d7f57612d7f612d55565b5060010190565b81835260006001600160fb1b03831115612d9f57600080fd5b8260051b8083602087013760009401602001938452509192915050565b606081526000612dd0606083018789612d86565b8281036020840152612de3818688612d86565b91505060018060a01b03831660408301529695505050505050565b600060208284031215612e1057600080fd5b6125c582612a5f565b61014081526000612e2f61014083018d8f612d86565b602083019b909b52506040810198909852606088019690965260808701949094526001600160a01b0392831660a0870152911660c085015260ff1660e08401526101008301526101209091015292915050565b6000610120808352612e978184018d8f612d86565b602084019b909b525050604081019790975260608701959095526001600160a01b0393841660808701529190921660a085015260ff90911660c084015260e08301526101009091015292915050565b6000816000190483118215151615612f0057612f00612d55565b500290565b60008219821115612f1857612f18612d55565b500190565b634e487b7160e01b600052604160045260246000fd5b600081612f4257612f42612d55565b50600019019056fea264697066735822122023ed71495e38f66a1d455443cde842982c3b06bad6b09689e2facfe108ef187364736f6c634300080c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000cb7ec73fd2bca2596794804a6ef5475d0dc4f42700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f00000000000000000000000064aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d500000000000000000000000004906695d6d12cf5459975d7c3c03356e4ccd460
-----Decoded View---------------
Arg [0] : treasury_ (address): 0xCB7ec73Fd2bca2596794804a6eF5475d0DC4F427
Arg [1] : xpDirectory_ (address): 0x0000000000000000000000000000000000000000
Arg [2] : xp_ (address): 0x0000000000000000000000000000000000000000
Arg [3] : ohmCurrencies_ (address[]): 0x0ab87046fBb341D058F17CBC4c1133F25a20a52f,0x64aa3364F17a4D01c6f1751Fd97C2BD3D7e7f1D5,0x04906695D6D12CF5459975d7C3C03356E4Ccd460
-----Encoded View---------------
8 Constructor Arguments found :
Arg [0] : 000000000000000000000000cb7ec73fd2bca2596794804a6ef5475d0dc4f427
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000003
Arg [5] : 0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f
Arg [6] : 00000000000000000000000064aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d5
Arg [7] : 00000000000000000000000004906695d6d12cf5459975d7c3c03356e4ccd460
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.