Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 139 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Lock | 20640019 | 45 hrs ago | IN | 0 ETH | 0.00025157 | ||||
Lock | 20638530 | 2 days ago | IN | 0 ETH | 0.00022282 | ||||
Lock | 20627430 | 3 days ago | IN | 0 ETH | 0.00027614 | ||||
Lock | 20038016 | 85 days ago | IN | 0 ETH | 0.00077749 | ||||
Lock | 15681190 | 696 days ago | IN | 0 ETH | 0.00011095 | ||||
Lock | 15506484 | 722 days ago | IN | 0 ETH | 0.0003271 | ||||
Recover | 15485232 | 725 days ago | IN | 0 ETH | 0.00275714 | ||||
Recover | 15306373 | 753 days ago | IN | 0 ETH | 0.00135082 | ||||
Lock | 15095881 | 786 days ago | IN | 0 ETH | 0.00112293 | ||||
Lock | 14872210 | 824 days ago | IN | 0 ETH | 0.0004608 | ||||
Lock | 14872199 | 824 days ago | IN | 0 ETH | 0.00050853 | ||||
Recover | 14842378 | 829 days ago | IN | 0 ETH | 0.00404572 | ||||
Lock | 14842334 | 829 days ago | IN | 0 ETH | 0.00154895 | ||||
Lock | 14842248 | 829 days ago | IN | 0 ETH | 0.00170796 | ||||
Lock | 14818459 | 833 days ago | IN | 0 ETH | 0.00085584 | ||||
Lock | 14807941 | 835 days ago | IN | 0 ETH | 0.0004643 | ||||
Recover | 14779047 | 839 days ago | IN | 0 ETH | 0.00408784 | ||||
Lock | 14724165 | 848 days ago | IN | 0 ETH | 0.00327269 | ||||
Recover | 14468250 | 888 days ago | IN | 0 ETH | 0.00315401 | ||||
Lock | 14464071 | 889 days ago | IN | 0 ETH | 0.00100891 | ||||
Lock | 14448716 | 891 days ago | IN | 0 ETH | 0.00061135 | ||||
Recover | 14442900 | 892 days ago | IN | 0 ETH | 0.00385477 | ||||
Recover | 14377006 | 902 days ago | IN | 0 ETH | 0.00154444 | ||||
Lock | 13957337 | 967 days ago | IN | 0 ETH | 0.0025095 | ||||
Lock | 13947321 | 969 days ago | IN | 0 ETH | 0.0037416 |
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:
FinalSecurityModule
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/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/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/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/security/GuardianModule.sol // Copyright 2017 Loopring Technology Limited. /// @title GuardianModule /// @author Brecht Devos - <[email protected]> /// @author Daniel Wang - <[email protected]> abstract contract GuardianModule is SecurityModule { using SignatureUtil for bytes32; using AddressUtil for address; bytes32 public immutable GUARDIAN_DOMAIN_SEPERATOR; uint public constant MAX_GUARDIANS = 10; uint public constant GUARDIAN_PENDING_PERIOD = 3 days; bytes32 public constant ADD_GUARDIAN_TYPEHASH = keccak256( "addGuardian(address wallet,uint256 validUntil,address guardian)" ); bytes32 public constant REMOVE_GUARDIAN_TYPEHASH = keccak256( "removeGuardian(address wallet,uint256 validUntil,address guardian)" ); bytes32 public constant RECOVER_TYPEHASH = keccak256( "recover(address wallet,uint256 validUntil,address newOwner)" ); bytes32 public constant LOCK_TYPEHASH = keccak256( "lock(address wallet,uint256 validUntil)" ); bytes32 public constant UNLOCK_TYPEHASH = keccak256( "unlock(address wallet,uint256 validUntil)" ); event GuardianAdded (address indexed wallet, address guardian, uint effectiveTime); event GuardianRemoved (address indexed wallet, address guardian, uint effectiveTime); event Recovered (address indexed wallet, address newOwner); constructor() { GUARDIAN_DOMAIN_SEPERATOR = EIP712.hash( EIP712.Domain("GuardianModule", "1.2.0", address(this)) ); } function addGuardian( address wallet, address guardian ) external txAwareHashNotAllowed() onlyFromWalletOrOwnerWhenUnlocked(wallet) notWalletOwner(wallet, guardian) { _addGuardian(wallet, guardian, GUARDIAN_PENDING_PERIOD, false); } function addGuardianWA( SignedRequest.Request calldata request, address guardian ) external notWalletOwner(request.wallet, guardian) { SignedRequest.verifyRequest( hashStore, securityStore, GUARDIAN_DOMAIN_SEPERATOR, txAwareHash(), GuardianUtils.SigRequirement.MAJORITY_OWNER_REQUIRED, request, abi.encode( ADD_GUARDIAN_TYPEHASH, request.wallet, request.validUntil, guardian ) ); _addGuardian(request.wallet, guardian, 0, true); } function removeGuardian( address wallet, address guardian ) external txAwareHashNotAllowed() onlyFromWalletOrOwnerWhenUnlocked(wallet) { _removeGuardian(wallet, guardian, GUARDIAN_PENDING_PERIOD, false); } function removeGuardianWA( SignedRequest.Request calldata request, address guardian ) external { SignedRequest.verifyRequest( hashStore, securityStore, GUARDIAN_DOMAIN_SEPERATOR, txAwareHash(), GuardianUtils.SigRequirement.MAJORITY_OWNER_REQUIRED, request, abi.encode( REMOVE_GUARDIAN_TYPEHASH, request.wallet, request.validUntil, guardian ) ); _removeGuardian(request.wallet, guardian, 0, true); } function lock(address wallet) external txAwareHashNotAllowed() { address payable _logicalSender = logicalSender(); require( _logicalSender == wallet || _logicalSender == Wallet(wallet).owner() || securityStore.isGuardian(wallet, _logicalSender, false), "NOT_FROM_WALLET_OR_OWNER_OR_GUARDIAN" ); _lockWallet(wallet, _logicalSender, true); } function lockWA( SignedRequest.Request calldata request ) external { SignedRequest.verifyRequest( hashStore, securityStore, GUARDIAN_DOMAIN_SEPERATOR, txAwareHash(), GuardianUtils.SigRequirement.OWNER_OR_ANY_GUARDIAN, request, abi.encode( LOCK_TYPEHASH, request.wallet, request.validUntil ) ); _lockWallet(request.wallet, request.signers[0], true); } function unlock( SignedRequest.Request calldata request ) external { SignedRequest.verifyRequest( hashStore, securityStore, GUARDIAN_DOMAIN_SEPERATOR, txAwareHash(), GuardianUtils.SigRequirement.MAJORITY_OWNER_REQUIRED, request, abi.encode( UNLOCK_TYPEHASH, request.wallet, request.validUntil ) ); _lockWallet(request.wallet, address(this), false); } /// @dev Recover a wallet by setting a new owner. /// @param request The general request object. /// @param newOwner The new owner address to set. function recover( SignedRequest.Request calldata request, address newOwner ) external notWalletOwner(request.wallet, newOwner) eligibleWalletOwner(newOwner) { SignedRequest.verifyRequest( hashStore, securityStore, GUARDIAN_DOMAIN_SEPERATOR, txAwareHash(), GuardianUtils.SigRequirement.MAJORITY_OWNER_NOT_ALLOWED, request, abi.encode( RECOVER_TYPEHASH, request.wallet, request.validUntil, newOwner ) ); SecurityStore ss = securityStore; if (ss.isGuardian(request.wallet, newOwner, true)) { ss.removeGuardian(request.wallet, newOwner, block.timestamp, true); } Wallet(request.wallet).setOwner(newOwner); _lockWallet(request.wallet, address(this), false); ss.cancelPendingGuardians(request.wallet); emit Recovered(request.wallet, newOwner); } function isLocked(address wallet) public view returns (bool) { return _isWalletLocked(wallet); } // ---- internal functions --- function _addGuardian( address wallet, address guardian, uint pendingPeriod, bool alwaysOverride ) private { require(guardian != wallet, "INVALID_ADDRESS"); require(guardian != address(0), "ZERO_ADDRESS"); SecurityStore ss = securityStore; uint numGuardians = ss.numGuardians(wallet, true); require(numGuardians < MAX_GUARDIANS, "TOO_MANY_GUARDIANS"); uint validSince = block.timestamp; if (numGuardians >= 2) { validSince = block.timestamp + pendingPeriod; } validSince = ss.addGuardian(wallet, guardian, validSince, alwaysOverride); emit GuardianAdded(wallet, guardian, validSince); } function _removeGuardian( address wallet, address guardian, uint pendingPeriod, bool alwaysOverride ) private { uint validUntil = block.timestamp + pendingPeriod; SecurityStore ss = securityStore; validUntil = ss.removeGuardian(wallet, guardian, validUntil, alwaysOverride); emit GuardianRemoved(wallet, guardian, validUntil); } } // File: contracts/modules/security/InheritanceModule.sol // Copyright 2017 Loopring Technology Limited. /// @title InheritanceModule /// @author Brecht Devos - <[email protected]> /// @author Daniel Wang - <[email protected]> abstract contract InheritanceModule is SecurityModule { using AddressUtil for address; using SignatureUtil for bytes32; event Inherited( address indexed wallet, address inheritor, address newOwner ); event InheritorChanged( address indexed wallet, address inheritor, uint32 waitingPeriod ); function inheritor(address wallet) public view returns (address _inheritor, uint _effectiveTimestamp) { return securityStore.inheritor(wallet); } function inherit( address wallet, address newOwner ) external txAwareHashNotAllowed() eligibleWalletOwner(newOwner) notWalletOwner(wallet, newOwner) { SecurityStore ss = securityStore; (address _inheritor, uint _effectiveTimestamp) = ss.inheritor(wallet); require(_effectiveTimestamp != 0 && _inheritor != address(0), "NO_INHERITOR"); require(_effectiveTimestamp <= block.timestamp, "TOO_EARLY"); require(_inheritor == logicalSender(), "UNAUTHORIZED"); ss.removeAllGuardians(wallet); ss.setInheritor(wallet, address(0), 0); _lockWallet(wallet, address(this), false); Wallet(wallet).setOwner(newOwner); emit Inherited(wallet, _inheritor, newOwner); } function setInheritor( address wallet, address _inheritor, uint32 _waitingPeriod ) external txAwareHashNotAllowed() onlyFromWalletOrOwnerWhenUnlocked(wallet) { require( _inheritor == address(0) && _waitingPeriod == 0 || _inheritor != address(0) && _waitingPeriod >= TOUCH_GRACE_PERIOD * 2 && _waitingPeriod <= 3650 days, "INVALID_INHERITOR_OR_WAITING_PERIOD" ); securityStore.setInheritor(wallet, _inheritor, _waitingPeriod); emit InheritorChanged(wallet, _inheritor, _waitingPeriod); } } // File: contracts/modules/security/WhitelistModule.sol // Copyright 2017 Loopring Technology Limited. /// @title WhitelistModule /// @dev Manages whitelisted addresses. /// @author Daniel Wang - <[email protected]> abstract contract WhitelistModule is SecurityModule { using MathUint for uint; bytes32 public immutable WHITELIST_DOMAIN_SEPERATOR; uint public constant WHITELIST_PENDING_PERIOD = 1 days; bytes32 public constant ADD_TO_WHITELIST_TYPEHASH = keccak256( "addToWhitelist(address wallet,uint256 validUntil,address addr)" ); bytes32 public constant REMOVE_FROM_WHITELIST_TYPEHASH = keccak256( "removeFromWhitelist(address wallet,uint256 validUntil,address addr)" ); constructor() { WHITELIST_DOMAIN_SEPERATOR = EIP712.hash( EIP712.Domain("WhitelistModule", "1.2.0", address(this)) ); } function addToWhitelist( address wallet, address addr ) external txAwareHashNotAllowed() onlyFromWalletOrOwnerWhenUnlocked(wallet) { whitelistStore.addToWhitelist( wallet, addr, block.timestamp.add(WHITELIST_PENDING_PERIOD) ); } function addToWhitelistWA( SignedRequest.Request calldata request, address addr ) external { SignedRequest.verifyRequest( hashStore, securityStore, WHITELIST_DOMAIN_SEPERATOR, txAwareHash(), GuardianUtils.SigRequirement.MAJORITY_OWNER_REQUIRED, request, abi.encode( ADD_TO_WHITELIST_TYPEHASH, request.wallet, request.validUntil, addr ) ); whitelistStore.addToWhitelist( request.wallet, addr, block.timestamp ); } function removeFromWhitelist( address wallet, address addr ) external txAwareHashNotAllowed() onlyFromWalletOrOwnerWhenUnlocked(wallet) { whitelistStore.removeFromWhitelist(wallet, addr); } function removeFromWhitelistWA( SignedRequest.Request calldata request, address addr ) external { SignedRequest.verifyRequest( hashStore, securityStore, WHITELIST_DOMAIN_SEPERATOR, txAwareHash(), GuardianUtils.SigRequirement.MAJORITY_OWNER_REQUIRED, request, abi.encode( REMOVE_FROM_WHITELIST_TYPEHASH, request.wallet, request.validUntil, addr ) ); whitelistStore.removeFromWhitelist(request.wallet, addr); } function getWhitelist(address wallet) public view returns ( address[] memory addresses, uint[] memory effectiveTimes ) { return whitelistStore.whitelist(wallet); } function isWhitelisted( address wallet, address addr) public view returns ( bool isWhitelistedAndEffective, uint effectiveTime ) { return whitelistStore.isWhitelisted(wallet, addr); } } // File: contracts/modules/security/FinalSecurityModule.sol // Copyright 2017 Loopring Technology Limited. /// @title FinalSecurityModule /// @dev This module combines multiple small modules to /// minimize the number of modules to reduce gas used /// by wallet creation. contract FinalSecurityModule is GuardianModule, InheritanceModule, WhitelistModule { ControllerImpl private immutable controller_; constructor( ControllerImpl _controller, address _metaTxForwarder ) SecurityModule(_controller, _metaTxForwarder) GuardianModule() InheritanceModule() WhitelistModule() { 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"}],"name":"Deactivated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"address","name":"guardian","type":"address"},{"indexed":false,"internalType":"uint256","name":"effectiveTime","type":"uint256"}],"name":"GuardianAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"address","name":"guardian","type":"address"},{"indexed":false,"internalType":"uint256","name":"effectiveTime","type":"uint256"}],"name":"GuardianRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"address","name":"inheritor","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"Inherited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"address","name":"inheritor","type":"address"},{"indexed":false,"internalType":"uint32","name":"waitingPeriod","type":"uint32"}],"name":"InheritorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"Recovered","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":"ADD_GUARDIAN_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ADD_TO_WHITELIST_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GUARDIAN_DOMAIN_SEPERATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GUARDIAN_PENDING_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LOCK_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_GUARDIANS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RECOVER_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REMOVE_FROM_WHITELIST_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REMOVE_GUARDIAN_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TOUCH_GRACE_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNLOCK_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WHITELIST_DOMAIN_SEPERATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WHITELIST_PENDING_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"activate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"guardian","type":"address"}],"name":"addGuardian","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":"guardian","type":"address"}],"name":"addGuardianWA","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"addr","type":"address"}],"name":"addToWhitelist","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":"addr","type":"address"}],"name":"addToWhitelistWA","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"bindableMethods","outputs":[{"internalType":"bytes4[]","name":"methods","type":"bytes4[]"}],"stateMutability":"pure","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":"getWhitelist","outputs":[{"internalType":"address[]","name":"addresses","type":"address[]"},{"internalType":"uint256[]","name":"effectiveTimes","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hashStore","outputs":[{"internalType":"contract HashStore","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"newOwner","type":"address"}],"name":"inherit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"}],"name":"inheritor","outputs":[{"internalType":"address","name":"_inheritor","type":"address"},{"internalType":"uint256","name":"_effectiveTimestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"}],"name":"isLocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"addr","type":"address"}],"name":"isWhitelisted","outputs":[{"internalType":"bool","name":"isWhitelistedAndEffective","type":"bool"},{"internalType":"uint256","name":"effectiveTime","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"}],"name":"lock","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"}],"name":"lockWA","outputs":[],"stateMutability":"nonpayable","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":[{"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":"newOwner","type":"address"}],"name":"recover","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"addr","type":"address"}],"name":"removeFromWhitelist","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":"addr","type":"address"}],"name":"removeFromWhitelistWA","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"guardian","type":"address"}],"name":"removeGuardian","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":"guardian","type":"address"}],"name":"removeGuardianWA","outputs":[],"stateMutability":"nonpayable","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":"_inheritor","type":"address"},{"internalType":"uint32","name":"_waitingPeriod","type":"uint32"}],"name":"setInheritor","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"}],"name":"unlock","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
6102006040523480156200001257600080fd5b506040516200508f3803806200508f833981016040819052620000359162000693565b818181818181806001600160a01b03166080816001600160a01b031660601b8152505050806001600160a01b031663b95459e46040518163ffffffff1660e01b815260040160206040518083038186803b1580156200009357600080fd5b505afa158015620000a8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000ce91906200066d565b6001600160a01b031660a0816001600160a01b031660601b81525050806001600160a01b031663d51b3a1b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156200012457600080fd5b505afa15801562000139573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200015f91906200066d565b6001600160a01b031660c0816001600160a01b031660601b81525050806001600160a01b03166337423d5e6040518163ffffffff1660e01b815260040160206040518083038186803b158015620001b557600080fd5b505afa158015620001ca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001f091906200066d565b6001600160a01b031660e0816001600160a01b031660601b81525050806001600160a01b031663d9d104846040518163ffffffff1660e01b815260040160206040518083038186803b1580156200024657600080fd5b505afa1580156200025b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200028191906200066d565b6001600160a01b0316610100816001600160a01b031660601b81525050806001600160a01b031663cbe45d186040518163ffffffff1660e01b815260040160206040518083038186803b158015620002d857600080fd5b505afa158015620002ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200031391906200066d565b6001600160a01b0316610120816001600160a01b031660601b81525050806001600160a01b031663c5c036996040518163ffffffff1660e01b815260040160206040518083038186803b1580156200036a57600080fd5b505afa1580156200037f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620003a591906200066d565b6001600160a01b0316610140816001600160a01b031660601b81525050806001600160a01b0316632630c12f6040518163ffffffff1660e01b815260040160206040518083038186803b158015620003fc57600080fd5b505afa15801562000411573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200043791906200066d565b6001600160a01b0316610160816001600160a01b031660601b81525050806001600160a01b031663c415b95c6040518163ffffffff1660e01b815260040160206040518083038186803b1580156200048e57600080fd5b505afa158015620004a3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620004c991906200066d565b6001600160a01b0316610180816001600160a01b031660601b8152505050505050506200056760405180606001604052806040518060400160405280600e81526020016d477561726469616e4d6f64756c6560901b8152508152602001604051806040016040528060058152602001640312e322e360dc1b8152508152602001306001600160a01b0316815250620005f460201b620030521760201c565b6101a0526040805160a081018252600f606082019081526e57686974656c6973744d6f64756c6560881b608083015281528151808301835260058152640312e322e360dc1b602082810191909152808301919091523092820192909252620005d89162003052620005f4821b17901c565b6101c0525060601b6001600160601b0319166101e05262000716565b6000804690507f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8360000151805190602001208460200151805190602001208386604001516040516020016200064f959493929190620006d1565b60405160208183030381529060405280519060200120915050919050565b6000602082840312156200067f578081fd5b81516200068c81620006fd565b9392505050565b60008060408385031215620006a6578081fd5b8251620006b381620006fd565b6020840151909250620006c681620006fd565b809150509250929050565b9485526020850193909352604084019190915260608301526001600160a01b0316608082015260a00190565b6001600160a01b03811681146200071357600080fd5b50565b60805160601c60a05160601c60c05160601c60e05160601c6101005160601c6101205160601c6101405160601c6101605160601c6101805160601c6101a0516101c0516101e05160601c6148036200088c600039508061057752806117335280611b7c5250806106955280610984528061139652806119085280611f75528061259d52508061277052508061078f525080612794525080610653528061094252806116f152806118c65280611b3a5280611f33528061255b5280612acb525080612b3752508061129e52806113725280611647528061181d5280611c665280612696528061301952508061067452806109635280610a585280610efc52806115c6528061171252806118e75280611a785280611b5b5280611e715280611f54528061220f5280612360528061257c52806129685280612b135280612cd05280612f72528061325152806133b652806134b452806136fd52508061274c525080611ef552806131a4528061391452506148036000f3fe608060405234801561001057600080fd5b50600436106102e95760003560e01c80638cff544c11610191578063c415b95c116100e3578063d51b3a1b11610097578063f435f5a711610071578063f435f5a714610547578063f5d7faf71461055a578063f8d3277d14610562576102e9565b8063d51b3a1b1461052f578063d9d1048414610537578063f26cfb661461053f576102e9565b8063c6845210116100c8578063c68452101461050c578063cbe45d181461051f578063d132d1db14610527576102e9565b8063c415b95c146104fc578063c5c0369914610504576102e9565b8063af5bea3611610145578063b6aa057e1161011f578063b6aa057e146104c0578063b6b35272146104d3578063b95459e4146104f4576102e9565b8063af5bea3614610490578063afa3363d146104a3578063b2924620146104b8576102e9565b80639b27a90e116101765780639b27a90e14610462578063a664eb8e14610475578063a79c930c1461047d576102e9565b80638cff544c1461042e5780638fbbddc41461044f576102e9565b806337423d5e1161024a57806357518243116101fe5780637abbb0d5116101d85780637abbb0d514610416578063840fdc781461041e5780638c8ca33c14610426576102e9565b806357518243146103dd5780636d60b18f146103f05780636e7a140a14610403576102e9565b8063422c1d371161022f578063422c1d37146103ad5780634a4fbeec146103b557806351b42b00146103d5576102e9565b806337423d5e1461039d5780633fa5e0cb146103a5576102e9565b806324db3d02116102a157806328ba79ab1161028657806328ba79ab146103565780632e41763c1461036957806330edc0f51461037c576102e9565b806324db3d021461032e5780632630c12f14610341576102e9565b80630b825749116102d25780630b825749146103145780630f15f4c01461031c5780631ec2bad014610326576102e9565b806301ed8ef7146102ee5780630b27c8031461030c575b600080fd5b6102f6610575565b6040516103039190614163565b60405180910390f35b6102f6610599565b6102f66105bd565b6103246105c4565b005b6102f6610613565b61032461033c366004613cb0565b610637565b61034961078d565b6040516103039190613ec4565b610324610364366004613cb0565b6107b1565b610324610377366004613abb565b610d38565b61038f61038a366004613a83565b611299565b604051610303929190614054565b610349611370565b6102f6611394565b6102f66113b8565b6103c86103c3366004613a83565b6113bd565b6040516103039190614148565b6103246113d0565b6103246103eb366004613abb565b611414565b6103246103fe366004613cb0565b6116d5565b610324610411366004613c75565b6118aa565b6102f6611a24565b6102f6611a48565b6102f6611a4f565b61044161043c366004613a83565b611a73565b604051610303929190614002565b61032461045d366004613cb0565b611b1e565b610324610470366004613abb565b611cbf565b610349611ef3565b61032461048b366004613c75565b611f17565b61032461049e366004613af3565b61205d565b6104ab612422565b60405161030391906140e2565b6102f6612427565b6103246104ce366004613cb0565b61244b565b6104e66104e1366004613abb565b612691565b604051610303929190614153565b61034961274a565b61034961276e565b610349612792565b61032461051a366004613abb565b6127b6565b610349612ac9565b6102f6612aed565b610349612b11565b610349612b35565b6102f6612b59565b610324610555366004613a83565b612b60565b6102f6612d9c565b610324610570366004613abb565b612dc0565b7f000000000000000000000000000000000000000000000000000000000000000081565b7fdb3c8034db0ef3555cd9d314db16ce45454e1a01d949a021318374472427defa81565b6201518081565b60006105ce6130c9565b90506105d9816130d9565b7f0cc43938d137e7efade6a531f663e78c1fc75257b0d65ffda2fdaf70cb49cdf9816040516106089190613ec4565b60405180910390a150565b7f04cbded2c195932058c24a5373dfbee6688eb058186f4edbd50275a6143b723081565b7335825b18e8948442abc361b361b007e31130f31463851edceb7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000006106bc61317e565b6002887fc2d601f9b2c71cd1f247d67d83d55307ed88800236d40a2069c0a6d2b207ef846106f06080830160608401613a83565b8b604001358b6040516020016107099493929190614198565b6040516020818303038152906040526040518863ffffffff1660e01b815260040161073a979695949392919061424d565b60006040518083038186803b15801561075257600080fd5b505af4158015610766573d6000803e3d6000fd5b50610789925061077f9150506080840160608501613a83565b8260006001613225565b5050565b7f000000000000000000000000000000000000000000000000000000000000000081565b6107c16080830160608401613a83565b818073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561081f57600080fd5b505afa158015610833573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108579190613a9f565b73ffffffffffffffffffffffffffffffffffffffff1614156108ae576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614590565b60405180910390fd5b8273ffffffffffffffffffffffffffffffffffffffff8116158015906108f057506108ee8173ffffffffffffffffffffffffffffffffffffffff1661333f565b155b610926576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a5906144eb565b7335825b18e8948442abc361b361b007e31130f31463851edceb7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000006109ab61317e565b60008b7f04cbded2c195932058c24a5373dfbee6688eb058186f4edbd50275a6143b72306109df6080830160608401613a83565b8e604001358e6040516020016109f89493929190614198565b6040516020818303038152906040526040518863ffffffff1660e01b8152600401610a29979695949392919061424d565b60006040518083038186803b158015610a4157600080fd5b505af4158015610a55573d6000803e3d6000fd5b507f000000000000000000000000000000000000000000000000000000000000000092505073ffffffffffffffffffffffffffffffffffffffff8216905063bb058a41610aa86080890160608a01613a83565b8760016040518463ffffffff1660e01b8152600401610ac993929190613f0c565b60206040518083038186803b158015610ae157600080fd5b505afa158015610af5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b199190613c34565b15610bc15773ffffffffffffffffffffffffffffffffffffffff811663a76163d4610b4a6080890160608a01613a83565b874260016040518563ffffffff1660e01b8152600401610b6d9493929190613fa4565b602060405180830381600087803b158015610b8757600080fd5b505af1158015610b9b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bbf9190613cf5565b505b610bd16080870160608801613a83565b73ffffffffffffffffffffffffffffffffffffffff166313af4035866040518263ffffffff1660e01b8152600401610c099190613ec4565b600060405180830381600087803b158015610c2357600080fd5b505af1158015610c37573d6000803e3d6000fd5b50610c589250610c509150506080880160608901613a83565b306000613379565b73ffffffffffffffffffffffffffffffffffffffff811663edc2a617610c846080890160608a01613a83565b6040518263ffffffff1660e01b8152600401610ca09190613ec4565b600060405180830381600087803b158015610cba57600080fd5b505af1158015610cce573d6000803e3d6000fd5b50610ce3925050506080870160608801613a83565b73ffffffffffffffffffffffffffffffffffffffff167fc4aef5da6b1f6085ebd05a82a61d94bf83a486f32ab4ac3f7843afeed66b144086604051610d289190613ec4565b60405180910390a2505050505050565b610d4061317e565b15610d77576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614344565b8073ffffffffffffffffffffffffffffffffffffffff811615801590610db95750610db78173ffffffffffffffffffffffffffffffffffffffff1661333f565b155b610def576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a5906144eb565b82828073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610e4e57600080fd5b505afa158015610e62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e869190613a9f565b73ffffffffffffffffffffffffffffffffffffffff161415610ed4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614590565b6040517f8cff544c0000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000090600090819073ffffffffffffffffffffffffffffffffffffffff841690638cff544c90610f4d908b90600401613ec4565b604080518083038186803b158015610f6457600080fd5b505afa158015610f78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f9c9190613b45565b9150915080600014158015610fc6575073ffffffffffffffffffffffffffffffffffffffff821615155b610ffc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a5906143b2565b42811115611036576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614420565b61103e6130c9565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16146110a2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a5906143e9565b6040517fae5967d400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84169063ae5967d4906110f4908b90600401613ec4565b600060405180830381600087803b15801561110e57600080fd5b505af1158015611122573d6000803e3d6000fd5b50506040517faf5bea3600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8616925063af5bea36915061117d908b906000908190600401613f3d565b600060405180830381600087803b15801561119757600080fd5b505af11580156111ab573d6000803e3d6000fd5b505050506111bb88306000613379565b6040517f13af403500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8916906313af40359061120d908a90600401613ec4565b600060405180830381600087803b15801561122757600080fd5b505af115801561123b573d6000803e3d6000fd5b505050508773ffffffffffffffffffffffffffffffffffffffff167f85c6e43dbd10c1b6d77c7c1da9a4645a2a4555d95a5f5e701ac347317cd085428389604051611287929190613ee5565b60405180910390a25050505050505050565b6060807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16639b19251a846040518263ffffffff1660e01b81526004016112f59190613ec4565b60006040518083038186803b15801561130d57600080fd5b505afa158015611321573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526113679190810190613b72565b91509150915091565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b600a81565b60006113c882613474565b90505b919050565b60006113da6130c9565b90506113e581613539565b7f749cb6b4c510bc468cf6b9c2086d6f0a54d6b18e25d37bf3200e68eab0880c00816040516106089190613ec4565b61141c61317e565b15611453576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614344565b81600061145e6130c9565b90508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148061155357508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156114db57600080fd5b505afa1580156114ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115139190613a9f565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148015611553575061155182613474565b155b611589576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a59061448e565b6040517fafaee9f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063afaee9f19061160090859062278d0090600401614002565b600060405180830381600087803b15801561161a57600080fd5b505af115801561162e573d6000803e3d6000fd5b505073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016915063e09e83e39050858561167f42620151806135df565b6040518463ffffffff1660e01b815260040161169d93929190613f73565b600060405180830381600087803b1580156116b757600080fd5b505af11580156116cb573d6000803e3d6000fd5b5050505050505050565b7335825b18e8948442abc361b361b007e31130f31463851edceb7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000061175a61317e565b6002887ff4b4ae271275adc850135662bc4e841b47c72bba882d2e0bc8ff83e11e6f23bd61178e6080830160608401613a83565b8b604001358b6040516020016117a79493929190614198565b6040516020818303038152906040526040518863ffffffff1660e01b81526004016117d8979695949392919061424d565b60006040518083038186803b1580156117f057600080fd5b505af4158015611804573d6000803e3d6000fd5b505073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016915063f8d3277d90506118566080850160608601613a83565b836040518363ffffffff1660e01b8152600401611874929190613ee5565b600060405180830381600087803b15801561188e57600080fd5b505af11580156118a2573d6000803e3d6000fd5b505050505050565b7335825b18e8948442abc361b361b007e31130f31463851edceb7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000061192f61317e565b6003877f5696502d32dd3c98d271b352ac3c61123af461c8bb2ee7f6a63b68a46aeb29a16119636080830160608401613a83565b8a6040013560405160200161197a9392919061416c565b6040516020818303038152906040526040518863ffffffff1660e01b81526004016119ab979695949392919061424d565b60006040518083038186803b1580156119c357600080fd5b505af41580156119d7573d6000803e3d6000fd5b50611a2192506119f09150506080830160608401613a83565b6119fa8380614681565b6000818110611a0557fe5b9050602002016020810190611a1a9190613a83565b6001613379565b50565b7fc2d601f9b2c71cd1f247d67d83d55307ed88800236d40a2069c0a6d2b207ef8481565b62278d0081565b7fdbfbdf039669d5766c6fd0a5920870f82fdd49034a4103cbda583d4c4176640e81565b6000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16638cff544c846040518263ffffffff1660e01b8152600401611acf9190613ec4565b604080518083038186803b158015611ae657600080fd5b505afa158015611afa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113679190613b45565b7335825b18e8948442abc361b361b007e31130f31463851edceb7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611ba361317e565b6002887fdb3c8034db0ef3555cd9d314db16ce45454e1a01d949a021318374472427defa611bd76080830160608401613a83565b8b604001358b604051602001611bf09493929190614198565b6040516020818303038152906040526040518863ffffffff1660e01b8152600401611c21979695949392919061424d565b60006040518083038186803b158015611c3957600080fd5b505af4158015611c4d573d6000803e3d6000fd5b505073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016915063e09e83e39050611c9f6080850160608601613a83565b83426040518463ffffffff1660e01b815260040161187493929190613f73565b611cc761317e565b15611cfe576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614344565b816000611d096130c9565b90508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161480611dfe57508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015611d8657600080fd5b505afa158015611d9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dbe9190613a9f565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148015611dfe5750611dfc82613474565b155b611e34576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a59061448e565b6040517fafaee9f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063afaee9f190611eab90859062278d0090600401614002565b600060405180830381600087803b158015611ec557600080fd5b505af1158015611ed9573d6000803e3d6000fd5b50505050611eed84846203f4806000613225565b50505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b7335825b18e8948442abc361b361b007e31130f31463851edceb7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611f9c61317e565b6002877fdbfbdf039669d5766c6fd0a5920870f82fdd49034a4103cbda583d4c4176640e611fd06080830160608401613a83565b8a60400135604051602001611fe79392919061416c565b6040516020818303038152906040526040518863ffffffff1660e01b8152600401612018979695949392919061424d565b60006040518083038186803b15801561203057600080fd5b505af4158015612044573d6000803e3d6000fd5b50611a219250610c509150506080830160608401613a83565b61206561317e565b1561209c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614344565b8260006120a76130c9565b90508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148061219c57508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561212457600080fd5b505afa158015612138573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061215c9190613a9f565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614801561219c575061219a82613474565b155b6121d2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a59061448e565b6040517fafaee9f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063afaee9f19061224990859062278d0090600401614002565b600060405180830381600087803b15801561226357600080fd5b505af1158015612277573d6000803e3d6000fd5b5050505073ffffffffffffffffffffffffffffffffffffffff84161580156122a3575063ffffffff8316155b806122ed575073ffffffffffffffffffffffffffffffffffffffff8416158015906122d75750624f1a0063ffffffff841610155b80156122ed57506312cc03008363ffffffff1611155b612323576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614624565b6040517faf5bea3600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063af5bea369061239990889088908890600401613f3d565b600060405180830381600087803b1580156123b357600080fd5b505af11580156123c7573d6000803e3d6000fd5b505050508473ffffffffffffffffffffffffffffffffffffffff167fff3d54be94bfa9ddad9986419c92a9c4b1e9a649ecf3fef10ea6b7d80860df6a8585604051612413929190614028565b60405180910390a25050505050565b606090565b7f5696502d32dd3c98d271b352ac3c61123af461c8bb2ee7f6a63b68a46aeb29a181565b61245b6080830160608401613a83565b818073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156124b957600080fd5b505afa1580156124cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124f19190613a9f565b73ffffffffffffffffffffffffffffffffffffffff16141561253f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614590565b7335825b18e8948442abc361b361b007e31130f31463851edceb7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000006125c461317e565b60028a7f3aea25321405f61fb6d5dfb4b53be5d64671d80dc2149264e167060f5996b67a6125f86080830160608401613a83565b8d604001358d6040516020016126119493929190614198565b6040516020818303038152906040526040518863ffffffff1660e01b8152600401612642979695949392919061424d565b60006040518083038186803b15801561265a57600080fd5b505af415801561266e573d6000803e3d6000fd5b50611eed92506126879150506080860160608701613a83565b8460006001613622565b6000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663b6b3527285856040518363ffffffff1660e01b81526004016126ef929190613ee5565b604080518083038186803b15801561270657600080fd5b505afa15801561271a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061273e9190613c54565b915091505b9250929050565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b6127be61317e565b156127f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614344565b8160006128006130c9565b90508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614806128f557508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561287d57600080fd5b505afa158015612891573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128b59190613a9f565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161480156128f557506128f382613474565b155b61292b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a59061448e565b6040517fafaee9f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063afaee9f1906129a290859062278d0090600401614002565b600060405180830381600087803b1580156129bc57600080fd5b505af11580156129d0573d6000803e3d6000fd5b5050505083838073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015612a3357600080fd5b505afa158015612a47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a6b9190613a9f565b73ffffffffffffffffffffffffffffffffffffffff161415612ab9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614590565b6118a286866203f4806000613622565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f3aea25321405f61fb6d5dfb4b53be5d64671d80dc2149264e167060f5996b67a81565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b6203f48081565b612b6861317e565b15612b9f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614344565b6000612ba96130c9565b90508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161480612c8d57508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015612c2657600080fd5b505afa158015612c3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c5e9190613a9f565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b80612d5a57506040517fbb058a4100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063bb058a4190612d0a9085908590600090600401613f0c565b60206040518083038186803b158015612d2257600080fd5b505afa158015612d36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d5a9190613c34565b612d90576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a5906145c7565b61078982826001613379565b7ff4b4ae271275adc850135662bc4e841b47c72bba882d2e0bc8ff83e11e6f23bd81565b612dc861317e565b15612dff576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614344565b816000612e0a6130c9565b90508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161480612eff57508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015612e8757600080fd5b505afa158015612e9b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ebf9190613a9f565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148015612eff5750612efd82613474565b155b612f35576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a59061448e565b6040517fafaee9f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063afaee9f190612fac90859062278d0090600401614002565b600060405180830381600087803b158015612fc657600080fd5b505af1158015612fda573d6000803e3d6000fd5b50506040517ff8d3277d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016925063f8d3277d915061169d9087908790600401613ee5565b6000804690507f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8360000151805190602001208460200151805190602001208386604001516040516020016130ab9594939291906141cc565b60405160208183030381529060405280519060200120915050919050565b60006130d36138ee565b90505b90565b8060606130e4612422565b905060005b8151811015611eed578273ffffffffffffffffffffffffffffffffffffffff1663b149206e83838151811061311a57fe5b6020026020010151306040518363ffffffff1660e01b8152600401613140929190614205565b600060405180830381600087803b15801561315a57600080fd5b505af115801561316e573d6000803e3d6000fd5b5050600190920191506130e99050565b6000603836108015906131c657503373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016145b1561321d5761321660206000369050036000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506139ab9050565b90506130d6565b5060006130d6565b6040517fa76163d4000000000000000000000000000000000000000000000000000000008152428301907f00000000000000000000000000000000000000000000000000000000000000009073ffffffffffffffffffffffffffffffffffffffff82169063a76163d4906132a3908990899087908990600401613fa4565b602060405180830381600087803b1580156132bd57600080fd5b505af11580156132d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132f59190613cf5565b91508573ffffffffffffffffffffffffffffffffffffffff167f3b474d5aea4286914f107a200bc4aefdff380346cf5f4b744aa13f5e7de416ac8684604051610d28929190614002565b6000813f801580159061337257507fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708114155b9392505050565b6040517fb03c4b3300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063b03c4b33906133ed9086908590600401613fda565b600060405180830381600087803b15801561340757600080fd5b505af115801561341b573d6000803e3d6000fd5b505050508273ffffffffffffffffffffffffffffffffffffffff167fb435250c8ab5b0393bace5c2fbf9699773f00b35ef348fc2107df2df331f36648383604051613467929190613fda565b60405180910390a2505050565b6040517f4a4fbeec00000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690634a4fbeec906134e9908590600401613ec4565b60206040518083038186803b15801561350157600080fd5b505afa158015613515573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113c89190613c34565b806060613544612422565b905060005b8151811015611eed578273ffffffffffffffffffffffffffffffffffffffff1663b149206e83838151811061357a57fe5b602002602001015160006040518363ffffffff1660e01b81526004016135a1929190614205565b600060405180830381600087803b1580156135bb57600080fd5b505af11580156135cf573d6000803e3d6000fd5b5050600190920191506135499050565b8181018281101561361c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614522565b92915050565b8373ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415613688576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a59061437b565b73ffffffffffffffffffffffffffffffffffffffff83166136d5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614457565b6040517fe0bba2a00000000000000000000000000000000000000000000000000000000081527f00000000000000000000000000000000000000000000000000000000000000009060009073ffffffffffffffffffffffffffffffffffffffff83169063e0bba2a09061374f908990600190600401613fda565b60206040518083038186803b15801561376757600080fd5b505afa15801561377b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061379f9190613cf5565b9050600a81106137db576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614559565b42600282106137e957504284015b6040517feb74483100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84169063eb74483190613841908a908a9086908a90600401613fa4565b602060405180830381600087803b15801561385b57600080fd5b505af115801561386f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138939190613cf5565b90508673ffffffffffffffffffffffffffffffffffffffff167f6d0391926fd0fcb9e582cc3f9b1d2302bfd0866212cc067d5b137352a23a41bc87836040516138dd929190614002565b60405180910390a250505050505050565b60006038361080159061393657503373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016145b156139a45761321661398960346000369050036000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506139c79050565b73ffffffffffffffffffffffffffffffffffffffff166130d6565b50336130d6565b600081602001835110156139be57600080fd5b50016020015190565b600081601401835110156139da57600080fd5b5001602001516c01000000000000000000000000900490565b805161361c816147ab565b600082601f830112613a0e578081fd5b8151613a21613a1c8261470d565b6146e6565b818152915060208083019084810181840286018201871015613a4257600080fd5b60005b84811015613a6157815184529282019290820190600101613a45565b505050505092915050565b600060808284031215613a7d578081fd5b50919050565b600060208284031215613a94578081fd5b8135613372816147ab565b600060208284031215613ab0578081fd5b8151613372816147ab565b60008060408385031215613acd578081fd5b8235613ad8816147ab565b91506020830135613ae8816147ab565b809150509250929050565b600080600060608486031215613b07578081fd5b8335613b12816147ab565b92506020840135613b22816147ab565b9150604084013563ffffffff81168114613b3a578182fd5b809150509250925092565b60008060408385031215613b57578182fd5b8251613b62816147ab565b6020939093015192949293505050565b60008060408385031215613b84578182fd5b825167ffffffffffffffff80821115613b9b578384fd5b818501915085601f830112613bae578384fd5b8151613bbc613a1c8261470d565b80828252602080830192508086018a828387028901011115613bdc578889fd5b8896505b84871015613c0657613bf28b826139f3565b845260019690960195928101928101613be0565b508801519096509350505080821115613c1d578283fd5b50613c2a858286016139fe565b9150509250929050565b600060208284031215613c45578081fd5b81518015158114613372578182fd5b60008060408385031215613c66578182fd5b82518015158114613b62578283fd5b600060208284031215613c86578081fd5b813567ffffffffffffffff811115613c9c578182fd5b613ca884828501613a6c565b949350505050565b60008060408385031215613cc2578182fd5b823567ffffffffffffffff811115613cd8578283fd5b613ce485828601613a6c565b9250506020830135613ae8816147ab565b600060208284031215613d06578081fd5b5051919050565b815260200190565b73ffffffffffffffffffffffffffffffffffffffff169052565b60008284526020808501945082825b85811015613d79578135613d51816147ab565b73ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613d3e565b509495945050505050565b818352602080840193600091908185020181018584845b87811015613e4e57828403895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1883603018112613dd9578687fd5b8701803567ffffffffffffffff811115613df1578788fd5b803603891315613dff578788fd5b8086528087830188880137858101870188905299860199601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169094018501935090840190600101613d9b565b5091979650505050505050565b60008151808452815b81811015613e8057602081850181015186830182015201613e64565b81811115613e915782602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152901515604082015260600190565b73ffffffffffffffffffffffffffffffffffffffff938416815291909216602082015263ffffffff909116604082015260600190565b73ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604081019190915260600190565b73ffffffffffffffffffffffffffffffffffffffff94851681529290931660208301526040820152901515606082015260800190565b73ffffffffffffffffffffffffffffffffffffffff9290921682521515602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff92909216825263ffffffff16602082015260400190565b604080825283519082018190526000906020906060840190828701845b8281101561409457614084848351613d15565b9284019290840190600101614071565b505050838103828501528085516140ab8184614163565b91508387019250845b818110156140d5576140c7838551613d0d565b9385019392506001016140b4565b5090979650505050505050565b6020808252825182820181905260009190848201906040850190845b8181101561413c5783517fffffffff0000000000000000000000000000000000000000000000000000000016835292840192918401916001016140fe565b50909695505050505050565b901515815260200190565b9115158252602082015260400190565b90815260200190565b92835273ffffffffffffffffffffffffffffffffffffffff919091166020830152604082015260600190565b93845273ffffffffffffffffffffffffffffffffffffffff9283166020850152604084019190915216606082015260800190565b94855260208501939093526040840191909152606083015273ffffffffffffffffffffffffffffffffffffffff16608082015260a00190565b7fffffffff0000000000000000000000000000000000000000000000000000000092909216825273ffffffffffffffffffffffffffffffffffffffff16602082015260400190565b600073ffffffffffffffffffffffffffffffffffffffff808a168352808916602084015250866040830152856060830152614287856147a0565b608083015260e060a083015261429d848561473a565b608060e08501526142b361016085018284613d2f565b9150506142c3602086018661473a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff20858403016101008601526142f9838284613d84565b925050506040850135610120840152614315606086018661472d565b614323610140850182613d15565b5082810360c08401526143368185613e5b565b9a9950505050505050505050565b60208082526015908201527f494e56414c49445f54585f41574152455f484153480000000000000000000000604082015260600190565b6020808252600f908201527f494e56414c49445f414444524553530000000000000000000000000000000000604082015260600190565b6020808252600c908201527f4e4f5f494e48455249544f520000000000000000000000000000000000000000604082015260600190565b6020808252600c908201527f554e415554484f52495a45440000000000000000000000000000000000000000604082015260600190565b60208082526009908201527f544f4f5f4541524c590000000000000000000000000000000000000000000000604082015260600190565b6020808252600c908201527f5a45524f5f414444524553530000000000000000000000000000000000000000604082015260600190565b60208082526029908201527f4e4f545f46524f4d5f57414c4c45545f4f525f4f574e45525f4f525f57414c4c60408201527f45545f4c4f434b45440000000000000000000000000000000000000000000000606082015260800190565b6020808252600d908201527f494e56414c49445f4f574e455200000000000000000000000000000000000000604082015260600190565b6020808252600c908201527f4144445f4f564552464c4f570000000000000000000000000000000000000000604082015260600190565b60208082526012908201527f544f4f5f4d414e595f475541524449414e530000000000000000000000000000604082015260600190565b6020808252600f908201527f49535f57414c4c45545f4f574e45520000000000000000000000000000000000604082015260600190565b60208082526024908201527f4e4f545f46524f4d5f57414c4c45545f4f525f4f574e45525f4f525f4755415260408201527f4449414e00000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526023908201527f494e56414c49445f494e48455249544f525f4f525f57414954494e475f50455260408201527f494f440000000000000000000000000000000000000000000000000000000000606082015260800190565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126146b5578283fd5b83018035915067ffffffffffffffff8211156146cf578283fd5b602090810192508102360382131561274357600080fd5b60405181810167ffffffffffffffff8111828210171561470557600080fd5b604052919050565b600067ffffffffffffffff821115614723578081fd5b5060209081020190565b60008235613372816147ab565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261476e578283fd5b830160208101925035905067ffffffffffffffff81111561478e57600080fd5b60208102360383131561274357600080fd5b80600581106113cb57fe5b73ffffffffffffffffffffffffffffffffffffffff81168114611a2157600080fdfea26469706673582212207505fbe551c28f7f8616f5356af71fbeba452426392fd73dbfab7edb86fb6d6e64736f6c63430007000033000000000000000000000000b39e09279d4035c0f92307741d9dd8ed66e74de0000000000000000000000000e915058df18e7efe92af5c44df3f575fba061b64
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102e95760003560e01c80638cff544c11610191578063c415b95c116100e3578063d51b3a1b11610097578063f435f5a711610071578063f435f5a714610547578063f5d7faf71461055a578063f8d3277d14610562576102e9565b8063d51b3a1b1461052f578063d9d1048414610537578063f26cfb661461053f576102e9565b8063c6845210116100c8578063c68452101461050c578063cbe45d181461051f578063d132d1db14610527576102e9565b8063c415b95c146104fc578063c5c0369914610504576102e9565b8063af5bea3611610145578063b6aa057e1161011f578063b6aa057e146104c0578063b6b35272146104d3578063b95459e4146104f4576102e9565b8063af5bea3614610490578063afa3363d146104a3578063b2924620146104b8576102e9565b80639b27a90e116101765780639b27a90e14610462578063a664eb8e14610475578063a79c930c1461047d576102e9565b80638cff544c1461042e5780638fbbddc41461044f576102e9565b806337423d5e1161024a57806357518243116101fe5780637abbb0d5116101d85780637abbb0d514610416578063840fdc781461041e5780638c8ca33c14610426576102e9565b806357518243146103dd5780636d60b18f146103f05780636e7a140a14610403576102e9565b8063422c1d371161022f578063422c1d37146103ad5780634a4fbeec146103b557806351b42b00146103d5576102e9565b806337423d5e1461039d5780633fa5e0cb146103a5576102e9565b806324db3d02116102a157806328ba79ab1161028657806328ba79ab146103565780632e41763c1461036957806330edc0f51461037c576102e9565b806324db3d021461032e5780632630c12f14610341576102e9565b80630b825749116102d25780630b825749146103145780630f15f4c01461031c5780631ec2bad014610326576102e9565b806301ed8ef7146102ee5780630b27c8031461030c575b600080fd5b6102f6610575565b6040516103039190614163565b60405180910390f35b6102f6610599565b6102f66105bd565b6103246105c4565b005b6102f6610613565b61032461033c366004613cb0565b610637565b61034961078d565b6040516103039190613ec4565b610324610364366004613cb0565b6107b1565b610324610377366004613abb565b610d38565b61038f61038a366004613a83565b611299565b604051610303929190614054565b610349611370565b6102f6611394565b6102f66113b8565b6103c86103c3366004613a83565b6113bd565b6040516103039190614148565b6103246113d0565b6103246103eb366004613abb565b611414565b6103246103fe366004613cb0565b6116d5565b610324610411366004613c75565b6118aa565b6102f6611a24565b6102f6611a48565b6102f6611a4f565b61044161043c366004613a83565b611a73565b604051610303929190614002565b61032461045d366004613cb0565b611b1e565b610324610470366004613abb565b611cbf565b610349611ef3565b61032461048b366004613c75565b611f17565b61032461049e366004613af3565b61205d565b6104ab612422565b60405161030391906140e2565b6102f6612427565b6103246104ce366004613cb0565b61244b565b6104e66104e1366004613abb565b612691565b604051610303929190614153565b61034961274a565b61034961276e565b610349612792565b61032461051a366004613abb565b6127b6565b610349612ac9565b6102f6612aed565b610349612b11565b610349612b35565b6102f6612b59565b610324610555366004613a83565b612b60565b6102f6612d9c565b610324610570366004613abb565b612dc0565b7fa8a23e80dc3374f66e131ce6c6546961788f417cceb42f2704535c557d8d3de481565b7fdb3c8034db0ef3555cd9d314db16ce45454e1a01d949a021318374472427defa81565b6201518081565b60006105ce6130c9565b90506105d9816130d9565b7f0cc43938d137e7efade6a531f663e78c1fc75257b0d65ffda2fdaf70cb49cdf9816040516106089190613ec4565b60405180910390a150565b7f04cbded2c195932058c24a5373dfbee6688eb058186f4edbd50275a6143b723081565b7335825b18e8948442abc361b361b007e31130f31463851edceb7f000000000000000000000000c6ea970917451fe149537779c20f721eb5e71e767f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a7f262ff49f06430bc23b459cabf68e7c459f0830079494cfd712ed6074c28030c16106bc61317e565b6002887fc2d601f9b2c71cd1f247d67d83d55307ed88800236d40a2069c0a6d2b207ef846106f06080830160608401613a83565b8b604001358b6040516020016107099493929190614198565b6040516020818303038152906040526040518863ffffffff1660e01b815260040161073a979695949392919061424d565b60006040518083038186803b15801561075257600080fd5b505af4158015610766573d6000803e3d6000fd5b50610789925061077f9150506080840160608501613a83565b8260006001613225565b5050565b7f0000000000000000000000004ead68830f45d73478c93953ed56c532bffff4b581565b6107c16080830160608401613a83565b818073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561081f57600080fd5b505afa158015610833573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108579190613a9f565b73ffffffffffffffffffffffffffffffffffffffff1614156108ae576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614590565b60405180910390fd5b8273ffffffffffffffffffffffffffffffffffffffff8116158015906108f057506108ee8173ffffffffffffffffffffffffffffffffffffffff1661333f565b155b610926576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a5906144eb565b7335825b18e8948442abc361b361b007e31130f31463851edceb7f000000000000000000000000c6ea970917451fe149537779c20f721eb5e71e767f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a7f262ff49f06430bc23b459cabf68e7c459f0830079494cfd712ed6074c28030c16109ab61317e565b60008b7f04cbded2c195932058c24a5373dfbee6688eb058186f4edbd50275a6143b72306109df6080830160608401613a83565b8e604001358e6040516020016109f89493929190614198565b6040516020818303038152906040526040518863ffffffff1660e01b8152600401610a29979695949392919061424d565b60006040518083038186803b158015610a4157600080fd5b505af4158015610a55573d6000803e3d6000fd5b507f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a92505073ffffffffffffffffffffffffffffffffffffffff8216905063bb058a41610aa86080890160608a01613a83565b8760016040518463ffffffff1660e01b8152600401610ac993929190613f0c565b60206040518083038186803b158015610ae157600080fd5b505afa158015610af5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b199190613c34565b15610bc15773ffffffffffffffffffffffffffffffffffffffff811663a76163d4610b4a6080890160608a01613a83565b874260016040518563ffffffff1660e01b8152600401610b6d9493929190613fa4565b602060405180830381600087803b158015610b8757600080fd5b505af1158015610b9b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bbf9190613cf5565b505b610bd16080870160608801613a83565b73ffffffffffffffffffffffffffffffffffffffff166313af4035866040518263ffffffff1660e01b8152600401610c099190613ec4565b600060405180830381600087803b158015610c2357600080fd5b505af1158015610c37573d6000803e3d6000fd5b50610c589250610c509150506080880160608901613a83565b306000613379565b73ffffffffffffffffffffffffffffffffffffffff811663edc2a617610c846080890160608a01613a83565b6040518263ffffffff1660e01b8152600401610ca09190613ec4565b600060405180830381600087803b158015610cba57600080fd5b505af1158015610cce573d6000803e3d6000fd5b50610ce3925050506080870160608801613a83565b73ffffffffffffffffffffffffffffffffffffffff167fc4aef5da6b1f6085ebd05a82a61d94bf83a486f32ab4ac3f7843afeed66b144086604051610d289190613ec4565b60405180910390a2505050505050565b610d4061317e565b15610d77576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614344565b8073ffffffffffffffffffffffffffffffffffffffff811615801590610db95750610db78173ffffffffffffffffffffffffffffffffffffffff1661333f565b155b610def576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a5906144eb565b82828073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610e4e57600080fd5b505afa158015610e62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e869190613a9f565b73ffffffffffffffffffffffffffffffffffffffff161415610ed4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614590565b6040517f8cff544c0000000000000000000000000000000000000000000000000000000081527f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a90600090819073ffffffffffffffffffffffffffffffffffffffff841690638cff544c90610f4d908b90600401613ec4565b604080518083038186803b158015610f6457600080fd5b505afa158015610f78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f9c9190613b45565b9150915080600014158015610fc6575073ffffffffffffffffffffffffffffffffffffffff821615155b610ffc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a5906143b2565b42811115611036576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614420565b61103e6130c9565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16146110a2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a5906143e9565b6040517fae5967d400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84169063ae5967d4906110f4908b90600401613ec4565b600060405180830381600087803b15801561110e57600080fd5b505af1158015611122573d6000803e3d6000fd5b50506040517faf5bea3600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8616925063af5bea36915061117d908b906000908190600401613f3d565b600060405180830381600087803b15801561119757600080fd5b505af11580156111ab573d6000803e3d6000fd5b505050506111bb88306000613379565b6040517f13af403500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8916906313af40359061120d908a90600401613ec4565b600060405180830381600087803b15801561122757600080fd5b505af115801561123b573d6000803e3d6000fd5b505050508773ffffffffffffffffffffffffffffffffffffffff167f85c6e43dbd10c1b6d77c7c1da9a4645a2a4555d95a5f5e701ac347317cd085428389604051611287929190613ee5565b60405180910390a25050505050505050565b6060807f0000000000000000000000001663647389993181d13cb45e2113c5d92fa89e7073ffffffffffffffffffffffffffffffffffffffff16639b19251a846040518263ffffffff1660e01b81526004016112f59190613ec4565b60006040518083038186803b15801561130d57600080fd5b505afa158015611321573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526113679190810190613b72565b91509150915091565b7f0000000000000000000000001663647389993181d13cb45e2113c5d92fa89e7081565b7f262ff49f06430bc23b459cabf68e7c459f0830079494cfd712ed6074c28030c181565b600a81565b60006113c882613474565b90505b919050565b60006113da6130c9565b90506113e581613539565b7f749cb6b4c510bc468cf6b9c2086d6f0a54d6b18e25d37bf3200e68eab0880c00816040516106089190613ec4565b61141c61317e565b15611453576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614344565b81600061145e6130c9565b90508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148061155357508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156114db57600080fd5b505afa1580156114ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115139190613a9f565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148015611553575061155182613474565b155b611589576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a59061448e565b6040517fafaee9f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a169063afaee9f19061160090859062278d0090600401614002565b600060405180830381600087803b15801561161a57600080fd5b505af115801561162e573d6000803e3d6000fd5b505073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000001663647389993181d13cb45e2113c5d92fa89e7016915063e09e83e39050858561167f42620151806135df565b6040518463ffffffff1660e01b815260040161169d93929190613f73565b600060405180830381600087803b1580156116b757600080fd5b505af11580156116cb573d6000803e3d6000fd5b5050505050505050565b7335825b18e8948442abc361b361b007e31130f31463851edceb7f000000000000000000000000c6ea970917451fe149537779c20f721eb5e71e767f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a7fa8a23e80dc3374f66e131ce6c6546961788f417cceb42f2704535c557d8d3de461175a61317e565b6002887ff4b4ae271275adc850135662bc4e841b47c72bba882d2e0bc8ff83e11e6f23bd61178e6080830160608401613a83565b8b604001358b6040516020016117a79493929190614198565b6040516020818303038152906040526040518863ffffffff1660e01b81526004016117d8979695949392919061424d565b60006040518083038186803b1580156117f057600080fd5b505af4158015611804573d6000803e3d6000fd5b505073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000001663647389993181d13cb45e2113c5d92fa89e7016915063f8d3277d90506118566080850160608601613a83565b836040518363ffffffff1660e01b8152600401611874929190613ee5565b600060405180830381600087803b15801561188e57600080fd5b505af11580156118a2573d6000803e3d6000fd5b505050505050565b7335825b18e8948442abc361b361b007e31130f31463851edceb7f000000000000000000000000c6ea970917451fe149537779c20f721eb5e71e767f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a7f262ff49f06430bc23b459cabf68e7c459f0830079494cfd712ed6074c28030c161192f61317e565b6003877f5696502d32dd3c98d271b352ac3c61123af461c8bb2ee7f6a63b68a46aeb29a16119636080830160608401613a83565b8a6040013560405160200161197a9392919061416c565b6040516020818303038152906040526040518863ffffffff1660e01b81526004016119ab979695949392919061424d565b60006040518083038186803b1580156119c357600080fd5b505af41580156119d7573d6000803e3d6000fd5b50611a2192506119f09150506080830160608401613a83565b6119fa8380614681565b6000818110611a0557fe5b9050602002016020810190611a1a9190613a83565b6001613379565b50565b7fc2d601f9b2c71cd1f247d67d83d55307ed88800236d40a2069c0a6d2b207ef8481565b62278d0081565b7fdbfbdf039669d5766c6fd0a5920870f82fdd49034a4103cbda583d4c4176640e81565b6000807f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a73ffffffffffffffffffffffffffffffffffffffff16638cff544c846040518263ffffffff1660e01b8152600401611acf9190613ec4565b604080518083038186803b158015611ae657600080fd5b505afa158015611afa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113679190613b45565b7335825b18e8948442abc361b361b007e31130f31463851edceb7f000000000000000000000000c6ea970917451fe149537779c20f721eb5e71e767f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a7fa8a23e80dc3374f66e131ce6c6546961788f417cceb42f2704535c557d8d3de4611ba361317e565b6002887fdb3c8034db0ef3555cd9d314db16ce45454e1a01d949a021318374472427defa611bd76080830160608401613a83565b8b604001358b604051602001611bf09493929190614198565b6040516020818303038152906040526040518863ffffffff1660e01b8152600401611c21979695949392919061424d565b60006040518083038186803b158015611c3957600080fd5b505af4158015611c4d573d6000803e3d6000fd5b505073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000001663647389993181d13cb45e2113c5d92fa89e7016915063e09e83e39050611c9f6080850160608601613a83565b83426040518463ffffffff1660e01b815260040161187493929190613f73565b611cc761317e565b15611cfe576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614344565b816000611d096130c9565b90508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161480611dfe57508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015611d8657600080fd5b505afa158015611d9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dbe9190613a9f565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148015611dfe5750611dfc82613474565b155b611e34576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a59061448e565b6040517fafaee9f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a169063afaee9f190611eab90859062278d0090600401614002565b600060405180830381600087803b158015611ec557600080fd5b505af1158015611ed9573d6000803e3d6000fd5b50505050611eed84846203f4806000613225565b50505050565b7f000000000000000000000000e915058df18e7efe92af5c44df3f575fba061b6481565b7335825b18e8948442abc361b361b007e31130f31463851edceb7f000000000000000000000000c6ea970917451fe149537779c20f721eb5e71e767f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a7f262ff49f06430bc23b459cabf68e7c459f0830079494cfd712ed6074c28030c1611f9c61317e565b6002877fdbfbdf039669d5766c6fd0a5920870f82fdd49034a4103cbda583d4c4176640e611fd06080830160608401613a83565b8a60400135604051602001611fe79392919061416c565b6040516020818303038152906040526040518863ffffffff1660e01b8152600401612018979695949392919061424d565b60006040518083038186803b15801561203057600080fd5b505af4158015612044573d6000803e3d6000fd5b50611a219250610c509150506080830160608401613a83565b61206561317e565b1561209c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614344565b8260006120a76130c9565b90508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148061219c57508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561212457600080fd5b505afa158015612138573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061215c9190613a9f565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614801561219c575061219a82613474565b155b6121d2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a59061448e565b6040517fafaee9f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a169063afaee9f19061224990859062278d0090600401614002565b600060405180830381600087803b15801561226357600080fd5b505af1158015612277573d6000803e3d6000fd5b5050505073ffffffffffffffffffffffffffffffffffffffff84161580156122a3575063ffffffff8316155b806122ed575073ffffffffffffffffffffffffffffffffffffffff8416158015906122d75750624f1a0063ffffffff841610155b80156122ed57506312cc03008363ffffffff1611155b612323576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614624565b6040517faf5bea3600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a169063af5bea369061239990889088908890600401613f3d565b600060405180830381600087803b1580156123b357600080fd5b505af11580156123c7573d6000803e3d6000fd5b505050508473ffffffffffffffffffffffffffffffffffffffff167fff3d54be94bfa9ddad9986419c92a9c4b1e9a649ecf3fef10ea6b7d80860df6a8585604051612413929190614028565b60405180910390a25050505050565b606090565b7f5696502d32dd3c98d271b352ac3c61123af461c8bb2ee7f6a63b68a46aeb29a181565b61245b6080830160608401613a83565b818073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156124b957600080fd5b505afa1580156124cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124f19190613a9f565b73ffffffffffffffffffffffffffffffffffffffff16141561253f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614590565b7335825b18e8948442abc361b361b007e31130f31463851edceb7f000000000000000000000000c6ea970917451fe149537779c20f721eb5e71e767f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a7f262ff49f06430bc23b459cabf68e7c459f0830079494cfd712ed6074c28030c16125c461317e565b60028a7f3aea25321405f61fb6d5dfb4b53be5d64671d80dc2149264e167060f5996b67a6125f86080830160608401613a83565b8d604001358d6040516020016126119493929190614198565b6040516020818303038152906040526040518863ffffffff1660e01b8152600401612642979695949392919061424d565b60006040518083038186803b15801561265a57600080fd5b505af415801561266e573d6000803e3d6000fd5b50611eed92506126879150506080860160608701613a83565b8460006001613622565b6000807f0000000000000000000000001663647389993181d13cb45e2113c5d92fa89e7073ffffffffffffffffffffffffffffffffffffffff1663b6b3527285856040518363ffffffff1660e01b81526004016126ef929190613ee5565b604080518083038186803b15801561270657600080fd5b505afa15801561271a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061273e9190613c54565b915091505b9250929050565b7f000000000000000000000000c8af9c2389af5710dba268050ebf9350cd0acab381565b7f000000000000000000000000ee94cf48924b720af939e732e98f30f9594f87c581565b7f0000000000000000000000009fad9ffcea95c345d41055a63bd099e1a057610981565b6127be61317e565b156127f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614344565b8160006128006130c9565b90508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614806128f557508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561287d57600080fd5b505afa158015612891573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128b59190613a9f565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161480156128f557506128f382613474565b155b61292b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a59061448e565b6040517fafaee9f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a169063afaee9f1906129a290859062278d0090600401614002565b600060405180830381600087803b1580156129bc57600080fd5b505af11580156129d0573d6000803e3d6000fd5b5050505083838073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015612a3357600080fd5b505afa158015612a47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a6b9190613a9f565b73ffffffffffffffffffffffffffffffffffffffff161415612ab9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614590565b6118a286866203f4806000613622565b7f000000000000000000000000c6ea970917451fe149537779c20f721eb5e71e7681565b7f3aea25321405f61fb6d5dfb4b53be5d64671d80dc2149264e167060f5996b67a81565b7f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a81565b7f00000000000000000000000015f50bb48ca4be1ad4a6ad5804b18fb7d198618f81565b6203f48081565b612b6861317e565b15612b9f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614344565b6000612ba96130c9565b90508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161480612c8d57508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015612c2657600080fd5b505afa158015612c3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c5e9190613a9f565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b80612d5a57506040517fbb058a4100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a169063bb058a4190612d0a9085908590600090600401613f0c565b60206040518083038186803b158015612d2257600080fd5b505afa158015612d36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d5a9190613c34565b612d90576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a5906145c7565b61078982826001613379565b7ff4b4ae271275adc850135662bc4e841b47c72bba882d2e0bc8ff83e11e6f23bd81565b612dc861317e565b15612dff576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614344565b816000612e0a6130c9565b90508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161480612eff57508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015612e8757600080fd5b505afa158015612e9b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ebf9190613a9f565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148015612eff5750612efd82613474565b155b612f35576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a59061448e565b6040517fafaee9f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a169063afaee9f190612fac90859062278d0090600401614002565b600060405180830381600087803b158015612fc657600080fd5b505af1158015612fda573d6000803e3d6000fd5b50506040517ff8d3277d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000001663647389993181d13cb45e2113c5d92fa89e7016925063f8d3277d915061169d9087908790600401613ee5565b6000804690507f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8360000151805190602001208460200151805190602001208386604001516040516020016130ab9594939291906141cc565b60405160208183030381529060405280519060200120915050919050565b60006130d36138ee565b90505b90565b8060606130e4612422565b905060005b8151811015611eed578273ffffffffffffffffffffffffffffffffffffffff1663b149206e83838151811061311a57fe5b6020026020010151306040518363ffffffff1660e01b8152600401613140929190614205565b600060405180830381600087803b15801561315a57600080fd5b505af115801561316e573d6000803e3d6000fd5b5050600190920191506130e99050565b6000603836108015906131c657503373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e915058df18e7efe92af5c44df3f575fba061b6416145b1561321d5761321660206000369050036000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506139ab9050565b90506130d6565b5060006130d6565b6040517fa76163d4000000000000000000000000000000000000000000000000000000008152428301907f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a9073ffffffffffffffffffffffffffffffffffffffff82169063a76163d4906132a3908990899087908990600401613fa4565b602060405180830381600087803b1580156132bd57600080fd5b505af11580156132d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132f59190613cf5565b91508573ffffffffffffffffffffffffffffffffffffffff167f3b474d5aea4286914f107a200bc4aefdff380346cf5f4b744aa13f5e7de416ac8684604051610d28929190614002565b6000813f801580159061337257507fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708114155b9392505050565b6040517fb03c4b3300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a169063b03c4b33906133ed9086908590600401613fda565b600060405180830381600087803b15801561340757600080fd5b505af115801561341b573d6000803e3d6000fd5b505050508273ffffffffffffffffffffffffffffffffffffffff167fb435250c8ab5b0393bace5c2fbf9699773f00b35ef348fc2107df2df331f36648383604051613467929190613fda565b60405180910390a2505050565b6040517f4a4fbeec00000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a1690634a4fbeec906134e9908590600401613ec4565b60206040518083038186803b15801561350157600080fd5b505afa158015613515573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113c89190613c34565b806060613544612422565b905060005b8151811015611eed578273ffffffffffffffffffffffffffffffffffffffff1663b149206e83838151811061357a57fe5b602002602001015160006040518363ffffffff1660e01b81526004016135a1929190614205565b600060405180830381600087803b1580156135bb57600080fd5b505af11580156135cf573d6000803e3d6000fd5b5050600190920191506135499050565b8181018281101561361c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614522565b92915050565b8373ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415613688576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a59061437b565b73ffffffffffffffffffffffffffffffffffffffff83166136d5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614457565b6040517fe0bba2a00000000000000000000000000000000000000000000000000000000081527f0000000000000000000000002f84f6f613280fd4df11ab2480e777ba8bb6282a9060009073ffffffffffffffffffffffffffffffffffffffff83169063e0bba2a09061374f908990600190600401613fda565b60206040518083038186803b15801561376757600080fd5b505afa15801561377b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061379f9190613cf5565b9050600a81106137db576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a590614559565b42600282106137e957504284015b6040517feb74483100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84169063eb74483190613841908a908a9086908a90600401613fa4565b602060405180830381600087803b15801561385b57600080fd5b505af115801561386f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138939190613cf5565b90508673ffffffffffffffffffffffffffffffffffffffff167f6d0391926fd0fcb9e582cc3f9b1d2302bfd0866212cc067d5b137352a23a41bc87836040516138dd929190614002565b60405180910390a250505050505050565b60006038361080159061393657503373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e915058df18e7efe92af5c44df3f575fba061b6416145b156139a45761321661398960346000369050036000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506139c79050565b73ffffffffffffffffffffffffffffffffffffffff166130d6565b50336130d6565b600081602001835110156139be57600080fd5b50016020015190565b600081601401835110156139da57600080fd5b5001602001516c01000000000000000000000000900490565b805161361c816147ab565b600082601f830112613a0e578081fd5b8151613a21613a1c8261470d565b6146e6565b818152915060208083019084810181840286018201871015613a4257600080fd5b60005b84811015613a6157815184529282019290820190600101613a45565b505050505092915050565b600060808284031215613a7d578081fd5b50919050565b600060208284031215613a94578081fd5b8135613372816147ab565b600060208284031215613ab0578081fd5b8151613372816147ab565b60008060408385031215613acd578081fd5b8235613ad8816147ab565b91506020830135613ae8816147ab565b809150509250929050565b600080600060608486031215613b07578081fd5b8335613b12816147ab565b92506020840135613b22816147ab565b9150604084013563ffffffff81168114613b3a578182fd5b809150509250925092565b60008060408385031215613b57578182fd5b8251613b62816147ab565b6020939093015192949293505050565b60008060408385031215613b84578182fd5b825167ffffffffffffffff80821115613b9b578384fd5b818501915085601f830112613bae578384fd5b8151613bbc613a1c8261470d565b80828252602080830192508086018a828387028901011115613bdc578889fd5b8896505b84871015613c0657613bf28b826139f3565b845260019690960195928101928101613be0565b508801519096509350505080821115613c1d578283fd5b50613c2a858286016139fe565b9150509250929050565b600060208284031215613c45578081fd5b81518015158114613372578182fd5b60008060408385031215613c66578182fd5b82518015158114613b62578283fd5b600060208284031215613c86578081fd5b813567ffffffffffffffff811115613c9c578182fd5b613ca884828501613a6c565b949350505050565b60008060408385031215613cc2578182fd5b823567ffffffffffffffff811115613cd8578283fd5b613ce485828601613a6c565b9250506020830135613ae8816147ab565b600060208284031215613d06578081fd5b5051919050565b815260200190565b73ffffffffffffffffffffffffffffffffffffffff169052565b60008284526020808501945082825b85811015613d79578135613d51816147ab565b73ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613d3e565b509495945050505050565b818352602080840193600091908185020181018584845b87811015613e4e57828403895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1883603018112613dd9578687fd5b8701803567ffffffffffffffff811115613df1578788fd5b803603891315613dff578788fd5b8086528087830188880137858101870188905299860199601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169094018501935090840190600101613d9b565b5091979650505050505050565b60008151808452815b81811015613e8057602081850181015186830182015201613e64565b81811115613e915782602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152901515604082015260600190565b73ffffffffffffffffffffffffffffffffffffffff938416815291909216602082015263ffffffff909116604082015260600190565b73ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604081019190915260600190565b73ffffffffffffffffffffffffffffffffffffffff94851681529290931660208301526040820152901515606082015260800190565b73ffffffffffffffffffffffffffffffffffffffff9290921682521515602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff92909216825263ffffffff16602082015260400190565b604080825283519082018190526000906020906060840190828701845b8281101561409457614084848351613d15565b9284019290840190600101614071565b505050838103828501528085516140ab8184614163565b91508387019250845b818110156140d5576140c7838551613d0d565b9385019392506001016140b4565b5090979650505050505050565b6020808252825182820181905260009190848201906040850190845b8181101561413c5783517fffffffff0000000000000000000000000000000000000000000000000000000016835292840192918401916001016140fe565b50909695505050505050565b901515815260200190565b9115158252602082015260400190565b90815260200190565b92835273ffffffffffffffffffffffffffffffffffffffff919091166020830152604082015260600190565b93845273ffffffffffffffffffffffffffffffffffffffff9283166020850152604084019190915216606082015260800190565b94855260208501939093526040840191909152606083015273ffffffffffffffffffffffffffffffffffffffff16608082015260a00190565b7fffffffff0000000000000000000000000000000000000000000000000000000092909216825273ffffffffffffffffffffffffffffffffffffffff16602082015260400190565b600073ffffffffffffffffffffffffffffffffffffffff808a168352808916602084015250866040830152856060830152614287856147a0565b608083015260e060a083015261429d848561473a565b608060e08501526142b361016085018284613d2f565b9150506142c3602086018661473a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff20858403016101008601526142f9838284613d84565b925050506040850135610120840152614315606086018661472d565b614323610140850182613d15565b5082810360c08401526143368185613e5b565b9a9950505050505050505050565b60208082526015908201527f494e56414c49445f54585f41574152455f484153480000000000000000000000604082015260600190565b6020808252600f908201527f494e56414c49445f414444524553530000000000000000000000000000000000604082015260600190565b6020808252600c908201527f4e4f5f494e48455249544f520000000000000000000000000000000000000000604082015260600190565b6020808252600c908201527f554e415554484f52495a45440000000000000000000000000000000000000000604082015260600190565b60208082526009908201527f544f4f5f4541524c590000000000000000000000000000000000000000000000604082015260600190565b6020808252600c908201527f5a45524f5f414444524553530000000000000000000000000000000000000000604082015260600190565b60208082526029908201527f4e4f545f46524f4d5f57414c4c45545f4f525f4f574e45525f4f525f57414c4c60408201527f45545f4c4f434b45440000000000000000000000000000000000000000000000606082015260800190565b6020808252600d908201527f494e56414c49445f4f574e455200000000000000000000000000000000000000604082015260600190565b6020808252600c908201527f4144445f4f564552464c4f570000000000000000000000000000000000000000604082015260600190565b60208082526012908201527f544f4f5f4d414e595f475541524449414e530000000000000000000000000000604082015260600190565b6020808252600f908201527f49535f57414c4c45545f4f574e45520000000000000000000000000000000000604082015260600190565b60208082526024908201527f4e4f545f46524f4d5f57414c4c45545f4f525f4f574e45525f4f525f4755415260408201527f4449414e00000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526023908201527f494e56414c49445f494e48455249544f525f4f525f57414954494e475f50455260408201527f494f440000000000000000000000000000000000000000000000000000000000606082015260800190565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126146b5578283fd5b83018035915067ffffffffffffffff8211156146cf578283fd5b602090810192508102360382131561274357600080fd5b60405181810167ffffffffffffffff8111828210171561470557600080fd5b604052919050565b600067ffffffffffffffff821115614723578081fd5b5060209081020190565b60008235613372816147ab565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261476e578283fd5b830160208101925035905067ffffffffffffffff81111561478e57600080fd5b60208102360383131561274357600080fd5b80600581106113cb57fe5b73ffffffffffffffffffffffffffffffffffffffff81168114611a2157600080fdfea26469706673582212207505fbe551c28f7f8616f5356af71fbeba452426392fd73dbfab7edb86fb6d6e64736f6c63430007000033
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
130692:450:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;127240:51;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;127363:143;;;:::i;127300:54::-;;;:::i;102046:192::-;;;:::i;:::-;;117529:131;;;:::i;119626:645::-;;;;;;:::i;:::-;;:::i;100729:43::-;;;:::i;:::-;;;;;;;:::i;122064:1075::-;;;;;;:::i;:::-;;:::i;125412:820::-;;;;;;:::i;:::-;;:::i;129848:247::-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;:::i;100527:46::-;;;:::i;117052:50::-;;;:::i;117111:49::-;;;:::i;123147:142::-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;102324:198::-;;;:::i;127843:349::-;;;;;;:::i;:::-;;:::i;129185:655::-;;;;;;:::i;:::-;;:::i;120744:571::-;;;;;;:::i;:::-;;:::i;117376:146::-;;;:::i;114632:49::-;;;:::i;117782:112::-;;;:::i;125213:191::-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;:::i;128200:707::-;;;;;;:::i;:::-;;:::i;119340:278::-;;;;;;:::i;:::-;;:::i;107376:40::-;;;:::i;121323:571::-;;;;;;:::i;:::-;;:::i;126240:663::-;;;;;;:::i;:::-;;:::i;102843:130::-;;;:::i;:::-;;;;;;;:::i;117667:108::-;;;:::i;118646:686::-;;;;;;:::i;:::-;;:::i;130103:282::-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;:::i;100422:46::-;;;:::i;100779:44::-;;;:::i;100677:45::-;;;:::i;118324:314::-;;;;;;:::i;:::-;;:::i;100629:41::-;;;:::i;117229:140::-;;;:::i;100475:45::-;;;:::i;100580:42::-;;;:::i;117167:53::-;;;:::i;120279:457::-;;;;;;:::i;:::-;;:::i;127513:153::-;;;:::i;128915:262::-;;;;;;:::i;:::-;;:::i;127240:51::-;;;:::o;127363:143::-;127415:91;127363:143;:::o;127300:54::-;127348:6;127300:54;:::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;117529:131::-;117572:88;117529:131;:::o;119626:645::-;119773:13;:27;119815:9;119839:13;119867:25;119907:13;:11;:13::i;:::-;119935:52;120002:7;117427:95;120096:14;;;;;;;;:::i;:::-;120129:7;:18;;;120166:8;120024:165;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;119773:427;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;120213:50:0;;-1:-1:-1;120229:14:0;;-1:-1:-1;;120229:14:0;;;;;;;:::i;:::-;120245:8;120255:1;120258:4;120213:15;:50::i;:::-;119626:645;;:::o;100729:43::-;;;:::o;122064:1075::-;122210:14;;;;;;;;:::i;:::-;122226:8;101286:4;101260:30;;101267:6;101260:20;;;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:30;;;;101252:58;;;;;;;;;;;;:::i;:::-;;;;;;;;;122265:8;101405:18:::1;::::0;::::1;::::0;;::::1;::::0;:40:::1;;;101428:17;:4;:15;;;:17::i;:::-;101427:18;101405:40;101397:66;;;;;;;;;;;;:::i;:::-;122291:13:::2;:27;122333:9;122357:13;122385:25;122425:13;:11;:13::i;:::-;122453:55;122523:7:::0;117572:88:::2;122609:14;::::0;;;::::2;::::0;::::2;;:::i;:::-;122642:7;:18;;;122679:8;122545:157;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;122291:422;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;-1:-1:-1::0;122745:13:0::2;::::0;-1:-1:-1;;122773:13:0::2;::::0;::::2;::::0;-1:-1:-1;122773:13:0::2;122787:14;::::0;;;::::2;::::0;::::2;;:::i;:::-;122803:8;122813:4;122773:45;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;122769:144;;;122835:17;::::0;::::2;;122853:14;::::0;;;::::2;::::0;::::2;;:::i;:::-;122869:8;122879:15;122896:4;122835:66;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;122769:144;122932:14;::::0;;;::::2;::::0;::::2;;:::i;:::-;122925:31;;;122957:8;122925:41;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;-1:-1:-1::0;122977:49:0::2;::::0;-1:-1:-1;122989:14:0::2;::::0;-1:-1:-1;;122989:14:0;;;::::2;::::0;::::2;;:::i;:::-;123013:4;123020:5;122977:11;:49::i;:::-;123037:25;::::0;::::2;;123063:14;::::0;;;::::2;::::0;::::2;;:::i;:::-;123037:41;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;-1:-1:-1::0;123106:14:0::2;::::0;-1:-1:-1;;;123106:14:0;;;::::2;::::0;::::2;;:::i;:::-;123096:35;;;123122:8;123096:35;;;;;;:::i;:::-;;;;;;;;101474:1;101321::::1;122064:1075:::0;;;;:::o;125412:820::-;107586:13;:11;:13::i;:::-;:18;107578:52;;;;;;;;;;;;:::i;:::-;125572:8;101405:18:::1;::::0;::::1;::::0;;::::1;::::0;:40:::1;;;101428:17;:4;:15;;;:17::i;:::-;101427:18;101405:40;101397:66;;;;;;;;;;;;:::i;:::-;125606:6:::2;125614:8;101286:4;101260:30;;101267:6;101260:20;;;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:30;;;;101252:58;;;;;;;;;;;;:::i;:::-;125732:20:::3;::::0;;;;125659:13:::3;::::0;125640:16:::3;::::0;;;125732:12:::3;::::0;::::3;::::0;::::3;::::0;:20:::3;::::0;125745:6;;125732:20:::3;;;:::i;:::-;;::::0;::::3;;;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;125683:69;;;;125773:19;125796:1;125773:24;;:52;;;;-1:-1:-1::0;125801:24:0::3;::::0;::::3;::::0;::::3;125773:52;125765:77;;;;;;;;;;;;:::i;:::-;125884:15;125861:19;:38;;125853:60;;;;;;;;;;;;:::i;:::-;125946:15;:13;:15::i;:::-;125932:29;;:10;:29;;;125924:54;;;;;;;;;;;;:::i;:::-;125991:29;::::0;;;;:21:::3;::::0;::::3;::::0;::::3;::::0;:29:::3;::::0;126013:6;;125991:29:::3;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;::::0;::::3;;;;;-1:-1:-1::0;;126031:38:0::3;::::0;;;;:15:::3;::::0;::::3;::::0;-1:-1:-1;126031:15:0::3;::::0;-1:-1:-1;126031:38:0::3;::::0;126047:6;;126063:1:::3;::::0;;;126031:38:::3;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;::::0;::::3;;;;;;;;;126080:41;126092:6;126108:4;126115:5;126080:11;:41::i;:::-;126134:33;::::0;;;;:23:::3;::::0;::::3;::::0;::::3;::::0;:33:::3;::::0;126158:8;;126134:33:::3;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;::::0;::::3;;;;;;;;;126195:6;126185:39;;;126203:10;126215:8;126185:39;;;;;;;:::i;:::-;;;;;;;;101321:1;;;101474::::2;;107641::::1;125412:820:::0;;:::o;129848:247::-;129948:26;129989:31;130055:14;:24;;;130080:6;130055:32;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;130048:39;;;;129848:247;;;:::o;100527:46::-;;;:::o;117052:50::-;;;:::o;117111:49::-;117158:2;117111:49;:::o;123147:142::-;123229:4;123258:23;123274:6;123258:15;:23::i;:::-;123251:30;;123147:142;;;;:::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;127843:349::-;107586:13;:11;:13::i;:::-;:18;107578:52;;;;;;;;;;;;:::i;:::-;128020: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;;;;;-1:-1:-1::0;;128044:29:0::2;:14;:29;::::0;-1:-1:-1;128044:29:0::2;::::0;-1:-1:-1;128088:6:0;128109:4;128128:45:::2;:15;127348:6;128128:19;:45::i;:::-;128044:140;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;107641:1:::1;;127843:349:::0;;:::o;129185:655::-;129333:13;:27;129375:9;129399:13;129427:26;129468:13;:11;:13::i;:::-;129496:52;129563:7;127570:96;129663:14;;;;;;;;:::i;:::-;129696:7;:18;;;129733:4;129585:167;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;129333:430;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;129776:34:0;:14;:34;;-1:-1:-1;129776:34:0;;-1:-1:-1;129811:14:0;;;;;;;;:::i;:::-;129827:4;129776:56;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;129185:655;;:::o;120744:571::-;120854:13;:27;120896:9;120920:13;120948:25;120988:13;:11;:13::i;:::-;121016:50;121081:7;117707:68;121164:14;;;;;;;;:::i;:::-;121197:7;:18;;;121103:127;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;120854:387;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;121254:53:0;;-1:-1:-1;121266:14:0;;-1:-1:-1;;121266:14:0;;;;;;;:::i;:::-;121282:15;:7;;:15;:::i;:::-;121298:1;121282:18;;;;;;;;;;;;;;;;;;;;:::i;:::-;121302:4;121254:11;:53::i;:::-;120744:571;:::o;117376:146::-;117427:95;117376:146;:::o;114632:49::-;114674:7;114632:49;:::o;117782:112::-;117824:70;117782:112;:::o;125213:191::-;125296:18;125316:24;125365:13;:23;;;125389:6;125365:31;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;128200:707::-;128343:13;:27;128385:9;128409:13;128437:26;128478:13;:11;:13::i;:::-;128506:52;128573:7;127415:91;128668:14;;;;;;;;:::i;:::-;128701:7;:18;;;128738:4;128595:162;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;128343:425;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;128781:29:0;:14;:29;;-1:-1:-1;128781:29:0;;-1:-1:-1;128825:14:0;;;;;;;;:::i;:::-;128854:4;128873:15;128781:118;;;;;;;;;;;;;;;;;:::i;119340:278::-;107586:13;:11;:13::i;:::-;:18;107578:52;;;;;;;;;;;;:::i;:::-;119521: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;;;;;;;;;119545:65:::2;119561:6;119569:8;117214:6;119604:5;119545:15;:65::i;:::-;107641:1:::1;;119340:278:::0;;:::o;107376:40::-;;;:::o;121323:571::-;121433:13;:27;121475:9;121499:13;121527:25;121567:13;:11;:13::i;:::-;121595:52;121662:7;117824:70;121747:14;;;;;;;;:::i;:::-;121780:7;:18;;;121684:129;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;121433:391;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;121837:49:0;;-1:-1:-1;121849:14:0;;-1:-1:-1;;121849:14:0;;;;;;;:::i;126240:663::-;107586:13;:11;:13::i;:::-;:18;107578:52;;;;;;;;;;;;:::i;:::-;126454: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;;;;;-1:-1:-1::0;;;;126500:24:0::2;::::0;::::2;::::0;:47;::::2;;;-1:-1:-1::0;126528:19:0::2;::::0;::::2;::::0;126500:47:::2;:189;;;-1:-1:-1::0;126564:24:0::2;::::0;::::2;::::0;;::::2;::::0;:81:::2;;-1:-1:-1::0;126623:22:0;126605:40:::2;::::0;::::2;;;126564:81;:125;;;;;126680:9;126662:14;:27;;;;126564:125;126478:274;;;;;;;;;;;;:::i;:::-;126765:62;::::0;;;;:26:::2;:13;:26;::::0;::::2;::::0;:62:::2;::::0;126792:6;;126800:10;;126812:14;;126765:62:::2;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;126860:6;126843:52;;;126868:10;126880:14;126843:52;;;;;;;:::i;:::-;;;;;;;;107641:1:::1;;126240:663:::0;;;:::o;102843:130::-;102935:23;102843:130;:::o;117667:108::-;117707:68;117667:108;:::o;118646:686::-;118798:14;;;;;;;;:::i;:::-;118814:8;101286:4;101260:30;;101267:6;101260:20;;;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:30;;;;101252:58;;;;;;;;;;;;:::i;:::-;118840:13:::1;:27;118882:9;118906:13;118934:25;118974:13;:11;:13::i;:::-;119002:52;119069:7:::0;117277:92:::1;119160:14;::::0;;;::::1;::::0;::::1;;:::i;:::-;119193:7;:18;;;119230:8;119091:162;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;118840:424;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;-1:-1:-1::0;119277:47:0::1;::::0;-1:-1:-1;119290:14:0::1;::::0;-1:-1:-1;;119290:14:0;;;::::1;::::0;::::1;;:::i;:::-;119306:8;119316:1;119319:4;119277:12;:47::i;130103:282::-:0;130237:30;130282:18;130335:14;:28;;;130364:6;130372:4;130335:42;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;130328:49;;;;130103:282;;;;;;:::o;100422:46::-;;;:::o;100779:44::-;;;:::o;100677:45::-;;;:::o;118324:314::-;107586:13;:11;:13::i;:::-;:18;107578:52;;;;;;;;;;;;:::i;:::-;118502: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;;;;;;;;;118534:6:::2;118542:8;101286:4;101260:30;;101267:6;101260:20;;;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:30;;;;101252:58;;;;;;;;;;;;:::i;:::-;118568:62:::3;118581:6;118589:8;117214:6;118624:5;118568:12;:62::i;100629:41::-:0;;;:::o;117229:140::-;117277:92;117229:140;:::o;100475:45::-;;;:::o;100580:42::-;;;:::o;117167:53::-;117214:6;117167:53;:::o;120279:457::-;107586:13;:11;:13::i;:::-;:18;107578:52;;;;;;;;;;;;:::i;:::-;120376:30:::1;120409:15;:13;:15::i;:::-;120376:48;;120475:6;120457:24;;:14;:24;;;:81;;;;120523:6;120516:20;;;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;120498:40;;:14;:40;;;120457:81;:153;;;-1:-1:-1::0;120555:55:0::1;::::0;;;;:24:::1;:13;:24;::::0;::::1;::::0;:55:::1;::::0;120580:6;;120588:14;;120604:5:::1;::::0;120555:55:::1;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;120435:239;;;;;;;;;;;;:::i;:::-;120687:41;120699:6;120707:14;120723:4;120687:11;:41::i;127513:153::-:0;127570:96;127513:153;:::o;128915:262::-;107586:13;:11;:13::i;:::-;:18;107578:52;;;;;;;;;;;;:::i;:::-;129097: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;;;;;-1:-1:-1::0;;129121:48:0::2;::::0;;;;:34:::2;:14;:34;::::0;-1:-1:-1;129121:34:0::2;::::0;-1:-1:-1;129121:48:0::2;::::0;129156:6;;129164:4;;129121:48:::2;;;:::i;556:466::-:0;642:7;667:13;714:9;702:21;;376:111;854:6;:11;;;838:29;;;;;;902:6;:14;;;886:32;;;;;;937:8;964:6;:24;;;768:235;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;744:270;;;;;;737:277;;;556: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;;124106:434;124408:63;;;;;124310:15;:31;;;124371:13;;124408:17;;;;;;:63;;124426:6;;124434:8;;124310:31;;124456:14;;124408:63;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;124395:76;;124503:6;124487:45;;;124511:8;124521:10;124487:45;;;;;;;:::i;8219:638::-;8323:4;8697:17;;8734:15;;;;;:114;;-1:-1:-1;8782:66:0;8770:78;;;8734:114;8726:123;8219:638;-1:-1:-1;;;8219:638:0:o;115925:189::-;116021:37;;;;;:21;:13;:21;;;;:37;;116043:6;;116051;;116021:37;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;116087:6;116074:32;;;116095:2;116099:6;116074:32;;;;;;;:::i;:::-;;;;;;;;115925:189;;;:::o;116122:158::-;116242:30;;;;;116213:4;;116242:22;:13;:22;;;;:30;;116265:6;;116242:30;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::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;12465:191;12601:5;;;12625:6;;;;12617:31;;;;;;;;;;;;:::i;:::-;12465:191;;;;:::o;123335:763::-;123538:6;123526:18;;:8;:18;;;;123518:46;;;;;;;;;;;;:::i;:::-;123583:22;;;123575:47;;;;;;;;;;;;:::i;:::-;123698:29;;;;;123654:13;;123635:16;;123698:15;;;;;;:29;;123714:6;;123722:4;;123698:29;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;123678:49;;117158:2;123746:12;:28;123738:59;;;;;;;;;;;;:::i;:::-;123828:15;123874:1;123858:17;;123854:94;;-1:-1:-1;123905:15:0;:31;;123854:94;123971:60;;;;;:14;;;;;;:60;;123986:6;;123994:8;;124004:10;;124016:14;;123971:60;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;123958:73;;124061:6;124047:43;;;124069:8;124079:10;124047:43;;;;;;;:::i;:::-;;;;;;;;123335:763;;;;;;;:::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;;7159:304;7236:7;7282:6;7291:2;7282:11;7264:6;:13;:30;;7256:39;;;;;;-1:-1:-1;7383:30:0;7399:4;7383:30;7377:37;;7159:304::o;4119:338::-;4196:7;4242:6;4251:2;4242:11;4224:6;:13;:30;;4216:39;;;;;;-1:-1:-1;4347:30:0;4363:4;4347:30;4341:37;4380:27;4337:71;;;4119:338::o;142:134:-1:-;220:13;;238:33;220:13;238:33;:::i;1049:722::-;;1177:3;1170:4;1162:6;1158:17;1154:27;1144:2;;-1:-1;;1185:12;1144:2;1225:6;1219:13;1247:80;1262:64;1319:6;1262:64;:::i;:::-;1247:80;:::i;:::-;1355:21;;;1238:89;-1:-1;1399:4;1412:14;;;;1387:17;;;1501;;;1492:27;;;;1489:36;-1:-1;1486:2;;;1538:1;;1528:12;1486:2;1563:1;1548:217;1573:6;1570:1;1567:13;1548:217;;;2332:13;;1641:61;;1716:14;;;;1744;;;;1595:1;1588:9;1548:217;;;1552:14;;;;;1137:634;;;;:::o;1951:159::-;;2062:3;2053:6;2048:3;2044:16;2040:26;2037:2;;;-1:-1;;2069:12;2037:2;-1:-1;2089:15;2030:80;-1:-1;2030:80::o;2530:241::-;;2634:2;2622:9;2613:7;2609:23;2605:32;2602:2;;;-1:-1;;2640:12;2602:2;85:6;72:20;97:33;124:5;97:33;:::i;2778:263::-;;2893:2;2881:9;2872:7;2868:23;2864:32;2861:2;;;-1:-1;;2899:12;2861:2;226:6;220:13;238:33;265:5;238:33;:::i;3048:366::-;;;3169:2;3157:9;3148:7;3144:23;3140:32;3137:2;;;-1:-1;;3175:12;3137:2;85:6;72:20;97:33;124:5;97:33;:::i;:::-;3227:63;-1:-1;3327:2;3366:22;;72:20;97:33;72:20;97:33;:::i;:::-;3335:63;;;;3131:283;;;;;:::o;3421:489::-;;;;3558:2;3546:9;3537:7;3533:23;3529:32;3526:2;;;-1:-1;;3564:12;3526:2;85:6;72:20;97:33;124:5;97:33;:::i;:::-;3616:63;-1:-1;3716:2;3755:22;;72:20;97:33;72:20;97:33;:::i;:::-;3724:63;-1:-1;3824:2;3862:22;;2461:20;46057:10;46046:22;;49641:34;;49631:2;;-1:-1;;49679:12;49631:2;3832:62;;;;3520:390;;;;;:::o;3917:399::-;;;4049:2;4037:9;4028:7;4024:23;4020:32;4017:2;;;-1:-1;;4055:12;4017:2;226:6;220:13;238:33;265:5;238:33;:::i;:::-;4218:2;4268:22;;;;2332:13;4107:74;;2332:13;;-1:-1;;;4011:305::o;4323:657::-;;;4505:2;4493:9;4484:7;4480:23;4476:32;4473:2;;;-1:-1;;4511:12;4473:2;4562:17;4556:24;4600:18;;4592:6;4589:30;4586:2;;;-1:-1;;4622:12;4586:2;4724:6;4713:9;4709:22;;;429:3;422:4;414:6;410:17;406:27;396:2;;-1:-1;;437:12;396:2;477:6;471:13;499:80;514:64;571:6;514:64;:::i;499:80::-;585:16;621:6;614:5;607:21;651:4;;668:3;664:14;657:21;;651:4;643:6;639:17;773:3;651:4;;757:6;753:17;643:6;744:27;;741:36;738:2;;;-1:-1;;780:12;738:2;-1:-1;806:10;;800:217;825:6;822:1;819:13;800:217;;;905:48;949:3;937:10;905:48;:::i;:::-;893:61;;847:1;840:9;;;;;968:14;;;;996;;800:217;;;-1:-1;4784:18;;4778:25;4642:99;;-1:-1;4778:25;-1:-1;;;4812:30;;;4809:2;;;-1:-1;;4845:12;4809:2;;4875:89;4956:7;4947:6;4936:9;4932:22;4875:89;:::i;:::-;4865:99;;;4467:513;;;;;:::o;4987:257::-;;5099:2;5087:9;5078:7;5074:23;5070:32;5067:2;;;-1:-1;;5105:12;5067:2;1860:6;1854:13;49422:5;45371:13;45364:21;49400:5;49397:32;49387:2;;-1:-1;;49433:12;5251:393;;;5380:2;5368:9;5359:7;5355:23;5351:32;5348:2;;;-1:-1;;5386:12;5348:2;1860:6;1854:13;49422:5;45371:13;45364:21;49400:5;49397:32;49387:2;;-1:-1;;49433:12;5651:381;;5782:2;5770:9;5761:7;5757:23;5753:32;5750:2;;;-1:-1;;5788:12;5750:2;5846:17;5833:31;5884:18;5876:6;5873:30;5870:2;;;-1:-1;;5906:12;5870:2;5936:80;6008:7;5999:6;5988:9;5984:22;5936:80;:::i;:::-;5926:90;5744:288;-1:-1;;;;5744:288::o;6039:506::-;;;6187:2;6175:9;6166:7;6162:23;6158:32;6155:2;;;-1:-1;;6193:12;6155:2;6251:17;6238:31;6289:18;6281:6;6278:30;6275:2;;;-1:-1;;6311:12;6275:2;6341:80;6413:7;6404:6;6393:9;6389:22;6341:80;:::i;:::-;6331:90;;;6458:2;6501:9;6497:22;72:20;97:33;124:5;97:33;:::i;6552:263::-;;6667:2;6655:9;6646:7;6642:23;6638:32;6635:2;;;-1:-1;;6673:12;6635:2;-1:-1;2332:13;;6629:186;-1:-1;6629:186::o;7615:173::-;12334:37;;7777:4;7768:14;;7695:93::o;7945:103::-;45851:42;45840:54;8006:37;;8000:48::o;8324:669::-;;42050:6;42045:3;42038:19;42087:4;;42082:3;42078:14;8469:91;;8645:21;-1:-1;8672:299;8697:6;8694:1;8691:13;8672:299;;;85:6;72:20;97:33;124:5;97:33;:::i;:::-;45851:42;45840:54;8006:37;;6976:14;;;;43479:12;;;;8719:1;8712:9;8672:299;;;-1:-1;8977:10;;8456:537;-1:-1;;;;;8456:537::o;10476:939::-;42038:19;;;42087:4;42078:14;;;;10476:939;;42087:4;10794:17;;;10785:27;;;42078:14;10908:21;10476:939;10935:441;10960:6;10957:1;10954:13;10935:441;;;11022:9;11016:4;11012:20;11007:3;11000:33;44694:3;44681:17;44738:48;44762:8;44746:14;44742:29;44738:48;44718:18;44714:73;44704:2;;-1:-1;;44791:12;44704:2;44820:33;;44875:19;;44945:18;44934:30;;44931:2;;;-1:-1;;44967:12;44931:2;45024:17;44746:14;45004:38;44994:8;44990:53;44987:2;;;-1:-1;;45046:12;44987:2;42050:6;42045:3;42038:19;48646:6;42087:4;44913:5;44909:16;42087:4;42082:3;42078:14;48623:30;48684:16;;;;;48677:27;;;11355:14;;;;49083:2;49063:14;49079:7;49059:28;13006:39;;;;;;-1:-1;41421:14;;;;10982:1;10975:9;10935:441;;;-1:-1;11399:10;;10628:787;-1:-1;;;;;;;10628:787::o;13059:359::-;;13209:5;40874:12;42050:6;42045:3;42038:19;-1:-1;48791:101;48805:6;48802:1;48799:13;48791:101;;;42087:4;48872:11;;;;;48866:18;48853:11;;;;;48846:39;48820:10;48791:101;;;48907:6;48904:1;48901:13;48898:2;;;-1:-1;42087:4;48963:6;42082:3;48954:16;;48947:27;48898:2;-1:-1;49083:2;49063:14;49079:7;49059:28;13374:39;;;;42087:4;13374:39;;13157:261;-1:-1;;13157:261::o;21401:222::-;45851:42;45840:54;;;;8006:37;;21528:2;21513:18;;21499:124::o;21630:333::-;45851:42;45840:54;;;8006:37;;45840:54;;21949:2;21934:18;;8006:37;21785:2;21770:18;;21756:207::o;21970:448::-;45851:42;45840:54;;;8006:37;;45840:54;;;;22327:2;22312:18;;7875:58;45371:13;;45364:21;22404:2;22389:18;;12217:34;22155:2;22140:18;;22126:292::o;22425:474::-;45851:42;45840:54;;;8006:37;;45840:54;;;;22795:2;22780:18;;7875:58;46057:10;46046:22;;;22885:2;22870:18;;15088:57;22623:2;22608:18;;22594:305::o;23345:444::-;45851:42;45840:54;;;8006:37;;45840:54;;;;23692:2;23677:18;;8006:37;23775:2;23760:18;;12334:37;;;;23528:2;23513:18;;23499:290::o;23796:544::-;45851:42;45840:54;;;8006:37;;45840:54;;;;24166:2;24151:18;;8006:37;24249:2;24234:18;;12334:37;45371:13;;45364:21;24326:2;24311:18;;12217:34;24001:3;23986:19;;23972:368::o;24794:321::-;45851:42;45840:54;;;;8006:37;;45371:13;45364:21;25101:2;25086:18;;12217:34;24943:2;24928:18;;24914:201::o;25122:333::-;45851:42;45840:54;;;;8006:37;;25441:2;25426:18;;12334:37;25277:2;25262:18;;25248:207::o;25462:329::-;45851:42;45840:54;;;;8006:37;;46057:10;46046:22;25777:2;25762:18;;21353:36;25615:2;25600:18;;25586:205::o;25798:629::-;26053:2;26067:47;;;40874:12;;26038:18;;;42038:19;;;25798:629;;42087:4;;42078:14;;;;40277;;;25798:629;9440:260;9465:6;9462:1;9459:13;9440:260;;;6910:46;6952:3;9532:6;9526:13;6910:46;:::i;:::-;6976:14;;;;41421;;;;9487:1;9480:9;9440:260;;;9444:14;;;26284:9;26278:4;26274:20;42087:4;26258:9;26254:18;26247:48;26309:108;11647:5;40874:12;11666:86;11745:6;11740:3;11666:86;:::i;:::-;11659:93;;42087:4;11823:5;40277:14;11835:21;;-1:-1;11862:260;11887:6;11884:1;11881:13;11862:260;;;11975:63;12034:3;11954:6;11948:13;11975:63;:::i;:::-;41421:14;;;;11968:70;-1:-1;9487:1;11902:9;11862:260;;;-1:-1;26301:116;;26024:403;-1:-1;;;;;;;26024:403::o;26434:366::-;26609:2;26623:47;;;40874:12;;26594:18;;;42038:19;;;26434:366;;26609:2;40277:14;;;;42078;;;;26434:366;10162:257;10187:6;10184:1;10181:13;10162:257;;;10248:13;;45548:66;45537:78;12570:36;;41421:14;;;;7352;;;;10209:1;10202:9;10162:257;;;-1:-1;26676:114;;26580:220;-1:-1;;;;;;26580:220::o;26807:210::-;45371:13;;45364:21;12217:34;;26928:2;26913:18;;26899:118::o;27024:321::-;45371:13;;45364:21;12217:34;;27331:2;27316:18;;12334:37;27173:2;27158:18;;27144:201::o;27352:222::-;12334:37;;;27479:2;27464:18;;27450:124::o;27581:444::-;12334:37;;;45851:42;45840:54;;;;27928:2;27913:18;;8006:37;28011:2;27996:18;;12334:37;27764:2;27749:18;;27735:290::o;28032:556::-;12334:37;;;45851:42;45840:54;;;28408:2;28393:18;;8006:37;28491:2;28476:18;;12334:37;;;;45840:54;28574:2;28559:18;;8006:37;28243:3;28228:19;;28214:374::o;28595:668::-;12334:37;;;28999:2;28984:18;;12334:37;;;;29082:2;29067:18;;12334:37;;;;29165:2;29150:18;;12334:37;45851:42;45840:54;29248:3;29233:19;;8006:37;28834:3;28819:19;;28805:458::o;29270:329::-;45548:66;45537:78;;;;12570:36;;45851:42;45840:54;29585:2;29570:18;;8006:37;29423:2;29408:18;;29394:205::o;30223:1306::-;;45851:42;;45287:5;45840:54;13521:3;13514:68;45851:42;45287:5;45840:54;30874:2;30863:9;30859:18;13514:68;;12364:5;30965:2;30954:9;30950:18;12334:37;12364:5;31056:2;31045:9;31041:18;12334:37;48145:43;14991:5;48145:43;:::i;:::-;31164:3;31153:9;31149:19;14931:67;30653:3;31202;31191:9;31187:19;31180:49;19846:77;19906:16;19899:5;19846:77;:::i;:::-;31164:3;30653;30642:9;30638:19;19936:38;19989:127;19754:14;30642:9;19754:14;20097:12;20083;19989:127;:::i;:::-;19981:135;;;20204:88;30874:2;20279:5;20275:16;20268:5;20204:88;:::i;:::-;20328:14;30642:9;20332:4;20328:14;;20312;30642:9;20312:14;20305:38;20358:147;20500:4;20486:12;20472;20358:147;:::i;:::-;20350:155;;;;30965:2;20616:5;20612:16;2184:20;20691:14;30642:9;20691:14;12334:37;20770:50;31056:2;20807:5;20803:16;20796:5;20770:50;:::i;:::-;20826:71;20882:14;30642:9;20882:14;20868:12;20826:71;:::i;:::-;;31410:9;31404:4;31400:20;31394:3;31383:9;31379:19;31372:49;31435:84;31514:4;31505:6;31435:84;:::i;:::-;31427:92;30624:905;-1:-1;;;;;;;;;;30624:905::o;32895:416::-;33095:2;33109:47;;;15382:2;33080:18;;;42038:19;15418:23;42078:14;;;15398:44;15461:12;;;33066:245::o;33318:416::-;33518:2;33532:47;;;15712:2;33503:18;;;42038:19;15748:17;42078:14;;;15728:38;15785:12;;;33489:245::o;33741:416::-;33941:2;33955:47;;;16036:2;33926:18;;;42038:19;16072:14;42078;;;16052:35;16106:12;;;33912:245::o;34164:416::-;34364:2;34378:47;;;16357:2;34349:18;;;42038:19;16393:14;42078;;;16373:35;16427:12;;;34335:245::o;34587:416::-;34787:2;34801:47;;;16678:1;34772:18;;;42038:19;16713:11;42078:14;;;16693:32;16744:12;;;34758:245::o;35010:416::-;35210:2;35224:47;;;16995:2;35195:18;;;42038:19;17031:14;42078;;;17011:35;17065:12;;;35181:245::o;35433:416::-;35633:2;35647:47;;;17316:2;35618:18;;;42038:19;17352:34;42078:14;;;17332:55;17421:11;17407:12;;;17400:33;17452:12;;;35604:245::o;35856:416::-;36056:2;36070:47;;;17703:2;36041:18;;;42038:19;17739:15;42078:14;;;17719:36;17774:12;;;36027:245::o;36279:416::-;36479:2;36493:47;;;18025:2;36464:18;;;42038:19;18061:14;42078;;;18041:35;18095:12;;;36450:245::o;36702:416::-;36902:2;36916:47;;;18346:2;36887:18;;;42038:19;18382:20;42078:14;;;18362:41;18422:12;;;36873:245::o;37125:416::-;37325:2;37339:47;;;18673:2;37310:18;;;42038:19;18709:17;42078:14;;;18689:38;18746:12;;;37296:245::o;37548:416::-;37748:2;37762:47;;;18997:2;37733:18;;;42038:19;19033:34;42078:14;;;19013:55;19102:6;19088:12;;;19081:28;19128:12;;;37719:245::o;37971:416::-;38171:2;38185:47;;;19379:2;38156:18;;;42038:19;19415:34;42078:14;;;19395:55;19484:5;19470:12;;;19463:27;19509:12;;;38142:245::o;38623:522::-;;;38774:11;38761:25;38825:48;38849:8;38833:14;38829:29;38825:48;38805:18;38801:73;38791:2;;-1:-1;;38878:12;38791:2;38905:33;;38959:18;;;-1:-1;38997:18;38986:30;;38983:2;;;-1:-1;;39019:12;38983:2;38864:4;39047:13;;;;-1:-1;39099:17;;38833:14;39079:38;39069:49;;39066:2;;;39131:1;;39121:12;39152:256;39214:2;39208:9;39240:17;;;39315:18;39300:34;;39336:22;;;39297:62;39294:2;;;39372:1;;39362:12;39294:2;39214;39381:22;39192:216;;-1:-1;39192:216::o;39415:304::-;;39574:18;39566:6;39563:30;39560:2;;;-1:-1;;39596:12;39560:2;-1:-1;39641:4;39629:17;;;39694:15;;39497:222::o;43379:119::-;;85:6;72:20;97:33;124:5;97:33;:::i;43507:517::-;;;43647:3;43634:17;43691:48;43715:8;43699:14;43695:29;43691:48;43671:18;43667:73;43657:2;;-1:-1;;43744:12;43657:2;43773:33;;43730:4;43862:16;;;-1:-1;43828:19;;-1:-1;43898:18;43887:30;;43884:2;;;43930:1;;43920:12;43884:2;43730:4;43981:6;43977:17;43699:14;43957:38;43947:8;43943:53;43940:2;;;44009:1;;43999:12;45627:144;45697:16;49188:1;49178:12;;49168:2;;49194:9;49217:117;45851:42;49304:5;45840:54;49279:5;49276:35;49266:2;;49325:1;;49315:12
Swarm Source
ipfs://7505fbe551c28f7f8616f5356af71fbeba452426392fd73dbfab7edb86fb6d6e
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 26 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.