Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
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:
FinalTransferModule
Compiler Version
v0.7.0+commit.9e61f92b
Contract Source Code (Solidity)
/** *Submitted for verification at Etherscan.io on 2020-11-12 */ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; // File: contracts/lib/MathUint.sol // Copyright 2017 Loopring Technology Limited. /// @title Utility Functions for uint /// @author Daniel Wang - <[email protected]> library MathUint { function mul( uint a, uint b ) internal pure returns (uint c) { c = a * b; require(a == 0 || c / a == b, "MUL_OVERFLOW"); } function sub( uint a, uint b ) internal pure returns (uint) { require(b <= a, "SUB_UNDERFLOW"); return a - b; } function add( uint a, uint b ) internal pure returns (uint c) { c = a + b; require(c >= a, "ADD_OVERFLOW"); } } // File: contracts/lib/EIP712.sol // Copyright 2017 Loopring Technology Limited. library EIP712 { struct Domain { string name; string version; address verifyingContract; } bytes32 constant internal EIP712_DOMAIN_TYPEHASH = keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ); string constant internal EIP191_HEADER = "\x19\x01"; function hash(Domain memory domain) internal pure returns (bytes32) { uint _chainid; assembly { _chainid := chainid() } return keccak256( abi.encode( EIP712_DOMAIN_TYPEHASH, keccak256(bytes(domain.name)), keccak256(bytes(domain.version)), _chainid, domain.verifyingContract ) ); } function hashPacked( bytes32 domainSeperator, bytes memory encodedData ) internal pure returns (bytes32) { return keccak256( abi.encodePacked(EIP191_HEADER, domainSeperator, keccak256(encodedData)) ); } } // File: contracts/thirdparty/BytesUtil.sol //Mainly taken from https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol library BytesUtil { function slice( bytes memory _bytes, uint _start, uint _length ) internal pure returns (bytes memory) { require(_bytes.length >= (_start + _length)); bytes memory tempBytes; assembly { switch iszero(_length) case 0 { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // The first word of the slice result is potentially a partial // word read from the original array. To read it, we calculate // the length of that partial word and start copying that many // bytes into the array. The first word we copy will start with // data we don't care about, but the last `lengthmod` bytes will // land at the beginning of the contents of the new array. When // we're done copying, we overwrite the full first word with // the actual length of the slice. let lengthmod := and(_length, 31) // The multiplication in the next line is necessary // because when slicing multiples of 32 bytes (lengthmod == 0) // the following copy loop was copying the origin's length // and then ending prematurely not copying everything it should. let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) let end := add(mc, _length) for { // The multiplication in the next line has the same exact purpose // as the one above. let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } mstore(tempBytes, _length) //update free-memory pointer //allocating the array padded to 32 bytes like the compiler does now mstore(0x40, and(add(mc, 31), not(31))) } //if we want a zero-length slice let's just return a zero-length array default { tempBytes := mload(0x40) mstore(0x40, add(tempBytes, 0x20)) } } return tempBytes; } function toAddress(bytes memory _bytes, uint _start) internal pure returns (address) { require(_bytes.length >= (_start + 20)); address tempAddress; assembly { tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) } return tempAddress; } function toUint8(bytes memory _bytes, uint _start) internal pure returns (uint8) { require(_bytes.length >= (_start + 1)); uint8 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x1), _start)) } return tempUint; } function toUint16(bytes memory _bytes, uint _start) internal pure returns (uint16) { require(_bytes.length >= (_start + 2)); uint16 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x2), _start)) } return tempUint; } function toUint24(bytes memory _bytes, uint _start) internal pure returns (uint24) { require(_bytes.length >= (_start + 3)); uint24 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x3), _start)) } return tempUint; } function toUint32(bytes memory _bytes, uint _start) internal pure returns (uint32) { require(_bytes.length >= (_start + 4)); uint32 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x4), _start)) } return tempUint; } function toUint64(bytes memory _bytes, uint _start) internal pure returns (uint64) { require(_bytes.length >= (_start + 8)); uint64 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x8), _start)) } return tempUint; } function toUint96(bytes memory _bytes, uint _start) internal pure returns (uint96) { require(_bytes.length >= (_start + 12)); uint96 tempUint; assembly { tempUint := mload(add(add(_bytes, 0xc), _start)) } return tempUint; } function toUint128(bytes memory _bytes, uint _start) internal pure returns (uint128) { require(_bytes.length >= (_start + 16)); uint128 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x10), _start)) } return tempUint; } function toUint(bytes memory _bytes, uint _start) internal pure returns (uint256) { require(_bytes.length >= (_start + 32)); uint256 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x20), _start)) } return tempUint; } function toBytes4(bytes memory _bytes, uint _start) internal pure returns (bytes4) { require(_bytes.length >= (_start + 4)); bytes4 tempBytes4; assembly { tempBytes4 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes4; } function toBytes32(bytes memory _bytes, uint _start) internal pure returns (bytes32) { require(_bytes.length >= (_start + 32)); bytes32 tempBytes32; assembly { tempBytes32 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes32; } function fastSHA256( bytes memory data ) internal view returns (bytes32) { bytes32[] memory result = new bytes32[](1); bool success; assembly { let ptr := add(data, 32) success := staticcall(sub(gas(), 2000), 2, ptr, mload(data), add(result, 32), 32) } require(success, "SHA256_FAILED"); return result[0]; } } // File: contracts/lib/AddressUtil.sol // Copyright 2017 Loopring Technology Limited. /// @title Utility Functions for addresses /// @author Daniel Wang - <[email protected]> /// @author Brecht Devos - <[email protected]> library AddressUtil { using AddressUtil for *; function isContract( address addr ) internal view returns (bool) { // According to EIP-1052, 0x0 is the value returned for not-yet created accounts // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned // for accounts without code, i.e. `keccak256('')` bytes32 codehash; // solhint-disable-next-line no-inline-assembly assembly { codehash := extcodehash(addr) } return (codehash != 0x0 && codehash != 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470); } function toPayable( address addr ) internal pure returns (address payable) { return payable(addr); } // Works like address.send but with a customizable gas limit // Make sure your code is safe for reentrancy when using this function! function sendETH( address to, uint amount, uint gasLimit ) internal returns (bool success) { if (amount == 0) { return true; } address payable recipient = to.toPayable(); /* solium-disable-next-line */ (success,) = recipient.call{value: amount, gas: gasLimit}(""); } // Works like address.transfer but with a customizable gas limit // Make sure your code is safe for reentrancy when using this function! function sendETHAndVerify( address to, uint amount, uint gasLimit ) internal returns (bool success) { success = to.sendETH(amount, gasLimit); require(success, "TRANSFER_FAILURE"); } // Works like call but is slightly more efficient when data // needs to be copied from memory to do the call. function fastCall( address to, uint gasLimit, uint value, bytes memory data ) internal returns (bool success, bytes memory returnData) { if (to != address(0)) { assembly { // Do the call success := call(gasLimit, to, value, add(data, 32), mload(data), 0, 0) // Copy the return data let size := returndatasize() returnData := mload(0x40) mstore(returnData, size) returndatacopy(add(returnData, 32), 0, size) // Update free memory pointer mstore(0x40, add(returnData, add(32, size))) } } } // Like fastCall, but throws when the call is unsuccessful. function fastCallAndVerify( address to, uint gasLimit, uint value, bytes memory data ) internal returns (bytes memory returnData) { bool success; (success, returnData) = fastCall(to, gasLimit, value, data); if (!success) { assembly { revert(add(returnData, 32), mload(returnData)) } } } } // File: contracts/lib/ERC1271.sol // Copyright 2017 Loopring Technology Limited. abstract contract ERC1271 { // bytes4(keccak256("isValidSignature(bytes32,bytes)") bytes4 constant internal ERC1271_MAGICVALUE = 0x1626ba7e; function isValidSignature( bytes32 _hash, bytes memory _signature) public view virtual returns (bytes4 magicValueB32); } // File: contracts/lib/SignatureUtil.sol // Copyright 2017 Loopring Technology Limited. /// @title SignatureUtil /// @author Daniel Wang - <[email protected]> /// @dev This method supports multihash standard. Each signature's last byte indicates /// the signature's type. library SignatureUtil { using BytesUtil for bytes; using MathUint for uint; using AddressUtil for address; enum SignatureType { ILLEGAL, INVALID, EIP_712, ETH_SIGN, WALLET // deprecated } bytes4 constant internal ERC1271_MAGICVALUE = 0x1626ba7e; function verifySignatures( bytes32 signHash, address[] memory signers, bytes[] memory signatures ) internal view returns (bool) { require(signers.length == signatures.length, "BAD_SIGNATURE_DATA"); address lastSigner; for (uint i = 0; i < signers.length; i++) { require(signers[i] > lastSigner, "INVALID_SIGNERS_ORDER"); lastSigner = signers[i]; if (!verifySignature(signHash, signers[i], signatures[i])) { return false; } } return true; } function verifySignature( bytes32 signHash, address signer, bytes memory signature ) internal view returns (bool) { if (signer == address(0)) { return false; } return signer.isContract()? verifyERC1271Signature(signHash, signer, signature): verifyEOASignature(signHash, signer, signature); } function recoverECDSASigner( bytes32 signHash, bytes memory signature ) internal pure returns (address) { if (signature.length != 65) { return address(0); } bytes32 r; bytes32 s; uint8 v; // we jump 32 (0x20) as the first slot of bytes contains the length // we jump 65 (0x41) per signature // for v we load 32 bytes ending with v (the first 31 come from s) then apply a mask assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := and(mload(add(signature, 0x41)), 0xff) } // See https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/cryptography/ECDSA.sol if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return address(0); } if (v == 27 || v == 28) { return ecrecover(signHash, v, r, s); } else { return address(0); } } function verifyEOASignature( bytes32 signHash, address signer, bytes memory signature ) private pure returns (bool success) { if (signer == address(0)) { return false; } uint signatureTypeOffset = signature.length.sub(1); SignatureType signatureType = SignatureType(signature.toUint8(signatureTypeOffset)); // Strip off the last byte of the signature by updating the length assembly { mstore(signature, signatureTypeOffset) } if (signatureType == SignatureType.EIP_712) { success = (signer == recoverECDSASigner(signHash, signature)); } else if (signatureType == SignatureType.ETH_SIGN) { bytes32 hash = keccak256( abi.encodePacked("\x19Ethereum Signed Message:\n32", signHash) ); success = (signer == recoverECDSASigner(hash, signature)); } else { success = false; } // Restore the signature length assembly { mstore(signature, add(signatureTypeOffset, 1)) } return success; } function verifyERC1271Signature( bytes32 signHash, address signer, bytes memory signature ) private view returns (bool) { bytes memory callData = abi.encodeWithSelector( ERC1271.isValidSignature.selector, signHash, signature ); (bool success, bytes memory result) = signer.staticcall(callData); return ( success && result.length == 32 && result.toBytes4(0) == ERC1271_MAGICVALUE ); } } // File: contracts/lib/Ownable.sol // Copyright 2017 Loopring Technology Limited. /// @title Ownable /// @author Brecht Devos - <[email protected]> /// @dev The Ownable contract has an owner address, and provides basic /// authorization control functions, this simplifies the implementation of /// "user permissions". contract Ownable { address public owner; event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); /// @dev The Ownable constructor sets the original `owner` of the contract /// to the sender. constructor() { owner = msg.sender; } /// @dev Throws if called by any account other than the owner. modifier onlyOwner() { require(msg.sender == owner, "UNAUTHORIZED"); _; } /// @dev Allows the current owner to transfer control of the contract to a /// new owner. /// @param newOwner The address to transfer ownership to. function transferOwnership( address newOwner ) public virtual onlyOwner { require(newOwner != address(0), "ZERO_ADDRESS"); emit OwnershipTransferred(owner, newOwner); owner = newOwner; } function renounceOwnership() public onlyOwner { emit OwnershipTransferred(owner, address(0)); owner = address(0); } } // File: contracts/iface/Wallet.sol // Copyright 2017 Loopring Technology Limited. /// @title Wallet /// @dev Base contract for smart wallets. /// Sub-contracts must NOT use non-default constructor to initialize /// wallet states, instead, `init` shall be used. This is to enable /// proxies to be deployed in front of the real wallet contract for /// saving gas. /// /// @author Daniel Wang - <[email protected]> interface Wallet { function version() external pure returns (string memory); function owner() external view returns (address); /// @dev Set a new owner. function setOwner(address newOwner) external; /// @dev Adds a new module. The `init` method of the module /// will be called with `address(this)` as the parameter. /// This method must throw if the module has already been added. /// @param _module The module's address. function addModule(address _module) external; /// @dev Removes an existing module. This method must throw if the module /// has NOT been added or the module is the wallet's only module. /// @param _module The module's address. function removeModule(address _module) external; /// @dev Checks if a module has been added to this wallet. /// @param _module The module to check. /// @return True if the module exists; False otherwise. function hasModule(address _module) external view returns (bool); /// @dev Binds a method from the given module to this /// wallet so the method can be invoked using this wallet's default /// function. /// Note that this method must throw when the given module has /// not been added to this wallet. /// @param _method The method's 4-byte selector. /// @param _module The module's address. Use address(0) to unbind the method. function bindMethod(bytes4 _method, address _module) external; /// @dev Returns the module the given method has been bound to. /// @param _method The method's 4-byte selector. /// @return _module The address of the bound module. If no binding exists, /// returns address(0) instead. function boundMethodModule(bytes4 _method) external view returns (address _module); /// @dev Performs generic transactions. Any module that has been added to this /// wallet can use this method to transact on any third-party contract with /// msg.sender as this wallet itself. /// /// Note: 1) this method must ONLY allow invocations from a module that has /// been added to this wallet. The wallet owner shall NOT be permitted /// to call this method directly. 2) Reentrancy inside this function should /// NOT cause any problems. /// /// @param mode The transaction mode, 1 for CALL, 2 for DELEGATECALL. /// @param to The desitination address. /// @param value The amount of Ether to transfer. /// @param data The data to send over using `to.call{value: value}(data)` /// @return returnData The transaction's return value. function transact( uint8 mode, address to, uint value, bytes calldata data ) external returns (bytes memory returnData); } // File: contracts/iface/Module.sol // Copyright 2017 Loopring Technology Limited. /// @title Module /// @dev Base contract for all smart wallet modules. /// /// @author Daniel Wang - <[email protected]> interface Module { /// @dev Activates the module for the given wallet (msg.sender) after the module is added. /// Warning: this method shall ONLY be callable by a wallet. function activate() external; /// @dev Deactivates the module for the given wallet (msg.sender) before the module is removed. /// Warning: this method shall ONLY be callable by a wallet. function deactivate() external; } // File: contracts/lib/ERC20.sol // Copyright 2017 Loopring Technology Limited. /// @title ERC20 Token Interface /// @dev see https://github.com/ethereum/EIPs/issues/20 /// @author Daniel Wang - <[email protected]> abstract contract ERC20 { function totalSupply() public view virtual returns (uint); function balanceOf( address who ) public view virtual returns (uint); function allowance( address owner, address spender ) public view virtual returns (uint); function transfer( address to, uint value ) public virtual returns (bool); function transferFrom( address from, address to, uint value ) public virtual returns (bool); function approve( address spender, uint value ) public virtual returns (bool); } // File: contracts/iface/ModuleRegistry.sol // Copyright 2017 Loopring Technology Limited. /// @title ModuleRegistry /// @dev A registry for modules. /// /// @author Daniel Wang - <[email protected]> interface ModuleRegistry { /// @dev Registers and enables a new module. function registerModule(address module) external; /// @dev Disables a module function disableModule(address module) external; /// @dev Returns true if the module is registered and enabled. function isModuleEnabled(address module) external view returns (bool); /// @dev Returns the list of enabled modules. function enabledModules() external view returns (address[] memory _modules); /// @dev Returns the number of enbaled modules. function numOfEnabledModules() external view returns (uint); /// @dev Returns true if the module is ever registered. function isModuleRegistered(address module) external view returns (bool); } // File: contracts/base/Controller.sol // Copyright 2017 Loopring Technology Limited. /// @title Controller /// /// @author Daniel Wang - <[email protected]> abstract contract Controller { function moduleRegistry() external view virtual returns (ModuleRegistry); function walletFactory() external view virtual returns (address); } // File: contracts/iface/PriceOracle.sol // Copyright 2017 Loopring Technology Limited. /// @title PriceOracle interface PriceOracle { // @dev Return's the token's value in ETH function tokenValue(address token, uint amount) external view returns (uint value); } // File: contracts/lib/Claimable.sol // Copyright 2017 Loopring Technology Limited. /// @title Claimable /// @author Brecht Devos - <[email protected]> /// @dev Extension for the Ownable contract, where the ownership needs /// to be claimed. This allows the new owner to accept the transfer. contract Claimable is Ownable { address public pendingOwner; /// @dev Modifier throws if called by any account other than the pendingOwner. modifier onlyPendingOwner() { require(msg.sender == pendingOwner, "UNAUTHORIZED"); _; } /// @dev Allows the current owner to set the pendingOwner address. /// @param newOwner The address to transfer ownership to. function transferOwnership( address newOwner ) public override onlyOwner { require(newOwner != address(0) && newOwner != owner, "INVALID_ADDRESS"); pendingOwner = newOwner; } /// @dev Allows the pendingOwner address to finalize the transfer. function claimOwnership() public onlyPendingOwner { emit OwnershipTransferred(owner, pendingOwner); owner = pendingOwner; pendingOwner = address(0); } } // File: contracts/base/DataStore.sol // Copyright 2017 Loopring Technology Limited. /// @title DataStore /// @dev Modules share states by accessing the same storage instance. /// Using ModuleStorage will achieve better module decoupling. /// /// @author Daniel Wang - <[email protected]> abstract contract DataStore { modifier onlyWalletModule(address wallet) { requireWalletModule(wallet); _; } function requireWalletModule(address wallet) view internal { require(Wallet(wallet).hasModule(msg.sender), "UNAUTHORIZED"); } } // File: contracts/stores/HashStore.sol // Copyright 2017 Loopring Technology Limited. /// @title HashStore /// @dev This store maintains all hashes for SignedRequest. contract HashStore is DataStore { // wallet => hash => consumed mapping(address => mapping(bytes32 => bool)) public hashes; constructor() {} function verifyAndUpdate(address wallet, bytes32 hash) external { require(!hashes[wallet][hash], "HASH_EXIST"); requireWalletModule(wallet); hashes[wallet][hash] = true; } } // File: contracts/thirdparty/SafeCast.sol // Taken from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/SafeCast.sol /** * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow * checks. * * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can * easily result in undesired exploitation or bugs, since developers usually * assume that overflows raise errors. `SafeCast` restores this intuition by * reverting the transaction when such an operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. * * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing * all math on `uint256` and `int256` and then downcasting. */ library SafeCast { /** * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits */ function toUint128(uint256 value) internal pure returns (uint128) { require(value < 2**128, "SafeCast: value doesn\'t fit in 128 bits"); return uint128(value); } /** * @dev Returns the downcasted uint96 from uint256, reverting on * overflow (when the input is greater than largest uint96). * * Counterpart to Solidity's `uint96` operator. * * Requirements: * * - input must fit into 96 bits */ function toUint96(uint256 value) internal pure returns (uint96) { require(value < 2**96, "SafeCast: value doesn\'t fit in 96 bits"); return uint96(value); } /** * @dev Returns the downcasted uint64 from uint256, reverting on * overflow (when the input is greater than largest uint64). * * Counterpart to Solidity's `uint64` operator. * * Requirements: * * - input must fit into 64 bits */ function toUint64(uint256 value) internal pure returns (uint64) { require(value < 2**64, "SafeCast: value doesn\'t fit in 64 bits"); return uint64(value); } /** * @dev Returns the downcasted uint32 from uint256, reverting on * overflow (when the input is greater than largest uint32). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 32 bits */ function toUint32(uint256 value) internal pure returns (uint32) { require(value < 2**32, "SafeCast: value doesn\'t fit in 32 bits"); return uint32(value); } /** * @dev Returns the downcasted uint40 from uint256, reverting on * overflow (when the input is greater than largest uint40). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 40 bits */ function toUint40(uint256 value) internal pure returns (uint40) { require(value < 2**40, "SafeCast: value doesn\'t fit in 40 bits"); return uint40(value); } /** * @dev Returns the downcasted uint16 from uint256, reverting on * overflow (when the input is greater than largest uint16). * * Counterpart to Solidity's `uint16` operator. * * Requirements: * * - input must fit into 16 bits */ function toUint16(uint256 value) internal pure returns (uint16) { require(value < 2**16, "SafeCast: value doesn\'t fit in 16 bits"); return uint16(value); } /** * @dev Returns the downcasted uint8 from uint256, reverting on * overflow (when the input is greater than largest uint8). * * Counterpart to Solidity's `uint8` operator. * * Requirements: * * - input must fit into 8 bits. */ function toUint8(uint256 value) internal pure returns (uint8) { require(value < 2**8, "SafeCast: value doesn\'t fit in 8 bits"); return uint8(value); } /** * @dev Converts a signed int256 into an unsigned uint256. * * Requirements: * * - input must be greater than or equal to 0. */ function toUint256(int256 value) internal pure returns (uint256) { require(value >= 0, "SafeCast: value must be positive"); return uint256(value); } /** * @dev Returns the downcasted int128 from int256, reverting on * overflow (when the input is less than smallest int128 or * greater than largest int128). * * Counterpart to Solidity's `int128` operator. * * Requirements: * * - input must fit into 128 bits * * _Available since v3.1._ */ function toInt128(int256 value) internal pure returns (int128) { require(value >= -2**127 && value < 2**127, "SafeCast: value doesn\'t fit in 128 bits"); return int128(value); } /** * @dev Returns the downcasted int64 from int256, reverting on * overflow (when the input is less than smallest int64 or * greater than largest int64). * * Counterpart to Solidity's `int64` operator. * * Requirements: * * - input must fit into 64 bits * * _Available since v3.1._ */ function toInt64(int256 value) internal pure returns (int64) { require(value >= -2**63 && value < 2**63, "SafeCast: value doesn\'t fit in 64 bits"); return int64(value); } /** * @dev Returns the downcasted int32 from int256, reverting on * overflow (when the input is less than smallest int32 or * greater than largest int32). * * Counterpart to Solidity's `int32` operator. * * Requirements: * * - input must fit into 32 bits * * _Available since v3.1._ */ function toInt32(int256 value) internal pure returns (int32) { require(value >= -2**31 && value < 2**31, "SafeCast: value doesn\'t fit in 32 bits"); return int32(value); } /** * @dev Returns the downcasted int16 from int256, reverting on * overflow (when the input is less than smallest int16 or * greater than largest int16). * * Counterpart to Solidity's `int16` operator. * * Requirements: * * - input must fit into 16 bits * * _Available since v3.1._ */ function toInt16(int256 value) internal pure returns (int16) { require(value >= -2**15 && value < 2**15, "SafeCast: value doesn\'t fit in 16 bits"); return int16(value); } /** * @dev Returns the downcasted int8 from int256, reverting on * overflow (when the input is less than smallest int8 or * greater than largest int8). * * Counterpart to Solidity's `int8` operator. * * Requirements: * * - input must fit into 8 bits. * * _Available since v3.1._ */ function toInt8(int256 value) internal pure returns (int8) { require(value >= -2**7 && value < 2**7, "SafeCast: value doesn\'t fit in 8 bits"); return int8(value); } /** * @dev Converts an unsigned uint256 into a signed int256. * * Requirements: * * - input must be less than or equal to maxInt256. */ function toInt256(uint256 value) internal pure returns (int256) { require(value < 2**255, "SafeCast: value doesn't fit in an int256"); return int256(value); } } // File: contracts/stores/QuotaStore.sol // Copyright 2017 Loopring Technology Limited. /// @title QuotaStore /// @dev This store maintains daily spending quota for each wallet. /// A rolling daily limit is used. contract QuotaStore is DataStore { using MathUint for uint; using SafeCast for uint; uint128 public constant MAX_QUOTA = uint128(-1); // Optimized to fit into 64 bytes (2 slots) struct Quota { uint128 currentQuota; uint128 pendingQuota; uint128 spentAmount; uint64 spentTimestamp; uint64 pendingUntil; } mapping (address => Quota) public quotas; event QuotaScheduled( address wallet, uint pendingQuota, uint64 pendingUntil ); constructor() DataStore() { } // 0 for newQuota indicates unlimited quota, or daily quota is disabled. function changeQuota( address wallet, uint newQuota, uint effectiveTime ) external onlyWalletModule(wallet) { require(newQuota <= MAX_QUOTA, "INVALID_VALUE"); if (newQuota == MAX_QUOTA) { newQuota = 0; } quotas[wallet].currentQuota = currentQuota(wallet).toUint128(); quotas[wallet].pendingQuota = newQuota.toUint128(); quotas[wallet].pendingUntil = effectiveTime.toUint64(); emit QuotaScheduled( wallet, newQuota, quotas[wallet].pendingUntil ); } function checkAndAddToSpent( address wallet, address token, uint amount, PriceOracle priceOracle ) external { Quota memory q = quotas[wallet]; uint available = _availableQuota(q); if (available != MAX_QUOTA) { uint value = (token == address(0)) ? amount : priceOracle.tokenValue(token, amount); if (value > 0) { require(available >= value, "QUOTA_EXCEEDED"); requireWalletModule(wallet); _addToSpent(wallet, q, value); } } } function addToSpent( address wallet, uint amount ) external onlyWalletModule(wallet) { _addToSpent(wallet, quotas[wallet], amount); } // Returns 0 to indiciate unlimited quota function currentQuota(address wallet) public view returns (uint) { return _currentQuota(quotas[wallet]); } // Returns 0 to indiciate unlimited quota function pendingQuota(address wallet) public view returns ( uint __pendingQuota, uint __pendingUntil ) { return _pendingQuota(quotas[wallet]); } function spentQuota(address wallet) public view returns (uint) { return _spentQuota(quotas[wallet]); } function availableQuota(address wallet) public view returns (uint) { return _availableQuota(quotas[wallet]); } function hasEnoughQuota( address wallet, uint requiredAmount ) public view returns (bool) { return _hasEnoughQuota(quotas[wallet], requiredAmount); } // Internal function _currentQuota(Quota memory q) private view returns (uint) { return q.pendingUntil <= block.timestamp ? q.pendingQuota : q.currentQuota; } function _pendingQuota(Quota memory q) private view returns ( uint __pendingQuota, uint __pendingUntil ) { if (q.pendingUntil > 0 && q.pendingUntil > block.timestamp) { __pendingQuota = q.pendingQuota; __pendingUntil = q.pendingUntil; } } function _spentQuota(Quota memory q) private view returns (uint) { uint timeSinceLastSpent = block.timestamp.sub(q.spentTimestamp); if (timeSinceLastSpent < 1 days) { return uint(q.spentAmount).sub(timeSinceLastSpent.mul(q.spentAmount) / 1 days); } else { return 0; } } function _availableQuota(Quota memory q) private view returns (uint) { uint quota = _currentQuota(q); if (quota == 0) { return MAX_QUOTA; } uint spent = _spentQuota(q); return quota > spent ? quota - spent : 0; } function _hasEnoughQuota( Quota memory q, uint requiredAmount ) private view returns (bool) { return _availableQuota(q) >= requiredAmount; } function _addToSpent( address wallet, Quota memory q, uint amount ) private { Quota storage s = quotas[wallet]; s.spentAmount = _spentQuota(q).add(amount).toUint128(); s.spentTimestamp = uint64(block.timestamp); } } // File: contracts/stores/Data.sol // Copyright 2017 Loopring Technology Limited. library Data { enum GuardianStatus { REMOVE, // Being removed or removed after validUntil timestamp ADD // Being added or added after validSince timestamp. } // Optimized to fit into 32 bytes (1 slot) struct Guardian { address addr; uint8 status; uint64 timestamp; // validSince if status = ADD; validUntil if adding = REMOVE; } } // File: contracts/stores/GuardianStore.sol // Copyright 2017 Loopring Technology Limited. /// @title GuardianStore /// /// @author Daniel Wang - <[email protected]> abstract contract GuardianStore is DataStore { using MathUint for uint; using SafeCast for uint; struct Wallet { address inheritor; uint32 inheritWaitingPeriod; uint64 lastActive; // the latest timestamp the owner is considered to be active bool locked; Data.Guardian[] guardians; mapping (address => uint) guardianIdx; } mapping (address => Wallet) public wallets; constructor() DataStore() {} function isGuardian( address wallet, address addr, bool includePendingAddition ) public view returns (bool) { Data.Guardian memory g = _getGuardian(wallet, addr); return _isActiveOrPendingAddition(g, includePendingAddition); } function guardians( address wallet, bool includePendingAddition ) public view returns (Data.Guardian[] memory _guardians) { Wallet storage w = wallets[wallet]; _guardians = new Data.Guardian[](w.guardians.length); uint index = 0; for (uint i = 0; i < w.guardians.length; i++) { Data.Guardian memory g = w.guardians[i]; if (_isActiveOrPendingAddition(g, includePendingAddition)) { _guardians[index] = g; index++; } } assembly { mstore(_guardians, index) } } function numGuardians( address wallet, bool includePendingAddition ) public view returns (uint count) { Wallet storage w = wallets[wallet]; for (uint i = 0; i < w.guardians.length; i++) { Data.Guardian memory g = w.guardians[i]; if (_isActiveOrPendingAddition(g, includePendingAddition)) { count++; } } } function removeAllGuardians(address wallet) external { Wallet storage w = wallets[wallet]; uint size = w.guardians.length; if (size == 0) return; requireWalletModule(wallet); for (uint i = 0; i < w.guardians.length; i++) { delete w.guardianIdx[w.guardians[i].addr]; } delete w.guardians; } function cancelPendingGuardians(address wallet) external { bool cancelled = false; Wallet storage w = wallets[wallet]; for (uint i = 0; i < w.guardians.length; i++) { Data.Guardian memory g = w.guardians[i]; if (_isPendingAddition(g)) { w.guardians[i].status = uint8(Data.GuardianStatus.REMOVE); w.guardians[i].timestamp = 0; cancelled = true; } if (_isPendingRemoval(g)) { w.guardians[i].status = uint8(Data.GuardianStatus.ADD); w.guardians[i].timestamp = 0; cancelled = true; } } if (cancelled) { requireWalletModule(wallet); } _cleanRemovedGuardians(wallet, true); } function cleanRemovedGuardians(address wallet) external { _cleanRemovedGuardians(wallet, true); } function addGuardian( address wallet, address addr, uint validSince, bool alwaysOverride ) external onlyWalletModule(wallet) returns (uint) { require(validSince >= block.timestamp, "INVALID_VALID_SINCE"); require(addr != address(0), "ZERO_ADDRESS"); Wallet storage w = wallets[wallet]; uint pos = w.guardianIdx[addr]; if(pos == 0) { // Add the new guardian Data.Guardian memory g = Data.Guardian( addr, uint8(Data.GuardianStatus.ADD), validSince.toUint64() ); w.guardians.push(g); w.guardianIdx[addr] = w.guardians.length; _cleanRemovedGuardians(wallet, false); return validSince; } Data.Guardian memory g = w.guardians[pos - 1]; if (_isRemoved(g)) { w.guardians[pos - 1].status = uint8(Data.GuardianStatus.ADD); w.guardians[pos - 1].timestamp = validSince.toUint64(); return validSince; } if (_isPendingRemoval(g)) { w.guardians[pos - 1].status = uint8(Data.GuardianStatus.ADD); w.guardians[pos - 1].timestamp = 0; return 0; } if (_isPendingAddition(g)) { if (!alwaysOverride) return g.timestamp; w.guardians[pos - 1].timestamp = validSince.toUint64(); return validSince; } require(_isAdded(g), "UNEXPECTED_RESULT"); return 0; } function removeGuardian( address wallet, address addr, uint validUntil, bool alwaysOverride ) external onlyWalletModule(wallet) returns (uint) { require(validUntil >= block.timestamp, "INVALID_VALID_UNTIL"); require(addr != address(0), "ZERO_ADDRESS"); Wallet storage w = wallets[wallet]; uint pos = w.guardianIdx[addr]; require(pos > 0, "GUARDIAN_NOT_EXISTS"); Data.Guardian memory g = w.guardians[pos - 1]; if (_isAdded(g)) { w.guardians[pos - 1].status = uint8(Data.GuardianStatus.REMOVE); w.guardians[pos - 1].timestamp = validUntil.toUint64(); return validUntil; } if (_isPendingAddition(g)) { w.guardians[pos - 1].status = uint8(Data.GuardianStatus.REMOVE); w.guardians[pos - 1].timestamp = 0; return 0; } if (_isPendingRemoval(g)) { if (!alwaysOverride) return g.timestamp; w.guardians[pos - 1].timestamp = validUntil.toUint64(); return validUntil; } require(_isRemoved(g), "UNEXPECTED_RESULT"); return 0; } // ---- internal functions --- function _getGuardian( address wallet, address addr ) private view returns (Data.Guardian memory) { Wallet storage w = wallets[wallet]; uint pos = w.guardianIdx[addr]; if (pos > 0) { return w.guardians[pos - 1]; } } function _isAdded(Data.Guardian memory guardian) private view returns (bool) { return guardian.status == uint8(Data.GuardianStatus.ADD) && guardian.timestamp <= block.timestamp; } function _isPendingAddition(Data.Guardian memory guardian) private view returns (bool) { return guardian.status == uint8(Data.GuardianStatus.ADD) && guardian.timestamp > block.timestamp; } function _isRemoved(Data.Guardian memory guardian) private view returns (bool) { return guardian.status == uint8(Data.GuardianStatus.REMOVE) && guardian.timestamp <= block.timestamp; } function _isPendingRemoval(Data.Guardian memory guardian) private view returns (bool) { return guardian.status == uint8(Data.GuardianStatus.REMOVE) && guardian.timestamp > block.timestamp; } function _isActive(Data.Guardian memory guardian) private view returns (bool) { return _isAdded(guardian) || _isPendingRemoval(guardian); } function _isActiveOrPendingAddition( Data.Guardian memory guardian, bool includePendingAddition ) private view returns (bool) { return _isActive(guardian) || includePendingAddition && _isPendingAddition(guardian); } function _cleanRemovedGuardians( address wallet, bool force ) private { Wallet storage w = wallets[wallet]; uint count = w.guardians.length; if (!force && count < 10) return; for (int i = int(count) - 1; i >= 0; i--) { Data.Guardian memory g = w.guardians[uint(i)]; if (_isRemoved(g)) { Data.Guardian memory lastGuardian = w.guardians[w.guardians.length - 1]; if (g.addr != lastGuardian.addr) { w.guardians[uint(i)] = lastGuardian; w.guardianIdx[lastGuardian.addr] = uint(i) + 1; } w.guardians.pop(); delete w.guardianIdx[g.addr]; } } } } // File: contracts/stores/SecurityStore.sol // Copyright 2017 Loopring Technology Limited. /// @title SecurityStore /// /// @author Daniel Wang - <[email protected]> contract SecurityStore is GuardianStore { using MathUint for uint; using SafeCast for uint; constructor() GuardianStore() {} function setLock( address wallet, bool locked ) external onlyWalletModule(wallet) { wallets[wallet].locked = locked; } function touchLastActive(address wallet) external onlyWalletModule(wallet) { wallets[wallet].lastActive = uint64(block.timestamp); } function touchLastActiveWhenRequired( address wallet, uint minInternval ) external { if (wallets[wallet].inheritor != address(0) && block.timestamp > lastActive(wallet) + minInternval) { requireWalletModule(wallet); wallets[wallet].lastActive = uint64(block.timestamp); } } function setInheritor( address wallet, address who, uint32 _inheritWaitingPeriod ) external onlyWalletModule(wallet) { wallets[wallet].inheritor = who; wallets[wallet].inheritWaitingPeriod = _inheritWaitingPeriod; wallets[wallet].lastActive = uint64(block.timestamp); } function isLocked(address wallet) public view returns (bool) { return wallets[wallet].locked; } function lastActive(address wallet) public view returns (uint) { return wallets[wallet].lastActive; } function inheritor(address wallet) public view returns ( address _who, uint _effectiveTimestamp ) { address _inheritor = wallets[wallet].inheritor; if (_inheritor == address(0)) { return (address(0), 0); } uint32 _inheritWaitingPeriod = wallets[wallet].inheritWaitingPeriod; if (_inheritWaitingPeriod == 0) { return (address(0), 0); } uint64 _lastActive = wallets[wallet].lastActive; if (_lastActive == 0) { _lastActive = uint64(block.timestamp); } _who = _inheritor; _effectiveTimestamp = _lastActive + _inheritWaitingPeriod; } } // File: contracts/lib/AddressSet.sol // Copyright 2017 Loopring Technology Limited. /// @title AddressSet /// @author Daniel Wang - <[email protected]> contract AddressSet { struct Set { address[] addresses; mapping (address => uint) positions; uint count; } mapping (bytes32 => Set) private sets; function addAddressToSet( bytes32 key, address addr, bool maintainList ) internal { Set storage set = sets[key]; require(set.positions[addr] == 0, "ALREADY_IN_SET"); if (maintainList) { require(set.addresses.length == set.count, "PREVIOUSLY_NOT_MAINTAILED"); set.addresses.push(addr); } else { require(set.addresses.length == 0, "MUST_MAINTAIN"); } set.count += 1; set.positions[addr] = set.count; } function removeAddressFromSet( bytes32 key, address addr ) internal { Set storage set = sets[key]; uint pos = set.positions[addr]; require(pos != 0, "NOT_IN_SET"); delete set.positions[addr]; set.count -= 1; if (set.addresses.length > 0) { address lastAddr = set.addresses[set.count]; if (lastAddr != addr) { set.addresses[pos - 1] = lastAddr; set.positions[lastAddr] = pos; } set.addresses.pop(); } } function removeSet(bytes32 key) internal { delete sets[key]; } function isAddressInSet( bytes32 key, address addr ) internal view returns (bool) { return sets[key].positions[addr] != 0; } function numAddressesInSet(bytes32 key) internal view returns (uint) { Set storage set = sets[key]; return set.count; } function addressesInSet(bytes32 key) internal view returns (address[] memory) { Set storage set = sets[key]; require(set.count == set.addresses.length, "NOT_MAINTAINED"); return sets[key].addresses; } } // File: contracts/lib/OwnerManagable.sol // Copyright 2017 Loopring Technology Limited. contract OwnerManagable is Claimable, AddressSet { bytes32 internal constant MANAGER = keccak256("__MANAGED__"); event ManagerAdded (address indexed manager); event ManagerRemoved(address indexed manager); modifier onlyManager { require(isManager(msg.sender), "NOT_MANAGER"); _; } modifier onlyOwnerOrManager { require(msg.sender == owner || isManager(msg.sender), "NOT_OWNER_OR_MANAGER"); _; } constructor() Claimable() {} /// @dev Gets the managers. /// @return The list of managers. function managers() public view returns (address[] memory) { return addressesInSet(MANAGER); } /// @dev Gets the number of managers. /// @return The numer of managers. function numManagers() public view returns (uint) { return numAddressesInSet(MANAGER); } /// @dev Checks if an address is a manger. /// @param addr The address to check. /// @return True if the address is a manager, False otherwise. function isManager(address addr) public view returns (bool) { return isAddressInSet(MANAGER, addr); } /// @dev Adds a new manager. /// @param manager The new address to add. function addManager(address manager) public onlyOwner { addManagerInternal(manager); } /// @dev Removes a manager. /// @param manager The manager to remove. function removeManager(address manager) public onlyOwner { removeAddressFromSet(MANAGER, manager); emit ManagerRemoved(manager); } function addManagerInternal(address manager) internal { addAddressToSet(MANAGER, manager, true); emit ManagerAdded(manager); } } // File: contracts/stores/WhitelistStore.sol // Copyright 2017 Loopring Technology Limited. /// @title WhitelistStore /// @dev This store maintains a wallet's whitelisted addresses. contract WhitelistStore is DataStore, AddressSet, OwnerManagable { bytes32 internal constant DAPPS = keccak256("__DAPPS__"); // wallet => whitelisted_addr => effective_since mapping(address => mapping(address => uint)) public effectiveTimeMap; event Whitelisted( address wallet, address addr, bool whitelisted, uint effectiveTime ); event DappWhitelisted( address addr, bool whitelisted ); constructor() DataStore() {} function addToWhitelist( address wallet, address addr, uint effectiveTime ) external onlyWalletModule(wallet) { addAddressToSet(_walletKey(wallet), addr, true); uint effective = effectiveTime >= block.timestamp ? effectiveTime : block.timestamp; effectiveTimeMap[wallet][addr] = effective; emit Whitelisted(wallet, addr, true, effective); } function removeFromWhitelist( address wallet, address addr ) external onlyWalletModule(wallet) { removeAddressFromSet(_walletKey(wallet), addr); delete effectiveTimeMap[wallet][addr]; emit Whitelisted(wallet, addr, false, 0); } function addDapp(address addr) external onlyManager { addAddressToSet(DAPPS, addr, true); emit DappWhitelisted(addr, true); } function removeDapp(address addr) external onlyManager { removeAddressFromSet(DAPPS, addr); emit DappWhitelisted(addr, false); } function whitelist(address wallet) public view returns ( address[] memory addresses, uint[] memory effectiveTimes ) { addresses = addressesInSet(_walletKey(wallet)); effectiveTimes = new uint[](addresses.length); for (uint i = 0; i < addresses.length; i++) { effectiveTimes[i] = effectiveTimeMap[wallet][addresses[i]]; } } function isWhitelisted( address wallet, address addr ) public view returns ( bool isWhitelistedAndEffective, uint effectiveTime ) { effectiveTime = effectiveTimeMap[wallet][addr]; isWhitelistedAndEffective = effectiveTime > 0 && effectiveTime <= block.timestamp; } function whitelistSize(address wallet) public view returns (uint) { return numAddressesInSet(_walletKey(wallet)); } function dapps() public view returns ( address[] memory addresses ) { return addressesInSet(DAPPS); } function isDapp( address addr ) public view returns (bool) { return isAddressInSet(DAPPS, addr); } function numDapps() public view returns (uint) { return numAddressesInSet(DAPPS); } function isDappOrWhitelisted( address wallet, address addr ) public view returns (bool res) { (res,) = isWhitelisted(wallet, addr); return res || isAddressInSet(DAPPS, addr); } function _walletKey(address addr) private pure returns (bytes32) { return keccak256(abi.encodePacked("__WHITELIST__", addr)); } } // File: contracts/thirdparty/strings.sol /* * @title String & slice utility library for Solidity contracts. * @author Nick Johnson <[email protected]> * * @dev Functionality in this library is largely implemented using an * abstraction called a 'slice'. A slice represents a part of a string - * anything from the entire string to a single character, or even no * characters at all (a 0-length slice). Since a slice only has to specify * an offset and a length, copying and manipulating slices is a lot less * expensive than copying and manipulating the strings they reference. * * To further reduce gas costs, most functions on slice that need to return * a slice modify the original one instead of allocating a new one; for * instance, `s.split(".")` will return the text up to the first '.', * modifying s to only contain the remainder of the string after the '.'. * In situations where you do not want to modify the original slice, you * can make a copy first with `.copy()`, for example: * `s.copy().split(".")`. Try and avoid using this idiom in loops; since * Solidity has no memory management, it will result in allocating many * short-lived slices that are later discarded. * * Functions that return two slices come in two versions: a non-allocating * version that takes the second slice as an argument, modifying it in * place, and an allocating version that allocates and returns the second * slice; see `nextRune` for example. * * Functions that have to copy string data will return strings rather than * slices; these can be cast back to slices for further processing if * required. * * For convenience, some functions are provided with non-modifying * variants that create a new slice and return both; for instance, * `s.splitNew('.')` leaves s unmodified, and returns two values * corresponding to the left and right parts of the string. */ /* solium-disable */ library strings { struct slice { uint _len; uint _ptr; } function memcpy(uint dest, uint src, uint len) private pure { // Copy word-length chunks while possible for(; len >= 32; len -= 32) { assembly { mstore(dest, mload(src)) } dest += 32; src += 32; } // Copy remaining bytes uint mask = 256 ** (32 - len) - 1; assembly { let srcpart := and(mload(src), not(mask)) let destpart := and(mload(dest), mask) mstore(dest, or(destpart, srcpart)) } } /* * @dev Returns a slice containing the entire string. * @param self The string to make a slice from. * @return A newly allocated slice containing the entire string. */ function toSlice(string memory self) internal pure returns (slice memory) { uint ptr; assembly { ptr := add(self, 0x20) } return slice(bytes(self).length, ptr); } /* * @dev Returns the length of a null-terminated bytes32 string. * @param self The value to find the length of. * @return The length of the string, from 0 to 32. */ function len(bytes32 self) internal pure returns (uint) { uint ret; if (self == 0) return 0; if (uint256(self) & 0xffffffffffffffffffffffffffffffff == 0) { ret += 16; self = bytes32(uint(self) / 0x100000000000000000000000000000000); } if (uint256(self) & 0xffffffffffffffff == 0) { ret += 8; self = bytes32(uint(self) / 0x10000000000000000); } if (uint256(self) & 0xffffffff == 0) { ret += 4; self = bytes32(uint(self) / 0x100000000); } if (uint256(self) & 0xffff == 0) { ret += 2; self = bytes32(uint(self) / 0x10000); } if (uint256(self) & 0xff == 0) { ret += 1; } return 32 - ret; } /* * @dev Returns a slice containing the entire bytes32, interpreted as a * null-terminated utf-8 string. * @param self The bytes32 value to convert to a slice. * @return A new slice containing the value of the input argument up to the * first null. */ function toSliceB32(bytes32 self) internal pure returns (slice memory ret) { // Allocate space for `self` in memory, copy it there, and point ret at it assembly { let ptr := mload(0x40) mstore(0x40, add(ptr, 0x20)) mstore(ptr, self) mstore(add(ret, 0x20), ptr) } ret._len = len(self); } /* * @dev Returns a new slice containing the same data as the current slice. * @param self The slice to copy. * @return A new slice containing the same data as `self`. */ function copy(slice memory self) internal pure returns (slice memory) { return slice(self._len, self._ptr); } /* * @dev Copies a slice to a new string. * @param self The slice to copy. * @return A newly allocated string containing the slice's text. */ function toString(slice memory self) internal pure returns (string memory) { string memory ret = new string(self._len); uint retptr; assembly { retptr := add(ret, 32) } memcpy(retptr, self._ptr, self._len); return ret; } /* * @dev Returns the length in runes of the slice. Note that this operation * takes time proportional to the length of the slice; avoid using it * in loops, and call `slice.empty()` if you only need to kblock.timestamp whether * the slice is empty or not. * @param self The slice to operate on. * @return The length of the slice in runes. */ function len(slice memory self) internal pure returns (uint l) { // Starting at ptr-31 means the LSB will be the byte we care about uint ptr = self._ptr - 31; uint end = ptr + self._len; for (l = 0; ptr < end; l++) { uint8 b; assembly { b := and(mload(ptr), 0xFF) } if (b < 0x80) { ptr += 1; } else if(b < 0xE0) { ptr += 2; } else if(b < 0xF0) { ptr += 3; } else if(b < 0xF8) { ptr += 4; } else if(b < 0xFC) { ptr += 5; } else { ptr += 6; } } } /* * @dev Returns true if the slice is empty (has a length of 0). * @param self The slice to operate on. * @return True if the slice is empty, False otherwise. */ function empty(slice memory self) internal pure returns (bool) { return self._len == 0; } /* * @dev Returns a positive number if `other` comes lexicographically after * `self`, a negative number if it comes before, or zero if the * contents of the two slices are equal. Comparison is done per-rune, * on unicode codepoints. * @param self The first slice to compare. * @param other The second slice to compare. * @return The result of the comparison. */ function compare(slice memory self, slice memory other) internal pure returns (int) { uint shortest = self._len; if (other._len < self._len) shortest = other._len; uint selfptr = self._ptr; uint otherptr = other._ptr; for (uint idx = 0; idx < shortest; idx += 32) { uint a; uint b; assembly { a := mload(selfptr) b := mload(otherptr) } if (a != b) { // Mask out irrelevant bytes and check again uint256 mask = uint256(-1); // 0xffff... if(shortest < 32) { mask = ~(2 ** (8 * (32 - shortest + idx)) - 1); } uint256 diff = (a & mask) - (b & mask); if (diff != 0) return int(diff); } selfptr += 32; otherptr += 32; } return int(self._len) - int(other._len); } /* * @dev Returns true if the two slices contain the same text. * @param self The first slice to compare. * @param self The second slice to compare. * @return True if the slices are equal, false otherwise. */ function equals(slice memory self, slice memory other) internal pure returns (bool) { return compare(self, other) == 0; } /* * @dev Extracts the first rune in the slice into `rune`, advancing the * slice to point to the next rune and returning `self`. * @param self The slice to operate on. * @param rune The slice that will contain the first rune. * @return `rune`. */ function nextRune(slice memory self, slice memory rune) internal pure returns (slice memory) { rune._ptr = self._ptr; if (self._len == 0) { rune._len = 0; return rune; } uint l; uint b; // Load the first byte of the rune into the LSBs of b assembly { b := and(mload(sub(mload(add(self, 32)), 31)), 0xFF) } if (b < 0x80) { l = 1; } else if(b < 0xE0) { l = 2; } else if(b < 0xF0) { l = 3; } else { l = 4; } // Check for truncated codepoints if (l > self._len) { rune._len = self._len; self._ptr += self._len; self._len = 0; return rune; } self._ptr += l; self._len -= l; rune._len = l; return rune; } /* * @dev Returns the first rune in the slice, advancing the slice to point * to the next rune. * @param self The slice to operate on. * @return A slice containing only the first rune from `self`. */ function nextRune(slice memory self) internal pure returns (slice memory ret) { nextRune(self, ret); } /* * @dev Returns the number of the first codepoint in the slice. * @param self The slice to operate on. * @return The number of the first codepoint in the slice. */ function ord(slice memory self) internal pure returns (uint ret) { if (self._len == 0) { return 0; } uint word; uint length; uint divisor = 2 ** 248; // Load the rune into the MSBs of b assembly { word:= mload(mload(add(self, 32))) } uint b = word / divisor; if (b < 0x80) { ret = b; length = 1; } else if(b < 0xE0) { ret = b & 0x1F; length = 2; } else if(b < 0xF0) { ret = b & 0x0F; length = 3; } else { ret = b & 0x07; length = 4; } // Check for truncated codepoints if (length > self._len) { return 0; } for (uint i = 1; i < length; i++) { divisor = divisor / 256; b = (word / divisor) & 0xFF; if (b & 0xC0 != 0x80) { // Invalid UTF-8 sequence return 0; } ret = (ret * 64) | (b & 0x3F); } return ret; } /* * @dev Returns the keccak-256 hash of the slice. * @param self The slice to hash. * @return The hash of the slice. */ function keccak(slice memory self) internal pure returns (bytes32 ret) { assembly { ret := keccak256(mload(add(self, 32)), mload(self)) } } /* * @dev Returns true if `self` starts with `needle`. * @param self The slice to operate on. * @param needle The slice to search for. * @return True if the slice starts with the provided text, false otherwise. */ function startsWith(slice memory self, slice memory needle) internal pure returns (bool) { if (self._len < needle._len) { return false; } if (self._ptr == needle._ptr) { return true; } bool equal; assembly { let length := mload(needle) let selfptr := mload(add(self, 0x20)) let needleptr := mload(add(needle, 0x20)) equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } return equal; } /* * @dev If `self` starts with `needle`, `needle` is removed from the * beginning of `self`. Otherwise, `self` is unmodified. * @param self The slice to operate on. * @param needle The slice to search for. * @return `self` */ function beyond(slice memory self, slice memory needle) internal pure returns (slice memory) { if (self._len < needle._len) { return self; } bool equal = true; if (self._ptr != needle._ptr) { assembly { let length := mload(needle) let selfptr := mload(add(self, 0x20)) let needleptr := mload(add(needle, 0x20)) equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } } if (equal) { self._len -= needle._len; self._ptr += needle._len; } return self; } /* * @dev Returns true if the slice ends with `needle`. * @param self The slice to operate on. * @param needle The slice to search for. * @return True if the slice starts with the provided text, false otherwise. */ function endsWith(slice memory self, slice memory needle) internal pure returns (bool) { if (self._len < needle._len) { return false; } uint selfptr = self._ptr + self._len - needle._len; if (selfptr == needle._ptr) { return true; } bool equal; assembly { let length := mload(needle) let needleptr := mload(add(needle, 0x20)) equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } return equal; } /* * @dev If `self` ends with `needle`, `needle` is removed from the * end of `self`. Otherwise, `self` is unmodified. * @param self The slice to operate on. * @param needle The slice to search for. * @return `self` */ function until(slice memory self, slice memory needle) internal pure returns (slice memory) { if (self._len < needle._len) { return self; } uint selfptr = self._ptr + self._len - needle._len; bool equal = true; if (selfptr != needle._ptr) { assembly { let length := mload(needle) let needleptr := mload(add(needle, 0x20)) equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } } if (equal) { self._len -= needle._len; } return self; } // Returns the memory address of the first byte of the first occurrence of // `needle` in `self`, or the first byte after `self` if not found. function findPtr(uint selflen, uint selfptr, uint needlelen, uint needleptr) private pure returns (uint) { uint ptr = selfptr; uint idx; if (needlelen <= selflen) { if (needlelen <= 32) { bytes32 mask = bytes32(~(2 ** (8 * (32 - needlelen)) - 1)); bytes32 needledata; assembly { needledata := and(mload(needleptr), mask) } uint end = selfptr + selflen - needlelen; bytes32 ptrdata; assembly { ptrdata := and(mload(ptr), mask) } while (ptrdata != needledata) { if (ptr >= end) return selfptr + selflen; ptr++; assembly { ptrdata := and(mload(ptr), mask) } } return ptr; } else { // For long needles, use hashing bytes32 hash; assembly { hash := keccak256(needleptr, needlelen) } for (idx = 0; idx <= selflen - needlelen; idx++) { bytes32 testHash; assembly { testHash := keccak256(ptr, needlelen) } if (hash == testHash) return ptr; ptr += 1; } } } return selfptr + selflen; } // Returns the memory address of the first byte after the last occurrence of // `needle` in `self`, or the address of `self` if not found. function rfindPtr(uint selflen, uint selfptr, uint needlelen, uint needleptr) private pure returns (uint) { uint ptr; if (needlelen <= selflen) { if (needlelen <= 32) { bytes32 mask = bytes32(~(2 ** (8 * (32 - needlelen)) - 1)); bytes32 needledata; assembly { needledata := and(mload(needleptr), mask) } ptr = selfptr + selflen - needlelen; bytes32 ptrdata; assembly { ptrdata := and(mload(ptr), mask) } while (ptrdata != needledata) { if (ptr <= selfptr) return selfptr; ptr--; assembly { ptrdata := and(mload(ptr), mask) } } return ptr + needlelen; } else { // For long needles, use hashing bytes32 hash; assembly { hash := keccak256(needleptr, needlelen) } ptr = selfptr + (selflen - needlelen); while (ptr >= selfptr) { bytes32 testHash; assembly { testHash := keccak256(ptr, needlelen) } if (hash == testHash) return ptr + needlelen; ptr -= 1; } } } return selfptr; } /* * @dev Modifies `self` to contain everything from the first occurrence of * `needle` to the end of the slice. `self` is set to the empty slice * if `needle` is not found. * @param self The slice to search and modify. * @param needle The text to search for. * @return `self`. */ function find(slice memory self, slice memory needle) internal pure returns (slice memory) { uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr); self._len -= ptr - self._ptr; self._ptr = ptr; return self; } /* * @dev Modifies `self` to contain the part of the string from the start of * `self` to the end of the first occurrence of `needle`. If `needle` * is not found, `self` is set to the empty slice. * @param self The slice to search and modify. * @param needle The text to search for. * @return `self`. */ function rfind(slice memory self, slice memory needle) internal pure returns (slice memory) { uint ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr); self._len = ptr - self._ptr; return self; } /* * @dev Splits the slice, setting `self` to everything after the first * occurrence of `needle`, and `token` to everything before it. If * `needle` does not occur in `self`, `self` is set to the empty slice, * and `token` is set to the entirety of `self`. * @param self The slice to split. * @param needle The text to search for in `self`. * @param token An output parameter to which the first token is written. * @return `token`. */ function split(slice memory self, slice memory needle, slice memory token) internal pure returns (slice memory) { uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr); token._ptr = self._ptr; token._len = ptr - self._ptr; if (ptr == self._ptr + self._len) { // Not found self._len = 0; } else { self._len -= token._len + needle._len; self._ptr = ptr + needle._len; } return token; } /* * @dev Splits the slice, setting `self` to everything after the first * occurrence of `needle`, and returning everything before it. If * `needle` does not occur in `self`, `self` is set to the empty slice, * and the entirety of `self` is returned. * @param self The slice to split. * @param needle The text to search for in `self`. * @return The part of `self` up to the first occurrence of `delim`. */ function split(slice memory self, slice memory needle) internal pure returns (slice memory token) { split(self, needle, token); } /* * @dev Splits the slice, setting `self` to everything before the last * occurrence of `needle`, and `token` to everything after it. If * `needle` does not occur in `self`, `self` is set to the empty slice, * and `token` is set to the entirety of `self`. * @param self The slice to split. * @param needle The text to search for in `self`. * @param token An output parameter to which the first token is written. * @return `token`. */ function rsplit(slice memory self, slice memory needle, slice memory token) internal pure returns (slice memory) { uint ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr); token._ptr = ptr; token._len = self._len - (ptr - self._ptr); if (ptr == self._ptr) { // Not found self._len = 0; } else { self._len -= token._len + needle._len; } return token; } /* * @dev Splits the slice, setting `self` to everything before the last * occurrence of `needle`, and returning everything after it. If * `needle` does not occur in `self`, `self` is set to the empty slice, * and the entirety of `self` is returned. * @param self The slice to split. * @param needle The text to search for in `self`. * @return The part of `self` after the last occurrence of `delim`. */ function rsplit(slice memory self, slice memory needle) internal pure returns (slice memory token) { rsplit(self, needle, token); } /* * @dev Counts the number of nonoverlapping occurrences of `needle` in `self`. * @param self The slice to search. * @param needle The text to search for in `self`. * @return The number of occurrences of `needle` found in `self`. */ function count(slice memory self, slice memory needle) internal pure returns (uint cnt) { uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr) + needle._len; while (ptr <= self._ptr + self._len) { cnt++; ptr = findPtr(self._len - (ptr - self._ptr), ptr, needle._len, needle._ptr) + needle._len; } } /* * @dev Returns True if `self` contains `needle`. * @param self The slice to search. * @param needle The text to search for in `self`. * @return True if `needle` is found in `self`, false otherwise. */ function contains(slice memory self, slice memory needle) internal pure returns (bool) { return rfindPtr(self._len, self._ptr, needle._len, needle._ptr) != self._ptr; } /* * @dev Returns a newly allocated string containing the concatenation of * `self` and `other`. * @param self The first slice to concatenate. * @param other The second slice to concatenate. * @return The concatenation of the two strings. */ function concat(slice memory self, slice memory other) internal pure returns (string memory) { string memory ret = new string(self._len + other._len); uint retptr; assembly { retptr := add(ret, 32) } memcpy(retptr, self._ptr, self._len); memcpy(retptr + self._len, other._ptr, other._len); return ret; } /* * @dev Joins an array of slices, using `self` as a delimiter, returning a * newly allocated string. * @param self The delimiter to use. * @param parts A list of slices to join. * @return A newly allocated string containing all the slices in `parts`, * joined with `self`. */ function join(slice memory self, slice[] memory parts) internal pure returns (string memory) { if (parts.length == 0) return ""; uint length = self._len * (parts.length - 1); for(uint i = 0; i < parts.length; i++) length += parts[i]._len; string memory ret = new string(length); uint retptr; assembly { retptr := add(ret, 32) } for(uint i = 0; i < parts.length; i++) { memcpy(retptr, parts[i]._ptr, parts[i]._len); retptr += parts[i]._len; if (i < parts.length - 1) { memcpy(retptr, self._ptr, self._len); retptr += self._len; } } return ret; } } // File: contracts/thirdparty/ens/ENS.sol // Taken from Argent's code base - https://github.com/argentlabs/argent-contracts/blob/develop/contracts/ens/ENS.sol // with few modifications. /** * ENS Registry interface. */ interface ENSRegistry { // Logged when the owner of a node assigns a new owner to a subnode. event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner); // Logged when the owner of a node transfers ownership to a new account. event Transfer(bytes32 indexed node, address owner); // Logged when the resolver for a node changes. event NewResolver(bytes32 indexed node, address resolver); // Logged when the TTL of a node changes event NewTTL(bytes32 indexed node, uint64 ttl); function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external; function setResolver(bytes32 node, address resolver) external; function setOwner(bytes32 node, address owner) external; function setTTL(bytes32 node, uint64 ttl) external; function owner(bytes32 node) external view returns (address); function resolver(bytes32 node) external view returns (address); function ttl(bytes32 node) external view returns (uint64); } /** * ENS Resolver interface. */ abstract contract ENSResolver { function addr(bytes32 _node) public view virtual returns (address); function setAddr(bytes32 _node, address _addr) public virtual; function name(bytes32 _node) public view virtual returns (string memory); function setName(bytes32 _node, string memory _name) public virtual; } /** * ENS Reverse Registrar interface. */ abstract contract ENSReverseRegistrar { function claim(address _owner) public virtual returns (bytes32 _node); function claimWithResolver(address _owner, address _resolver) public virtual returns (bytes32); function setName(string memory _name) public virtual returns (bytes32); function node(address _addr) public view virtual returns (bytes32); } // File: contracts/thirdparty/ens/ENSConsumer.sol // Taken from Argent's code base - https://github.com/argentlabs/argent-contracts/blob/develop/contracts/ens/ENSConsumer.sol // with few modifications. /** * @title ENSConsumer * @dev Helper contract to resolve ENS names. * @author Julien Niset - <[email protected]> */ contract ENSConsumer { using strings for *; // namehash('addr.reverse') bytes32 public constant ADDR_REVERSE_NODE = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2; // the address of the ENS registry address immutable ensRegistry; /** * @dev No address should be provided when deploying on Mainnet to avoid storage cost. The * contract will use the hardcoded value. */ constructor(address _ensRegistry) { ensRegistry = _ensRegistry; } /** * @dev Resolves an ENS name to an address. * @param _node The namehash of the ENS name. */ function resolveEns(bytes32 _node) public view returns (address) { address resolver = getENSRegistry().resolver(_node); return ENSResolver(resolver).addr(_node); } /** * @dev Gets the official ENS registry. */ function getENSRegistry() public view returns (ENSRegistry) { return ENSRegistry(ensRegistry); } /** * @dev Gets the official ENS reverse registrar. */ function getENSReverseRegistrar() public view returns (ENSReverseRegistrar) { return ENSReverseRegistrar(getENSRegistry().owner(ADDR_REVERSE_NODE)); } } // File: contracts/thirdparty/ens/BaseENSManager.sol // Taken from Argent's code base - https://github.com/argentlabs/argent-contracts/blob/develop/contracts/ens/ArgentENSManager.sol // with few modifications. /** * @dev Interface for an ENS Mananger. */ interface IENSManager { function changeRootnodeOwner(address _newOwner) external; function isAvailable(bytes32 _subnode) external view returns (bool); function resolveName(address _wallet) external view returns (string memory); function register( address _wallet, address _owner, string calldata _label, bytes calldata _approval ) external; } /** * @title BaseENSManager * @dev Implementation of an ENS manager that orchestrates the complete * registration of subdomains for a single root (e.g. argent.eth). * The contract defines a manager role who is the only role that can trigger the registration of * a new subdomain. * @author Julien Niset - <[email protected]> */ contract BaseENSManager is IENSManager, OwnerManagable, ENSConsumer { using strings for *; using BytesUtil for bytes; using MathUint for uint; // The managed root name string public rootName; // The managed root node bytes32 public immutable rootNode; // The address of the ENS resolver address public ensResolver; // *************** Events *************************** // event RootnodeOwnerChange(bytes32 indexed _rootnode, address indexed _newOwner); event ENSResolverChanged(address addr); event Registered(address indexed _wallet, address _owner, string _ens); event Unregistered(string _ens); // *************** Constructor ********************** // /** * @dev Constructor that sets the ENS root name and root node to manage. * @param _rootName The root name (e.g. argentx.eth). * @param _rootNode The node of the root name (e.g. namehash(argentx.eth)). */ constructor(string memory _rootName, bytes32 _rootNode, address _ensRegistry, address _ensResolver) ENSConsumer(_ensRegistry) { rootName = _rootName; rootNode = _rootNode; ensResolver = _ensResolver; } // *************** External Functions ********************* // /** * @dev This function must be called when the ENS Manager contract is replaced * and the address of the new Manager should be provided. * @param _newOwner The address of the new ENS manager that will manage the root node. */ function changeRootnodeOwner(address _newOwner) external override onlyOwner { getENSRegistry().setOwner(rootNode, _newOwner); emit RootnodeOwnerChange(rootNode, _newOwner); } /** * @dev Lets the owner change the address of the ENS resolver contract. * @param _ensResolver The address of the ENS resolver contract. */ function changeENSResolver(address _ensResolver) external onlyOwner { require(_ensResolver != address(0), "WF: address cannot be null"); ensResolver = _ensResolver; emit ENSResolverChanged(_ensResolver); } /** * @dev Lets the manager assign an ENS subdomain of the root node to a target address. * Registers both the forward and reverse ENS. * @param _wallet The wallet which owns the subdomain. * @param _owner The wallet's owner. * @param _label The subdomain label. * @param _approval The signature of _wallet, _owner and _label by a manager. */ function register( address _wallet, address _owner, string calldata _label, bytes calldata _approval ) external override onlyManager { verifyApproval(_wallet, _owner, _label, _approval); ENSRegistry _ensRegistry = getENSRegistry(); ENSResolver _ensResolver = ENSResolver(ensResolver); bytes32 labelNode = keccak256(abi.encodePacked(_label)); bytes32 node = keccak256(abi.encodePacked(rootNode, labelNode)); address currentOwner = _ensRegistry.owner(node); require(currentOwner == address(0), "AEM: _label is alrealdy owned"); // Forward ENS _ensRegistry.setSubnodeOwner(rootNode, labelNode, address(this)); _ensRegistry.setResolver(node, address(_ensResolver)); _ensRegistry.setOwner(node, _wallet); _ensResolver.setAddr(node, _wallet); // Reverse ENS strings.slice[] memory parts = new strings.slice[](2); parts[0] = _label.toSlice(); parts[1] = rootName.toSlice(); string memory name = ".".toSlice().join(parts); bytes32 reverseNode = getENSReverseRegistrar().node(_wallet); _ensResolver.setName(reverseNode, name); emit Registered(_wallet, _owner, name); } // *************** Public Functions ********************* // /** * @dev Resolves an address to an ENS name * @param _wallet The ENS owner address */ function resolveName(address _wallet) public view override returns (string memory) { bytes32 reverseNode = getENSReverseRegistrar().node(_wallet); return ENSResolver(ensResolver).name(reverseNode); } /** * @dev Returns true is a given subnode is available. * @param _subnode The target subnode. * @return true if the subnode is available. */ function isAvailable(bytes32 _subnode) public view override returns (bool) { bytes32 node = keccak256(abi.encodePacked(rootNode, _subnode)); address currentOwner = getENSRegistry().owner(node); if(currentOwner == address(0)) { return true; } return false; } function verifyApproval( address _wallet, address _owner, string calldata _label, bytes calldata _approval ) internal view { bytes32 messageHash = keccak256( abi.encodePacked( _wallet, _owner, _label ) ); bytes32 hash = keccak256( abi.encodePacked( "\x19Ethereum Signed Message:\n32", messageHash ) ); address signer = SignatureUtil.recoverECDSASigner(hash, _approval); require(isManager(signer), "UNAUTHORIZED"); } } // File: contracts/modules/ControllerImpl.sol // Copyright 2017 Loopring Technology Limited. /// @title ControllerImpl /// @dev Basic implementation of a Controller. /// /// @author Daniel Wang - <[email protected]> contract ControllerImpl is Claimable, Controller { HashStore public immutable hashStore; QuotaStore public immutable quotaStore; SecurityStore public immutable securityStore; WhitelistStore public immutable whitelistStore; ModuleRegistry public immutable override moduleRegistry; address public override walletFactory; address public immutable feeCollector; BaseENSManager public immutable ensManager; PriceOracle public immutable priceOracle; event AddressChanged( string name, address addr ); constructor( HashStore _hashStore, QuotaStore _quotaStore, SecurityStore _securityStore, WhitelistStore _whitelistStore, ModuleRegistry _moduleRegistry, address _feeCollector, BaseENSManager _ensManager, PriceOracle _priceOracle ) { hashStore = _hashStore; quotaStore = _quotaStore; securityStore = _securityStore; whitelistStore = _whitelistStore; moduleRegistry = _moduleRegistry; require(_feeCollector != address(0), "ZERO_ADDRESS"); feeCollector = _feeCollector; ensManager = _ensManager; priceOracle = _priceOracle; } function initWalletFactory(address _walletFactory) external onlyOwner { require(walletFactory == address(0), "INITIALIZED_ALREADY"); require(_walletFactory != address(0), "ZERO_ADDRESS"); walletFactory = _walletFactory; emit AddressChanged("WalletFactory", walletFactory); } } // File: contracts/modules/base/BaseModule.sol // Copyright 2017 Loopring Technology Limited. /// @title BaseModule /// @dev This contract implements some common functions that are likely /// be useful for all modules. /// /// @author Daniel Wang - <[email protected]> abstract contract BaseModule is Module { using MathUint for uint; using AddressUtil for address; event Activated (address wallet); event Deactivated (address wallet); ModuleRegistry public immutable moduleRegistry; SecurityStore public immutable securityStore; WhitelistStore public immutable whitelistStore; QuotaStore public immutable quotaStore; HashStore public immutable hashStore; address public immutable walletFactory; PriceOracle public immutable priceOracle; address public immutable feeCollector; function logicalSender() internal view virtual returns (address payable) { return msg.sender; } modifier onlyWalletOwner(address wallet, address addr) virtual { require(Wallet(wallet).owner() == addr, "NOT_WALLET_OWNER"); _; } modifier notWalletOwner(address wallet, address addr) virtual { require(Wallet(wallet).owner() != addr, "IS_WALLET_OWNER"); _; } modifier eligibleWalletOwner(address addr) { require(addr != address(0) && !addr.isContract(), "INVALID_OWNER"); _; } constructor(ControllerImpl _controller) { moduleRegistry = _controller.moduleRegistry(); securityStore = _controller.securityStore(); whitelistStore = _controller.whitelistStore(); quotaStore = _controller.quotaStore(); hashStore = _controller.hashStore(); walletFactory = _controller.walletFactory(); priceOracle = _controller.priceOracle(); feeCollector = _controller.feeCollector(); } /// @dev This method will cause an re-entry to the same module contract. function activate() external override virtual { address wallet = logicalSender(); bindMethods(wallet); emit Activated(wallet); } /// @dev This method will cause an re-entry to the same module contract. function deactivate() external override virtual { address wallet = logicalSender(); unbindMethods(wallet); emit Deactivated(wallet); } ///.@dev Gets the list of methods for binding to wallets. /// Sub-contracts should override this method to provide methods for /// wallet binding. /// @return methods A list of method selectors for binding to the wallet /// when this module is activated for the wallet. function bindableMethods() public pure virtual returns (bytes4[] memory methods) { } // ===== internal & private methods ===== /// @dev Binds all methods to the given wallet. function bindMethods(address wallet) internal { Wallet w = Wallet(wallet); bytes4[] memory methods = bindableMethods(); for (uint i = 0; i < methods.length; i++) { w.bindMethod(methods[i], address(this)); } } /// @dev Unbinds all methods from the given wallet. function unbindMethods(address wallet) internal { Wallet w = Wallet(wallet); bytes4[] memory methods = bindableMethods(); for (uint i = 0; i < methods.length; i++) { w.bindMethod(methods[i], address(0)); } } function transactCall( address wallet, address to, uint value, bytes memory data ) internal returns (bytes memory) { return Wallet(wallet).transact(uint8(1), to, value, data); } // Special case for transactCall to support transfers on "bad" ERC20 tokens function transactTokenTransfer( address wallet, address token, address to, uint amount ) internal { if (token == address(0)) { transactCall(wallet, to, amount, ""); return; } bytes memory txData = abi.encodeWithSelector( ERC20.transfer.selector, to, amount ); bytes memory returnData = transactCall(wallet, token, 0, txData); // `transactCall` will revert if the call was unsuccessful. // The only extra check we have to do is verify if the return value (if there is any) is correct. bool success = returnData.length == 0 ? true : abi.decode(returnData, (bool)); require(success, "ERC20_TRANSFER_FAILED"); } // Special case for transactCall to support approvals on "bad" ERC20 tokens function transactTokenApprove( address wallet, address token, address spender, uint amount ) internal { require(token != address(0), "INVALID_TOKEN"); bytes memory txData = abi.encodeWithSelector( ERC20.approve.selector, spender, amount ); bytes memory returnData = transactCall(wallet, token, 0, txData); // `transactCall` will revert if the call was unsuccessful. // The only extra check we have to do is verify if the return value (if there is any) is correct. bool success = returnData.length == 0 ? true : abi.decode(returnData, (bool)); require(success, "ERC20_APPROVE_FAILED"); } function transactDelegateCall( address wallet, address to, uint value, bytes calldata data ) internal returns (bytes memory) { return Wallet(wallet).transact(uint8(2), to, value, data); } function transactStaticCall( address wallet, address to, bytes calldata data ) internal returns (bytes memory) { return Wallet(wallet).transact(uint8(3), to, 0, data); } function reimburseGasFee( address wallet, address recipient, address gasToken, uint gasPrice, uint gasAmount ) internal { uint gasCost = gasAmount.mul(gasPrice); quotaStore.checkAndAddToSpent( wallet, gasToken, gasAmount, priceOracle ); transactTokenTransfer(wallet, gasToken, recipient, gasCost); } } // File: contracts/modules/base/MetaTxAware.sol // Copyright 2017 Loopring Technology Limited. /// @title MetaTxAware /// @author Daniel Wang - <[email protected]> /// /// The design of this contract is inspired by GSN's contract codebase: /// https://github.com/opengsn/gsn/contracts /// /// @dev Inherit this abstract contract to make a module meta-transaction /// aware. `msgSender()` shall be used to replace `msg.sender` for /// verifying permissions. abstract contract MetaTxAware { using AddressUtil for address; using BytesUtil for bytes; address public immutable metaTxForwarder; constructor(address _metaTxForwarder) { metaTxForwarder = _metaTxForwarder; } modifier txAwareHashNotAllowed() { require(txAwareHash() == 0, "INVALID_TX_AWARE_HASH"); _; } /// @dev Return's the function's logicial message sender. This method should be // used to replace `msg.sender` for all meta-tx enabled functions. function msgSender() internal view returns (address payable) { if (msg.data.length >= 56 && msg.sender == metaTxForwarder) { return msg.data.toAddress(msg.data.length - 52).toPayable(); } else { return msg.sender; } } function txAwareHash() internal view returns (bytes32) { if (msg.data.length >= 56 && msg.sender == metaTxForwarder) { return msg.data.toBytes32(msg.data.length - 32); } else { return 0; } } } // File: contracts/modules/base/MetaTxModule.sol // Copyright 2017 Loopring Technology Limited. /// @title MetaTxModule /// @dev Base contract for all modules that support meta-transactions. /// /// @author Daniel Wang - <[email protected]> /// /// The design of this contract is inspired by GSN's contract codebase: /// https://github.com/opengsn/gsn/contracts abstract contract MetaTxModule is MetaTxAware, BaseModule { using SignatureUtil for bytes32; constructor( ControllerImpl _controller, address _metaTxForwarder ) MetaTxAware(_metaTxForwarder) BaseModule(_controller) { } function logicalSender() internal view virtual override returns (address payable) { return msgSender(); } } // File: contracts/modules/security/GuardianUtils.sol // Copyright 2017 Loopring Technology Limited. /// @title GuardianUtils /// @author Brecht Devos - <[email protected]> library GuardianUtils { enum SigRequirement { MAJORITY_OWNER_NOT_ALLOWED, MAJORITY_OWNER_ALLOWED, MAJORITY_OWNER_REQUIRED, OWNER_OR_ANY_GUARDIAN, ANY_GUARDIAN } function requireMajority( SecurityStore securityStore, address wallet, address[] memory signers, SigRequirement requirement ) internal view returns (bool) { // We always need at least one signer if (signers.length == 0) { return false; } // Calculate total group sizes Data.Guardian[] memory allGuardians = securityStore.guardians(wallet, false); require(allGuardians.length > 0, "NO_GUARDIANS"); address lastSigner; bool walletOwnerSigned = false; address owner = Wallet(wallet).owner(); for (uint i = 0; i < signers.length; i++) { // Check for duplicates require(signers[i] > lastSigner, "INVALID_SIGNERS_ORDER"); lastSigner = signers[i]; if (signers[i] == owner) { walletOwnerSigned = true; } else { require(_isWalletGuardian(allGuardians, signers[i]), "SIGNER_NOT_GUARDIAN"); } } if (requirement == SigRequirement.OWNER_OR_ANY_GUARDIAN) { return signers.length == 1; } else if (requirement == SigRequirement.ANY_GUARDIAN) { require(!walletOwnerSigned, "WALLET_OWNER_SIGNATURE_NOT_ALLOWED"); return signers.length == 1; } // Check owner requirements if (requirement == SigRequirement.MAJORITY_OWNER_REQUIRED) { require(walletOwnerSigned, "WALLET_OWNER_SIGNATURE_REQUIRED"); } else if (requirement == SigRequirement.MAJORITY_OWNER_NOT_ALLOWED) { require(!walletOwnerSigned, "WALLET_OWNER_SIGNATURE_NOT_ALLOWED"); } uint numExtendedSigners = allGuardians.length; if (walletOwnerSigned) { numExtendedSigners += 1; require(signers.length > 1, "NO_GUARDIAN_SIGNED_BESIDES_OWNER"); } return _hasMajority(signers.length, numExtendedSigners); } function _isWalletGuardian( Data.Guardian[] memory allGuardians, address signer ) private pure returns (bool) { for (uint i = 0; i < allGuardians.length; i++) { if (allGuardians[i].addr == signer) { return true; } } return false; } function _hasMajority( uint signed, uint total ) private pure returns (bool) { return total > 0 && signed >= (total >> 1) + 1; } } // File: contracts/modules/security/SignedRequest.sol // Copyright 2017 Loopring Technology Limited. /// @title SignedRequest /// @dev Utility library for better handling of signed wallet requests. /// This library must be deployed and linked to other modules. /// /// @author Daniel Wang - <[email protected]> library SignedRequest { using SignatureUtil for bytes32; struct Request { address[] signers; bytes[] signatures; uint validUntil; address wallet; } function verifyRequest( HashStore hashStore, SecurityStore securityStore, bytes32 domainSeperator, bytes32 txAwareHash, GuardianUtils.SigRequirement sigRequirement, Request memory request, bytes memory encodedRequest ) public { require(block.timestamp <= request.validUntil, "EXPIRED_SIGNED_REQUEST"); bytes32 _txAwareHash = EIP712.hashPacked(domainSeperator, encodedRequest); // If txAwareHash from the meta-transaction is non-zero, // we must verify it matches the hash signed by the respective signers. require( txAwareHash == 0 || txAwareHash == _txAwareHash, "TX_INNER_HASH_MISMATCH" ); // Save hash to prevent replay attacks hashStore.verifyAndUpdate(request.wallet, _txAwareHash); require( _txAwareHash.verifySignatures(request.signers, request.signatures), "INVALID_SIGNATURES" ); require( GuardianUtils.requireMajority( securityStore, request.wallet, request.signers, sigRequirement ), "PERMISSION_DENIED" ); } } // File: contracts/modules/security/SecurityModule.sol // Copyright 2017 Loopring Technology Limited. /// @title SecurityStore /// /// @author Daniel Wang - <[email protected]> abstract contract SecurityModule is MetaTxModule { // The minimal number of guardians for recovery and locking. uint public constant TOUCH_GRACE_PERIOD = 30 days; event WalletLocked( address indexed wallet, address by, bool locked ); constructor( ControllerImpl _controller, address _metaTxForwarder ) MetaTxModule(_controller, _metaTxForwarder) { } modifier onlyFromWalletOrOwnerWhenUnlocked(address wallet) { address payable _logicalSender = logicalSender(); // If the wallet's signature verfication passes, the wallet must be unlocked. require( _logicalSender == wallet || (_logicalSender == Wallet(wallet).owner() && !_isWalletLocked(wallet)), "NOT_FROM_WALLET_OR_OWNER_OR_WALLET_LOCKED" ); securityStore.touchLastActiveWhenRequired(wallet, TOUCH_GRACE_PERIOD); _; } modifier onlyWalletGuardian(address wallet, address guardian) { require(securityStore.isGuardian(wallet, guardian, false), "NOT_GUARDIAN"); _; } modifier notWalletGuardian(address wallet, address guardian) { require(!securityStore.isGuardian(wallet, guardian, false), "IS_GUARDIAN"); _; } // ----- internal methods ----- function _lockWallet(address wallet, address by, bool locked) internal { securityStore.setLock(wallet, locked); emit WalletLocked(wallet, by, locked); } function _isWalletLocked(address wallet) internal view returns (bool) { return securityStore.isLocked(wallet); } function _updateQuota( QuotaStore qs, address wallet, address token, uint amount ) internal { if (amount == 0) return; if (qs == QuotaStore(0)) return; qs.checkAndAddToSpent( wallet, token, amount, priceOracle ); } } // File: contracts/modules/transfers/BaseTransferModule.sol // Copyright 2017 Loopring Technology Limited. /// @title BaseTransferModule /// @author Brecht Devos - <[email protected]> /// @author Daniel Wang - <[email protected]> abstract contract BaseTransferModule is SecurityModule { using MathUint for uint; event Transfered (address wallet, address token, address to, uint amount, bytes logdata); event Approved (address wallet, address token, address spender, uint amount); event ContractCalled(address wallet, address to, uint value, bytes data); function transferInternal( address wallet, address token, address to, uint amount, bytes calldata logdata ) internal { transactTokenTransfer(wallet, token, to, amount); emit Transfered(wallet, token, to, amount, logdata); } function approveInternal( address wallet, address token, address spender, uint amount ) internal returns (uint additionalAllowance) { // Current allowance uint allowance = ERC20(token).allowance(wallet, spender); if (amount != allowance) { // First reset the approved amount if needed if (allowance > 0) { transactTokenApprove(wallet, token, spender, 0); } // Now approve the requested amount transactTokenApprove(wallet, token, spender, amount); } // If we increased the allowance, calculate by how much if (amount > allowance) { additionalAllowance = amount.sub(allowance); } emit Approved(wallet, token, spender, amount); } function callContractInternal( address wallet, address to, uint value, bytes calldata txData ) internal virtual returns (bytes memory returnData) { // Calls from the wallet to itself are deemed special // (e.g. this is used for updating the wallet implementation) // We also disallow calls to module functions directly // (e.g. this is used for some special wallet <-> module interaction) require(wallet != to && !Wallet(wallet).hasModule(to), "CALL_DISALLOWED"); // Disallow general calls to token contracts (for tokens that have price data // so the quota is actually used). require(priceOracle.tokenValue(to, 1e18) == 0, "CALL_DISALLOWED"); returnData = transactCall(wallet, to, value, txData); emit ContractCalled(wallet, to, value, txData); } function isAddressWhitelisted(address wallet, address to) internal view returns (bool res) { (res,) = whitelistStore.isWhitelisted(wallet, to); } function isAddressDappOrWhitelisted(address wallet, address to) internal view returns (bool) { return whitelistStore.isDappOrWhitelisted(wallet, to); } } // File: contracts/modules/transfers/TransferModule.sol // Copyright 2017 Loopring Technology Limited. /// @title TransferModule /// @author Brecht Devos - <[email protected]> /// @author Daniel Wang - <[email protected]> abstract contract TransferModule is BaseTransferModule { using MathUint for uint; bytes32 public immutable TRANSFER_DOMAIN_SEPERATOR; uint public constant QUOTA_PENDING_PERIOD = 1 days; bytes32 public constant CHANGE_DAILY_QUOTE_TYPEHASH = keccak256( "changeDailyQuota(address wallet,uint256 validUntil,uint256 newQuota)" ); bytes32 public constant TRANSFER_TOKEN_TYPEHASH = keccak256( "transferToken(address wallet,uint256 validUntil,address token,address to,uint256 amount,bytes logdata)" ); bytes32 public constant APPROVE_TOKEN_TYPEHASH = keccak256( "approveToken(address wallet,uint256 validUntil,address token,address to,uint256 amount)" ); bytes32 public constant CALL_CONTRACT_TYPEHASH = keccak256( "callContract(address wallet,uint256 validUntil,address to,uint256 value,bytes data)" ); bytes32 public constant APPROVE_THEN_CALL_CONTRACT_TYPEHASH = keccak256( "approveThenCallContract(address wallet,uint256 validUntil,address token,address to,uint256 amount,uint256 value,bytes data)" ); constructor() { TRANSFER_DOMAIN_SEPERATOR = EIP712.hash( EIP712.Domain("TransferModule", "1.2.0", address(this)) ); } function changeDailyQuota( address wallet, uint newQuota ) external txAwareHashNotAllowed() onlyFromWalletOrOwnerWhenUnlocked(wallet) { _changeQuota(wallet, newQuota, QUOTA_PENDING_PERIOD); } function changeDailyQuotaWA( SignedRequest.Request calldata request, uint newQuota ) external { SignedRequest.verifyRequest( hashStore, securityStore, TRANSFER_DOMAIN_SEPERATOR, txAwareHash(), GuardianUtils.SigRequirement.MAJORITY_OWNER_REQUIRED, request, abi.encode( CHANGE_DAILY_QUOTE_TYPEHASH, request.wallet, request.validUntil, newQuota ) ); _changeQuota(request.wallet, newQuota, 0); } function transferToken( address wallet, address token, address to, uint amount, bytes calldata logdata, bool forceUseQuota ) external txAwareHashNotAllowed() onlyFromWalletOrOwnerWhenUnlocked(wallet) { if (forceUseQuota || !isAddressWhitelisted(wallet, to)) { _updateQuota(quotaStore, wallet, token, amount); } transferInternal(wallet, token, to, amount, logdata); } function transferTokenWA( SignedRequest.Request calldata request, address token, address to, uint amount, bytes calldata logdata ) external { SignedRequest.verifyRequest( hashStore, securityStore, TRANSFER_DOMAIN_SEPERATOR, txAwareHash(), GuardianUtils.SigRequirement.MAJORITY_OWNER_REQUIRED, request, abi.encode( TRANSFER_TOKEN_TYPEHASH, request.wallet, request.validUntil, token, to, amount, keccak256(logdata) ) ); transferInternal(request.wallet, token, to, amount, logdata); } function callContract( address wallet, address to, uint value, bytes calldata data, bool forceUseQuota ) external txAwareHashNotAllowed() onlyFromWalletOrOwnerWhenUnlocked(wallet) returns (bytes memory returnData) { if (forceUseQuota || !isAddressDappOrWhitelisted(wallet, to)) { _updateQuota(quotaStore, wallet, address(0), value); } return callContractInternal(wallet, to, value, data); } function callContractWA( SignedRequest.Request calldata request, address to, uint value, bytes calldata data ) external returns (bytes memory returnData) { SignedRequest.verifyRequest( hashStore, securityStore, TRANSFER_DOMAIN_SEPERATOR, txAwareHash(), GuardianUtils.SigRequirement.MAJORITY_OWNER_REQUIRED, request, abi.encode( CALL_CONTRACT_TYPEHASH, request.wallet, request.validUntil, to, value, keccak256(data) ) ); return callContractInternal(request.wallet, to, value, data); } function approveToken( address wallet, address token, address to, uint amount, bool forceUseQuota ) external txAwareHashNotAllowed() onlyFromWalletOrOwnerWhenUnlocked(wallet) { uint additionalAllowance = approveInternal(wallet, token, to, amount); if (forceUseQuota || !isAddressDappOrWhitelisted(wallet, to)) { _updateQuota(quotaStore, wallet, token, additionalAllowance); } } function approveTokenWA( SignedRequest.Request calldata request, address token, address to, uint amount ) external { SignedRequest.verifyRequest( hashStore, securityStore, TRANSFER_DOMAIN_SEPERATOR, txAwareHash(), GuardianUtils.SigRequirement.MAJORITY_OWNER_REQUIRED, request, abi.encode( APPROVE_TOKEN_TYPEHASH, request.wallet, request.validUntil, token, to, amount ) ); approveInternal(request.wallet, token, to, amount); } function approveThenCallContract( address wallet, address token, address to, uint amount, uint value, bytes calldata data, bool forceUseQuota ) external txAwareHashNotAllowed() onlyFromWalletOrOwnerWhenUnlocked(wallet) returns (bytes memory returnData) { uint additionalAllowance = approveInternal(wallet, token, to, amount); if (forceUseQuota || !isAddressDappOrWhitelisted(wallet, to)) { _updateQuota(quotaStore, wallet, token, additionalAllowance); _updateQuota(quotaStore, wallet, address(0), value); } return callContractInternal(wallet, to, value, data); } function approveThenCallContractWA( SignedRequest.Request calldata request, address token, address to, uint amount, uint value, bytes calldata data ) external returns (bytes memory returnData) { bytes memory encoded = abi.encode( APPROVE_THEN_CALL_CONTRACT_TYPEHASH, request.wallet, request.validUntil, token, to, amount, value, keccak256(data) ); SignedRequest.verifyRequest( hashStore, securityStore, TRANSFER_DOMAIN_SEPERATOR, txAwareHash(), GuardianUtils.SigRequirement.MAJORITY_OWNER_REQUIRED, request, encoded ); approveInternal(request.wallet, token, to, amount); return callContractInternal(request.wallet, to, value, data); } function getDailyQuota(address wallet) public view returns ( uint total, // 0 indicates quota is disabled uint spent, uint available ) { total = quotaStore.currentQuota(wallet); spent = quotaStore.spentQuota(wallet); available = quotaStore.availableQuota(wallet); } function _changeQuota( address wallet, uint newQuota, uint pendingPeriod ) private { uint _currentQuota = quotaStore.currentQuota(wallet); uint _pendingPeriod = pendingPeriod; if (_currentQuota == 0 || (newQuota > 0 && newQuota <= _currentQuota)) { _pendingPeriod = 0; } quotaStore.changeQuota(wallet, newQuota, block.timestamp.add(_pendingPeriod)); } } // File: contracts/modules/transfers/FinalTransferModule.sol // Copyright 2017 Loopring Technology Limited. /// @title FinalTransferModule /// @dev This module combines multiple small modules to /// minimize the number of modules to reduce gas used /// by wallet creation. contract FinalTransferModule is TransferModule { ControllerImpl private immutable controller_; constructor( ControllerImpl _controller, address _metaTxForwarder ) SecurityModule(_controller, _metaTxForwarder) TransferModule() { controller_ = _controller; } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"contract ControllerImpl","name":"_controller","type":"address"},{"internalType":"address","name":"_metaTxForwarder","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"wallet","type":"address"}],"name":"Activated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"ContractCalled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"wallet","type":"address"}],"name":"Deactivated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"logdata","type":"bytes"}],"name":"Transfered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"bool","name":"locked","type":"bool"}],"name":"WalletLocked","type":"event"},{"inputs":[],"name":"APPROVE_THEN_CALL_CONTRACT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"APPROVE_TOKEN_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CALL_CONTRACT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CHANGE_DAILY_QUOTE_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"QUOTA_PENDING_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TOUCH_GRACE_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TRANSFER_DOMAIN_SEPERATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TRANSFER_TOKEN_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"activate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"bool","name":"forceUseQuota","type":"bool"}],"name":"approveThenCallContract","outputs":[{"internalType":"bytes","name":"returnData","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct SignedRequest.Request","name":"request","type":"tuple"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"approveThenCallContractWA","outputs":[{"internalType":"bytes","name":"returnData","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"forceUseQuota","type":"bool"}],"name":"approveToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct SignedRequest.Request","name":"request","type":"tuple"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approveTokenWA","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"bindableMethods","outputs":[{"internalType":"bytes4[]","name":"methods","type":"bytes4[]"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"bool","name":"forceUseQuota","type":"bool"}],"name":"callContract","outputs":[{"internalType":"bytes","name":"returnData","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct SignedRequest.Request","name":"request","type":"tuple"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"callContractWA","outputs":[{"internalType":"bytes","name":"returnData","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"uint256","name":"newQuota","type":"uint256"}],"name":"changeDailyQuota","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct SignedRequest.Request","name":"request","type":"tuple"},{"internalType":"uint256","name":"newQuota","type":"uint256"}],"name":"changeDailyQuotaWA","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"deactivate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeCollector","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"}],"name":"getDailyQuota","outputs":[{"internalType":"uint256","name":"total","type":"uint256"},{"internalType":"uint256","name":"spent","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hashStore","outputs":[{"internalType":"contract HashStore","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"metaTxForwarder","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"moduleRegistry","outputs":[{"internalType":"contract ModuleRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceOracle","outputs":[{"internalType":"contract PriceOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"quotaStore","outputs":[{"internalType":"contract QuotaStore","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"securityStore","outputs":[{"internalType":"contract SecurityStore","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"logdata","type":"bytes"},{"internalType":"bool","name":"forceUseQuota","type":"bool"}],"name":"transferToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct SignedRequest.Request","name":"request","type":"tuple"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"logdata","type":"bytes"}],"name":"transferTokenWA","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"walletFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"whitelistStore","outputs":[{"internalType":"contract WhitelistStore","name":"","type":"address"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
6101e06040523480156200001257600080fd5b50604051620044ca380380620044ca833981016040819052620000359162000622565b818181818181806001600160a01b03166080816001600160a01b031660601b8152505050806001600160a01b031663b95459e46040518163ffffffff1660e01b815260040160206040518083038186803b1580156200009357600080fd5b505afa158015620000a8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000ce9190620005fc565b6001600160a01b031660a0816001600160a01b031660601b81525050806001600160a01b031663d51b3a1b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156200012457600080fd5b505afa15801562000139573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200015f9190620005fc565b6001600160a01b031660c0816001600160a01b031660601b81525050806001600160a01b03166337423d5e6040518163ffffffff1660e01b815260040160206040518083038186803b158015620001b557600080fd5b505afa158015620001ca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001f09190620005fc565b6001600160a01b031660e0816001600160a01b031660601b81525050806001600160a01b031663d9d104846040518163ffffffff1660e01b815260040160206040518083038186803b1580156200024657600080fd5b505afa1580156200025b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002819190620005fc565b6001600160a01b0316610100816001600160a01b031660601b81525050806001600160a01b031663cbe45d186040518163ffffffff1660e01b815260040160206040518083038186803b158015620002d857600080fd5b505afa158015620002ed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620003139190620005fc565b6001600160a01b0316610120816001600160a01b031660601b81525050806001600160a01b031663c5c036996040518163ffffffff1660e01b815260040160206040518083038186803b1580156200036a57600080fd5b505afa1580156200037f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620003a59190620005fc565b6001600160a01b0316610140816001600160a01b031660601b81525050806001600160a01b0316632630c12f6040518163ffffffff1660e01b815260040160206040518083038186803b158015620003fc57600080fd5b505afa15801562000411573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620004379190620005fc565b6001600160a01b0316610160816001600160a01b031660601b81525050806001600160a01b031663c415b95c6040518163ffffffff1660e01b815260040160206040518083038186803b1580156200048e57600080fd5b505afa158015620004a3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620004c99190620005fc565b6001600160a01b0316610180816001600160a01b031660601b8152505050505050506200056760405180606001604052806040518060400160405280600e81526020016d5472616e736665724d6f64756c6560901b8152508152602001604051806040016040528060058152602001640312e322e360dc1b8152508152602001306001600160a01b03168152506200058360201b62001c401760201c565b6101a0525060601b6001600160601b0319166101c052620006a5565b6000804690507f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f836000015180519060200120846020015180519060200120838660400151604051602001620005de95949392919062000660565b60405160208183030381529060405280519060200120915050919050565b6000602082840312156200060e578081fd5b81516200061b816200068c565b9392505050565b6000806040838503121562000635578081fd5b825162000642816200068c565b602084015190925062000655816200068c565b809150509250929050565b9485526020850193909352604084019190915260608301526001600160a01b0316608082015260a00190565b6001600160a01b0381168114620006a257600080fd5b50565b60805160601c60a05160601c60c05160601c60e05160601c6101005160601c6101205160601c6101405160601c6101605160601c6101805160601c6101a0516101c05160601c613cd7620007f36000395080610789528061095d5280610ac7528061142152806115395280611b2b52508061155d525080610709528061213852806122de525080611581525080610747528061091b5280610a8552806113df52806115a55280611ae9525080610672528061069e5280610e3e52806110ae52806115ed528061161652806116fd52806117c25280611a9a52806124705280612530525080610a23528061203052806126935250806105d95280610768528061093c5280610aa65280610db5528061102552806112a7528061140052806115c95280611a015280611b0a5280611e545250806113a15250806113545280611d93528061279c5250613cd76000f3fe608060405234801561001057600080fd5b50600436106101e55760003560e01c80638bd79ddd1161010f578063c415b95c116100a2578063d9d1048411610071578063d9d1048414610359578063e321531b14610361578063e3562a3614610383578063e9f8db0414610396576101e5565b8063c415b95c14610339578063c5c0369914610341578063cbe45d1814610349578063d51b3a1b14610351576101e5565b8063b345c8c1116100de578063b345c8c11461030e578063b95459e414610316578063bb64cabe1461031e578063bf01ce1114610331576101e5565b80638bd79ddd146102e15780639cdef1e5146102e9578063a664eb8e146102f1578063afa3363d146102f9576101e5565b806337423d5e1161018757806368e5d7e71161015657806368e5d7e7146102a0578063804fd8d9146102b3578063840fdc78146102c657806388666d81146102ce576101e5565b806337423d5e1461027557806344b5087b1461027d57806351953ea61461028557806351b42b0014610298576101e5565b80632483a854116101c35780632483a854146102325780632630c12f1461023a57806326e1c9781461024f57806331ea76b114610262576101e5565b80630c37ef4c146101ea5780630f15f4c01461020857806314c30b6614610212575b600080fd5b6101f26103a9565b6040516101ff91906136aa565b60405180910390f35b6102106103cd565b005b610225610220366004612e13565b61041c565b6040516101ff9190613888565b6101f26106e3565b610242610707565b6040516101ff919061349a565b61021061025d366004613059565b61072b565b61022561027036600461315b565b610886565b610242610a21565b6101f2610a45565b610210610293366004613285565b610a69565b610210610bbd565b6102256102ae366004612eb3565b610c01565b6102106102c1366004612d7c565b610e73565b6101f26110ee565b6102106102dc366004612f36565b6110f5565b6101f2611327565b6101f261132e565b610242611352565b610301611376565b6040516101ff9190613644565b6101f261137b565b61024261139f565b61021061032c3660046130c1565b6113c3565b6101f2611537565b61024261155b565b61024261157f565b6102426115a3565b6102426115c7565b6102426115eb565b61037461036f366004612cdd565b61160f565b6040516101ff93929190613b70565b610210610391366004612d15565b61184f565b6102256103a43660046131ff565b611acb565b7f5d26fd0f24f42b91458dbfab1103638e2751069d9a0e976ead0d06dc61d8c44881565b60006103d7611cb8565b90506103e281611cc8565b7f0cc43938d137e7efade6a531f663e78c1fc75257b0d65ffda2fdaf70cb49cdf981604051610411919061349a565b60405180910390a150565b6060610426611d6d565b15610466576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613992565b60405180910390fd5b886000610471611cb8565b90508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148061056657508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156104ee57600080fd5b505afa158015610502573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105269190612cf9565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148015610566575061056482611e14565b155b61059c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613a6e565b6040517fafaee9f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063afaee9f19061061390859062278d00906004016135f0565b600060405180830381600087803b15801561062d57600080fd5b505af1158015610641573d6000803e3d6000fd5b5050505060006106538c8c8c8c611edf565b9050848061066857506106668c8b611ff0565b155b156106c6576106997f00000000000000000000000000000000000000000000000000000000000000008d8d846120be565b6106c67f00000000000000000000000000000000000000000000000000000000000000008d60008b6120be565b6106d38c8b8a8a8a61218e565b9c9b505050505050505050505050565b7f1abaa7a2a14b14fa6512c7a9134df9bb505c491a37f68535dbd3f08222869e4881565b7f000000000000000000000000000000000000000000000000000000000000000081565b7335825b18e8948442abc361b361b007e31130f31463851edceb7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000006107b0611d6d565b60028a7f03651c586dcf8446a7e5a13bf0db304ab0ee2009d26debf6048a1f17bf02d1076107e46080830160608401612cdd565b8d604001358d8d8d604051602001610801969594939291906136b3565b6040516020818303038152906040526040518863ffffffff1660e01b8152600401610832979695949392919061389b565b60006040518083038186803b15801561084a57600080fd5b505af415801561085e573d6000803e3d6000fd5b5061087f92506108779150506080860160608701612cdd565b848484611edf565b5050505050565b6060807fbb030a49d1b37364f7119260cc9737cd720c251c3c1030d5e7b6cc0567e290206108b960808b018b8401612cdd565b8a604001358a8a8a8a8a8a6040516108d292919061348a565b6040519081900381206108ee9897969594939291602001613741565b60405160208183030381529060405290507335825b18e8948442abc361b361b007e31130f31463851edceb7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000610984611d6d565b60028f886040518863ffffffff1660e01b81526004016109aa979695949392919061389b565b60006040518083038186803b1580156109c257600080fd5b505af41580156109d6573d6000803e3d6000fd5b506109f792506109ef91505060808b0160608c01612cdd565b898989611edf565b50610a14610a0b60808b0160608c01612cdd565b8887878761218e565b9998505050505050505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f8a86a7c966e57488ccc873d575261078b72776cbeaa8a765c7f917c324d42f9881565b7335825b18e8948442abc361b361b007e31130f31463851edceb7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000610aee611d6d565b6002887f1abaa7a2a14b14fa6512c7a9134df9bb505c491a37f68535dbd3f08222869e48610b226080830160608401612cdd565b8b604001358b604051602001610b3b94939291906137d6565b6040516020818303038152906040526040518863ffffffff1660e01b8152600401610b6c979695949392919061389b565b60006040518083038186803b158015610b8457600080fd5b505af4158015610b98573d6000803e3d6000fd5b50610bb99250610bb19150506080840160608501612cdd565b826000612430565b5050565b6000610bc7611cb8565b9050610bd2816125ad565b7f749cb6b4c510bc468cf6b9c2086d6f0a54d6b18e25d37bf3200e68eab0880c0081604051610411919061349a565b6060610c0b611d6d565b15610c42576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613992565b866000610c4d611cb8565b90508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161480610d4257508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610cca57600080fd5b505afa158015610cde573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d029190612cf9565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148015610d425750610d4082611e14565b155b610d78576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613a6e565b6040517fafaee9f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063afaee9f190610def90859062278d00906004016135f0565b600060405180830381600087803b158015610e0957600080fd5b505af1158015610e1d573d6000803e3d6000fd5b505050508380610e345750610e328989611ff0565b155b15610e6657610e667f00000000000000000000000000000000000000000000000000000000000000008a60008a6120be565b610a14898989898961218e565b610e7b611d6d565b15610eb2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613992565b866000610ebd611cb8565b90508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161480610fb257508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610f3a57600080fd5b505afa158015610f4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f729190612cf9565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148015610fb25750610fb082611e14565b155b610fe8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613a6e565b6040517fafaee9f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063afaee9f19061105f90859062278d00906004016135f0565b600060405180830381600087803b15801561107957600080fd5b505af115801561108d573d6000803e3d6000fd5b5050505082806110a457506110a28988612653565b155b156110d5576110d57f00000000000000000000000000000000000000000000000000000000000000008a8a896120be565b6110e3898989898989612721565b505050505050505050565b62278d0081565b6110fd611d6d565b15611134576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613992565b81600061113f611cb8565b90508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148061123457508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156111bc57600080fd5b505afa1580156111d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111f49190612cf9565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148015611234575061123282611e14565b155b61126a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613a6e565b6040517fafaee9f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063afaee9f1906112e190859062278d00906004016135f0565b600060405180830381600087803b1580156112fb57600080fd5b505af115801561130f573d6000803e3d6000fd5b50505050611321848462015180612430565b50505050565b6201518081565b7fbb030a49d1b37364f7119260cc9737cd720c251c3c1030d5e7b6cc0567e2902081565b7f000000000000000000000000000000000000000000000000000000000000000081565b606090565b7f03651c586dcf8446a7e5a13bf0db304ab0ee2009d26debf6048a1f17bf02d10781565b7f000000000000000000000000000000000000000000000000000000000000000081565b7335825b18e8948442abc361b361b007e31130f31463851edceb7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611448611d6d565b60028c7f5d26fd0f24f42b91458dbfab1103638e2751069d9a0e976ead0d06dc61d8c44861147c6080830160608401612cdd565b8f604001358f8f8f8f8f60405161149492919061348a565b6040519081900381206114af979695949392916020016136f6565b6040516020818303038152906040526040518863ffffffff1660e01b81526004016114e0979695949392919061389b565b60006040518083038186803b1580156114f857600080fd5b505af415801561150c573d6000803e3d6000fd5b5061152f92506115259150506080880160608901612cdd565b8686868686612721565b505050505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b60008060007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663b0f191dc856040518263ffffffff1660e01b815260040161166d919061349a565b60206040518083038186803b15801561168557600080fd5b505afa158015611699573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116bd91906132c8565b6040517f564d3ca700000000000000000000000000000000000000000000000000000000815290935073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063564d3ca79061173290879060040161349a565b60206040518083038186803b15801561174a57600080fd5b505afa15801561175e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061178291906132c8565b6040517fa6ba012700000000000000000000000000000000000000000000000000000000815290925073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063a6ba0127906117f790879060040161349a565b60206040518083038186803b15801561180f57600080fd5b505afa158015611823573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061184791906132c8565b929491935050565b611857611d6d565b1561188e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613992565b846000611899611cb8565b90508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148061198e57508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561191657600080fd5b505afa15801561192a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061194e9190612cf9565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614801561198e575061198c82611e14565b155b6119c4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613a6e565b6040517fafaee9f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063afaee9f190611a3b90859062278d00906004016135f0565b600060405180830381600087803b158015611a5557600080fd5b505af1158015611a69573d6000803e3d6000fd5b505050506000611a7b88888888611edf565b90508380611a905750611a8e8887611ff0565b155b15611ac157611ac17f00000000000000000000000000000000000000000000000000000000000000008989846120be565b5050505050505050565b60607335825b18e8948442abc361b361b007e31130f31463851edceb7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611b52611d6d565b60028c7f8a86a7c966e57488ccc873d575261078b72776cbeaa8a765c7f917c324d42f98611b866080830160608401612cdd565b8f604001358f8f8f8f604051611b9d92919061348a565b604051908190038120611bb7969594939291602001613794565b6040516020818303038152906040526040518863ffffffff1660e01b8152600401611be8979695949392919061389b565b60006040518083038186803b158015611c0057600080fd5b505af4158015611c14573d6000803e3d6000fd5b50611c369250611c2d9150506080880160608901612cdd565b8686868661218e565b9695505050505050565b6000804690507f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f836000015180519060200120846020015180519060200120838660400151604051602001611c99959493929190613807565b604051602081830303815290604052805190602001209150505b919050565b6000611cc2612776565b90505b90565b806060611cd3611376565b905060005b8151811015611321578273ffffffffffffffffffffffffffffffffffffffff1663b149206e838381518110611d0957fe5b6020026020010151306040518363ffffffff1660e01b8152600401611d2f929190613840565b600060405180830381600087803b158015611d4957600080fd5b505af1158015611d5d573d6000803e3d6000fd5b505060019092019150611cd89050565b600060383610801590611db557503373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016145b15611e0c57611e0560206000369050036000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506128339050565b9050611cc5565b506000611cc5565b6040517f4a4fbeec00000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690634a4fbeec90611e8990859060040161349a565b60206040518083038186803b158015611ea157600080fd5b505afa158015611eb5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ed99190612f61565b92915050565b6000808473ffffffffffffffffffffffffffffffffffffffff1663dd62ed3e87866040518363ffffffff1660e01b8152600401611f1d9291906134bb565b60206040518083038186803b158015611f3557600080fd5b505afa158015611f49573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f6d91906132c8565b9050808314611f95578015611f8957611f89868686600061284f565b611f958686868661284f565b80831115611faa57611fa783826129b2565b91505b7f8d924fb660ea5dc99861c06d5104285681bb68ef281ebe73b6245e399a1ce2ff86868686604051611fdf94939291906134e2565b60405180910390a150949350505050565b6040517f439b7b7d00000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063439b7b7d9061206790869086906004016134bb565b60206040518083038186803b15801561207f57600080fd5b505afa158015612093573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120b79190612f61565b9392505050565b806120c857611321565b73ffffffffffffffffffffffffffffffffffffffff84166120e857611321565b6040517f71689b2b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516906371689b2b90612160908690869086907f0000000000000000000000000000000000000000000000000000000000000000906004016135b8565b600060405180830381600087803b15801561217a57600080fd5b505af1158015611ac1573d6000803e3d6000fd5b60608473ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff161415801561226b57506040517fc7b2e59600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff87169063c7b2e5969061221990889060040161349a565b60206040518083038186803b15801561223157600080fd5b505afa158015612245573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122699190612f61565b155b6122a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d906139c9565b6040517ff182178300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063f18217839061231d908890670de0b6b3a7640000906004016135f0565b60206040518083038186803b15801561233557600080fd5b505afa158015612349573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061236d91906132c8565b156123a4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d906139c9565b6123e686868686868080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506129f492505050565b90507f7d533d6faad77168a7f3e416e981e7d4f7b02844ddbbbdd26807b66a5002eb8e868686868660405161241f95949392919061356d565b60405180910390a195945050505050565b6040517fb0f191dc00000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063b0f191dc906124a590879060040161349a565b60206040518083038186803b1580156124bd57600080fd5b505afa1580156124d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124f591906132c8565b90508181158061251057506000841180156125105750818411155b15612519575060005b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001663054e378286866125614286612acd565b6040518463ffffffff1660e01b815260040161257f93929190613616565b600060405180830381600087803b15801561259957600080fd5b505af11580156110e3573d6000803e3d6000fd5b8060606125b8611376565b905060005b8151811015611321578273ffffffffffffffffffffffffffffffffffffffff1663b149206e8383815181106125ee57fe5b602002602001015160006040518363ffffffff1660e01b8152600401612615929190613840565b600060405180830381600087803b15801561262f57600080fd5b505af1158015612643573d6000803e3d6000fd5b5050600190920191506125bd9050565b6040517fb6b3527200000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063b6b35272906126ca90869086906004016134bb565b604080518083038186803b1580156126e157600080fd5b505afa1580156126f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127199190612f7d565b509392505050565b61272d86868686612b0a565b7fc88755fe083d57a3909c60ab246eef52835769b920f0a49045b2b1058afda71286868686868660405161276696959493929190613519565b60405180910390a1505050505050565b6000603836108015906127be57503373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016145b1561282c57611e0561281160346000369050036000368080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293925050612c539050565b73ffffffffffffffffffffffffffffffffffffffff16611cc5565b5033611cc5565b6000816020018351101561284657600080fd5b50016020015190565b73ffffffffffffffffffffffffffffffffffffffff831661289c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613a00565b606063095ea7b360e01b83836040516024016128b99291906135f0565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091529050606061294786866000856129f4565b90506000815160001461296d57818060200190518101906129689190612f61565b612970565b60015b9050806129a9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613b39565b50505050505050565b6000828211156129ee576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613a37565b50900390565b6040517f7122b74c00000000000000000000000000000000000000000000000000000000815260609073ffffffffffffffffffffffffffffffffffffffff861690637122b74c90612a5090600190889088908890600401613b86565b600060405180830381600087803b158015612a6a57600080fd5b505af1158015612a7e573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052612ac49190810190612faa565b95945050505050565b81810182811015611ed9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613acb565b73ffffffffffffffffffffffffffffffffffffffff8316612b4657612b40848383604051806020016040528060008152506129f4565b50611321565b606063a9059cbb60e01b8383604051602401612b639291906135f0565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290506060612bf186866000856129f4565b905060008151600014612c175781806020019051810190612c129190612f61565b612c1a565b60015b9050806129a9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613b02565b60008160140183511015612c6657600080fd5b5001602001516c01000000000000000000000000900490565b60008083601f840112612c90578182fd5b50813567ffffffffffffffff811115612ca7578182fd5b602083019150836020828501011115612cbf57600080fd5b9250929050565b600060808284031215612cd7578081fd5b50919050565b600060208284031215612cee578081fd5b81356120b781613c6e565b600060208284031215612d0a578081fd5b81516120b781613c6e565b600080600080600060a08688031215612d2c578081fd5b8535612d3781613c6e565b94506020860135612d4781613c6e565b93506040860135612d5781613c6e565b9250606086013591506080860135612d6e81613c93565b809150509295509295909350565b600080600080600080600060c0888a031215612d96578182fd5b8735612da181613c6e565b96506020880135612db181613c6e565b95506040880135612dc181613c6e565b945060608801359350608088013567ffffffffffffffff811115612de3578283fd5b612def8a828b01612c7f565b90945092505060a0880135612e0381613c93565b8091505092959891949750929550565b60008060008060008060008060e0898b031215612e2e578081fd5b8835612e3981613c6e565b97506020890135612e4981613c6e565b96506040890135612e5981613c6e565b9550606089013594506080890135935060a089013567ffffffffffffffff811115612e82578182fd5b612e8e8b828c01612c7f565b90945092505060c0890135612ea281613c93565b809150509295985092959890939650565b60008060008060008060a08789031215612ecb578182fd5b8635612ed681613c6e565b95506020870135612ee681613c6e565b945060408701359350606087013567ffffffffffffffff811115612f08578283fd5b612f1489828a01612c7f565b9094509250506080870135612f2881613c93565b809150509295509295509295565b60008060408385031215612f48578182fd5b8235612f5381613c6e565b946020939093013593505050565b600060208284031215612f72578081fd5b81516120b781613c93565b60008060408385031215612f8f578182fd5b8251612f9a81613c93565b6020939093015192949293505050565b600060208284031215612fbb578081fd5b815167ffffffffffffffff80821115612fd2578283fd5b818401915084601f830112612fe5578283fd5b815181811115612ff3578384fd5b60405160207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401168201018181108482111715613031578586fd5b604052818152838201602001871015613048578485fd5b611c36826020830160208701613c42565b6000806000806080858703121561306e578182fd5b843567ffffffffffffffff811115613084578283fd5b61309087828801612cc6565b94505060208501356130a181613c6e565b925060408501356130b181613c6e565b9396929550929360600135925050565b60008060008060008060a087890312156130d9578384fd5b863567ffffffffffffffff808211156130f0578586fd5b6130fc8a838b01612cc6565b97506020890135915061310e82613c6e565b90955060408801359061312082613c6e565b909450606088013593506080880135908082111561313c578384fd5b5061314989828a01612c7f565b979a9699509497509295939492505050565b600080600080600080600060c0888a031215613175578081fd5b873567ffffffffffffffff8082111561318c578283fd5b6131988b838c01612cc6565b985060208a013591506131aa82613c6e565b9096506040890135906131bc82613c6e565b909550606089013594506080890135935060a089013590808211156131df578283fd5b506131ec8a828b01612c7f565b989b979a50959850939692959293505050565b600080600080600060808688031215613216578283fd5b853567ffffffffffffffff8082111561322d578485fd5b61323989838a01612cc6565b96506020880135915061324b82613c6e565b9094506040870135935060608701359080821115613267578283fd5b5061327488828901612c7f565b969995985093965092949392505050565b60008060408385031215613297578182fd5b823567ffffffffffffffff8111156132ad578283fd5b6132b985828601612cc6565b95602094909401359450505050565b6000602082840312156132d9578081fd5b5051919050565b73ffffffffffffffffffffffffffffffffffffffff169052565b60008284526020808501945082825b8581101561334457813561331c81613c6e565b73ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613309565b509495945050505050565b818352602080840193600091908185020181018584845b878110156133eb57828403895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18836030181126133a4578687fd5b8701803567ffffffffffffffff8111156133bc578788fd5b8036038913156133ca578788fd5b6133d786828985016133f8565b9a87019a9550505090840190600101613366565b5091979650505050505050565b600082845282826020860137806020848601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011685010190509392505050565b60008151808452613458816020860160208601613c42565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000828483379101908152919050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff9485168152928416602084015292166040820152606081019190915260800190565b600073ffffffffffffffffffffffffffffffffffffffff8089168352808816602084015280871660408401525084606083015260a0608083015261356160a0830184866133f8565b98975050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff8088168352808716602084015250846040830152608060608301526135ad6080830184866133f8565b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff948516815292841660208401526040830191909152909116606082015260800190565b73ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff9390931683526020830191909152604082015260600190565b6020808252825182820181905260009190848201906040850190845b8181101561369e5783517fffffffff000000000000000000000000000000000000000000000000000000001683529284019291840191600101613660565b50909695505050505050565b90815260200190565b95865273ffffffffffffffffffffffffffffffffffffffff948516602087015260408601939093529083166060850152909116608083015260a082015260c00190565b96875273ffffffffffffffffffffffffffffffffffffffff958616602088015260408701949094529184166060860152909216608084015260a083019190915260c082015260e00190565b97885273ffffffffffffffffffffffffffffffffffffffff9687166020890152604088019590955292851660608701529316608085015260a084019290925260c083019190915260e08201526101000190565b95865273ffffffffffffffffffffffffffffffffffffffff9485166020870152604086019390935292166060840152608083019190915260a082015260c00190565b93845273ffffffffffffffffffffffffffffffffffffffff9290921660208401526040830152606082015260800190565b94855260208501939093526040840191909152606083015273ffffffffffffffffffffffffffffffffffffffff16608082015260a00190565b7fffffffff0000000000000000000000000000000000000000000000000000000092909216825273ffffffffffffffffffffffffffffffffffffffff16602082015260400190565b6000602082526120b76020830184613440565b600073ffffffffffffffffffffffffffffffffffffffff808a1683528089166020840152508660408301528560608301526138d585613c37565b608083015260e060a08301526138eb8485613bd1565b608060e0850152613901610160850182846132fa565b9150506139116020860186613bd1565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff208584030161010086015261394783828461334f565b9250505060408501356101208401526139636060860186613bc4565b6139716101408501826132e0565b5082810360c08401526139848185613440565b9a9950505050505050505050565b60208082526015908201527f494e56414c49445f54585f41574152455f484153480000000000000000000000604082015260600190565b6020808252600f908201527f43414c4c5f444953414c4c4f5745440000000000000000000000000000000000604082015260600190565b6020808252600d908201527f494e56414c49445f544f4b454e00000000000000000000000000000000000000604082015260600190565b6020808252600d908201527f5355425f554e444552464c4f5700000000000000000000000000000000000000604082015260600190565b60208082526029908201527f4e4f545f46524f4d5f57414c4c45545f4f525f4f574e45525f4f525f57414c4c60408201527f45545f4c4f434b45440000000000000000000000000000000000000000000000606082015260800190565b6020808252600c908201527f4144445f4f564552464c4f570000000000000000000000000000000000000000604082015260600190565b60208082526015908201527f45524332305f5452414e534645525f4641494c45440000000000000000000000604082015260600190565b60208082526014908201527f45524332305f415050524f56455f4641494c4544000000000000000000000000604082015260600190565b9283526020830191909152604082015260600190565b600060ff8616825273ffffffffffffffffffffffffffffffffffffffff8516602083015283604083015260806060830152611c366080830184613440565b600082356120b781613c6e565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112613c05578283fd5b830160208101925035905067ffffffffffffffff811115613c2557600080fd5b602081023603831315612cbf57600080fd5b8060058110611cb357fe5b60005b83811015613c5d578181015183820152602001613c45565b838111156113215750506000910152565b73ffffffffffffffffffffffffffffffffffffffff81168114613c9057600080fd5b50565b8015158114613c9057600080fdfea2646970667358221220340b52b18c986023465d22bab59e371777d70f64f9ec357141b9122d82d9d63664736f6c63430007000033000000000000000000000000b39e09279d4035c0f92307741d9dd8ed66e74de0000000000000000000000000e915058df18e7efe92af5c44df3f575fba061b64
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101e55760003560e01c80638bd79ddd1161010f578063c415b95c116100a2578063d9d1048411610071578063d9d1048414610359578063e321531b14610361578063e3562a3614610383578063e9f8db0414610396576101e5565b8063c415b95c14610339578063c5c0369914610341578063cbe45d1814610349578063d51b3a1b14610351576101e5565b8063b345c8c1116100de578063b345c8c11461030e578063b95459e414610316578063bb64cabe1461031e578063bf01ce1114610331576101e5565b80638bd79ddd146102e15780639cdef1e5146102e9578063a664eb8e146102f1578063afa3363d146102f9576101e5565b806337423d5e1161018757806368e5d7e71161015657806368e5d7e7146102a0578063804fd8d9146102b3578063840fdc78146102c657806388666d81146102ce576101e5565b806337423d5e1461027557806344b5087b1461027d57806351953ea61461028557806351b42b0014610298576101e5565b80632483a854116101c35780632483a854146102325780632630c12f1461023a57806326e1c9781461024f57806331ea76b114610262576101e5565b80630c37ef4c146101ea5780630f15f4c01461020857806314c30b6614610212575b600080fd5b6101f26103a9565b6040516101ff91906136aa565b60405180910390f35b6102106103cd565b005b610225610220366004612e13565b61041c565b6040516101ff9190613888565b6101f26106e3565b610242610707565b6040516101ff919061349a565b61021061025d366004613059565b61072b565b61022561027036600461315b565b610886565b610242610a21565b6101f2610a45565b610210610293366004613285565b610a69565b610210610bbd565b6102256102ae366004612eb3565b610c01565b6102106102c1366004612d7c565b610e73565b6101f26110ee565b6102106102dc366004612f36565b6110f5565b6101f2611327565b6101f261132e565b610242611352565b610301611376565b6040516101ff9190613644565b6101f261137b565b61024261139f565b61021061032c3660046130c1565b6113c3565b6101f2611537565b61024261155b565b61024261157f565b6102426115a3565b6102426115c7565b6102426115eb565b61037461036f366004612cdd565b61160f565b6040516101ff93929190613b70565b610210610391366004612d15565b61184f565b6102256103a43660046131ff565b611acb565b7f5d26fd0f24f42b91458dbfab1103638e2751069d9a0e976ead0d06dc61d8c44881565b60006103d7611cb8565b90506103e281611cc8565b7f0cc43938d137e7efade6a531f663e78c1fc75257b0d65ffda2fdaf70cb49cdf981604051610411919061349a565b60405180910390a150565b6060610426611d6d565b15610466576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613992565b60405180910390fd5b886000610471611cb8565b90508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148061056657508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156104ee57600080fd5b505afa158015610502573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105269190612cf9565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148015610566575061056482611e14565b155b61059c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613a6e565b6040517fafaee9f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a169063afaee9f19061061390859062278d00906004016135f0565b600060405180830381600087803b15801561062d57600080fd5b505af1158015610641573d6000803e3d6000fd5b5050505060006106538c8c8c8c611edf565b9050848061066857506106668c8b611ff0565b155b156106c6576106997f00000000000000000000000015f50bb48ca4be1ad4a6ad5804b18fb7d198618f8d8d846120be565b6106c67f00000000000000000000000015f50bb48ca4be1ad4a6ad5804b18fb7d198618f8d60008b6120be565b6106d38c8b8a8a8a61218e565b9c9b505050505050505050505050565b7f1abaa7a2a14b14fa6512c7a9134df9bb505c491a37f68535dbd3f08222869e4881565b7f0000000000000000000000004ead68830f45d73478c93953ed56c532bffff4b581565b7335825b18e8948442abc361b361b007e31130f31463851edceb7f000000000000000000000000c6ea970917451fe149537779c20f721eb5e71e767f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a7fe29830c6257a4a6af6ab85cf07b35cc164e7f225341f35ada8f444ca301c8ab96107b0611d6d565b60028a7f03651c586dcf8446a7e5a13bf0db304ab0ee2009d26debf6048a1f17bf02d1076107e46080830160608401612cdd565b8d604001358d8d8d604051602001610801969594939291906136b3565b6040516020818303038152906040526040518863ffffffff1660e01b8152600401610832979695949392919061389b565b60006040518083038186803b15801561084a57600080fd5b505af415801561085e573d6000803e3d6000fd5b5061087f92506108779150506080860160608701612cdd565b848484611edf565b5050505050565b6060807fbb030a49d1b37364f7119260cc9737cd720c251c3c1030d5e7b6cc0567e290206108b960808b018b8401612cdd565b8a604001358a8a8a8a8a8a6040516108d292919061348a565b6040519081900381206108ee9897969594939291602001613741565b60405160208183030381529060405290507335825b18e8948442abc361b361b007e31130f31463851edceb7f000000000000000000000000c6ea970917451fe149537779c20f721eb5e71e767f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a7fe29830c6257a4a6af6ab85cf07b35cc164e7f225341f35ada8f444ca301c8ab9610984611d6d565b60028f886040518863ffffffff1660e01b81526004016109aa979695949392919061389b565b60006040518083038186803b1580156109c257600080fd5b505af41580156109d6573d6000803e3d6000fd5b506109f792506109ef91505060808b0160608c01612cdd565b898989611edf565b50610a14610a0b60808b0160608c01612cdd565b8887878761218e565b9998505050505050505050565b7f0000000000000000000000001663647389993181d13cb45e2113c5d92fa89e7081565b7f8a86a7c966e57488ccc873d575261078b72776cbeaa8a765c7f917c324d42f9881565b7335825b18e8948442abc361b361b007e31130f31463851edceb7f000000000000000000000000c6ea970917451fe149537779c20f721eb5e71e767f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a7fe29830c6257a4a6af6ab85cf07b35cc164e7f225341f35ada8f444ca301c8ab9610aee611d6d565b6002887f1abaa7a2a14b14fa6512c7a9134df9bb505c491a37f68535dbd3f08222869e48610b226080830160608401612cdd565b8b604001358b604051602001610b3b94939291906137d6565b6040516020818303038152906040526040518863ffffffff1660e01b8152600401610b6c979695949392919061389b565b60006040518083038186803b158015610b8457600080fd5b505af4158015610b98573d6000803e3d6000fd5b50610bb99250610bb19150506080840160608501612cdd565b826000612430565b5050565b6000610bc7611cb8565b9050610bd2816125ad565b7f749cb6b4c510bc468cf6b9c2086d6f0a54d6b18e25d37bf3200e68eab0880c0081604051610411919061349a565b6060610c0b611d6d565b15610c42576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613992565b866000610c4d611cb8565b90508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161480610d4257508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610cca57600080fd5b505afa158015610cde573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d029190612cf9565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148015610d425750610d4082611e14565b155b610d78576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613a6e565b6040517fafaee9f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a169063afaee9f190610def90859062278d00906004016135f0565b600060405180830381600087803b158015610e0957600080fd5b505af1158015610e1d573d6000803e3d6000fd5b505050508380610e345750610e328989611ff0565b155b15610e6657610e667f00000000000000000000000015f50bb48ca4be1ad4a6ad5804b18fb7d198618f8a60008a6120be565b610a14898989898961218e565b610e7b611d6d565b15610eb2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613992565b866000610ebd611cb8565b90508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161480610fb257508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610f3a57600080fd5b505afa158015610f4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f729190612cf9565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148015610fb25750610fb082611e14565b155b610fe8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613a6e565b6040517fafaee9f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a169063afaee9f19061105f90859062278d00906004016135f0565b600060405180830381600087803b15801561107957600080fd5b505af115801561108d573d6000803e3d6000fd5b5050505082806110a457506110a28988612653565b155b156110d5576110d57f00000000000000000000000015f50bb48ca4be1ad4a6ad5804b18fb7d198618f8a8a896120be565b6110e3898989898989612721565b505050505050505050565b62278d0081565b6110fd611d6d565b15611134576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613992565b81600061113f611cb8565b90508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148061123457508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156111bc57600080fd5b505afa1580156111d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111f49190612cf9565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148015611234575061123282611e14565b155b61126a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613a6e565b6040517fafaee9f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a169063afaee9f1906112e190859062278d00906004016135f0565b600060405180830381600087803b1580156112fb57600080fd5b505af115801561130f573d6000803e3d6000fd5b50505050611321848462015180612430565b50505050565b6201518081565b7fbb030a49d1b37364f7119260cc9737cd720c251c3c1030d5e7b6cc0567e2902081565b7f000000000000000000000000e915058df18e7efe92af5c44df3f575fba061b6481565b606090565b7f03651c586dcf8446a7e5a13bf0db304ab0ee2009d26debf6048a1f17bf02d10781565b7f000000000000000000000000c8af9c2389af5710dba268050ebf9350cd0acab381565b7335825b18e8948442abc361b361b007e31130f31463851edceb7f000000000000000000000000c6ea970917451fe149537779c20f721eb5e71e767f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a7fe29830c6257a4a6af6ab85cf07b35cc164e7f225341f35ada8f444ca301c8ab9611448611d6d565b60028c7f5d26fd0f24f42b91458dbfab1103638e2751069d9a0e976ead0d06dc61d8c44861147c6080830160608401612cdd565b8f604001358f8f8f8f8f60405161149492919061348a565b6040519081900381206114af979695949392916020016136f6565b6040516020818303038152906040526040518863ffffffff1660e01b81526004016114e0979695949392919061389b565b60006040518083038186803b1580156114f857600080fd5b505af415801561150c573d6000803e3d6000fd5b5061152f92506115259150506080880160608901612cdd565b8686868686612721565b505050505050565b7fe29830c6257a4a6af6ab85cf07b35cc164e7f225341f35ada8f444ca301c8ab981565b7f000000000000000000000000ee94cf48924b720af939e732e98f30f9594f87c581565b7f0000000000000000000000009fad9ffcea95c345d41055a63bd099e1a057610981565b7f000000000000000000000000c6ea970917451fe149537779c20f721eb5e71e7681565b7f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a81565b7f00000000000000000000000015f50bb48ca4be1ad4a6ad5804b18fb7d198618f81565b60008060007f00000000000000000000000015f50bb48ca4be1ad4a6ad5804b18fb7d198618f73ffffffffffffffffffffffffffffffffffffffff1663b0f191dc856040518263ffffffff1660e01b815260040161166d919061349a565b60206040518083038186803b15801561168557600080fd5b505afa158015611699573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116bd91906132c8565b6040517f564d3ca700000000000000000000000000000000000000000000000000000000815290935073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000015f50bb48ca4be1ad4a6ad5804b18fb7d198618f169063564d3ca79061173290879060040161349a565b60206040518083038186803b15801561174a57600080fd5b505afa15801561175e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061178291906132c8565b6040517fa6ba012700000000000000000000000000000000000000000000000000000000815290925073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000015f50bb48ca4be1ad4a6ad5804b18fb7d198618f169063a6ba0127906117f790879060040161349a565b60206040518083038186803b15801561180f57600080fd5b505afa158015611823573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061184791906132c8565b929491935050565b611857611d6d565b1561188e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613992565b846000611899611cb8565b90508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148061198e57508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561191657600080fd5b505afa15801561192a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061194e9190612cf9565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614801561198e575061198c82611e14565b155b6119c4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613a6e565b6040517fafaee9f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a169063afaee9f190611a3b90859062278d00906004016135f0565b600060405180830381600087803b158015611a5557600080fd5b505af1158015611a69573d6000803e3d6000fd5b505050506000611a7b88888888611edf565b90508380611a905750611a8e8887611ff0565b155b15611ac157611ac17f00000000000000000000000015f50bb48ca4be1ad4a6ad5804b18fb7d198618f8989846120be565b5050505050505050565b60607335825b18e8948442abc361b361b007e31130f31463851edceb7f000000000000000000000000c6ea970917451fe149537779c20f721eb5e71e767f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a7fe29830c6257a4a6af6ab85cf07b35cc164e7f225341f35ada8f444ca301c8ab9611b52611d6d565b60028c7f8a86a7c966e57488ccc873d575261078b72776cbeaa8a765c7f917c324d42f98611b866080830160608401612cdd565b8f604001358f8f8f8f604051611b9d92919061348a565b604051908190038120611bb7969594939291602001613794565b6040516020818303038152906040526040518863ffffffff1660e01b8152600401611be8979695949392919061389b565b60006040518083038186803b158015611c0057600080fd5b505af4158015611c14573d6000803e3d6000fd5b50611c369250611c2d9150506080880160608901612cdd565b8686868661218e565b9695505050505050565b6000804690507f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f836000015180519060200120846020015180519060200120838660400151604051602001611c99959493929190613807565b604051602081830303815290604052805190602001209150505b919050565b6000611cc2612776565b90505b90565b806060611cd3611376565b905060005b8151811015611321578273ffffffffffffffffffffffffffffffffffffffff1663b149206e838381518110611d0957fe5b6020026020010151306040518363ffffffff1660e01b8152600401611d2f929190613840565b600060405180830381600087803b158015611d4957600080fd5b505af1158015611d5d573d6000803e3d6000fd5b505060019092019150611cd89050565b600060383610801590611db557503373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e915058df18e7efe92af5c44df3f575fba061b6416145b15611e0c57611e0560206000369050036000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506128339050565b9050611cc5565b506000611cc5565b6040517f4a4fbeec00000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a1690634a4fbeec90611e8990859060040161349a565b60206040518083038186803b158015611ea157600080fd5b505afa158015611eb5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ed99190612f61565b92915050565b6000808473ffffffffffffffffffffffffffffffffffffffff1663dd62ed3e87866040518363ffffffff1660e01b8152600401611f1d9291906134bb565b60206040518083038186803b158015611f3557600080fd5b505afa158015611f49573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f6d91906132c8565b9050808314611f95578015611f8957611f89868686600061284f565b611f958686868661284f565b80831115611faa57611fa783826129b2565b91505b7f8d924fb660ea5dc99861c06d5104285681bb68ef281ebe73b6245e399a1ce2ff86868686604051611fdf94939291906134e2565b60405180910390a150949350505050565b6040517f439b7b7d00000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000001663647389993181d13cb45e2113c5d92fa89e70169063439b7b7d9061206790869086906004016134bb565b60206040518083038186803b15801561207f57600080fd5b505afa158015612093573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120b79190612f61565b9392505050565b806120c857611321565b73ffffffffffffffffffffffffffffffffffffffff84166120e857611321565b6040517f71689b2b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516906371689b2b90612160908690869086907f0000000000000000000000004ead68830f45d73478c93953ed56c532bffff4b5906004016135b8565b600060405180830381600087803b15801561217a57600080fd5b505af1158015611ac1573d6000803e3d6000fd5b60608473ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff161415801561226b57506040517fc7b2e59600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff87169063c7b2e5969061221990889060040161349a565b60206040518083038186803b15801561223157600080fd5b505afa158015612245573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122699190612f61565b155b6122a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d906139c9565b6040517ff182178300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004ead68830f45d73478c93953ed56c532bffff4b5169063f18217839061231d908890670de0b6b3a7640000906004016135f0565b60206040518083038186803b15801561233557600080fd5b505afa158015612349573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061236d91906132c8565b156123a4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d906139c9565b6123e686868686868080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506129f492505050565b90507f7d533d6faad77168a7f3e416e981e7d4f7b02844ddbbbdd26807b66a5002eb8e868686868660405161241f95949392919061356d565b60405180910390a195945050505050565b6040517fb0f191dc00000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000015f50bb48ca4be1ad4a6ad5804b18fb7d198618f169063b0f191dc906124a590879060040161349a565b60206040518083038186803b1580156124bd57600080fd5b505afa1580156124d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124f591906132c8565b90508181158061251057506000841180156125105750818411155b15612519575060005b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000015f50bb48ca4be1ad4a6ad5804b18fb7d198618f1663054e378286866125614286612acd565b6040518463ffffffff1660e01b815260040161257f93929190613616565b600060405180830381600087803b15801561259957600080fd5b505af11580156110e3573d6000803e3d6000fd5b8060606125b8611376565b905060005b8151811015611321578273ffffffffffffffffffffffffffffffffffffffff1663b149206e8383815181106125ee57fe5b602002602001015160006040518363ffffffff1660e01b8152600401612615929190613840565b600060405180830381600087803b15801561262f57600080fd5b505af1158015612643573d6000803e3d6000fd5b5050600190920191506125bd9050565b6040517fb6b3527200000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000001663647389993181d13cb45e2113c5d92fa89e70169063b6b35272906126ca90869086906004016134bb565b604080518083038186803b1580156126e157600080fd5b505afa1580156126f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127199190612f7d565b509392505050565b61272d86868686612b0a565b7fc88755fe083d57a3909c60ab246eef52835769b920f0a49045b2b1058afda71286868686868660405161276696959493929190613519565b60405180910390a1505050505050565b6000603836108015906127be57503373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e915058df18e7efe92af5c44df3f575fba061b6416145b1561282c57611e0561281160346000369050036000368080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293925050612c539050565b73ffffffffffffffffffffffffffffffffffffffff16611cc5565b5033611cc5565b6000816020018351101561284657600080fd5b50016020015190565b73ffffffffffffffffffffffffffffffffffffffff831661289c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613a00565b606063095ea7b360e01b83836040516024016128b99291906135f0565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091529050606061294786866000856129f4565b90506000815160001461296d57818060200190518101906129689190612f61565b612970565b60015b9050806129a9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613b39565b50505050505050565b6000828211156129ee576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613a37565b50900390565b6040517f7122b74c00000000000000000000000000000000000000000000000000000000815260609073ffffffffffffffffffffffffffffffffffffffff861690637122b74c90612a5090600190889088908890600401613b86565b600060405180830381600087803b158015612a6a57600080fd5b505af1158015612a7e573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052612ac49190810190612faa565b95945050505050565b81810182811015611ed9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613acb565b73ffffffffffffffffffffffffffffffffffffffff8316612b4657612b40848383604051806020016040528060008152506129f4565b50611321565b606063a9059cbb60e01b8383604051602401612b639291906135f0565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290506060612bf186866000856129f4565b905060008151600014612c175781806020019051810190612c129190612f61565b612c1a565b60015b9050806129a9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045d90613b02565b60008160140183511015612c6657600080fd5b5001602001516c01000000000000000000000000900490565b60008083601f840112612c90578182fd5b50813567ffffffffffffffff811115612ca7578182fd5b602083019150836020828501011115612cbf57600080fd5b9250929050565b600060808284031215612cd7578081fd5b50919050565b600060208284031215612cee578081fd5b81356120b781613c6e565b600060208284031215612d0a578081fd5b81516120b781613c6e565b600080600080600060a08688031215612d2c578081fd5b8535612d3781613c6e565b94506020860135612d4781613c6e565b93506040860135612d5781613c6e565b9250606086013591506080860135612d6e81613c93565b809150509295509295909350565b600080600080600080600060c0888a031215612d96578182fd5b8735612da181613c6e565b96506020880135612db181613c6e565b95506040880135612dc181613c6e565b945060608801359350608088013567ffffffffffffffff811115612de3578283fd5b612def8a828b01612c7f565b90945092505060a0880135612e0381613c93565b8091505092959891949750929550565b60008060008060008060008060e0898b031215612e2e578081fd5b8835612e3981613c6e565b97506020890135612e4981613c6e565b96506040890135612e5981613c6e565b9550606089013594506080890135935060a089013567ffffffffffffffff811115612e82578182fd5b612e8e8b828c01612c7f565b90945092505060c0890135612ea281613c93565b809150509295985092959890939650565b60008060008060008060a08789031215612ecb578182fd5b8635612ed681613c6e565b95506020870135612ee681613c6e565b945060408701359350606087013567ffffffffffffffff811115612f08578283fd5b612f1489828a01612c7f565b9094509250506080870135612f2881613c93565b809150509295509295509295565b60008060408385031215612f48578182fd5b8235612f5381613c6e565b946020939093013593505050565b600060208284031215612f72578081fd5b81516120b781613c93565b60008060408385031215612f8f578182fd5b8251612f9a81613c93565b6020939093015192949293505050565b600060208284031215612fbb578081fd5b815167ffffffffffffffff80821115612fd2578283fd5b818401915084601f830112612fe5578283fd5b815181811115612ff3578384fd5b60405160207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401168201018181108482111715613031578586fd5b604052818152838201602001871015613048578485fd5b611c36826020830160208701613c42565b6000806000806080858703121561306e578182fd5b843567ffffffffffffffff811115613084578283fd5b61309087828801612cc6565b94505060208501356130a181613c6e565b925060408501356130b181613c6e565b9396929550929360600135925050565b60008060008060008060a087890312156130d9578384fd5b863567ffffffffffffffff808211156130f0578586fd5b6130fc8a838b01612cc6565b97506020890135915061310e82613c6e565b90955060408801359061312082613c6e565b909450606088013593506080880135908082111561313c578384fd5b5061314989828a01612c7f565b979a9699509497509295939492505050565b600080600080600080600060c0888a031215613175578081fd5b873567ffffffffffffffff8082111561318c578283fd5b6131988b838c01612cc6565b985060208a013591506131aa82613c6e565b9096506040890135906131bc82613c6e565b909550606089013594506080890135935060a089013590808211156131df578283fd5b506131ec8a828b01612c7f565b989b979a50959850939692959293505050565b600080600080600060808688031215613216578283fd5b853567ffffffffffffffff8082111561322d578485fd5b61323989838a01612cc6565b96506020880135915061324b82613c6e565b9094506040870135935060608701359080821115613267578283fd5b5061327488828901612c7f565b969995985093965092949392505050565b60008060408385031215613297578182fd5b823567ffffffffffffffff8111156132ad578283fd5b6132b985828601612cc6565b95602094909401359450505050565b6000602082840312156132d9578081fd5b5051919050565b73ffffffffffffffffffffffffffffffffffffffff169052565b60008284526020808501945082825b8581101561334457813561331c81613c6e565b73ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613309565b509495945050505050565b818352602080840193600091908185020181018584845b878110156133eb57828403895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18836030181126133a4578687fd5b8701803567ffffffffffffffff8111156133bc578788fd5b8036038913156133ca578788fd5b6133d786828985016133f8565b9a87019a9550505090840190600101613366565b5091979650505050505050565b600082845282826020860137806020848601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011685010190509392505050565b60008151808452613458816020860160208601613c42565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000828483379101908152919050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff9485168152928416602084015292166040820152606081019190915260800190565b600073ffffffffffffffffffffffffffffffffffffffff8089168352808816602084015280871660408401525084606083015260a0608083015261356160a0830184866133f8565b98975050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff8088168352808716602084015250846040830152608060608301526135ad6080830184866133f8565b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff948516815292841660208401526040830191909152909116606082015260800190565b73ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff9390931683526020830191909152604082015260600190565b6020808252825182820181905260009190848201906040850190845b8181101561369e5783517fffffffff000000000000000000000000000000000000000000000000000000001683529284019291840191600101613660565b50909695505050505050565b90815260200190565b95865273ffffffffffffffffffffffffffffffffffffffff948516602087015260408601939093529083166060850152909116608083015260a082015260c00190565b96875273ffffffffffffffffffffffffffffffffffffffff958616602088015260408701949094529184166060860152909216608084015260a083019190915260c082015260e00190565b97885273ffffffffffffffffffffffffffffffffffffffff9687166020890152604088019590955292851660608701529316608085015260a084019290925260c083019190915260e08201526101000190565b95865273ffffffffffffffffffffffffffffffffffffffff9485166020870152604086019390935292166060840152608083019190915260a082015260c00190565b93845273ffffffffffffffffffffffffffffffffffffffff9290921660208401526040830152606082015260800190565b94855260208501939093526040840191909152606083015273ffffffffffffffffffffffffffffffffffffffff16608082015260a00190565b7fffffffff0000000000000000000000000000000000000000000000000000000092909216825273ffffffffffffffffffffffffffffffffffffffff16602082015260400190565b6000602082526120b76020830184613440565b600073ffffffffffffffffffffffffffffffffffffffff808a1683528089166020840152508660408301528560608301526138d585613c37565b608083015260e060a08301526138eb8485613bd1565b608060e0850152613901610160850182846132fa565b9150506139116020860186613bd1565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff208584030161010086015261394783828461334f565b9250505060408501356101208401526139636060860186613bc4565b6139716101408501826132e0565b5082810360c08401526139848185613440565b9a9950505050505050505050565b60208082526015908201527f494e56414c49445f54585f41574152455f484153480000000000000000000000604082015260600190565b6020808252600f908201527f43414c4c5f444953414c4c4f5745440000000000000000000000000000000000604082015260600190565b6020808252600d908201527f494e56414c49445f544f4b454e00000000000000000000000000000000000000604082015260600190565b6020808252600d908201527f5355425f554e444552464c4f5700000000000000000000000000000000000000604082015260600190565b60208082526029908201527f4e4f545f46524f4d5f57414c4c45545f4f525f4f574e45525f4f525f57414c4c60408201527f45545f4c4f434b45440000000000000000000000000000000000000000000000606082015260800190565b6020808252600c908201527f4144445f4f564552464c4f570000000000000000000000000000000000000000604082015260600190565b60208082526015908201527f45524332305f5452414e534645525f4641494c45440000000000000000000000604082015260600190565b60208082526014908201527f45524332305f415050524f56455f4641494c4544000000000000000000000000604082015260600190565b9283526020830191909152604082015260600190565b600060ff8616825273ffffffffffffffffffffffffffffffffffffffff8516602083015283604083015260806060830152611c366080830184613440565b600082356120b781613c6e565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112613c05578283fd5b830160208101925035905067ffffffffffffffff811115613c2557600080fd5b602081023603831315612cbf57600080fd5b8060058110611cb357fe5b60005b83811015613c5d578181015183820152602001613c45565b838111156113215750506000910152565b73ffffffffffffffffffffffffffffffffffffffff81168114613c9057600080fd5b50565b8015158114613c9057600080fdfea2646970667358221220340b52b18c986023465d22bab59e371777d70f64f9ec357141b9122d82d9d63664736f6c63430007000033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000b39e09279d4035c0f92307741d9dd8ed66e74de0000000000000000000000000e915058df18e7efe92af5c44df3f575fba061b64
-----Decoded View---------------
Arg [0] : _controller (address): 0xb39e09279D4035c0F92307741d9dd8ed66e74de0
Arg [1] : _metaTxForwarder (address): 0xe915058dF18e7Efe92aF5c44Df3F575FBA061B64
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000b39e09279d4035c0f92307741d9dd8ed66e74de0
Arg [1] : 000000000000000000000000e915058df18e7efe92af5c44df3f575fba061b64
Deployed Bytecode Sourcemap
129362:343:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;120474:181;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;102046:192;;;:::i;:::-;;126374:797;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;120316:151::-;;;:::i;100729:43::-;;;:::i;:::-;;;;;;;:::i;125639:727::-;;;;;;:::i;:::-;;:::i;127179:1012::-;;;;;;:::i;:::-;;:::i;100527:46::-;;;:::i;120834:161::-;;;:::i;121666:636::-;;;;;;:::i;:::-;;:::i;102324:198::-;;;:::i;123705:586::-;;;;;;:::i;:::-;;:::i;122310:549::-;;;;;;:::i;:::-;;:::i;114632:49::-;;;:::i;121391:267::-;;;;;;:::i;:::-;;:::i;120257:50::-;;;:::i;121002:214::-;;;:::i;107376:40::-;;;:::i;102843:130::-;;;:::i;:::-;;;;;;;:::i;120662:165::-;;;:::i;100422:46::-;;;:::i;122867:830::-;;;;;;:::i;:::-;;:::i;120198:50::-;;;:::i;100779:44::-;;;:::i;100677:45::-;;;:::i;100629:41::-;;;:::i;100475:45::-;;;:::i;100580:42::-;;;:::i;128199:377::-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;:::i;125115:516::-;;;;;;:::i;:::-;;:::i;124299:808::-;;;;;;:::i;:::-;;:::i;120474:181::-;120524:131;120474:181;:::o;102046:192::-;102135:14;102152:15;:13;:15::i;:::-;102135:32;;102178:19;102190:6;102178:11;:19::i;:::-;102213:17;102223:6;102213:17;;;;;;:::i;:::-;;;;;;;;102046:192;:::o;126374:797::-;126761:23;107586:13;:11;:13::i;:::-;:18;107578:52;;;;;;;;;;;;:::i;:::-;;;;;;;;;126735:6:::1;115070:30;115103:15;:13;:15::i;:::-;115070:48;;115256:6;115238:24;;:14;:24;;;:111;;;;115305:6;115298:20;;;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;115280:40;;:14;:40;;;:68;;;;;115325:23;115341:6;115325:15;:23::i;:::-;115324:24;115280:68;115216:203;;;;;;;;;;;;:::i;:::-;115430:69;::::0;;;;:41:::1;:13;:41;::::0;::::1;::::0;:69:::1;::::0;115472:6;;114674:7:::1;::::0;115430:69:::1;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;126802:24:::2;126829:42;126845:6;126853:5;126860:2;126864:6;126829:15;:42::i;:::-;126802:69;;126888:13;:56;;;;126906:38;126933:6;126941:2;126906:26;:38::i;:::-;126905:39;126888:56;126884:215;;;126961:60;126974:10;126986:6;126994:5;127001:19;126961:12;:60::i;:::-;127036:51;127049:10;127061:6;127077:1;127081:5;127036:12;:51::i;:::-;127118:45;127139:6;127147:2;127151:5;127158:4;;127118:20;:45::i;:::-;127111:52:::0;126374:797;-1:-1:-1;;;;;;;;;;;;126374:797:0:o;120316:151::-;120370:97;120316:151;:::o;100729:43::-;;;:::o;125639:727::-;125827:13;:27;125869:9;125893:13;125921:25;125961:13;:11;:13::i;:::-;125989:52;126056:7;120711:116;126148:14;;;;;;;;:::i;:::-;126181:7;:18;;;126218:5;126242:2;126263:6;126078:206;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;125827:468;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;126308:50:0;;-1:-1:-1;126324:14:0;;-1:-1:-1;;126324:14:0;;;;;;;:::i;:::-;126340:5;126347:2;126351:6;126308:15;:50::i;:::-;;125639:727;;;;:::o;127179:1012::-;127462:23;;121064:152;127601:14;;;;;;;;:::i;:::-;127630:7;:18;;;127663:5;127683:2;127700:6;127721:5;127751:4;;127741:15;;;;;;;:::i;:::-;;;;;;;;;127526:241;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;127503:264;;127780:13;:27;127822:9;127846:13;127874:25;127914:13;:11;:13::i;:::-;127942:52;128009:7;128031;127780:269;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;128062:50:0;;-1:-1:-1;128078:14:0;;-1:-1:-1;;128078:14:0;;;;;;;:::i;:::-;128094:5;128101:2;128105:6;128062:15;:50::i;:::-;-1:-1:-1;128130:53:0;128151:14;;;;;;;;:::i;:::-;128167:2;128171:5;128178:4;;128130:20;:53::i;:::-;128123:60;127179:1012;-1:-1:-1;;;;;;;;;127179:1012:0:o;100527:46::-;;;:::o;120834:161::-;120883:112;120834:161;:::o;121666:636::-;121812:13;:27;121854:9;121878:13;121906:25;121946:13;:11;:13::i;:::-;121974:52;122041:7;120370:97;122138:14;;;;;;;;:::i;:::-;122171:7;:18;;;122208:8;122063:168;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;121812:430;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;122253:41:0;;-1:-1:-1;122266:14:0;;-1:-1:-1;;122266:14:0;;;;;;;:::i;:::-;122282:8;122292:1;122253:12;:41::i;:::-;121666:636;;:::o;102324:198::-;102415:14;102432:15;:13;:15::i;:::-;102415:32;;102458:21;102472:6;102458:13;:21::i;:::-;102495:19;102507:6;102495:19;;;;;;:::i;123705:586::-;124038:23;107586:13;:11;:13::i;:::-;:18;107578:52;;;;;;;;;;;;:::i;:::-;124012:6:::1;115070:30;115103:15;:13;:15::i;:::-;115070:48;;115256:6;115238:24;;:14;:24;;;:111;;;;115305:6;115298:20;;;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;115280:40;;:14;:40;;;:68;;;;;115325:23;115341:6;115325:15;:23::i;:::-;115324:24;115280:68;115216:203;;;;;;;;;;;;:::i;:::-;115430:69;::::0;;;;:41:::1;:13;:41;::::0;::::1;::::0;:69:::1;::::0;115472:6;;114674:7:::1;::::0;115430:69:::1;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;124083:13:::2;:56;;;;124101:38;124128:6;124136:2;124101:26;:38::i;:::-;124100:39;124083:56;124079:140;;;124156:51;124169:10;124181:6;124197:1;124201:5;124156:12;:51::i;:::-;124238:45;124259:6;124267:2;124271:5;124278:4;;124238:20;:45::i;122310:549::-:0;107586:13;:11;:13::i;:::-;:18;107578:52;;;;;;;;;;;;:::i;:::-;122633:6:::1;115070:30;115103:15;:13;:15::i;:::-;115070:48;;115256:6;115238:24;;:14;:24;;;:111;;;;115305:6;115298:20;;;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;115280:40;;:14;:40;;;:68;;;;;115325:23;115341:6;115325:15;:23::i;:::-;115324:24;115280:68;115216:203;;;;;;;;;;;;:::i;:::-;115430:69;::::0;;;;:41:::1;:13;:41;::::0;::::1;::::0;:69:::1;::::0;115472:6;;114674:7:::1;::::0;115430:69:::1;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;122661:13:::2;:50;;;;122679:32;122700:6;122708:2;122679:20;:32::i;:::-;122678:33;122661:50;122657:130;;;122728:47;122741:10;122753:6;122761:5;122768:6;122728:12;:47::i;:::-;122799:52;122816:6;122824:5;122831:2;122835:6;122843:7;;122799:16;:52::i;:::-;107641:1:::1;;122310:549:::0;;;;;;;:::o;114632:49::-;114674:7;114632:49;:::o;121391:267::-;107586:13;:11;:13::i;:::-;:18;107578:52;;;;;;;;;;;;:::i;:::-;121574:6:::1;115070:30;115103:15;:13;:15::i;:::-;115070:48;;115256:6;115238:24;;:14;:24;;;:111;;;;115305:6;115298:20;;;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;115280:40;;:14;:40;;;:68;;;;;115325:23;115341:6;115325:15;:23::i;:::-;115324:24;115280:68;115216:203;;;;;;;;;;;;:::i;:::-;115430:69;::::0;;;;:41:::1;:13;:41;::::0;::::1;::::0;:69:::1;::::0;115472:6;;114674:7:::1;::::0;115430:69:::1;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;121598:52:::2;121611:6;121619:8;120301:6;121598:12;:52::i;:::-;107641:1:::1;;121391:267:::0;;:::o;120257:50::-;120301:6;120257:50;:::o;121002:214::-;121064:152;121002:214;:::o;107376:40::-;;;:::o;102843:130::-;102935:23;102843:130;:::o;120662:165::-;120711:116;120662:165;:::o;100422:46::-;;;:::o;122867:830::-;123110:13;:27;123152:9;123176:13;123204:25;123244:13;:11;:13::i;:::-;123272:52;123339:7;120524:131;123432:14;;;;;;;;:::i;:::-;123465:7;:18;;;123502:5;123526:2;123547:6;123582:7;;123572:18;;;;;;;:::i;:::-;;;;;;;;;123361:244;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;123110:506;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;123629:60:0;;-1:-1:-1;123646:14:0;;-1:-1:-1;;123646:14:0;;;;;;;:::i;:::-;123662:5;123669:2;123673:6;123681:7;;123629:16;:60::i;:::-;122867:830;;;;;;:::o;120198:50::-;;;:::o;100779:44::-;;;:::o;100677:45::-;;;:::o;100629:41::-;;;:::o;100475:45::-;;;:::o;100580:42::-;;;:::o;128199:377::-;128300:10;128358;128383:14;128433:10;:23;;;128457:6;128433:31;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;128483:29;;;;;128425:39;;-1:-1:-1;128483:21:0;:10;:21;;;;:29;;128505:6;;128483:29;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;128535:33;;;;;128475:37;;-1:-1:-1;128535:25:0;:10;:25;;;;:33;;128561:6;;128535:33;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;128199:377;;;;-1:-1:-1;;128199:377:0:o;125115:516::-;107586:13;:11;:13::i;:::-;:18;107578:52;;;;;;;;;;;;:::i;:::-;125369:6:::1;115070:30;115103:15;:13;:15::i;:::-;115070:48;;115256:6;115238:24;;:14;:24;;;:111;;;;115305:6;115298:20;;;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;115280:40;;:14;:40;;;:68;;;;;115325:23;115341:6;115325:15;:23::i;:::-;115324:24;115280:68;115216:203;;;;;;;;;;;;:::i;:::-;115430:69;::::0;;;;:41:::1;:13;:41;::::0;::::1;::::0;:69:::1;::::0;115472:6;;114674:7:::1;::::0;115430:69:::1;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;125393:24:::2;125420:42;125436:6;125444:5;125451:2;125455:6;125420:15;:42::i;:::-;125393:69;;125479:13;:56;;;;125497:38;125524:6;125532:2;125497:26;:38::i;:::-;125496:39;125479:56;125475:149;;;125552:60;125565:10;125577:6;125585:5;125592:19;125552:12;:60::i;:::-;115510:1;107641::::1;;125115:516:::0;;;;;:::o;124299:808::-;124508:23;124549:13;:27;124591:9;124615:13;124643:25;124683:13;:11;:13::i;:::-;124711:52;124778:7;120883:112;124870:14;;;;;;;;:::i;:::-;124903:7;:18;;;124940:2;124961:5;124995:4;;124985:15;;;;;;;:::i;:::-;;;;;;;;;124800:215;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;124549:477;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;125046:53:0;;-1:-1:-1;125067:14:0;;-1:-1:-1;;125067:14:0;;;;;;;:::i;:::-;125083:2;125087:5;125094:4;;125046:20;:53::i;:::-;125039:60;124299:808;-1:-1:-1;;;;;;124299:808:0:o;1372:466::-;1458:7;1483:13;1530:9;1518:21;;1192:111;1670:6;:11;;;1654:29;;;;;;1718:6;:14;;;1702:32;;;;;;1753:8;1780:6;:24;;;1584:235;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;1560:270;;;;;;1553:277;;;1372:466;;;;:::o;109104:169::-;109214:15;109254:11;:9;:11::i;:::-;109247:18;;109104:169;;:::o;103083:276::-;103172:6;103190:23;103216:17;:15;:17::i;:::-;103190:43;;103249:6;103244:108;103265:7;:14;103261:1;:18;103244:108;;;103301:1;:12;;;103314:7;103322:1;103314:10;;;;;;;;;;;;;;103334:4;103301:39;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;103281:3:0;;;;;-1:-1:-1;103244:108:0;;-1:-1:-1;103244:108:0;108130:280;108203:7;108251:2;108232:8;:21;;;;:54;;-1:-1:-1;108257:10:0;:29;108271:15;108257:29;;108232:54;108228:175;;;108310:40;108347:2;108329:8;;:15;;:20;108310:8;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;108310:18:0;;:40;-1:-1:-1;;108310:18:0;:40;-1:-1:-1;108310:40:0:i;:::-;108303:47;;;;108228:175;-1:-1:-1;108390:1:0;108383:8;;116122:158;116242:30;;;;;116213:4;;116242:22;:13;:22;;;;:30;;116265:6;;116242:30;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;116235:37;116122:158;-1:-1:-1;;116122:158:0:o;117631:875::-;117803:24;117875:14;117898:5;117892:22;;;117915:6;117923:7;117892:39;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;117875:56;;117958:9;117948:6;:19;117944:328;;118046:13;;118042:101;;118080:47;118101:6;118109:5;118116:7;118125:1;118080:20;:47::i;:::-;118208:52;118229:6;118237:5;118244:7;118253:6;118208:20;:52::i;:::-;118362:9;118353:6;:18;118349:94;;;118410:21;:6;118421:9;118410:10;:21::i;:::-;118388:43;;118349:94;118458:40;118467:6;118475:5;118482:7;118491:6;118458:40;;;;;;;;;:::i;:::-;;;;;;;;117631:875;;;;;;;:::o;119649:197::-;119792:46;;;;;119763:4;;119792:34;:14;:34;;;;:46;;119827:6;;119835:2;;119792:46;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;119785:53;119649:197;-1:-1:-1;;;119649:197:0:o;116288:380::-;116466:11;116462:24;;116479:7;;116462:24;116500:19;;;116496:32;;116521:7;;116496:32;116540:120;;;;;:21;;;;;;:120;;116576:6;;116597:5;;116617:6;;116638:11;;116540:120;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;118514:928;118712:23;119048:2;119038:12;;:6;:12;;;;:45;;;;-1:-1:-1;119055:28:0;;;;;:24;;;;;;:28;;119080:2;;119055:28;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;119054:29;119038:45;119030:73;;;;;;;;;;;;:::i;:::-;119255:32;;;;;:22;:11;:22;;;;:32;;119278:2;;119282:4;;119255:32;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:37;119247:65;;;;;;;;;;;;:::i;:::-;119338:39;119351:6;119359:2;119363:5;119370:6;;119338:39;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;119338:12:0;;-1:-1:-1;;;119338:39:0:i;:::-;119325:52;;119393:41;119408:6;119416:2;119420:5;119427:6;;119393:41;;;;;;;;;;:::i;:::-;;;;;;;;118514:928;;;;;;;:::o;128584:474::-;128755:31;;;;;128734:18;;128755:23;:10;:23;;;;:31;;128779:6;;128755:31;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;128734:52;-1:-1:-1;128821:13:0;128849:18;;;:65;;;128883:1;128872:8;:12;:41;;;;;128900:13;128888:8;:25;;128872:41;128845:116;;;-1:-1:-1;128948:1:0;128845:116;128973:22;:10;:22;;128996:6;129004:8;129014:35;:15;129034:14;129014:19;:35::i;:::-;128973:77;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;103424:275;103515:6;103533:23;103559:17;:15;:17::i;:::-;103533:43;;103592:6;103587:105;103608:7;:14;103604:1;:18;103587:105;;;103644:1;:12;;;103657:7;103665:1;103657:10;;;;;;;;;;;;;;103677:1;103644:36;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;103624:3:0;;;;;-1:-1:-1;103587:105:0;;-1:-1:-1;103587:105:0;119450:191;119593:40;;;;;119558:8;;119593:28;:14;:28;;;;:40;;119622:6;;119630:2;;119593:40;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;119584:49:0;119450:191;-1:-1:-1;;;119450:191:0:o;117304:319::-;117505:48;117527:6;117535:5;117542:2;117546:6;117505:21;:48::i;:::-;117569:46;117580:6;117588:5;117595:2;117599:6;117607:7;;117569:46;;;;;;;;;;;:::i;:::-;;;;;;;;117304:319;;;;;;:::o;107815:307::-;107886:15;107942:2;107923:8;:21;;;;:54;;-1:-1:-1;107948:10:0;:29;107962:15;107948:29;;107923:54;107919:196;;;108001:52;:40;108038:2;108020:8;;:15;;:20;108001:8;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;108001:18:0;;:40;-1:-1:-1;;108001:18:0;:40;-1:-1:-1;108001:40:0:i;:::-;:50;;;:52::i;107919:196::-;-1:-1:-1;108093:10:0;108086:17;;7975:304;8052:7;8098:6;8107:2;8098:11;8080:6;:13;:30;;8072:39;;;;;;-1:-1:-1;8199:30:0;8215:4;8199:30;8193:37;;7975:304::o;104972:765::-;105155:19;;;105147:45;;;;;;;;;;;;:::i;:::-;105203:19;105262:22;;;105299:7;105321:6;105225:113;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;105349:23:0;105375:38;105388:6;105396:5;-1:-1:-1;105225:113:0;105375:12;:38::i;:::-;105349:64;;105600:12;105615:10;:17;105636:1;105615:22;:63;;105659:10;105648:30;;;;;;;;;;;;:::i;:::-;105615:63;;;105640:4;105615:63;105600:78;;105697:7;105689:40;;;;;;;;;;;;:::i;:::-;104972:765;;;;;;;:::o;517:193::-;625:4;660:1;655;:6;;647:32;;;;;;;;;;;;:::i;:::-;-1:-1:-1;697:5:0;;;517:193::o;103707:264::-;103913:50;;;;;103876:12;;103913:23;;;;;;:50;;103943:1;;103947:2;;103951:5;;103958:4;;103913:50;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;103906:57;103707:264;-1:-1:-1;;;;;103707:264:0:o;718:191::-;854:5;;;878:6;;;;870:31;;;;;;;;;;;;:::i;104060:823::-;104235:19;;;104231:109;;104271:36;104284:6;104292:2;104296:6;104271:36;;;;;;;;;;;;:12;:36::i;:::-;;104322:7;;104231:109;104352:19;104411:23;;;104449:2;104466:6;104374:109;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;104494:23:0;104520:38;104533:6;104541:5;-1:-1:-1;104374:109:0;104520:12;:38::i;:::-;104494:64;;104745:12;104760:10;:17;104781:1;104760:22;:63;;104804:10;104793:30;;;;;;;;;;;;:::i;:::-;104760:63;;;104785:4;104760:63;104745:78;;104842:7;104834:41;;;;;;;;;;;;:::i;4935:338::-;5012:7;5058:6;5067:2;5058:11;5040:6;:13;:30;;5032:39;;;;;;-1:-1:-1;5163:30:0;5179:4;5163:30;5157:37;5196:27;5153:71;;;4935:338::o;563:336:-1:-;;;677:3;670:4;662:6;658:17;654:27;644:2;;-1:-1;;685:12;644:2;-1:-1;715:20;;755:18;744:30;;741:2;;;-1:-1;;777:12;741:2;821:4;813:6;809:17;797:29;;872:3;821:4;852:17;813:6;838:32;;835:41;832:2;;;889:1;;879:12;832:2;637:262;;;;;:::o;1395:159::-;;1506:3;1497:6;1492:3;1488:16;1484:26;1481:2;;;-1:-1;;1513:12;1481:2;-1:-1;1533:15;1474:80;-1:-1;1474:80::o;1839:241::-;;1943:2;1931:9;1922:7;1918:23;1914:32;1911:2;;;-1:-1;;1949:12;1911:2;85:6;72:20;97:33;124:5;97:33;:::i;2087:263::-;;2202:2;2190:9;2181:7;2177:23;2173:32;2170:2;;;-1:-1;;2208:12;2170:2;226:6;220:13;238:33;265:5;238:33;:::i;2357:737::-;;;;;;2526:3;2514:9;2505:7;2501:23;2497:33;2494:2;;;-1:-1;;2533:12;2494:2;85:6;72:20;97:33;124:5;97:33;:::i;:::-;2585:63;-1:-1;2685:2;2724:22;;72:20;97:33;72:20;97:33;:::i;:::-;2693:63;-1:-1;2793:2;2832:22;;72:20;97:33;72:20;97:33;:::i;:::-;2801:63;-1:-1;2901:2;2940:22;;1628:20;;-1:-1;3009:3;3046:22;;347:20;372:30;347:20;372:30;:::i;:::-;3018:60;;;;2488:606;;;;;;;;:::o;3101:987::-;;;;;;;;3306:3;3294:9;3285:7;3281:23;3277:33;3274:2;;;-1:-1;;3313:12;3274:2;85:6;72:20;97:33;124:5;97:33;:::i;:::-;3365:63;-1:-1;3465:2;3504:22;;72:20;97:33;72:20;97:33;:::i;:::-;3473:63;-1:-1;3573:2;3612:22;;72:20;97:33;72:20;97:33;:::i;:::-;3581:63;-1:-1;3681:2;3720:22;;1628:20;;-1:-1;3817:3;3802:19;;3789:33;3842:18;3831:30;;3828:2;;;-1:-1;;3864:12;3828:2;3902:64;3958:7;3949:6;3938:9;3934:22;3902:64;:::i;:::-;3884:82;;-1:-1;3884:82;-1:-1;;4003:3;4040:22;;347:20;372:30;347:20;372:30;:::i;:::-;4012:60;;;;3268:820;;;;;;;;;;:::o;4095:1113::-;;;;;;;;;4317:3;4305:9;4296:7;4292:23;4288:33;4285:2;;;-1:-1;;4324:12;4285:2;85:6;72:20;97:33;124:5;97:33;:::i;:::-;4376:63;-1:-1;4476:2;4515:22;;72:20;97:33;72:20;97:33;:::i;:::-;4484:63;-1:-1;4584:2;4623:22;;72:20;97:33;72:20;97:33;:::i;:::-;4592:63;-1:-1;4692:2;4731:22;;1628:20;;-1:-1;4800:3;4840:22;;1628:20;;-1:-1;4937:3;4922:19;;4909:33;4962:18;4951:30;;4948:2;;;-1:-1;;4984:12;4948:2;5022:64;5078:7;5069:6;5058:9;5054:22;5022:64;:::i;:::-;5004:82;;-1:-1;5004:82;-1:-1;;5123:3;5160:22;;347:20;372:30;347:20;372:30;:::i;:::-;5132:60;;;;4279:929;;;;;;;;;;;:::o;5215:861::-;;;;;;;5403:3;5391:9;5382:7;5378:23;5374:33;5371:2;;;-1:-1;;5410:12;5371:2;85:6;72:20;97:33;124:5;97:33;:::i;:::-;5462:63;-1:-1;5562:2;5601:22;;72:20;97:33;72:20;97:33;:::i;:::-;5570:63;-1:-1;5670:2;5709:22;;1628:20;;-1:-1;5806:2;5791:18;;5778:32;5830:18;5819:30;;5816:2;;;-1:-1;;5852:12;5816:2;5890:64;5946:7;5937:6;5926:9;5922:22;5890:64;:::i;:::-;5872:82;;-1:-1;5872:82;-1:-1;;5991:3;6028:22;;347:20;372:30;347:20;372:30;:::i;:::-;6000:60;;;;5365:711;;;;;;;;:::o;6083:366::-;;;6204:2;6192:9;6183:7;6179:23;6175:32;6172:2;;;-1:-1;;6210:12;6172:2;85:6;72:20;97:33;124:5;97:33;:::i;:::-;6262:63;6362:2;6401:22;;;;1628:20;;-1:-1;;;6166:283::o;6456:257::-;;6568:2;6556:9;6547:7;6543:23;6539:32;6536:2;;;-1:-1;;6574:12;6536:2;495:6;489:13;507:30;531:5;507:30;:::i;6720:393::-;;;6849:2;6837:9;6828:7;6824:23;6820:32;6817:2;;;-1:-1;;6855:12;6817:2;495:6;489:13;507:30;531:5;507:30;:::i;:::-;7015:2;7065:22;;;;1776:13;6907:71;;1776:13;;-1:-1;;;6811:302::o;7120:360::-;;7244:2;7232:9;7223:7;7219:23;7215:32;7212:2;;;-1:-1;;7250:12;7212:2;7301:17;7295:24;7339:18;;7331:6;7328:30;7325:2;;;-1:-1;;7361:12;7325:2;7447:6;7436:9;7432:22;;;1020:3;1013:4;1005:6;1001:17;997:27;987:2;;-1:-1;;1028:12;987:2;1068:6;1062:13;7339:18;42868:6;42865:30;42862:2;;;-1:-1;;42898:12;42862:2;42532;42526:9;7244:2;42971:9;1013:4;42956:6;42952:17;42948:33;42562:6;42558:17;;42669:6;42657:10;42654:22;7339:18;42621:10;42618:34;42615:62;42612:2;;;-1:-1;;42680:12;42612:2;42532;42699:22;1160:21;;;1260:16;;;7244:2;1260:16;1257:25;-1:-1;1254:2;;;-1:-1;;1285:12;1254:2;1305:39;1337:6;7244:2;1236:5;1232:16;7244:2;1202:6;1198:17;1305:39;:::i;7487:757::-;;;;;7669:3;7657:9;7648:7;7644:23;7640:33;7637:2;;;-1:-1;;7676:12;7637:2;7734:17;7721:31;7772:18;7764:6;7761:30;7758:2;;;-1:-1;;7794:12;7758:2;7824:80;7896:7;7887:6;7876:9;7872:22;7824:80;:::i;:::-;7814:90;;;7941:2;7984:9;7980:22;72:20;97:33;124:5;97:33;:::i;:::-;7949:63;-1:-1;8049:2;8088:22;;72:20;97:33;72:20;97:33;:::i;:::-;7631:613;;;;-1:-1;8057:63;;8157:2;8196:22;1628:20;;-1:-1;;7631:613::o;8251:1007::-;;;;;;;8469:3;8457:9;8448:7;8444:23;8440:33;8437:2;;;-1:-1;;8476:12;8437:2;8534:17;8521:31;8572:18;;8564:6;8561:30;8558:2;;;-1:-1;;8594:12;8558:2;8624:80;8696:7;8687:6;8676:9;8672:22;8624:80;:::i;:::-;8614:90;;8741:2;8784:9;8780:22;72:20;63:29;;97:33;124:5;97:33;:::i;:::-;8749:63;;-1:-1;8849:2;8888:22;;72:20;;97:33;72:20;97:33;:::i;:::-;8857:63;;-1:-1;8957:2;8996:22;;1628:20;;-1:-1;9093:3;9078:19;;9065:33;;9107:30;;;9104:2;;;-1:-1;;9140:12;9104:2;;9178:64;9234:7;9225:6;9214:9;9210:22;9178:64;:::i;:::-;8431:827;;;;-1:-1;8431:827;;-1:-1;8431:827;;9160:82;;8431:827;-1:-1;;;8431:827::o;9265:1133::-;;;;;;;;9500:3;9488:9;9479:7;9475:23;9471:33;9468:2;;;-1:-1;;9507:12;9468:2;9565:17;9552:31;9603:18;;9595:6;9592:30;9589:2;;;-1:-1;;9625:12;9589:2;9655:80;9727:7;9718:6;9707:9;9703:22;9655:80;:::i;:::-;9645:90;;9772:2;9815:9;9811:22;72:20;63:29;;97:33;124:5;97:33;:::i;:::-;9780:63;;-1:-1;9880:2;9919:22;;72:20;;97:33;72:20;97:33;:::i;:::-;9888:63;;-1:-1;9988:2;10027:22;;1628:20;;-1:-1;10096:3;10136:22;;1628:20;;-1:-1;10233:3;10218:19;;10205:33;;10247:30;;;10244:2;;;-1:-1;;10280:12;10244:2;;10318:64;10374:7;10365:6;10354:9;10350:22;10318:64;:::i;:::-;9462:936;;;;-1:-1;9462:936;;-1:-1;9462:936;;;;10300:82;;-1:-1;;;9462:936::o;10405:881::-;;;;;;10606:3;10594:9;10585:7;10581:23;10577:33;10574:2;;;-1:-1;;10613:12;10574:2;10671:17;10658:31;10709:18;;10701:6;10698:30;10695:2;;;-1:-1;;10731:12;10695:2;10761:80;10833:7;10824:6;10813:9;10809:22;10761:80;:::i;:::-;10751:90;;10878:2;10921:9;10917:22;72:20;63:29;;97:33;124:5;97:33;:::i;:::-;10886:63;;-1:-1;10986:2;11025:22;;1628:20;;-1:-1;11122:2;11107:18;;11094:32;;11135:30;;;11132:2;;;-1:-1;;11168:12;11132:2;;11206:64;11262:7;11253:6;11242:9;11238:22;11206:64;:::i;:::-;10568:718;;;;-1:-1;10568:718;;-1:-1;11188:82;;;10568:718;-1:-1;;;10568:718::o;11293:506::-;;;11441:2;11429:9;11420:7;11416:23;11412:32;11409:2;;;-1:-1;;11447:12;11409:2;11505:17;11492:31;11543:18;11535:6;11532:30;11529:2;;;-1:-1;;11565:12;11529:2;11595:80;11667:7;11658:6;11647:9;11643:22;11595:80;:::i;:::-;11585:90;11712:2;11751:22;;;;1628:20;;-1:-1;;;;11403:396::o;11806:263::-;;11921:2;11909:9;11900:7;11896:23;11892:32;11889:2;;;-1:-1;;11927:12;11889:2;-1:-1;1776:13;;11883:186;-1:-1;11883:186::o;12835:113::-;47991:42;47980:54;12906:37;;12900:48::o;13104:669::-;;44238:6;44233:3;44226:19;44275:4;;44270:3;44266:14;13249:91;;13425:21;-1:-1;13452:299;13477:6;13474:1;13471:13;13452:299;;;85:6;72:20;97:33;124:5;97:33;:::i;:::-;47991:42;47980:54;12906:37;;12246:14;;;;45619:12;;;;13499:1;13492:9;13452:299;;;-1:-1;13757:10;;13236:537;-1:-1;;;;;13236:537::o;14527:939::-;44226:19;;;44275:4;44266:14;;;;14527:939;;44275:4;14845:17;;;14836:27;;;44266:14;14959:21;14527:939;14986:441;15011:6;15008:1;15005:13;14986:441;;;15073:9;15067:4;15063:20;15058:3;15051:33;46834:3;46821:17;46878:48;46902:8;46886:14;46882:29;46878:48;46858:18;46854:73;46844:2;;-1:-1;;46931:12;46844:2;46960:33;;47015:19;;47085:18;47074:30;;47071:2;;;-1:-1;;47107:12;47071:2;47164:17;46886:14;47144:38;47134:8;47130:53;47127:2;;;-1:-1;;47186:12;47127:2;12590:82;15297:4;15282:13;44275:4;47053:5;47049:16;12590:82;:::i;:::-;15406:14;;;;15187:115;-1:-1;;;43841:14;;;;15033:1;15026:9;14986:441;;;-1:-1;15450:10;;14679:787;-1:-1;;;;;;;14679:787::o;15969:297::-;;44238:6;44233:3;44226:19;50799:6;50794:3;44275:4;44270:3;44266:14;50776:30;-1:-1;44275:4;50846:6;44270:3;50837:16;;50830:27;44275:4;51232:7;51236:2;16252:6;51216:14;51212:28;44270:3;16221:39;;16214:46;;16069:197;;;;;:::o;16939:343::-;;17081:5;43581:12;44238:6;44233:3;44226:19;17174:52;17219:6;44275:4;44270:3;44266:14;44275:4;17200:5;17196:16;17174:52;:::i;:::-;51236:2;51216:14;51232:7;51212:28;17238:39;;;;44275:4;17238:39;;17029:253;-1:-1;;17029:253::o;23843:291::-;;50799:6;50794:3;50789;50776:30;50837:16;;50830:27;;;50837:16;23987:147;-1:-1;23987:147::o;24141:222::-;47991:42;47980:54;;;;12906:37;;24268:2;24253:18;;24239:124::o;24370:333::-;47991:42;47980:54;;;12906:37;;47980:54;;24689:2;24674:18;;12906:37;24525:2;24510:18;;24496:207::o;24710:556::-;47991:42;47980:54;;;12906:37;;47980:54;;;25086:2;25071:18;;12906:37;47980:54;;25169:2;25154:18;;12906:37;25252:2;25237:18;;15545:37;;;;24921:3;24906:19;;24892:374::o;25273:772::-;;47991:42;;47427:5;47980:54;12913:3;12906:37;47991:42;47427:5;47980:54;25705:2;25694:9;25690:18;12906:37;47991:42;47427:5;47980:54;25788:2;25777:9;25773:18;12906:37;;15575:5;25871:2;25860:9;25856:18;15545:37;25540:3;25908;25897:9;25893:19;25886:49;25949:86;25540:3;25529:9;25525:19;26021:6;26013;25949:86;:::i;:::-;25941:94;25511:534;-1:-1;;;;;;;;25511:534::o;26052:660::-;;47991:42;;47427:5;47980:54;12913:3;12906:37;47991:42;47427:5;47980:54;26456:2;26445:9;26441:18;12906:37;;15575:5;26539:2;26528:9;26524:18;15545:37;26291:3;26576:2;26565:9;26561:18;26554:48;26616:86;26291:3;26280:9;26276:19;26688:6;26680;26616:86;:::i;:::-;26608:94;26262:450;-1:-1;;;;;;;26262:450::o;26719:596::-;47991:42;47980:54;;;12906:37;;47980:54;;;27115:2;27100:18;;12906:37;27198:2;27183:18;;15545:37;;;;47980:54;;;27301:2;27286:18;;17744:68;26950:3;26935:19;;26921:394::o;27322:385::-;47991:42;47980:54;;;;12906:37;;27693:2;27678:18;;19337:76;27503:2;27488:18;;27474:233::o;28054:444::-;47991:42;47980:54;;;;12906:37;;28401:2;28386:18;;15545:37;;;;28484:2;28469:18;;15545:37;28237:2;28222:18;;28208:290::o;28505:366::-;28680:2;28694:47;;;43581:12;;28665:18;;;44226:19;;;28505:366;;28680:2;43300:14;;;;44266;;;;28505:366;14213:257;14238:6;14235:1;14232:13;14213:257;;;14299:13;;47688:66;47677:78;15781:36;;43841:14;;;;12424;;;;14260:1;14253:9;14213:257;;;-1:-1;28747:114;;28651:220;-1:-1;;;;;;28651:220::o;28878:222::-;15545:37;;;29005:2;28990:18;;28976:124::o;29107:780::-;15545:37;;;47991:42;47980:54;;;29539:2;29524:18;;12906:37;29622:2;29607:18;;15545:37;;;;47980:54;;;29705:2;29690:18;;12906:37;47980:54;;;29788:3;29773:19;;12906:37;29872:3;29857:19;;15545:37;29374:3;29359:19;;29345:542::o;29894:892::-;15545:37;;;47991:42;47980:54;;;30354:2;30339:18;;12906:37;30437:2;30422:18;;15545:37;;;;47980:54;;;30520:2;30505:18;;12906:37;47980:54;;;30603:3;30588:19;;12906:37;30687:3;30672:19;;15545:37;;;;30771:3;30756:19;;15545:37;30189:3;30174:19;;30160:626::o;30793:1004::-;15545:37;;;47991:42;47980:54;;;31281:2;31266:18;;12906:37;31364:2;31349:18;;15545:37;;;;47980:54;;;31447:2;31432:18;;12906:37;47980:54;;31530:3;31515:19;;12906:37;31614:3;31599:19;;15545:37;;;;31698:3;31683:19;;15545:37;;;;31782:3;31767:19;;15545:37;31116:3;31101:19;;31087:710::o;31804:780::-;15545:37;;;47991:42;47980:54;;;32236:2;32221:18;;12906:37;32319:2;32304:18;;15545:37;;;;47980:54;;32402:2;32387:18;;12906:37;32485:3;32470:19;;15545:37;;;;32569:3;32554:19;;15545:37;32071:3;32056:19;;32042:542::o;32591:556::-;15545:37;;;47991:42;47980:54;;;;32967:2;32952:18;;12906:37;33050:2;33035:18;;15545:37;33133:2;33118:18;;15545:37;32802:3;32787:19;;32773:374::o;33154:668::-;15545:37;;;33558:2;33543:18;;15545:37;;;;33641:2;33626:18;;15545:37;;;;33724:2;33709:18;;15545:37;47991:42;47980:54;33807:3;33792:19;;12906:37;33393:3;33378:19;;33364:458::o;33829:329::-;47688:66;47677:78;;;;15781:36;;47991:42;47980:54;34144:2;34129:18;;12906:37;33982:2;33967:18;;33953:205::o;34517:306::-;;34662:2;34683:17;34676:47;34737:76;34662:2;34651:9;34647:18;34799:6;34737:76;:::i;35095:1306::-;;47991:42;;47427:5;47980:54;17751:3;17744:68;47991:42;47427:5;47980:54;35746:2;35735:9;35731:18;17744:68;;15575:5;35837:2;35826:9;35822:18;15545:37;15575:5;35928:2;35917:9;35913:18;15545:37;50278:43;19221:5;50278:43;:::i;:::-;36036:3;36025:9;36021:19;19161:67;35525:3;36074;36063:9;36059:19;36052:49;22401:77;22461:16;22454:5;22401:77;:::i;:::-;36036:3;35525;35514:9;35510:19;22491:38;22544:127;22309:14;35514:9;22309:14;22652:12;22638;22544:127;:::i;:::-;22536:135;;;22759:88;35746:2;22834:5;22830:16;22823:5;22759:88;:::i;:::-;22883:14;35514:9;22887:4;22883:14;;22867;35514:9;22867:14;22860:38;22913:147;23055:4;23041:12;23027;22913:147;:::i;:::-;22905:155;;;;35837:2;23171:5;23167:16;1628:20;23246:14;35514:9;23246:14;15545:37;23325:50;35928:2;23362:5;23358:16;23351:5;23325:50;:::i;:::-;23381:71;23437:14;35514:9;23437:14;23423:12;23381:71;:::i;:::-;;36282:9;36276:4;36272:20;36266:3;36255:9;36251:19;36244:49;36307:84;36386:4;36377:6;36307:84;:::i;:::-;36299:92;35496:905;-1:-1;;;;;;;;;;35496:905::o;37767:416::-;37967:2;37981:47;;;19650:2;37952:18;;;44226:19;19686:23;44266:14;;;19666:44;19729:12;;;37938:245::o;38190:416::-;38390:2;38404:47;;;19980:2;38375:18;;;44226:19;20016:17;44266:14;;;19996:38;20053:12;;;38361:245::o;38613:416::-;38813:2;38827:47;;;20304:2;38798:18;;;44226:19;20340:15;44266:14;;;20320:36;20375:12;;;38784:245::o;39036:416::-;39236:2;39250:47;;;20626:2;39221:18;;;44226:19;20662:15;44266:14;;;20642:36;20697:12;;;39207:245::o;39459:416::-;39659:2;39673:47;;;20948:2;39644:18;;;44226:19;20984:34;44266:14;;;20964:55;21053:11;21039:12;;;21032:33;21084:12;;;39630:245::o;39882:416::-;40082:2;40096:47;;;21335:2;40067:18;;;44226:19;21371:14;44266;;;21351:35;21405:12;;;40053:245::o;40305:416::-;40505:2;40519:47;;;21656:2;40490:18;;;44226:19;21692:23;44266:14;;;21672:44;21735:12;;;40476:245::o;40728:416::-;40928:2;40942:47;;;21986:2;40913:18;;;44226:19;22022:22;44266:14;;;22002:43;22064:12;;;40899:245::o;41380:444::-;15545:37;;;41727:2;41712:18;;15545:37;;;;41810:2;41795:18;;15545:37;41563:2;41548:18;;41534:290::o;41831:632::-;;48196:4;23824:5;48185:16;23803:3;23796:35;47991:42;47427:5;47980:54;42217:2;42206:9;42202:18;12906:37;15575:5;42300:2;42289:9;42285:18;15545:37;42056:3;42337:2;42326:9;42322:18;42315:48;42377:76;42056:3;42045:9;42041:19;42439:6;42377:76;:::i;45519:119::-;;85:6;72:20;97:33;124:5;97:33;:::i;45647:517::-;;;45787:3;45774:17;45831:48;45855:8;45839:14;45835:29;45831:48;45811:18;45807:73;45797:2;;-1:-1;;45884:12;45797:2;45913:33;;45870:4;46002:16;;;-1:-1;45968:19;;-1:-1;46038:18;46027:30;;46024:2;;;46070:1;;46060:12;46024:2;45870:4;46121:6;46117:17;45839:14;46097:38;46087:8;46083:53;46080:2;;;46149:1;;46139:12;47767:144;47837:16;51341:1;51331:12;;51321:2;;51347:9;50872:268;50937:1;50944:101;50958:6;50955:1;50952:13;50944:101;;;51025:11;;;51019:18;51006:11;;;50999:39;50980:2;50973:10;50944:101;;;51060:6;51057:1;51054:13;51051:2;;;-1:-1;;50937:1;51107:16;;51100:27;50921:219::o;51370:117::-;47991:42;51457:5;47980:54;51432:5;51429:35;51419:2;;51478:1;;51468:12;51419:2;51413:74;:::o;51494:111::-;51575:5;47511:13;47504:21;51553:5;51550:32;51540:2;;51596:1;;51586:12
Swarm Source
ipfs://340b52b18c986023465d22bab59e371777d70f64f9ec357141b9122d82d9d636
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
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.