Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Activate | 11018920 | 1574 days ago | IN | 0 ETH | 0.0006635 |
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 Source Code Verified (Exact Match)
Contract Name:
FinalTransferModule
Compiler Version
v0.7.0+commit.9e61f92b
Contract Source Code (Solidity)
/** *Submitted for verification at Etherscan.io on 2020-10-09 */ // SPDX-License-Identifier: Apache-2.0 // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; /// @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]> /// /// The design of this contract is inspired by Argent's contract codebase: /// https://github.com/argentlabs/argent-contracts 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. /// /// This method will emit `Transacted` event if it doesn't throw. /// /// Note: 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. /// /// @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); } // 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); } } /* * @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; } } // 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); } // 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 constant public ADDR_REVERSE_NODE = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2; // the address of the ENS registry address 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)); } } // 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; } } // 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]> /// /// The design of this contract is inspired by Argent's contract codebase: /// https://github.com/argentlabs/argent-contracts abstract contract DataStore { modifier onlyWalletModule(address wallet) { require(Wallet(wallet).hasModule(msg.sender), "UNAUTHORIZED"); _; } } // 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 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); } } // 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); } } // 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"); } } //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]; } } // 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)) } } } } // 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); } // Copyright 2017 Loopring Technology Limited. library Data { // Optimized to fit into 32 bytes (1 slot) struct Guardian { address addr; uint16 group; uint40 validSince; uint40 validUntil; } } // Copyright 2017 Loopring Technology Limited. /// @title WalletRegistry /// @dev A registry for wallets. /// @author Daniel Wang - <[email protected]> interface WalletRegistry { function registerWallet(address wallet) external; function isWalletRegistered(address addr) external view returns (bool); function numOfWallets() external view returns (uint); } // 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); } // Copyright 2017 Loopring Technology Limited. // 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); } /// @title Controller /// /// @author Daniel Wang - <[email protected]> abstract contract Controller { ModuleRegistry public moduleRegistry; WalletRegistry public walletRegistry; address public walletFactory; } // Copyright 2017 Loopring Technology Limited. pragma experimental ABIEncoderV2; 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)) ); } } // 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); } // Copyright 2017 Loopring Technology Limited. // Copyright 2017 Loopring Technology Limited. /// @title SecurityStore /// /// @author Daniel Wang - <[email protected]> /// /// The design of this contract is inspired by Argent's contract codebase: /// https://github.com/argentlabs/argent-contracts contract SecurityStore is DataStore { using MathUint for uint; using SafeCast for uint; struct Wallet { address inheritor; uint64 lastActive; // the latest timestamp the owner is considered to be active address lockedBy; // the module that locked the wallet. uint64 lock; Data.Guardian[] guardians; mapping (address => uint) guardianIdx; } mapping (address => Wallet) public wallets; constructor() DataStore() {} function isGuardian( address wallet, address addr ) public view returns (bool) { Data.Guardian memory guardian = getGuardian(wallet, addr); return guardian.addr != address(0) && isGuardianActive(guardian); } function isGuardianOrPendingAddition( address wallet, address addr ) public view returns (bool) { Data.Guardian memory guardian = getGuardian(wallet, addr); return guardian.addr != address(0) && (isGuardianActive(guardian) || isGuardianPendingAddition(guardian)); } function getGuardian( address wallet, address guardianAddr ) public view returns (Data.Guardian memory) { uint index = wallets[wallet].guardianIdx[guardianAddr]; if (index > 0) { return wallets[wallet].guardians[index-1]; } } // @dev Returns active guardians. function guardians(address wallet) 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 (isGuardianActive(g)) { _guardians[index] = g; index ++; } } assembly { mstore(_guardians, index) } } // @dev Returns the number of active guardians. function numGuardians(address wallet) public view returns (uint count) { Wallet storage w = wallets[wallet]; for (uint i = 0; i < w.guardians.length; i++) { if (isGuardianActive(w.guardians[i])) { count ++; } } } // @dev Returns guardians who are either active or pending addition. function guardiansWithPending(address wallet) 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 (isGuardianActive(g) || isGuardianPendingAddition(g)) { _guardians[index] = g; index ++; } } assembly { mstore(_guardians, index) } } // @dev Returns the number of guardians who are active or pending addition. function numGuardiansWithPending(address wallet) 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 (isGuardianActive(g) || isGuardianPendingAddition(g)) { count ++; } } } function addGuardian( address wallet, address guardianAddr, uint group, uint validSince ) public onlyWalletModule(wallet) { cleanRemovedGuardians(wallet); require(guardianAddr != address(0), "ZERO_ADDRESS"); Wallet storage w = wallets[wallet]; uint pos = w.guardianIdx[guardianAddr]; require(pos == 0, "GUARDIAN_EXISTS"); // Add the new guardian Data.Guardian memory g = Data.Guardian( guardianAddr, group.toUint16(), validSince.toUint40(), uint40(0) ); w.guardians.push(g); w.guardianIdx[guardianAddr] = w.guardians.length; } function cancelGuardianAddition( address wallet, address guardianAddr ) public onlyWalletModule(wallet) { cleanRemovedGuardians(wallet); Wallet storage w = wallets[wallet]; uint idx = w.guardianIdx[guardianAddr]; require(idx > 0, "GUARDIAN_NOT_EXISTS"); require( isGuardianPendingAddition(w.guardians[idx - 1]), "NOT_PENDING_ADDITION" ); Data.Guardian memory lastGuardian = w.guardians[w.guardians.length - 1]; if (guardianAddr != lastGuardian.addr) { w.guardians[idx - 1] = lastGuardian; w.guardianIdx[lastGuardian.addr] = idx; } w.guardians.pop(); delete w.guardianIdx[guardianAddr]; } function removeGuardian( address wallet, address guardianAddr, uint validUntil ) public onlyWalletModule(wallet) { cleanRemovedGuardians(wallet); Wallet storage w = wallets[wallet]; uint idx = w.guardianIdx[guardianAddr]; require(idx > 0, "GUARDIAN_NOT_EXISTS"); w.guardians[idx - 1].validUntil = validUntil.toUint40(); } function removeAllGuardians(address wallet) public onlyWalletModule(wallet) { Wallet storage w = wallets[wallet]; for (uint i = 0; i < w.guardians.length; i++) { delete w.guardianIdx[w.guardians[i].addr]; } delete w.guardians; } function cancelGuardianRemoval( address wallet, address guardianAddr ) public onlyWalletModule(wallet) { cleanRemovedGuardians(wallet); Wallet storage w = wallets[wallet]; uint idx = w.guardianIdx[guardianAddr]; require(idx > 0, "GUARDIAN_NOT_EXISTS"); require( isGuardianPendingRemoval(w.guardians[idx - 1]), "NOT_PENDING_REMOVAL" ); w.guardians[idx - 1].validUntil = 0; } function getLock(address wallet) public view returns (uint _lock, address _lockedBy) { _lock = wallets[wallet].lock; _lockedBy = wallets[wallet].lockedBy; } function setLock( address wallet, uint lock ) public onlyWalletModule(wallet) { require(lock == 0 || lock > block.timestamp, "INVALID_LOCK_TIME"); wallets[wallet].lock = lock.toUint64(); wallets[wallet].lockedBy = msg.sender; } function lastActive(address wallet) public view returns (uint) { return wallets[wallet].lastActive; } function touchLastActive(address wallet) public onlyWalletModule(wallet) { wallets[wallet].lastActive = uint64(block.timestamp); } function inheritor(address wallet) public view returns ( address _who, uint _lastActive ) { _who = wallets[wallet].inheritor; _lastActive = wallets[wallet].lastActive; } function setInheritor(address wallet, address who) public onlyWalletModule(wallet) { wallets[wallet].inheritor = who; wallets[wallet].lastActive = uint64(block.timestamp); } function cleanRemovedGuardians(address wallet) private { Wallet storage w = wallets[wallet]; for (int i = int(w.guardians.length) - 1; i >= 0; i--) { Data.Guardian memory g = w.guardians[uint(i)]; if (isGuardianExpired(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]; } } } function isGuardianActive(Data.Guardian memory guardian) private view returns (bool) { return guardian.validSince > 0 && guardian.validSince <= block.timestamp && !isGuardianExpired(guardian); } function isGuardianPendingAddition(Data.Guardian memory guardian) private view returns (bool) { return guardian.validSince > block.timestamp; } function isGuardianPendingRemoval(Data.Guardian memory guardian) private view returns (bool) { return guardian.validUntil > block.timestamp; } function isGuardianExpired(Data.Guardian memory guardian) private view returns (bool) { return guardian.validUntil > 0 && guardian.validUntil <= block.timestamp; } } /// @title GuardianUtils /// @author Brecht Devos - <[email protected]> library GuardianUtils { uint constant public MAX_NUM_GROUPS = 16; enum SigRequirement { OwnerNotAllowed, OwnerAllowed, OwnerRequired } 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); uint[MAX_NUM_GROUPS] memory total = countGuardians(allGuardians); // Calculate how many signers are in each group bool walletOwnerSigned = false; Data.Guardian[] memory signingGuardians = new Data.Guardian[](signers.length); address walletOwner = Wallet(wallet).owner(); uint numGuardians = 0; address lastSigner; for (uint i = 0; i < signers.length; i++) { // Check for duplicates require(signers[i] > lastSigner, "INVALID_SIGNERS_ORDER"); lastSigner = signers[i]; if (signers[i] == walletOwner) { walletOwnerSigned = true; } else { require(securityStore.isGuardian(wallet, signers[i]), "SIGNER_NOT_GUARDIAN"); signingGuardians[numGuardians++] = securityStore.getGuardian(wallet, signers[i]); } } // Check owner requirements if (requirement == SigRequirement.OwnerRequired) { require(walletOwnerSigned, "WALLET_OWNER_SIGNATURE_REQUIRED"); } else if (requirement == SigRequirement.OwnerNotAllowed) { require(!walletOwnerSigned, "WALLET_OWNER_SIGNATURE_NOT_ALLOWED"); } // Update the signingGuardians array with the actual number of guardians that have signed // (could be 1 less than the length if the owner signed as well) assembly { mstore(signingGuardians, numGuardians) } uint[MAX_NUM_GROUPS] memory signed = countGuardians(signingGuardians); // Count the number of votes uint totalNumVotes = 0; uint numVotes = 0; if (requirement != SigRequirement.OwnerNotAllowed) { totalNumVotes += 1; numVotes += walletOwnerSigned ? 1 : 0; } if (total[0] > 0) { // Group 0: No grouping totalNumVotes += total[0]; numVotes += signed[0]; } for (uint i = 1; i < MAX_NUM_GROUPS; i++) { if (total[i] > 0) { totalNumVotes += 1; if (i < 6) { // Groups [1, 5]: Single guardian needed per group numVotes += signed[i] > 0 ? 1 : 0; } else if (i < 11) { // Groups [6, 10]: Half the guardians needed per group numVotes += hasHalf(signed[i], total[i]) ? 1 : 0; } else { // Groups [11, 15]: A majority of guardians needed per group numVotes += hasMajority(signed[i], total[i]) ? 1 : 0; } } } // We need a majority of votes require(hasMajority(numVotes, totalNumVotes), "NOT_ENOUGH_SIGNERS"); return true; } function hasHalf( uint count, uint total ) internal pure returns (bool) { return (count >= (total + 1) / 2); } function hasMajority( uint count, uint total ) internal pure returns (bool) { return (count >= (total / 2) + 1); } function countGuardians( Data.Guardian[] memory guardians ) internal pure returns (uint[MAX_NUM_GROUPS] memory total) { for (uint i = 0; i < guardians.length; i++) { total[guardians[i].group]++; } } } // 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 ); } } // Copyright 2017 Loopring Technology Limited. // Copyright 2017 Loopring Technology Limited. // Copyright 2017 Loopring Technology Limited. // Copyright 2017 Loopring Technology Limited. // Copyright 2017 Loopring Technology Limited. // Copyright 2017 Loopring Technology Limited. /// @title Module /// @dev Base contract for all smart wallet modules. /// /// @author Daniel Wang - <[email protected]> /// /// The design of this contract is inspired by Argent's contract codebase: /// https://github.com/argentlabs/argent-contracts 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; } // Copyright 2017 Loopring Technology Limited. /// @title ReentrancyGuard /// @author Brecht Devos - <[email protected]> /// @dev Exposes a modifier that guards a function against reentrancy /// Changing the value of the same storage value multiple times in a transaction /// is cheap (starting from Istanbul) so there is no need to minimize /// the number of times the value is changed contract ReentrancyGuard { //The default value must be 0 in order to work behind a proxy. uint private _guardValue; modifier nonReentrant() { require(_guardValue == 0, "REENTRANCY"); _guardValue = 1; _; _guardValue = 0; } } /// @title BaseModule /// @dev This contract implements some common functions that are likely /// be useful for all modules. /// /// @author Daniel Wang - <[email protected]> /// /// The design of this contract is inspired by Argent's contract codebase: /// https://github.com/argentlabs/argent-contracts abstract contract BaseModule is ReentrancyGuard, Module { using MathUint for uint; using AddressUtil for address; event Activated (address wallet); event Deactivated (address wallet); 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"); _; } function controller() internal view virtual returns(ControllerImpl); /// @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, bool skipQuota ) internal { uint gasCost = gasAmount.mul(gasPrice); if (!skipQuota) { uint value = controller().priceOracle().tokenValue(gasToken, gasCost); if (value > 0) { controller().quotaStore().checkAndAddToSpent(wallet, value); } } transactTokenTransfer(wallet, gasToken, recipient, gasCost); } } // 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 trustedForwarder; constructor(address _trustedForwarder) { trustedForwarder = _trustedForwarder; } 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 == trustedForwarder) { 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 == trustedForwarder) { return msg.data.toBytes32(msg.data.length - 32); } else { return 0; } } } /// @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(address _trustedForwarder) MetaTxAware(_trustedForwarder) { } function logicalSender() internal view virtual override returns (address payable) { return msgSender(); } } // Copyright 2017 Loopring Technology Limited. // Copyright 2017 Loopring Technology Limited. // Copyright 2017 Loopring Technology Limited. // Copyright 2017 Loopring Technology Limited. contract OwnerManagable is Claimable, AddressSet { bytes32 internal constant MANAGER = keccak256("__MANAGED__"); event ManagerAdded (address manager); event ManagerRemoved(address 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); } } /// @title DappAddressStore /// @dev This store maintains global whitelist dapps. contract DappAddressStore is DataStore, OwnerManagable { bytes32 internal constant DAPPS = keccak256("__DAPPS__"); event Whitelisted( address addr, bool whitelisted ); constructor() DataStore() {} function addDapp(address addr) public onlyManager { addAddressToSet(DAPPS, addr, true); emit Whitelisted(addr, true); } function removeDapp(address addr) public onlyManager { removeAddressFromSet(DAPPS, addr); emit Whitelisted(addr, false); } 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); } } // 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) public onlyWalletModule(wallet) { require(!hashes[wallet][hash], "HASH_EXIST"); hashes[wallet][hash] = true; } } // Copyright 2017 Loopring Technology Limited. /// @title NonceStore /// @dev This store maintains all nonces for metaTx contract NonceStore is DataStore { mapping(address => uint) public nonces; constructor() DataStore() {} function lastNonce(address wallet) public view returns (uint) { return nonces[wallet]; } function isNonceValid(address wallet, uint nonce) public view returns (bool) { return nonce > nonces[wallet] && (nonce >> 128) <= block.number; } function verifyAndUpdate(address wallet, uint nonce) public onlyWalletModule(wallet) { require(isNonceValid(wallet, nonce), "INVALID_NONCE"); nonces[wallet] = nonce; } } // 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, Claimable { using MathUint for uint; using SafeCast for uint; uint128 public defaultQuota; // Optimized to fit into 64 bytes (2 slots) struct Quota { uint128 currentQuota; // 0 indicates default uint128 pendingQuota; uint128 spentAmount; uint64 spentTimestamp; uint64 pendingUntil; } mapping (address => Quota) public quotas; event DefaultQuotaChanged( uint prevValue, uint currentValue ); event QuotaScheduled( address wallet, uint pendingQuota, uint64 pendingUntil ); constructor(uint128 _defaultQuota) DataStore() { defaultQuota = _defaultQuota; } function changeDefaultQuota(uint128 _defaultQuota) external onlyOwner { require( _defaultQuota != defaultQuota && _defaultQuota >= 1 ether && _defaultQuota <= 100 ether, "INVALID_DEFAULT_QUOTA" ); emit DefaultQuotaChanged(defaultQuota, _defaultQuota); defaultQuota = _defaultQuota; } function changeQuota( address wallet, uint newQuota, uint effectiveTime ) public onlyWalletModule(wallet) { 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, uint amount ) public onlyWalletModule(wallet) { require(hasEnoughQuota(wallet, amount), "QUOTA_EXCEEDED"); addToSpent(wallet, amount); } function addToSpent( address wallet, uint amount ) public onlyWalletModule(wallet) { Quota storage q = quotas[wallet]; q.spentAmount = spentQuota(wallet).add(amount).toUint128(); q.spentTimestamp = uint64(block.timestamp); } function currentQuota(address wallet) public view returns (uint) { Quota storage q = quotas[wallet]; uint value = q.pendingUntil <= block.timestamp ? q.pendingQuota : q.currentQuota; return value == 0 ? defaultQuota : value; } function pendingQuota(address wallet) public view returns ( uint _pendingQuota, uint _pendingUntil ) { Quota storage q = quotas[wallet]; if (q.pendingUntil > 0 && q.pendingUntil > block.timestamp) { _pendingQuota = q.pendingQuota > 0 ? q.pendingQuota : defaultQuota; _pendingUntil = q.pendingUntil; } } function spentQuota(address wallet) public view returns (uint) { Quota storage q = quotas[wallet]; 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(address wallet) public view returns (uint) { uint quota = currentQuota(wallet); uint spent = spentQuota(wallet); return quota > spent ? quota - spent : 0; } function hasEnoughQuota( address wallet, uint requiredAmount ) public view returns (bool) { return availableQuota(wallet) >= requiredAmount; } } // Copyright 2017 Loopring Technology Limited. /// @title WhitelistStore /// @dev This store maintains a wallet's whitelisted addresses. contract WhitelistStore is DataStore, AddressSet { // wallet => whitelisted_addr => effective_since mapping(address => mapping(address => uint)) public effectiveTimeMap; event Whitelisted( address wallet, address addr, bool whitelisted, uint effectiveTime ); constructor() DataStore() {} function addToWhitelist( address wallet, address addr, uint effectiveTime ) public 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 ) public onlyWalletModule(wallet) { removeAddressFromSet(walletKey(wallet), addr); delete effectiveTimeMap[wallet][addr]; emit Whitelisted(wallet, addr, false, 0); } 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 walletKey(address addr) public pure returns (bytes32) { return keccak256(abi.encodePacked("__WHITELIST__", addr)); } } // 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 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); bytes32 labelNode = keccak256(abi.encodePacked(_label)); bytes32 node = keccak256(abi.encodePacked(rootNode, labelNode)); address currentOwner = getENSRegistry().owner(node); require(currentOwner == address(0), "AEM: _label is alrealdy owned"); // Forward ENS getENSRegistry().setSubnodeOwner(rootNode, labelNode, address(this)); getENSRegistry().setResolver(node, ensResolver); getENSRegistry().setOwner(node, _wallet); ENSResolver(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(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"); } } /// @title ControllerImpl /// @dev Basic implementation of a Controller. /// /// @author Daniel Wang - <[email protected]> contract ControllerImpl is Claimable, Controller { address public collectTo; uint public defaultLockPeriod; BaseENSManager public ensManager; PriceOracle public priceOracle; DappAddressStore public dappAddressStore; HashStore public hashStore; NonceStore public nonceStore; QuotaStore public quotaStore; SecurityStore public securityStore; WhitelistStore public whitelistStore; // Make sure this value if false in production env. // Ideally we can use chainid(), but there is a bug in truffle so testing is buggy: // https://github.com/trufflesuite/ganache/issues/1643 bool public allowChangingWalletFactory; event AddressChanged( string name, address addr ); constructor( ModuleRegistry _moduleRegistry, WalletRegistry _walletRegistry, uint _defaultLockPeriod, address _collectTo, BaseENSManager _ensManager, PriceOracle _priceOracle, bool _allowChangingWalletFactory ) { moduleRegistry = _moduleRegistry; walletRegistry = _walletRegistry; defaultLockPeriod = _defaultLockPeriod; require(_collectTo != address(0), "ZERO_ADDRESS"); collectTo = _collectTo; ensManager = _ensManager; priceOracle = _priceOracle; allowChangingWalletFactory = _allowChangingWalletFactory; } function initStores( DappAddressStore _dappAddressStore, HashStore _hashStore, NonceStore _nonceStore, QuotaStore _quotaStore, SecurityStore _securityStore, WhitelistStore _whitelistStore ) external onlyOwner { require( address(_dappAddressStore) != address(0), "ZERO_ADDRESS" ); // Make sure this function can only invoked once. require( address(dappAddressStore) == address(0), "INITIALIZED_ALREADY" ); dappAddressStore = _dappAddressStore; hashStore = _hashStore; nonceStore = _nonceStore; quotaStore = _quotaStore; securityStore = _securityStore; whitelistStore = _whitelistStore; } function initWalletFactory(address _walletFactory) external onlyOwner { require( allowChangingWalletFactory || walletFactory == address(0), "INITIALIZED_ALREADY" ); require(_walletFactory != address(0), "ZERO_ADDRESS"); walletFactory = _walletFactory; emit AddressChanged("WalletFactory", walletFactory); } function setCollectTo(address _collectTo) external onlyOwner { require(_collectTo != address(0), "ZERO_ADDRESS"); collectTo = _collectTo; emit AddressChanged("CollectTo", collectTo); } function setPriceOracle(PriceOracle _priceOracle) external onlyOwner { priceOracle = _priceOracle; emit AddressChanged("PriceOracle", address(priceOracle)); } } /// @title SignedRequest /// @dev Utilitiy library for better handling of signed wallet requests. /// This library must be deployed and link 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( ControllerImpl controller, 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); // Save hash to prevent replay attacks controller.hashStore().verifyAndUpdate(request.wallet, _txAwareHash); // If txAwareHash from the mata-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" ); require( _txAwareHash.verifySignatures(request.signers, request.signatures), "INVALID_SIGNATURES" ); require( GuardianUtils.requireMajority( controller.securityStore(), request.wallet, request.signers, sigRequirement ), "PERMISSION_DENIED" ); } } /// @title SecurityStore /// /// @author Daniel Wang - <[email protected]> /// /// The design of this contract is inspired by Argent's contract codebase: /// https://github.com/argentlabs/argent-contracts abstract contract SecurityModule is MetaTxModule { using SignedRequest for ControllerImpl; // The minimal number of guardians for recovery and locking. uint constant public MIN_ACTIVE_GUARDIANS = 2; uint constant public MIN_TOUCH_INTERVAL = 7 days; event WalletLock( address indexed wallet, uint lock ); constructor(address _trustedForwarder) MetaTxModule(_trustedForwarder) { } 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 ss = controller().securityStore(); if (block.timestamp > ss.lastActive(wallet) + MIN_TOUCH_INTERVAL) { ss.touchLastActive(wallet); } _; } modifier onlyFromGuardian(address wallet) { require( controller().securityStore().isGuardian(wallet, logicalSender()), "NOT_FROM_GUARDIAN" ); _; } modifier onlyWhenWalletLocked(address wallet) { require(isWalletLocked(wallet), "NOT_LOCKED"); _; } modifier onlyWhenWalletUnlocked(address wallet) { require(!isWalletLocked(wallet), "LOCKED"); _; } modifier onlyWalletGuardian(address wallet, address guardian) { require(controller().securityStore().isGuardian(wallet, guardian), "NOT_GUARDIAN"); _; } modifier notWalletGuardian(address wallet, address guardian) { require(!controller().securityStore().isGuardian(wallet, guardian), "IS_GUARDIAN"); _; } modifier onlyHaveEnoughGuardians(address wallet) { require( controller().securityStore().numGuardians(wallet) >= MIN_ACTIVE_GUARDIANS, "NO_ENOUGH_ACTIVE_GUARDIANS" ); _; } // ----- internal methods ----- function quotaStore() internal view returns (address) { return address(controller().quotaStore()); } function lockWallet(address wallet) internal { lockWallet(wallet, controller().defaultLockPeriod()); } function lockWallet(address wallet, uint _lockPeriod) internal onlyWhenWalletUnlocked(wallet) { // cannot lock the wallet twice by different modules. require(_lockPeriod > 0, "ZERO_VALUE"); uint lock = block.timestamp + _lockPeriod; controller().securityStore().setLock(wallet, lock); emit WalletLock(wallet, lock); } function unlockWallet(address wallet, bool forceUnlock) internal { (uint _lock, address _lockedBy) = controller().securityStore().getLock(wallet); if (_lock > block.timestamp) { require(forceUnlock || _lockedBy == address(this), "UNABLE_TO_UNLOCK"); controller().securityStore().setLock(wallet, 0); } emit WalletLock(wallet, 0); } function getWalletLock(address wallet) internal view returns (uint _lock, address _lockedBy) { return controller().securityStore().getLock(wallet); } function isWalletLocked(address wallet) internal view returns (bool) { (uint _lock,) = controller().securityStore().getLock(wallet); return _lock > block.timestamp; } function updateQuota( address wallet, address token, uint amount ) internal { if (amount > 0 && quotaStore() != address(0)) { uint value = controller().priceOracle().tokenValue(token, amount); QuotaStore(quotaStore()).checkAndAddToSpent(wallet, value); } } } // Copyright 2017 Loopring Technology Limited. /// @title BaseTransferModule /// @author Brecht Devos - <[email protected]> /// @author Daniel Wang - <[email protected]> abstract contract BaseTransferModule is SecurityModule { using MathUint for uint; event Transfered( address wallet, address token, address to, uint amount, bytes logdata ); event Approved( address wallet, address token, address spender, uint amount ); event ContractCalled( address wallet, address to, uint value, bytes data ); function transferInternal( address wallet, address token, address to, uint amount, bytes calldata logdata ) internal { transactTokenTransfer(wallet, token, to, amount); emit Transfered(wallet, token, to, amount, logdata); } function approveInternal( address wallet, address token, address spender, uint amount ) internal returns (uint additionalAllowance) { // Current allowance uint allowance = ERC20(token).allowance(wallet, spender); if (amount != allowance) { // First reset the approved amount if needed if (allowance > 0) { transactTokenApprove(wallet, token, spender, 0); } // Now approve the requested amount transactTokenApprove(wallet, token, spender, amount); } // If we increased the allowance, calculate by how much if (amount > allowance) { additionalAllowance = amount.sub(allowance); } emit Approved(wallet, token, spender, amount); } function callContractInternal( address wallet, address to, uint value, bytes calldata txData ) internal virtual returns (bytes memory returnData) { // Calls from the wallet to itself are deemed special // (e.g. this is used for updating the wallet implementation) // We also disallow calls to module functions directly // (e.g. this is used for some special wallet <-> module interaction) require(wallet != to && !Wallet(wallet).hasModule(to), "CALL_DISALLOWED"); // Disallow general calls to token contracts (for tokens that have price data // so the quota is actually used). require(controller().priceOracle().tokenValue(to, 1e18) == 0, "CALL_DISALLOWED"); returnData = transactCall(wallet, to, value, txData); emit ContractCalled(wallet, to, value, txData); } function isTargetWhitelisted(address wallet, address to) internal view returns (bool res) { (res,) = controller().whitelistStore().isWhitelisted(wallet, to); res = res || controller().dappAddressStore().isDapp(to); } } /// @title TransferModule /// @author Brecht Devos - <[email protected]> /// @author Daniel Wang - <[email protected]> abstract contract TransferModule is BaseTransferModule { using MathUint for uint; using SignedRequest for ControllerImpl; bytes32 public TRANSFER_DOMAIN_SEPERATOR; bytes32 public constant CHANGE_DAILY_QUOTE_IMMEDIATELY_TYPEHASH = keccak256( "changeDailyQuotaImmediately(address wallet,uint256 validUntil,uint256 newQuota)" ); bytes32 public constant TRANSFER_TOKEN_TYPEHASH = keccak256( "transferTokenWithApproval(address wallet,uint256 validUntil,address token,address to,uint256 amount,bytes logdata)" ); bytes32 public constant APPROVE_TOKEN_TYPEHASH = keccak256( "approveTokenWithApproval(address wallet,uint256 validUntil,address token,address to,uint256 amount)" ); bytes32 public constant CALL_CONTRACT_TYPEHASH = keccak256( "callContractWithApproval(address wallet,uint256 validUntil,address to,uint256 value,bytes data)" ); bytes32 public constant APPROVE_THEN_CALL_CONTRACT_TYPEHASH = keccak256( "approveThenCallContractWithApproval(address wallet,uint256 validUntil,address token,address to,uint256 amount,uint256 value,bytes data)" ); uint public transferDelayPeriod; constructor(uint _transferDelayPeriod) { require(_transferDelayPeriod > 0, "INVALID_DELAY"); TRANSFER_DOMAIN_SEPERATOR = EIP712.hash( EIP712.Domain("TransferModule", "1.1.0", address(this)) ); transferDelayPeriod = _transferDelayPeriod; } function changeDailyQuota( address wallet, uint newQuota ) external nonReentrant txAwareHashNotAllowed() onlyFromWalletOrOwnerWhenUnlocked(wallet) { QuotaStore qs = controller().quotaStore(); uint _newQuota = newQuota == 0 ? qs.defaultQuota(): newQuota; uint _currentQuota = qs.currentQuota(wallet); if (_currentQuota >= _newQuota) { qs.changeQuota(wallet, _newQuota, block.timestamp); } else { qs.changeQuota(wallet, _newQuota, block.timestamp.add(transferDelayPeriod)); } } function changeDailyQuotaImmediately( SignedRequest.Request calldata request, uint newQuota ) external nonReentrant onlyWhenWalletUnlocked(request.wallet) { controller().verifyRequest( TRANSFER_DOMAIN_SEPERATOR, txAwareHash(), GuardianUtils.SigRequirement.OwnerRequired, request, abi.encode( CHANGE_DAILY_QUOTE_IMMEDIATELY_TYPEHASH, request.wallet, request.validUntil, newQuota ) ); controller().quotaStore().changeQuota(request.wallet, newQuota, block.timestamp); } function transferToken( address wallet, address token, address to, uint amount, bytes calldata logdata ) external nonReentrant txAwareHashNotAllowed() onlyFromWalletOrOwnerWhenUnlocked(wallet) { if (amount > 0 && !isTargetWhitelisted(wallet, to)) { updateQuota(wallet, token, amount); } transferInternal(wallet, token, to, amount, logdata); } function callContract( address wallet, address to, uint value, bytes calldata data ) external nonReentrant txAwareHashNotAllowed() onlyFromWalletOrOwnerWhenUnlocked(wallet) returns (bytes memory returnData) { if (value > 0 && !isTargetWhitelisted(wallet, to)) { updateQuota(wallet, address(0), value); } return callContractInternal(wallet, to, value, data); } function approveToken( address wallet, address token, address to, uint amount ) external nonReentrant txAwareHashNotAllowed() onlyFromWalletOrOwnerWhenUnlocked(wallet) { uint additionalAllowance = approveInternal(wallet, token, to, amount); if (additionalAllowance > 0 && !isTargetWhitelisted(wallet, to)) { updateQuota(wallet, token, additionalAllowance); } } function approveThenCallContract( address wallet, address token, address to, uint amount, uint value, bytes calldata data ) external nonReentrant txAwareHashNotAllowed() onlyFromWalletOrOwnerWhenUnlocked(wallet) returns (bytes memory returnData) { uint additionalAllowance = approveInternal(wallet, token, to, amount); if ((additionalAllowance > 0 || value > 0) && !isTargetWhitelisted(wallet, to)) { updateQuota(wallet, token, additionalAllowance); updateQuota(wallet, address(0), value); } return callContractInternal(wallet, to, value, data); } function getDailyQuota(address wallet) public view returns ( uint total, uint spent, uint available ) { total = controller().quotaStore().currentQuota(wallet); spent = controller().quotaStore().spentQuota(wallet); available = controller().quotaStore().availableQuota(wallet); } function transferTokenWithApproval( SignedRequest.Request calldata request, address token, address to, uint amount, bytes calldata logdata ) external nonReentrant onlyWhenWalletUnlocked(request.wallet) { controller().verifyRequest( TRANSFER_DOMAIN_SEPERATOR, txAwareHash(), GuardianUtils.SigRequirement.OwnerRequired, request, abi.encode( TRANSFER_TOKEN_TYPEHASH, request.wallet, request.validUntil, token, to, amount, keccak256(logdata) ) ); transferInternal(request.wallet, token, to, amount, logdata); } function approveTokenWithApproval( SignedRequest.Request calldata request, address token, address to, uint amount ) external nonReentrant onlyWhenWalletUnlocked(request.wallet) { controller().verifyRequest( TRANSFER_DOMAIN_SEPERATOR, txAwareHash(), GuardianUtils.SigRequirement.OwnerRequired, request, abi.encode( APPROVE_TOKEN_TYPEHASH, request.wallet, request.validUntil, token, to, amount ) ); approveInternal(request.wallet, token, to, amount); } function callContractWithApproval( SignedRequest.Request calldata request, address to, uint value, bytes calldata data ) external nonReentrant onlyWhenWalletUnlocked(request.wallet) returns (bytes memory returnData) { controller().verifyRequest( TRANSFER_DOMAIN_SEPERATOR, txAwareHash(), GuardianUtils.SigRequirement.OwnerRequired, request, abi.encode( CALL_CONTRACT_TYPEHASH, request.wallet, request.validUntil, to, value, keccak256(data) ) ); return callContractInternal(request.wallet, to, value, data); } function approveThenCallContractWithApproval( SignedRequest.Request calldata request, address token, address to, uint amount, uint value, bytes calldata data ) external nonReentrant onlyWhenWalletUnlocked(request.wallet) returns (bytes memory returnData) { bytes memory encoded = abi.encode( APPROVE_THEN_CALL_CONTRACT_TYPEHASH, request.wallet, request.validUntil, token, to, amount, value, keccak256(data) ); controller().verifyRequest( TRANSFER_DOMAIN_SEPERATOR, txAwareHash(), GuardianUtils.SigRequirement.OwnerRequired, request, encoded ); approveInternal(request.wallet, token, to, amount); return callContractInternal(request.wallet, to, value, data); } } /// @title FinalTransferModule /// @dev This module combines multiple small modules to /// minimize the number of modules to reduce gas used /// by wallet creation. contract FinalTransferModule is TransferModule { ControllerImpl private controller_; constructor( ControllerImpl _controller, address _trustedForwarder, uint _transferDelayPeriod ) SecurityModule(_trustedForwarder) TransferModule(_transferDelayPeriod) { controller_ = _controller; } function controller() internal view override returns(ControllerImpl) { return ControllerImpl(controller_); } function bindableMethods() public pure override returns (bytes4[] memory methods) { } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"contract ControllerImpl","name":"_controller","type":"address"},{"internalType":"address","name":"_trustedForwarder","type":"address"},{"internalType":"uint256","name":"_transferDelayPeriod","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"wallet","type":"address"}],"name":"Activated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"ContractCalled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"wallet","type":"address"}],"name":"Deactivated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"logdata","type":"bytes"}],"name":"Transfered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"uint256","name":"lock","type":"uint256"}],"name":"WalletLock","type":"event"},{"inputs":[],"name":"APPROVE_THEN_CALL_CONTRACT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"APPROVE_TOKEN_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CALL_CONTRACT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CHANGE_DAILY_QUOTE_IMMEDIATELY_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_ACTIVE_GUARDIANS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_TOUCH_INTERVAL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TRANSFER_DOMAIN_SEPERATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TRANSFER_TOKEN_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"activate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"approveThenCallContract","outputs":[{"internalType":"bytes","name":"returnData","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct SignedRequest.Request","name":"request","type":"tuple"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"approveThenCallContractWithApproval","outputs":[{"internalType":"bytes","name":"returnData","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approveToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct SignedRequest.Request","name":"request","type":"tuple"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approveTokenWithApproval","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"bindableMethods","outputs":[{"internalType":"bytes4[]","name":"methods","type":"bytes4[]"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"callContract","outputs":[{"internalType":"bytes","name":"returnData","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct SignedRequest.Request","name":"request","type":"tuple"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"callContractWithApproval","outputs":[{"internalType":"bytes","name":"returnData","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"uint256","name":"newQuota","type":"uint256"}],"name":"changeDailyQuota","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct SignedRequest.Request","name":"request","type":"tuple"},{"internalType":"uint256","name":"newQuota","type":"uint256"}],"name":"changeDailyQuotaImmediately","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"deactivate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"}],"name":"getDailyQuota","outputs":[{"internalType":"uint256","name":"total","type":"uint256"},{"internalType":"uint256","name":"spent","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"transferDelayPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"logdata","type":"bytes"}],"name":"transferToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct SignedRequest.Request","name":"request","type":"tuple"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"logdata","type":"bytes"}],"name":"transferTokenWithApproval","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"trustedForwarder","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
60806040523480156200001157600080fd5b5060405162003e8138038062003e8183398101604081905262000034916200019c565b600080546001600160a01b0319166001600160a01b0384161790558080620000795760405162461bcd60e51b815260040162000070906200020f565b60405180910390fd5b620000f560405180606001604052806040518060400160405280600e81526020016d5472616e736665724d6f64756c6560901b8152508152602001604051806040016040528060058152602001640312e312e360dc1b8152508152602001306001600160a01b03168152506200012360201b620020001760201c565b6002556003555050600480546001600160a01b0319166001600160a01b03929092169190911790556200024f565b6000804690507f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8360000151805190602001208460200151805190602001208386604001516040516020016200017e959493929190620001e3565b60405160208183030381529060405280519060200120915050919050565b600080600060608486031215620001b1578283fd5b8351620001be8162000236565b6020850151909350620001d18162000236565b80925050604084015190509250925092565b9485526020850193909352604084019190915260608301526001600160a01b0316608082015260a00190565b6020808252600d908201526c494e56414c49445f44454c415960981b604082015260600190565b6001600160a01b03811681146200024c57600080fd5b50565b613c22806200025f6000396000f3fe608060405234801561001057600080fd5b50600436106101585760003560e01c80637da0a877116100c3578063bf01ce111161007c578063bf01ce1114610279578063c6441d0c14610281578063dbb07b7414610294578063e321531b1461029c578063f1eb90b1146102be578063fd6ac309146102d157610158565b80637da0a8771461022457806388666d81146102395780639a53228a1461024c5780639cdef1e514610254578063afa3363d1461025c578063b345c8c11461027157610158565b80632df546f4116101155780632df546f4146101d357806336c0ad37146101e657806344b5087b146101f957806351b42b001461020157806355e9a6691461020957806375529de81461021157610158565b80630c37ef4c1461015d5780630f15f4c01461017b5780630fca973a1461018557806312ef080d146101a557806313d53dcf146101b85780632b30907b146101c0575b600080fd5b6101656102e4565b604051610172919061370c565b60405180910390f35b610183610308565b005b6101986101933660046132a8565b610357565b6040516101729190613877565b6101836101b3366004612ef0565b610510565b6101656107dd565b6101836101ce366004613220565b6107e2565b6101836101e1366004612f40565b610968565b6101836101f43660046131d8565b610c34565b610165610da1565b610183610dc5565b610165610e09565b61019861021f366004612fc3565b610e2d565b61022c611128565b60405161017291906135a5565b6101836102473660046130c1565b611137565b610165611638565b61016561163f565b610264611663565b60405161017291906136be565b610165611668565b61016561168c565b61018361028f3660046133ae565b611692565b6101656118b5565b6102af6102aa366004612eb8565b6118bb565b60405161017293929190613b02565b6101986102cc366004613339565b611ba5565b6101986102df366004613050565b611d2e565b7f318096867a993ed9ca8cd85acec91d02ecf5fcc7cbd42172636c06fe60679a4b81565b6000610312612078565b905061031d81612088565b7f0cc43938d137e7efade6a531f663e78c1fc75257b0d65ffda2fdaf70cb49cdf98160405161034c91906135a5565b60405180910390a150565b60606001546000146103845760405162461bcd60e51b815260040161037b9061397b565b60405180910390fd5b600180556103986080890160608a01612eb8565b6103a181612126565b156103be5760405162461bcd60e51b815260040161037b90613ae2565b60607f7445021182251861baff597516a504c20ceb3c0ea0cbb378707b3ff80172da206103f060808c018c8401612eb8565b8b604001358b8b8b8b8b8b604051610409929190613595565b6040519081900381206104259897969594939291602001613789565b604051602081830303815290604052905061043e612225565b6001600160a01b031673353e18fa10e73d307d1f5804bf2866e3555396f363999343c7909160025461046e612234565b60028f876040518763ffffffff1660e01b81526004016104939695949392919061388a565b60006040518083038186803b1580156104ab57600080fd5b505af41580156104bf573d6000803e3d6000fd5b506104e092506104d891505060808c0160608d01612eb8565b8a8a8a6122b0565b506104fd6104f460808c0160608d01612eb8565b898888886123b4565b60006001559a9950505050505050505050565b600154156105305760405162461bcd60e51b815260040161037b9061397b565b6001805561053c612234565b156105595760405162461bcd60e51b815260040161037b9061394c565b836000610564612078565b9050816001600160a01b0316816001600160a01b031614806106185750816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156105ba57600080fd5b505afa1580156105ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f29190612ed4565b6001600160a01b0316816001600160a01b0316148015610618575061061682612126565b155b6106345760405162461bcd60e51b815260040161037b90613a16565b600061063e612225565b6001600160a01b031663d51b3a1b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561067657600080fd5b505afa15801561068a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106ae9190612ed4565b905062093a80816001600160a01b031663c2ec28d6856040518263ffffffff1660e01b81526004016106e091906135a5565b60206040518083038186803b1580156106f857600080fd5b505afa15801561070c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107309190613418565b014211156107975760405163d36e351760e01b81526001600160a01b0382169063d36e3517906107649086906004016135a5565b600060405180830381600087803b15801561077e57600080fd5b505af1158015610792573d6000803e3d6000fd5b505050505b60006107a5888888886122b0565b90506000811180156107be57506107bc8887612613565b155b156107ce576107ce88888361280d565b50506000600155505050505050565b600281565b600154156108025760405162461bcd60e51b815260040161037b9061397b565b600180556108166080870160608801612eb8565b61081f81612126565b1561083c5760405162461bcd60e51b815260040161037b90613ae2565b610844612225565b6001600160a01b031673353e18fa10e73d307d1f5804bf2866e3555396f363999343c79091600254610874612234565b60028c7f318096867a993ed9ca8cd85acec91d02ecf5fcc7cbd42172636c06fe60679a4b6108a86080830160608401612eb8565b8f604001358f8f8f8f8f6040516108c0929190613595565b6040519081900381206108db9796959493929160200161374b565b6040516020818303038152906040526040518763ffffffff1660e01b815260040161090b9695949392919061388a565b60006040518083038186803b15801561092357600080fd5b505af4158015610937573d6000803e3d6000fd5b5061095a92506109509150506080890160608a01612eb8565b878787878761299a565b505060006001555050505050565b600154156109885760405162461bcd60e51b815260040161037b9061397b565b60018055610994612234565b156109b15760405162461bcd60e51b815260040161037b9061394c565b8560006109bc612078565b9050816001600160a01b0316816001600160a01b03161480610a705750816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610a1257600080fd5b505afa158015610a26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a4a9190612ed4565b6001600160a01b0316816001600160a01b0316148015610a705750610a6e82612126565b155b610a8c5760405162461bcd60e51b815260040161037b90613a16565b6000610a96612225565b6001600160a01b031663d51b3a1b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610ace57600080fd5b505afa158015610ae2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b069190612ed4565b905062093a80816001600160a01b031663c2ec28d6856040518263ffffffff1660e01b8152600401610b3891906135a5565b60206040518083038186803b158015610b5057600080fd5b505afa158015610b64573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b889190613418565b01421115610bef5760405163d36e351760e01b81526001600160a01b0382169063d36e351790610bbc9086906004016135a5565b600060405180830381600087803b158015610bd657600080fd5b505af1158015610bea573d6000803e3d6000fd5b505050505b600086118015610c065750610c048988612613565b155b15610c1657610c1689898861280d565b610c2489898989898961299a565b5050600060015550505050505050565b60015415610c545760405162461bcd60e51b815260040161037b9061397b565b60018055610c686080850160608601612eb8565b610c7181612126565b15610c8e5760405162461bcd60e51b815260040161037b90613ae2565b610c96612225565b6001600160a01b031673353e18fa10e73d307d1f5804bf2866e3555396f363999343c79091600254610cc6612234565b60028a7fbf396b7bca433049fa198a0f2196e349f42bafe693fc380709806ff0e5981050610cfa6080830160608401612eb8565b8d604001358d8d8d604051602001610d1796959493929190613715565b6040516020818303038152906040526040518763ffffffff1660e01b8152600401610d479695949392919061388a565b60006040518083038186803b158015610d5f57600080fd5b505af4158015610d73573d6000803e3d6000fd5b50610d949250610d8c9150506080870160608801612eb8565b8585856122b0565b5050600060015550505050565b7f6684041b14f7240c87b9475c99b65953b05c26a0b41e768674a948013418bfa581565b6000610dcf612078565b9050610dda816129ef565b7f749cb6b4c510bc468cf6b9c2086d6f0a54d6b18e25d37bf3200e68eab0880c008160405161034c91906135a5565b7facc9ea428cc905b226e8b5801d63f262835e4f35726f4caa71b13b86899e833681565b6060600154600014610e515760405162461bcd60e51b815260040161037b9061397b565b60018055610e5d612234565b15610e7a5760405162461bcd60e51b815260040161037b9061394c565b876000610e85612078565b9050816001600160a01b0316816001600160a01b03161480610f395750816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610edb57600080fd5b505afa158015610eef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f139190612ed4565b6001600160a01b0316816001600160a01b0316148015610f395750610f3782612126565b155b610f555760405162461bcd60e51b815260040161037b90613a16565b6000610f5f612225565b6001600160a01b031663d51b3a1b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610f9757600080fd5b505afa158015610fab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fcf9190612ed4565b905062093a80816001600160a01b031663c2ec28d6856040518263ffffffff1660e01b815260040161100191906135a5565b60206040518083038186803b15801561101957600080fd5b505afa15801561102d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110519190613418565b014211156110b85760405163d36e351760e01b81526001600160a01b0382169063d36e3517906110859086906004016135a5565b600060405180830381600087803b15801561109f57600080fd5b505af11580156110b3573d6000803e3d6000fd5b505050505b60006110c68c8c8c8c6122b0565b905060008111806110d75750600088115b80156110ea57506110e88c8b612613565b155b15611106576110fa8c8c8361280d565b6111068c60008a61280d565b6111138c8b8a8a8a6123b4565b60006001559c9b505050505050505050505050565b6000546001600160a01b031681565b600154156111575760405162461bcd60e51b815260040161037b9061397b565b60018055611163612234565b156111805760405162461bcd60e51b815260040161037b9061394c565b81600061118b612078565b9050816001600160a01b0316816001600160a01b0316148061123f5750816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156111e157600080fd5b505afa1580156111f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112199190612ed4565b6001600160a01b0316816001600160a01b031614801561123f575061123d82612126565b155b61125b5760405162461bcd60e51b815260040161037b90613a16565b6000611265612225565b6001600160a01b031663d51b3a1b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561129d57600080fd5b505afa1580156112b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112d59190612ed4565b905062093a80816001600160a01b031663c2ec28d6856040518263ffffffff1660e01b815260040161130791906135a5565b60206040518083038186803b15801561131f57600080fd5b505afa158015611333573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113579190613418565b014211156113be5760405163d36e351760e01b81526001600160a01b0382169063d36e35179061138b9086906004016135a5565b600060405180830381600087803b1580156113a557600080fd5b505af11580156113b9573d6000803e3d6000fd5b505050505b60006113c8612225565b6001600160a01b031663d9d104846040518163ffffffff1660e01b815260040160206040518083038186803b15801561140057600080fd5b505afa158015611414573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114389190612ed4565b90506000851561144857856114c3565b816001600160a01b031663561f30d96040518163ffffffff1660e01b815260040160206040518083038186803b15801561148157600080fd5b505afa158015611495573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114b991906133f1565b6001600160801b03165b90506000826001600160a01b031663b0f191dc896040518263ffffffff1660e01b81526004016114f391906135a5565b60206040518083038186803b15801561150b57600080fd5b505afa15801561151f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115439190613418565b90508181106115b3576040516302a71bc160e11b81526001600160a01b0384169063054e37829061157c908b908690429060040161369d565b600060405180830381600087803b15801561159657600080fd5b505af11580156115aa573d6000803e3d6000fd5b505050506107ce565b826001600160a01b031663054e378289846115d960035442612a8890919063ffffffff16565b6040518463ffffffff1660e01b81526004016115f79392919061369d565b600060405180830381600087803b15801561161157600080fd5b505af1158015611625573d6000803e3d6000fd5b5050505050506000600155505050505050565b62093a8081565b7f7445021182251861baff597516a504c20ceb3c0ea0cbb378707b3ff80172da2081565b606090565b7fbf396b7bca433049fa198a0f2196e349f42bafe693fc380709806ff0e598105081565b60025481565b600154156116b25760405162461bcd60e51b815260040161037b9061397b565b600180556116c66080830160608401612eb8565b6116cf81612126565b156116ec5760405162461bcd60e51b815260040161037b90613ae2565b6116f4612225565b6001600160a01b031673353e18fa10e73d307d1f5804bf2866e3555396f363999343c79091600254611724612234565b6002887facc9ea428cc905b226e8b5801d63f262835e4f35726f4caa71b13b86899e83366117586080830160608401612eb8565b8b604001358b6040516020016117719493929190613804565b6040516020818303038152906040526040518763ffffffff1660e01b81526004016117a19695949392919061388a565b60006040518083038186803b1580156117b957600080fd5b505af41580156117cd573d6000803e3d6000fd5b505050506117d9612225565b6001600160a01b031663d9d104846040518163ffffffff1660e01b815260040160206040518083038186803b15801561181157600080fd5b505afa158015611825573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118499190612ed4565b6001600160a01b031663054e37826118676080860160608701612eb8565b84426040518463ffffffff1660e01b81526004016118879392919061369d565b600060405180830381600087803b1580156118a157600080fd5b505af115801561095a573d6000803e3d6000fd5b60035481565b60008060006118c8612225565b6001600160a01b031663d9d104846040518163ffffffff1660e01b815260040160206040518083038186803b15801561190057600080fd5b505afa158015611914573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119389190612ed4565b6001600160a01b031663b0f191dc856040518263ffffffff1660e01b815260040161196391906135a5565b60206040518083038186803b15801561197b57600080fd5b505afa15801561198f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119b39190613418565b92506119bd612225565b6001600160a01b031663d9d104846040518163ffffffff1660e01b815260040160206040518083038186803b1580156119f557600080fd5b505afa158015611a09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a2d9190612ed4565b6001600160a01b031663564d3ca7856040518263ffffffff1660e01b8152600401611a5891906135a5565b60206040518083038186803b158015611a7057600080fd5b505afa158015611a84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611aa89190613418565b9150611ab2612225565b6001600160a01b031663d9d104846040518163ffffffff1660e01b815260040160206040518083038186803b158015611aea57600080fd5b505afa158015611afe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b229190612ed4565b6001600160a01b031663a6ba0127856040518263ffffffff1660e01b8152600401611b4d91906135a5565b60206040518083038186803b158015611b6557600080fd5b505afa158015611b79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b9d9190613418565b929491935050565b6060600154600014611bc95760405162461bcd60e51b815260040161037b9061397b565b60018055611bdd6080870160608801612eb8565b611be681612126565b15611c035760405162461bcd60e51b815260040161037b90613ae2565b611c0b612225565b6001600160a01b031673353e18fa10e73d307d1f5804bf2866e3555396f363999343c79091600254611c3b612234565b60028c7f6684041b14f7240c87b9475c99b65953b05c26a0b41e768674a948013418bfa5611c6f6080830160608401612eb8565b8f604001358f8f8f8f604051611c86929190613595565b604051908190038120611ca09695949392916020016137cf565b6040516020818303038152906040526040518763ffffffff1660e01b8152600401611cd09695949392919061388a565b60006040518083038186803b158015611ce857600080fd5b505af4158015611cfc573d6000803e3d6000fd5b50611d1e9250611d159150506080890160608a01612eb8565b878787876123b4565b6000600155979650505050505050565b6060600154600014611d525760405162461bcd60e51b815260040161037b9061397b565b60018055611d5e612234565b15611d7b5760405162461bcd60e51b815260040161037b9061394c565b856000611d86612078565b9050816001600160a01b0316816001600160a01b03161480611e3a5750816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015611ddc57600080fd5b505afa158015611df0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e149190612ed4565b6001600160a01b0316816001600160a01b0316148015611e3a5750611e3882612126565b155b611e565760405162461bcd60e51b815260040161037b90613a16565b6000611e60612225565b6001600160a01b031663d51b3a1b6040518163ffffffff1660e01b815260040160206040518083038186803b158015611e9857600080fd5b505afa158015611eac573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ed09190612ed4565b905062093a80816001600160a01b031663c2ec28d6856040518263ffffffff1660e01b8152600401611f0291906135a5565b60206040518083038186803b158015611f1a57600080fd5b505afa158015611f2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f529190613418565b01421115611fb95760405163d36e351760e01b81526001600160a01b0382169063d36e351790611f869086906004016135a5565b600060405180830381600087803b158015611fa057600080fd5b505af1158015611fb4573d6000803e3d6000fd5b505050505b600087118015611fd05750611fce8989612613565b155b15611fe157611fe18960008961280d565b611fee89898989896123b4565b60006001559998505050505050505050565b6000804690507f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f836000015180519060200120846020015180519060200120838660400151604051602001612059959493929190613828565b604051602081830303815290604052805190602001209150505b919050565b6000612082612ab1565b90505b90565b806060612093611663565b905060005b815181101561212057826001600160a01b031663b149206e8383815181106120bc57fe5b6020026020010151306040518363ffffffff1660e01b81526004016120e2929190613854565b600060405180830381600087803b1580156120fc57600080fd5b505af1158015612110573d6000803e3d6000fd5b5050600190920191506120989050565b50505050565b600080612131612225565b6001600160a01b031663d51b3a1b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561216957600080fd5b505afa15801561217d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121a19190612ed4565b6001600160a01b0316636b9db4e6846040518263ffffffff1660e01b81526004016121cc91906135a5565b604080518083038186803b1580156121e357600080fd5b505afa1580156121f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061221b9190613430565b5042109392505050565b6004546001600160a01b031690565b60006038361080159061225157506000546001600160a01b031633145b156122a8576122a160206000369050036000368080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293925050612b369050565b9050612085565b506000612085565b600080846001600160a01b031663dd62ed3e87866040518363ffffffff1660e01b81526004016122e19291906135b9565b60206040518083038186803b1580156122f957600080fd5b505afa15801561230d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123319190613418565b905080831461235957801561234d5761234d8686866000612b52565b61235986868686612b52565b8083111561236e5761236b8382612c29565b91505b7f8d924fb660ea5dc99861c06d5104285681bb68ef281ebe73b6245e399a1ce2ff868686866040516123a394939291906135d3565b60405180910390a150949350505050565b6060846001600160a01b0316866001600160a01b03161415801561245157506040516363d972cb60e11b81526001600160a01b0387169063c7b2e596906123ff9088906004016135a5565b60206040518083038186803b15801561241757600080fd5b505afa15801561242b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061244f91906130ec565b155b61246d5760405162461bcd60e51b815260040161037b9061399f565b612475612225565b6001600160a01b0316632630c12f6040518163ffffffff1660e01b815260040160206040518083038186803b1580156124ad57600080fd5b505afa1580156124c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124e59190612ed4565b6001600160a01b031663f182178386670de0b6b3a76400006040518363ffffffff1660e01b815260040161251a929190613684565b60206040518083038186803b15801561253257600080fd5b505afa158015612546573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061256a9190613418565b156125875760405162461bcd60e51b815260040161037b9061399f565b6125c986868686868080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612c5192505050565b90507f7d533d6faad77168a7f3e416e981e7d4f7b02844ddbbbdd26807b66a5002eb8e8686868686604051612602959493929190613645565b60405180910390a195945050505050565b600061261d612225565b6001600160a01b03166337423d5e6040518163ffffffff1660e01b815260040160206040518083038186803b15801561265557600080fd5b505afa158015612669573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061268d9190612ed4565b6001600160a01b031663b6b3527284846040518363ffffffff1660e01b81526004016126ba9291906135b9565b604080518083038186803b1580156126d157600080fd5b505afa1580156126e5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612709919061310c565b5090508080612806575061271b612225565b6001600160a01b0316637ce771ce6040518163ffffffff1660e01b815260040160206040518083038186803b15801561275357600080fd5b505afa158015612767573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061278b9190612ed4565b6001600160a01b031663e52c3558836040518263ffffffff1660e01b81526004016127b691906135a5565b60206040518083038186803b1580156127ce57600080fd5b505afa1580156127e2573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061280691906130ec565b9392505050565b60008111801561282e57506000612822612ce6565b6001600160a01b031614155b1561299557600061283d612225565b6001600160a01b0316632630c12f6040518163ffffffff1660e01b815260040160206040518083038186803b15801561287557600080fd5b505afa158015612889573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128ad9190612ed4565b6001600160a01b031663f182178384846040518363ffffffff1660e01b81526004016128da929190613684565b60206040518083038186803b1580156128f257600080fd5b505afa158015612906573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061292a9190613418565b9050612934612ce6565b6001600160a01b031663c976bd9a85836040518363ffffffff1660e01b8152600401612961929190613684565b600060405180830381600087803b15801561297b57600080fd5b505af115801561298f573d6000803e3d6000fd5b50505050505b505050565b6129a686868686612d60565b7fc88755fe083d57a3909c60ab246eef52835769b920f0a49045b2b1058afda7128686868686866040516129df969594939291906135fd565b60405180910390a1505050505050565b8060606129fa611663565b905060005b815181101561212057826001600160a01b031663b149206e838381518110612a2357fe5b602002602001015160006040518363ffffffff1660e01b8152600401612a4a929190613854565b600060405180830381600087803b158015612a6457600080fd5b505af1158015612a78573d6000803e3d6000fd5b5050600190920191506129ff9050565b81810182811015612aab5760405162461bcd60e51b815260040161037b90613a5f565b92915050565b600060383610801590612ace57506000546001600160a01b031633145b15612b2f576122a1612b2160346000369050036000368080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293925050612e379050565b6001600160a01b0316612085565b5033612085565b60008160200183511015612b4957600080fd5b50016020015190565b6001600160a01b038316612b785760405162461bcd60e51b815260040161037b906139c8565b606063095ea7b360e01b8383604051602401612b95929190613684565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915290506060612bd88686600085612c51565b905060008151600014612bfe5781806020019051810190612bf991906130ec565b612c01565b60015b905080612c205760405162461bcd60e51b815260040161037b90613ab4565b50505050505050565b600082821115612c4b5760405162461bcd60e51b815260040161037b906139ef565b50900390565b604051631c48add360e21b81526060906001600160a01b03861690637122b74c90612c8790600190889088908890600401613b18565b600060405180830381600087803b158015612ca157600080fd5b505af1158015612cb5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612cdd919081019061313d565b95945050505050565b6000612cf0612225565b6001600160a01b031663d9d104846040518163ffffffff1660e01b815260040160206040518083038186803b158015612d2857600080fd5b505afa158015612d3c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120829190612ed4565b6001600160a01b038316612d8f57612d8984838360405180602001604052806000815250612c51565b50612120565b606063a9059cbb60e01b8383604051602401612dac929190613684565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915290506060612def8686600085612c51565b905060008151600014612e155781806020019051810190612e1091906130ec565b612e18565b60015b905080612c205760405162461bcd60e51b815260040161037b90613a85565b60008160140183511015612e4a57600080fd5b500160200151600160601b900490565b60008083601f840112612e6b578182fd5b50813567ffffffffffffffff811115612e82578182fd5b602083019150836020828501011115612e9a57600080fd5b9250929050565b600060808284031215612eb2578081fd5b50919050565b600060208284031215612ec9578081fd5b813561280681613bd4565b600060208284031215612ee5578081fd5b815161280681613bd4565b60008060008060808587031215612f05578283fd5b8435612f1081613bd4565b93506020850135612f2081613bd4565b92506040850135612f3081613bd4565b9396929550929360600135925050565b60008060008060008060a08789031215612f58578182fd5b8635612f6381613bd4565b95506020870135612f7381613bd4565b94506040870135612f8381613bd4565b935060608701359250608087013567ffffffffffffffff811115612fa5578283fd5b612fb189828a01612e5a565b979a9699509497509295939492505050565b600080600080600080600060c0888a031215612fdd578081fd5b8735612fe881613bd4565b96506020880135612ff881613bd4565b9550604088013561300881613bd4565b9450606088013593506080880135925060a088013567ffffffffffffffff811115613031578182fd5b61303d8a828b01612e5a565b989b979a50959850939692959293505050565b600080600080600060808688031215613067578081fd5b853561307281613bd4565b9450602086013561308281613bd4565b935060408601359250606086013567ffffffffffffffff8111156130a4578182fd5b6130b088828901612e5a565b969995985093965092949392505050565b600080604083850312156130d3578182fd5b82356130de81613bd4565b946020939093013593505050565b6000602082840312156130fd578081fd5b81518015158114612806578182fd5b6000806040838503121561311e578081fd5b8251801515811461312d578182fd5b6020939093015192949293505050565b60006020828403121561314e578081fd5b815167ffffffffffffffff80821115613165578283fd5b818401915084601f830112613178578283fd5b815181811115613186578384fd5b604051601f8201601f1916810160200183811182821017156131a6578586fd5b6040528181528382016020018710156131bd578485fd5b6131ce826020830160208701613ba8565b9695505050505050565b600080600080608085870312156131ed578182fd5b843567ffffffffffffffff811115613203578283fd5b61320f87828801612ea1565b9450506020850135612f2081613bd4565b60008060008060008060a08789031215613238578384fd5b863567ffffffffffffffff8082111561324f578586fd5b61325b8a838b01612ea1565b97506020890135915061326d82613bd4565b90955060408801359061327f82613bd4565b909450606088013593506080880135908082111561329b578384fd5b50612fb189828a01612e5a565b600080600080600080600060c0888a0312156132c2578081fd5b873567ffffffffffffffff808211156132d9578283fd5b6132e58b838c01612ea1565b985060208a013591506132f782613bd4565b90965060408901359061330982613bd4565b909550606089013594506080890135935060a0890135908082111561332c578283fd5b5061303d8a828b01612e5a565b600080600080600060808688031215613350578283fd5b853567ffffffffffffffff80821115613367578485fd5b61337389838a01612ea1565b96506020880135915061338582613bd4565b90945060408701359350606087013590808211156133a1578283fd5b506130b088828901612e5a565b600080604083850312156133c0578182fd5b823567ffffffffffffffff8111156133d6578283fd5b6133e285828601612ea1565b95602094909401359450505050565b600060208284031215613402578081fd5b81516001600160801b0381168114612806578182fd5b600060208284031215613429578081fd5b5051919050565b60008060408385031215613442578182fd5b82519150602083015161345481613bd4565b809150509250929050565b6001600160a01b03169052565b60008284526020808501945082825b858110156134a957813561348e81613bd4565b6001600160a01b03168752958201959082019060010161347b565b509495945050505050565b818352602080840193600091908185020181018584845b878110156135325782840389528135601e198836030181126134eb578687fd5b8701803567ffffffffffffffff811115613503578788fd5b803603891315613511578788fd5b61351e868289850161353f565b9a87019a95505050908401906001016134cb565b5091979650505050505050565b60008284528282602086013780602084860101526020601f19601f85011685010190509392505050565b60008151808452613581816020860160208601613ba8565b601f01601f19169290920160200192915050565b6000828483379101908152919050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039485168152928416602084015292166040820152606081019190915260800190565b6001600160a01b0387811682528681166020830152851660408201526060810184905260a060808201819052600090613639908301848661353f565b98975050505050505050565b6001600160a01b0386811682528516602082015260408101849052608060608201819052600090613679908301848661353f565b979650505050505050565b6001600160a01b03929092168252602082015260400190565b6001600160a01b039390931683526020830191909152604082015260600190565b6020808252825182820181905260009190848201906040850190845b818110156137005783516001600160e01b031916835292840192918401916001016136da565b50909695505050505050565b90815260200190565b9586526001600160a01b03948516602087015260408601939093529083166060850152909116608083015260a082015260c00190565b9687526001600160a01b03958616602088015260408701949094529184166060860152909216608084015260a083019190915260c082015260e00190565b9788526001600160a01b039687166020890152604088019590955292851660608701529316608085015260a084019290925260c083019190915260e08201526101000190565b9586526001600160a01b039485166020870152604086019390935292166060840152608083019190915260a082015260c00190565b9384526001600160a01b039290921660208401526040830152606082015260800190565b9485526020850193909352604084019190915260608301526001600160a01b0316608082015260a00190565b6001600160e01b03199290921682526001600160a01b0316602082015260400190565b6000602082526128066020830184613569565b6001600160a01b0387168152602081018690526040810185905260006138af85613b9d565b606083015260c060808301526138c58485613b55565b608060c08501526138db6101408501828461346c565b9150506138eb6020860186613b55565b84830360bf190160e08601526139028382846134b4565b92505050604085013561010084015261391e6060860186613b48565b61392c61012085018261345f565b5082810360a084015261393f8185613569565b9998505050505050505050565b6020808252601590820152740929cac82989288bea8b0be82ae82a48abe9082a69605b1b604082015260600190565b6020808252600a90820152695245454e5452414e435960b01b604082015260600190565b6020808252600f908201526e10d0531317d11254d0531313d5d151608a1b604082015260600190565b6020808252600d908201526c24a72b20a624a22faa27a5a2a760991b604082015260600190565b6020808252600d908201526c5355425f554e444552464c4f5760981b604082015260600190565b60208082526029908201527f4e4f545f46524f4d5f57414c4c45545f4f525f4f574e45525f4f525f57414c4c604082015268115517d313d0d2d15160ba1b606082015260800190565b6020808252600c908201526b4144445f4f564552464c4f5760a01b604082015260600190565b602080825260159082015274115490cc8c17d514905394d1915497d19052531151605a1b604082015260600190565b602080825260149082015273115490cc8c17d054141493d59157d1905253115160621b604082015260600190565b6020808252600690820152651313d0d2d15160d21b604082015260600190565b9283526020830191909152604082015260600190565b600060ff8616825260018060a01b0385166020830152836040830152608060608301526131ce6080830184613569565b6000823561280681613bd4565b6000808335601e19843603018112613b6b578283fd5b830160208101925035905067ffffffffffffffff811115613b8b57600080fd5b602081023603831315612e9a57600080fd5b806003811061207357fe5b60005b83811015613bc3578181015183820152602001613bab565b838111156121205750506000910152565b6001600160a01b0381168114613be957600080fd5b5056fea26469706673582212202090042fc21907ac63f77ceec11558a7d53960443485b6181dd7961035147f5f64736f6c63430007000033000000000000000000000000188d315b1c698ca678c8e3891ac21e097763afde000000000000000000000000af25b62b1aff8fe3fc72d0d7f3cf1cc6941eafb20000000000000000000000000000000000000000000000000000000000015180
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101585760003560e01c80637da0a877116100c3578063bf01ce111161007c578063bf01ce1114610279578063c6441d0c14610281578063dbb07b7414610294578063e321531b1461029c578063f1eb90b1146102be578063fd6ac309146102d157610158565b80637da0a8771461022457806388666d81146102395780639a53228a1461024c5780639cdef1e514610254578063afa3363d1461025c578063b345c8c11461027157610158565b80632df546f4116101155780632df546f4146101d357806336c0ad37146101e657806344b5087b146101f957806351b42b001461020157806355e9a6691461020957806375529de81461021157610158565b80630c37ef4c1461015d5780630f15f4c01461017b5780630fca973a1461018557806312ef080d146101a557806313d53dcf146101b85780632b30907b146101c0575b600080fd5b6101656102e4565b604051610172919061370c565b60405180910390f35b610183610308565b005b6101986101933660046132a8565b610357565b6040516101729190613877565b6101836101b3366004612ef0565b610510565b6101656107dd565b6101836101ce366004613220565b6107e2565b6101836101e1366004612f40565b610968565b6101836101f43660046131d8565b610c34565b610165610da1565b610183610dc5565b610165610e09565b61019861021f366004612fc3565b610e2d565b61022c611128565b60405161017291906135a5565b6101836102473660046130c1565b611137565b610165611638565b61016561163f565b610264611663565b60405161017291906136be565b610165611668565b61016561168c565b61018361028f3660046133ae565b611692565b6101656118b5565b6102af6102aa366004612eb8565b6118bb565b60405161017293929190613b02565b6101986102cc366004613339565b611ba5565b6101986102df366004613050565b611d2e565b7f318096867a993ed9ca8cd85acec91d02ecf5fcc7cbd42172636c06fe60679a4b81565b6000610312612078565b905061031d81612088565b7f0cc43938d137e7efade6a531f663e78c1fc75257b0d65ffda2fdaf70cb49cdf98160405161034c91906135a5565b60405180910390a150565b60606001546000146103845760405162461bcd60e51b815260040161037b9061397b565b60405180910390fd5b600180556103986080890160608a01612eb8565b6103a181612126565b156103be5760405162461bcd60e51b815260040161037b90613ae2565b60607f7445021182251861baff597516a504c20ceb3c0ea0cbb378707b3ff80172da206103f060808c018c8401612eb8565b8b604001358b8b8b8b8b8b604051610409929190613595565b6040519081900381206104259897969594939291602001613789565b604051602081830303815290604052905061043e612225565b6001600160a01b031673353e18fa10e73d307d1f5804bf2866e3555396f363999343c7909160025461046e612234565b60028f876040518763ffffffff1660e01b81526004016104939695949392919061388a565b60006040518083038186803b1580156104ab57600080fd5b505af41580156104bf573d6000803e3d6000fd5b506104e092506104d891505060808c0160608d01612eb8565b8a8a8a6122b0565b506104fd6104f460808c0160608d01612eb8565b898888886123b4565b60006001559a9950505050505050505050565b600154156105305760405162461bcd60e51b815260040161037b9061397b565b6001805561053c612234565b156105595760405162461bcd60e51b815260040161037b9061394c565b836000610564612078565b9050816001600160a01b0316816001600160a01b031614806106185750816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156105ba57600080fd5b505afa1580156105ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f29190612ed4565b6001600160a01b0316816001600160a01b0316148015610618575061061682612126565b155b6106345760405162461bcd60e51b815260040161037b90613a16565b600061063e612225565b6001600160a01b031663d51b3a1b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561067657600080fd5b505afa15801561068a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106ae9190612ed4565b905062093a80816001600160a01b031663c2ec28d6856040518263ffffffff1660e01b81526004016106e091906135a5565b60206040518083038186803b1580156106f857600080fd5b505afa15801561070c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107309190613418565b014211156107975760405163d36e351760e01b81526001600160a01b0382169063d36e3517906107649086906004016135a5565b600060405180830381600087803b15801561077e57600080fd5b505af1158015610792573d6000803e3d6000fd5b505050505b60006107a5888888886122b0565b90506000811180156107be57506107bc8887612613565b155b156107ce576107ce88888361280d565b50506000600155505050505050565b600281565b600154156108025760405162461bcd60e51b815260040161037b9061397b565b600180556108166080870160608801612eb8565b61081f81612126565b1561083c5760405162461bcd60e51b815260040161037b90613ae2565b610844612225565b6001600160a01b031673353e18fa10e73d307d1f5804bf2866e3555396f363999343c79091600254610874612234565b60028c7f318096867a993ed9ca8cd85acec91d02ecf5fcc7cbd42172636c06fe60679a4b6108a86080830160608401612eb8565b8f604001358f8f8f8f8f6040516108c0929190613595565b6040519081900381206108db9796959493929160200161374b565b6040516020818303038152906040526040518763ffffffff1660e01b815260040161090b9695949392919061388a565b60006040518083038186803b15801561092357600080fd5b505af4158015610937573d6000803e3d6000fd5b5061095a92506109509150506080890160608a01612eb8565b878787878761299a565b505060006001555050505050565b600154156109885760405162461bcd60e51b815260040161037b9061397b565b60018055610994612234565b156109b15760405162461bcd60e51b815260040161037b9061394c565b8560006109bc612078565b9050816001600160a01b0316816001600160a01b03161480610a705750816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610a1257600080fd5b505afa158015610a26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a4a9190612ed4565b6001600160a01b0316816001600160a01b0316148015610a705750610a6e82612126565b155b610a8c5760405162461bcd60e51b815260040161037b90613a16565b6000610a96612225565b6001600160a01b031663d51b3a1b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610ace57600080fd5b505afa158015610ae2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b069190612ed4565b905062093a80816001600160a01b031663c2ec28d6856040518263ffffffff1660e01b8152600401610b3891906135a5565b60206040518083038186803b158015610b5057600080fd5b505afa158015610b64573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b889190613418565b01421115610bef5760405163d36e351760e01b81526001600160a01b0382169063d36e351790610bbc9086906004016135a5565b600060405180830381600087803b158015610bd657600080fd5b505af1158015610bea573d6000803e3d6000fd5b505050505b600086118015610c065750610c048988612613565b155b15610c1657610c1689898861280d565b610c2489898989898961299a565b5050600060015550505050505050565b60015415610c545760405162461bcd60e51b815260040161037b9061397b565b60018055610c686080850160608601612eb8565b610c7181612126565b15610c8e5760405162461bcd60e51b815260040161037b90613ae2565b610c96612225565b6001600160a01b031673353e18fa10e73d307d1f5804bf2866e3555396f363999343c79091600254610cc6612234565b60028a7fbf396b7bca433049fa198a0f2196e349f42bafe693fc380709806ff0e5981050610cfa6080830160608401612eb8565b8d604001358d8d8d604051602001610d1796959493929190613715565b6040516020818303038152906040526040518763ffffffff1660e01b8152600401610d479695949392919061388a565b60006040518083038186803b158015610d5f57600080fd5b505af4158015610d73573d6000803e3d6000fd5b50610d949250610d8c9150506080870160608801612eb8565b8585856122b0565b5050600060015550505050565b7f6684041b14f7240c87b9475c99b65953b05c26a0b41e768674a948013418bfa581565b6000610dcf612078565b9050610dda816129ef565b7f749cb6b4c510bc468cf6b9c2086d6f0a54d6b18e25d37bf3200e68eab0880c008160405161034c91906135a5565b7facc9ea428cc905b226e8b5801d63f262835e4f35726f4caa71b13b86899e833681565b6060600154600014610e515760405162461bcd60e51b815260040161037b9061397b565b60018055610e5d612234565b15610e7a5760405162461bcd60e51b815260040161037b9061394c565b876000610e85612078565b9050816001600160a01b0316816001600160a01b03161480610f395750816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610edb57600080fd5b505afa158015610eef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f139190612ed4565b6001600160a01b0316816001600160a01b0316148015610f395750610f3782612126565b155b610f555760405162461bcd60e51b815260040161037b90613a16565b6000610f5f612225565b6001600160a01b031663d51b3a1b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610f9757600080fd5b505afa158015610fab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fcf9190612ed4565b905062093a80816001600160a01b031663c2ec28d6856040518263ffffffff1660e01b815260040161100191906135a5565b60206040518083038186803b15801561101957600080fd5b505afa15801561102d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110519190613418565b014211156110b85760405163d36e351760e01b81526001600160a01b0382169063d36e3517906110859086906004016135a5565b600060405180830381600087803b15801561109f57600080fd5b505af11580156110b3573d6000803e3d6000fd5b505050505b60006110c68c8c8c8c6122b0565b905060008111806110d75750600088115b80156110ea57506110e88c8b612613565b155b15611106576110fa8c8c8361280d565b6111068c60008a61280d565b6111138c8b8a8a8a6123b4565b60006001559c9b505050505050505050505050565b6000546001600160a01b031681565b600154156111575760405162461bcd60e51b815260040161037b9061397b565b60018055611163612234565b156111805760405162461bcd60e51b815260040161037b9061394c565b81600061118b612078565b9050816001600160a01b0316816001600160a01b0316148061123f5750816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156111e157600080fd5b505afa1580156111f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112199190612ed4565b6001600160a01b0316816001600160a01b031614801561123f575061123d82612126565b155b61125b5760405162461bcd60e51b815260040161037b90613a16565b6000611265612225565b6001600160a01b031663d51b3a1b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561129d57600080fd5b505afa1580156112b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112d59190612ed4565b905062093a80816001600160a01b031663c2ec28d6856040518263ffffffff1660e01b815260040161130791906135a5565b60206040518083038186803b15801561131f57600080fd5b505afa158015611333573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113579190613418565b014211156113be5760405163d36e351760e01b81526001600160a01b0382169063d36e35179061138b9086906004016135a5565b600060405180830381600087803b1580156113a557600080fd5b505af11580156113b9573d6000803e3d6000fd5b505050505b60006113c8612225565b6001600160a01b031663d9d104846040518163ffffffff1660e01b815260040160206040518083038186803b15801561140057600080fd5b505afa158015611414573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114389190612ed4565b90506000851561144857856114c3565b816001600160a01b031663561f30d96040518163ffffffff1660e01b815260040160206040518083038186803b15801561148157600080fd5b505afa158015611495573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114b991906133f1565b6001600160801b03165b90506000826001600160a01b031663b0f191dc896040518263ffffffff1660e01b81526004016114f391906135a5565b60206040518083038186803b15801561150b57600080fd5b505afa15801561151f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115439190613418565b90508181106115b3576040516302a71bc160e11b81526001600160a01b0384169063054e37829061157c908b908690429060040161369d565b600060405180830381600087803b15801561159657600080fd5b505af11580156115aa573d6000803e3d6000fd5b505050506107ce565b826001600160a01b031663054e378289846115d960035442612a8890919063ffffffff16565b6040518463ffffffff1660e01b81526004016115f79392919061369d565b600060405180830381600087803b15801561161157600080fd5b505af1158015611625573d6000803e3d6000fd5b5050505050506000600155505050505050565b62093a8081565b7f7445021182251861baff597516a504c20ceb3c0ea0cbb378707b3ff80172da2081565b606090565b7fbf396b7bca433049fa198a0f2196e349f42bafe693fc380709806ff0e598105081565b60025481565b600154156116b25760405162461bcd60e51b815260040161037b9061397b565b600180556116c66080830160608401612eb8565b6116cf81612126565b156116ec5760405162461bcd60e51b815260040161037b90613ae2565b6116f4612225565b6001600160a01b031673353e18fa10e73d307d1f5804bf2866e3555396f363999343c79091600254611724612234565b6002887facc9ea428cc905b226e8b5801d63f262835e4f35726f4caa71b13b86899e83366117586080830160608401612eb8565b8b604001358b6040516020016117719493929190613804565b6040516020818303038152906040526040518763ffffffff1660e01b81526004016117a19695949392919061388a565b60006040518083038186803b1580156117b957600080fd5b505af41580156117cd573d6000803e3d6000fd5b505050506117d9612225565b6001600160a01b031663d9d104846040518163ffffffff1660e01b815260040160206040518083038186803b15801561181157600080fd5b505afa158015611825573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118499190612ed4565b6001600160a01b031663054e37826118676080860160608701612eb8565b84426040518463ffffffff1660e01b81526004016118879392919061369d565b600060405180830381600087803b1580156118a157600080fd5b505af115801561095a573d6000803e3d6000fd5b60035481565b60008060006118c8612225565b6001600160a01b031663d9d104846040518163ffffffff1660e01b815260040160206040518083038186803b15801561190057600080fd5b505afa158015611914573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119389190612ed4565b6001600160a01b031663b0f191dc856040518263ffffffff1660e01b815260040161196391906135a5565b60206040518083038186803b15801561197b57600080fd5b505afa15801561198f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119b39190613418565b92506119bd612225565b6001600160a01b031663d9d104846040518163ffffffff1660e01b815260040160206040518083038186803b1580156119f557600080fd5b505afa158015611a09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a2d9190612ed4565b6001600160a01b031663564d3ca7856040518263ffffffff1660e01b8152600401611a5891906135a5565b60206040518083038186803b158015611a7057600080fd5b505afa158015611a84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611aa89190613418565b9150611ab2612225565b6001600160a01b031663d9d104846040518163ffffffff1660e01b815260040160206040518083038186803b158015611aea57600080fd5b505afa158015611afe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b229190612ed4565b6001600160a01b031663a6ba0127856040518263ffffffff1660e01b8152600401611b4d91906135a5565b60206040518083038186803b158015611b6557600080fd5b505afa158015611b79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b9d9190613418565b929491935050565b6060600154600014611bc95760405162461bcd60e51b815260040161037b9061397b565b60018055611bdd6080870160608801612eb8565b611be681612126565b15611c035760405162461bcd60e51b815260040161037b90613ae2565b611c0b612225565b6001600160a01b031673353e18fa10e73d307d1f5804bf2866e3555396f363999343c79091600254611c3b612234565b60028c7f6684041b14f7240c87b9475c99b65953b05c26a0b41e768674a948013418bfa5611c6f6080830160608401612eb8565b8f604001358f8f8f8f604051611c86929190613595565b604051908190038120611ca09695949392916020016137cf565b6040516020818303038152906040526040518763ffffffff1660e01b8152600401611cd09695949392919061388a565b60006040518083038186803b158015611ce857600080fd5b505af4158015611cfc573d6000803e3d6000fd5b50611d1e9250611d159150506080890160608a01612eb8565b878787876123b4565b6000600155979650505050505050565b6060600154600014611d525760405162461bcd60e51b815260040161037b9061397b565b60018055611d5e612234565b15611d7b5760405162461bcd60e51b815260040161037b9061394c565b856000611d86612078565b9050816001600160a01b0316816001600160a01b03161480611e3a5750816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015611ddc57600080fd5b505afa158015611df0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e149190612ed4565b6001600160a01b0316816001600160a01b0316148015611e3a5750611e3882612126565b155b611e565760405162461bcd60e51b815260040161037b90613a16565b6000611e60612225565b6001600160a01b031663d51b3a1b6040518163ffffffff1660e01b815260040160206040518083038186803b158015611e9857600080fd5b505afa158015611eac573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ed09190612ed4565b905062093a80816001600160a01b031663c2ec28d6856040518263ffffffff1660e01b8152600401611f0291906135a5565b60206040518083038186803b158015611f1a57600080fd5b505afa158015611f2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f529190613418565b01421115611fb95760405163d36e351760e01b81526001600160a01b0382169063d36e351790611f869086906004016135a5565b600060405180830381600087803b158015611fa057600080fd5b505af1158015611fb4573d6000803e3d6000fd5b505050505b600087118015611fd05750611fce8989612613565b155b15611fe157611fe18960008961280d565b611fee89898989896123b4565b60006001559998505050505050505050565b6000804690507f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f836000015180519060200120846020015180519060200120838660400151604051602001612059959493929190613828565b604051602081830303815290604052805190602001209150505b919050565b6000612082612ab1565b90505b90565b806060612093611663565b905060005b815181101561212057826001600160a01b031663b149206e8383815181106120bc57fe5b6020026020010151306040518363ffffffff1660e01b81526004016120e2929190613854565b600060405180830381600087803b1580156120fc57600080fd5b505af1158015612110573d6000803e3d6000fd5b5050600190920191506120989050565b50505050565b600080612131612225565b6001600160a01b031663d51b3a1b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561216957600080fd5b505afa15801561217d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121a19190612ed4565b6001600160a01b0316636b9db4e6846040518263ffffffff1660e01b81526004016121cc91906135a5565b604080518083038186803b1580156121e357600080fd5b505afa1580156121f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061221b9190613430565b5042109392505050565b6004546001600160a01b031690565b60006038361080159061225157506000546001600160a01b031633145b156122a8576122a160206000369050036000368080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293925050612b369050565b9050612085565b506000612085565b600080846001600160a01b031663dd62ed3e87866040518363ffffffff1660e01b81526004016122e19291906135b9565b60206040518083038186803b1580156122f957600080fd5b505afa15801561230d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123319190613418565b905080831461235957801561234d5761234d8686866000612b52565b61235986868686612b52565b8083111561236e5761236b8382612c29565b91505b7f8d924fb660ea5dc99861c06d5104285681bb68ef281ebe73b6245e399a1ce2ff868686866040516123a394939291906135d3565b60405180910390a150949350505050565b6060846001600160a01b0316866001600160a01b03161415801561245157506040516363d972cb60e11b81526001600160a01b0387169063c7b2e596906123ff9088906004016135a5565b60206040518083038186803b15801561241757600080fd5b505afa15801561242b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061244f91906130ec565b155b61246d5760405162461bcd60e51b815260040161037b9061399f565b612475612225565b6001600160a01b0316632630c12f6040518163ffffffff1660e01b815260040160206040518083038186803b1580156124ad57600080fd5b505afa1580156124c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124e59190612ed4565b6001600160a01b031663f182178386670de0b6b3a76400006040518363ffffffff1660e01b815260040161251a929190613684565b60206040518083038186803b15801561253257600080fd5b505afa158015612546573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061256a9190613418565b156125875760405162461bcd60e51b815260040161037b9061399f565b6125c986868686868080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612c5192505050565b90507f7d533d6faad77168a7f3e416e981e7d4f7b02844ddbbbdd26807b66a5002eb8e8686868686604051612602959493929190613645565b60405180910390a195945050505050565b600061261d612225565b6001600160a01b03166337423d5e6040518163ffffffff1660e01b815260040160206040518083038186803b15801561265557600080fd5b505afa158015612669573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061268d9190612ed4565b6001600160a01b031663b6b3527284846040518363ffffffff1660e01b81526004016126ba9291906135b9565b604080518083038186803b1580156126d157600080fd5b505afa1580156126e5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612709919061310c565b5090508080612806575061271b612225565b6001600160a01b0316637ce771ce6040518163ffffffff1660e01b815260040160206040518083038186803b15801561275357600080fd5b505afa158015612767573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061278b9190612ed4565b6001600160a01b031663e52c3558836040518263ffffffff1660e01b81526004016127b691906135a5565b60206040518083038186803b1580156127ce57600080fd5b505afa1580156127e2573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061280691906130ec565b9392505050565b60008111801561282e57506000612822612ce6565b6001600160a01b031614155b1561299557600061283d612225565b6001600160a01b0316632630c12f6040518163ffffffff1660e01b815260040160206040518083038186803b15801561287557600080fd5b505afa158015612889573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128ad9190612ed4565b6001600160a01b031663f182178384846040518363ffffffff1660e01b81526004016128da929190613684565b60206040518083038186803b1580156128f257600080fd5b505afa158015612906573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061292a9190613418565b9050612934612ce6565b6001600160a01b031663c976bd9a85836040518363ffffffff1660e01b8152600401612961929190613684565b600060405180830381600087803b15801561297b57600080fd5b505af115801561298f573d6000803e3d6000fd5b50505050505b505050565b6129a686868686612d60565b7fc88755fe083d57a3909c60ab246eef52835769b920f0a49045b2b1058afda7128686868686866040516129df969594939291906135fd565b60405180910390a1505050505050565b8060606129fa611663565b905060005b815181101561212057826001600160a01b031663b149206e838381518110612a2357fe5b602002602001015160006040518363ffffffff1660e01b8152600401612a4a929190613854565b600060405180830381600087803b158015612a6457600080fd5b505af1158015612a78573d6000803e3d6000fd5b5050600190920191506129ff9050565b81810182811015612aab5760405162461bcd60e51b815260040161037b90613a5f565b92915050565b600060383610801590612ace57506000546001600160a01b031633145b15612b2f576122a1612b2160346000369050036000368080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293925050612e379050565b6001600160a01b0316612085565b5033612085565b60008160200183511015612b4957600080fd5b50016020015190565b6001600160a01b038316612b785760405162461bcd60e51b815260040161037b906139c8565b606063095ea7b360e01b8383604051602401612b95929190613684565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915290506060612bd88686600085612c51565b905060008151600014612bfe5781806020019051810190612bf991906130ec565b612c01565b60015b905080612c205760405162461bcd60e51b815260040161037b90613ab4565b50505050505050565b600082821115612c4b5760405162461bcd60e51b815260040161037b906139ef565b50900390565b604051631c48add360e21b81526060906001600160a01b03861690637122b74c90612c8790600190889088908890600401613b18565b600060405180830381600087803b158015612ca157600080fd5b505af1158015612cb5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612cdd919081019061313d565b95945050505050565b6000612cf0612225565b6001600160a01b031663d9d104846040518163ffffffff1660e01b815260040160206040518083038186803b158015612d2857600080fd5b505afa158015612d3c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120829190612ed4565b6001600160a01b038316612d8f57612d8984838360405180602001604052806000815250612c51565b50612120565b606063a9059cbb60e01b8383604051602401612dac929190613684565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915290506060612def8686600085612c51565b905060008151600014612e155781806020019051810190612e1091906130ec565b612e18565b60015b905080612c205760405162461bcd60e51b815260040161037b90613a85565b60008160140183511015612e4a57600080fd5b500160200151600160601b900490565b60008083601f840112612e6b578182fd5b50813567ffffffffffffffff811115612e82578182fd5b602083019150836020828501011115612e9a57600080fd5b9250929050565b600060808284031215612eb2578081fd5b50919050565b600060208284031215612ec9578081fd5b813561280681613bd4565b600060208284031215612ee5578081fd5b815161280681613bd4565b60008060008060808587031215612f05578283fd5b8435612f1081613bd4565b93506020850135612f2081613bd4565b92506040850135612f3081613bd4565b9396929550929360600135925050565b60008060008060008060a08789031215612f58578182fd5b8635612f6381613bd4565b95506020870135612f7381613bd4565b94506040870135612f8381613bd4565b935060608701359250608087013567ffffffffffffffff811115612fa5578283fd5b612fb189828a01612e5a565b979a9699509497509295939492505050565b600080600080600080600060c0888a031215612fdd578081fd5b8735612fe881613bd4565b96506020880135612ff881613bd4565b9550604088013561300881613bd4565b9450606088013593506080880135925060a088013567ffffffffffffffff811115613031578182fd5b61303d8a828b01612e5a565b989b979a50959850939692959293505050565b600080600080600060808688031215613067578081fd5b853561307281613bd4565b9450602086013561308281613bd4565b935060408601359250606086013567ffffffffffffffff8111156130a4578182fd5b6130b088828901612e5a565b969995985093965092949392505050565b600080604083850312156130d3578182fd5b82356130de81613bd4565b946020939093013593505050565b6000602082840312156130fd578081fd5b81518015158114612806578182fd5b6000806040838503121561311e578081fd5b8251801515811461312d578182fd5b6020939093015192949293505050565b60006020828403121561314e578081fd5b815167ffffffffffffffff80821115613165578283fd5b818401915084601f830112613178578283fd5b815181811115613186578384fd5b604051601f8201601f1916810160200183811182821017156131a6578586fd5b6040528181528382016020018710156131bd578485fd5b6131ce826020830160208701613ba8565b9695505050505050565b600080600080608085870312156131ed578182fd5b843567ffffffffffffffff811115613203578283fd5b61320f87828801612ea1565b9450506020850135612f2081613bd4565b60008060008060008060a08789031215613238578384fd5b863567ffffffffffffffff8082111561324f578586fd5b61325b8a838b01612ea1565b97506020890135915061326d82613bd4565b90955060408801359061327f82613bd4565b909450606088013593506080880135908082111561329b578384fd5b50612fb189828a01612e5a565b600080600080600080600060c0888a0312156132c2578081fd5b873567ffffffffffffffff808211156132d9578283fd5b6132e58b838c01612ea1565b985060208a013591506132f782613bd4565b90965060408901359061330982613bd4565b909550606089013594506080890135935060a0890135908082111561332c578283fd5b5061303d8a828b01612e5a565b600080600080600060808688031215613350578283fd5b853567ffffffffffffffff80821115613367578485fd5b61337389838a01612ea1565b96506020880135915061338582613bd4565b90945060408701359350606087013590808211156133a1578283fd5b506130b088828901612e5a565b600080604083850312156133c0578182fd5b823567ffffffffffffffff8111156133d6578283fd5b6133e285828601612ea1565b95602094909401359450505050565b600060208284031215613402578081fd5b81516001600160801b0381168114612806578182fd5b600060208284031215613429578081fd5b5051919050565b60008060408385031215613442578182fd5b82519150602083015161345481613bd4565b809150509250929050565b6001600160a01b03169052565b60008284526020808501945082825b858110156134a957813561348e81613bd4565b6001600160a01b03168752958201959082019060010161347b565b509495945050505050565b818352602080840193600091908185020181018584845b878110156135325782840389528135601e198836030181126134eb578687fd5b8701803567ffffffffffffffff811115613503578788fd5b803603891315613511578788fd5b61351e868289850161353f565b9a87019a95505050908401906001016134cb565b5091979650505050505050565b60008284528282602086013780602084860101526020601f19601f85011685010190509392505050565b60008151808452613581816020860160208601613ba8565b601f01601f19169290920160200192915050565b6000828483379101908152919050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039485168152928416602084015292166040820152606081019190915260800190565b6001600160a01b0387811682528681166020830152851660408201526060810184905260a060808201819052600090613639908301848661353f565b98975050505050505050565b6001600160a01b0386811682528516602082015260408101849052608060608201819052600090613679908301848661353f565b979650505050505050565b6001600160a01b03929092168252602082015260400190565b6001600160a01b039390931683526020830191909152604082015260600190565b6020808252825182820181905260009190848201906040850190845b818110156137005783516001600160e01b031916835292840192918401916001016136da565b50909695505050505050565b90815260200190565b9586526001600160a01b03948516602087015260408601939093529083166060850152909116608083015260a082015260c00190565b9687526001600160a01b03958616602088015260408701949094529184166060860152909216608084015260a083019190915260c082015260e00190565b9788526001600160a01b039687166020890152604088019590955292851660608701529316608085015260a084019290925260c083019190915260e08201526101000190565b9586526001600160a01b039485166020870152604086019390935292166060840152608083019190915260a082015260c00190565b9384526001600160a01b039290921660208401526040830152606082015260800190565b9485526020850193909352604084019190915260608301526001600160a01b0316608082015260a00190565b6001600160e01b03199290921682526001600160a01b0316602082015260400190565b6000602082526128066020830184613569565b6001600160a01b0387168152602081018690526040810185905260006138af85613b9d565b606083015260c060808301526138c58485613b55565b608060c08501526138db6101408501828461346c565b9150506138eb6020860186613b55565b84830360bf190160e08601526139028382846134b4565b92505050604085013561010084015261391e6060860186613b48565b61392c61012085018261345f565b5082810360a084015261393f8185613569565b9998505050505050505050565b6020808252601590820152740929cac82989288bea8b0be82ae82a48abe9082a69605b1b604082015260600190565b6020808252600a90820152695245454e5452414e435960b01b604082015260600190565b6020808252600f908201526e10d0531317d11254d0531313d5d151608a1b604082015260600190565b6020808252600d908201526c24a72b20a624a22faa27a5a2a760991b604082015260600190565b6020808252600d908201526c5355425f554e444552464c4f5760981b604082015260600190565b60208082526029908201527f4e4f545f46524f4d5f57414c4c45545f4f525f4f574e45525f4f525f57414c4c604082015268115517d313d0d2d15160ba1b606082015260800190565b6020808252600c908201526b4144445f4f564552464c4f5760a01b604082015260600190565b602080825260159082015274115490cc8c17d514905394d1915497d19052531151605a1b604082015260600190565b602080825260149082015273115490cc8c17d054141493d59157d1905253115160621b604082015260600190565b6020808252600690820152651313d0d2d15160d21b604082015260600190565b9283526020830191909152604082015260600190565b600060ff8616825260018060a01b0385166020830152836040830152608060608301526131ce6080830184613569565b6000823561280681613bd4565b6000808335601e19843603018112613b6b578283fd5b830160208101925035905067ffffffffffffffff811115613b8b57600080fd5b602081023603831315612e9a57600080fd5b806003811061207357fe5b60005b83811015613bc3578181015183820152602001613bab565b838111156121205750506000910152565b6001600160a01b0381168114613be957600080fd5b5056fea26469706673582212202090042fc21907ac63f77ceec11558a7d53960443485b6181dd7961035147f5f64736f6c63430007000033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000188d315b1c698ca678c8e3891ac21e097763afde000000000000000000000000af25b62b1aff8fe3fc72d0d7f3cf1cc6941eafb20000000000000000000000000000000000000000000000000000000000015180
-----Decoded View---------------
Arg [0] : _controller (address): 0x188d315b1c698Ca678c8E3891Ac21E097763Afde
Arg [1] : _trustedForwarder (address): 0xAF25B62B1AfF8fe3Fc72d0d7F3CF1cc6941eAFB2
Arg [2] : _transferDelayPeriod (uint256): 86400
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000188d315b1c698ca678c8e3891ac21e097763afde
Arg [1] : 000000000000000000000000af25b62b1aff8fe3fc72d0d7f3cf1cc6941eafb2
Arg [2] : 0000000000000000000000000000000000000000000000000000000000015180
Deployed Bytecode Sourcemap
130580:698:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;121662:193;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;83551:192;;;:::i;:::-;;129361:1029;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;125240:496::-;;;;;;:::i;:::-;;:::i;114018:45::-;;;:::i;126921:847::-;;;;;;:::i;:::-;;:::i;124168:515::-;;;;;;:::i;:::-;;:::i;127776:744::-;;;;;;:::i;:::-;;:::i;122050:173::-;;;:::i;83829:198::-;;;:::i;121479:174::-;;;:::i;125744:772::-;;;;;;:::i;:::-;;:::i;88955:31::-;;;:::i;:::-;;;;;;;:::i;122814:633::-;;;;;;:::i;:::-;;:::i;114070:50::-;;;:::i;122232:226::-;;;:::i;131144:131::-;;;:::i;:::-;;;;;;;:::i;121864:177::-;;;:::i;121430:40::-;;;:::i;123455:705::-;;;;;;:::i;:::-;;:::i;122467:31::-;;;:::i;126524:389::-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;:::i;128528:825::-;;;;;;:::i;:::-;;:::i;124691:541::-;;;;;;:::i;:::-;;:::i;121662:193::-;121712:143;121662:193;:::o;83551:192::-;83640:14;83657:15;:13;:15::i;:::-;83640:32;;83683:19;83695:6;83683:11;:19::i;:::-;83718:17;83728:6;83718:17;;;;;;:::i;:::-;;;;;;;;83551:192;:::o;129361:1029::-;129724:23;82043:11;;82058:1;82043:16;82035:39;;;;-1:-1:-1;;;82035:39:0;;;;;;;:::i;:::-;;;;;;;;;82099:1;82085:15;;129690:14:::1;::::0;;;::::1;::::0;::::1;;:::i;:::-;115389:22;115404:6;115389:14;:22::i;:::-;115388:23;115380:42;;;;-1:-1:-1::0;;;115380:42:0::1;;;;;;;:::i;:::-;129765:20:::2;122294:164;129863:14;::::0;;;;;::::2;;:::i;:::-;129892:7;:18;;;129925:5;129945:2;129962:6;129983:5;130013:4;;130003:15;;;;;;;:::i;:::-;;::::0;;;;::::2;::::0;;129788:241:::2;::::0;;;;;;;;::::2;;;:::i;:::-;;;;;;;;;;;;;129765:264;;130042:12;:10;:12::i;:::-;-1:-1:-1::0;;;;;130042:26:0::2;;;;;130083:25;;130123:13;:11;:13::i;:::-;130151:42;130208:7;130230;130042:206;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;-1:-1:-1::0;130261:50:0::2;::::0;-1:-1:-1;130277:14:0::2;::::0;-1:-1:-1;;130277:14:0;;;::::2;::::0;::::2;;:::i;:::-;130293:5;130300:2;130304:6;130261:15;:50::i;:::-;-1:-1:-1::0;130329:53:0::2;130350:14;::::0;;;::::2;::::0;::::2;;:::i;:::-;130366:2;130370:5;130377:4;;130329:20;:53::i;:::-;82137:1:::0;82123:11;:15;130322:60;129361:1029;-1:-1:-1;;;;;;;;;;129361:1029:0:o;125240:496::-;82043:11;;:16;82035:39;;;;-1:-1:-1;;;82035:39:0;;;;;;;:::i;:::-;82099:1;82085:15;;89159:13:::1;:11;:13::i;:::-;:18:::0;89151:52:::1;;;;-1:-1:-1::0;;;89151:52:0::1;;;;;;;:::i;:::-;125484:6:::2;114401:30;114434:15;:13;:15::i;:::-;114401:48;;114587:6;-1:-1:-1::0;;;;;114569:24:0::2;:14;-1:-1:-1::0;;;;;114569:24:0::2;;:110;;;;114636:6;-1:-1:-1::0;;;;;114629:20:0::2;;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1::0;;;;;114611:40:0::2;:14;-1:-1:-1::0;;;;;114611:40:0::2;;:67;;;;;114656:22;114671:6;114656:14;:22::i;:::-;114655:23;114611:67;114547:202;;;;-1:-1:-1::0;;;114547:202:0::2;;;;;;;:::i;:::-;114760:16;114779:12;:10;:12::i;:::-;-1:-1:-1::0;;;;;114779:26:0::2;;:28;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;114760:47;;114114:6;114840:2;-1:-1:-1::0;;;;;114840:13:0::2;;114854:6;114840:21;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:42;114822:15;:60;114818:119;;;114899:26;::::0;-1:-1:-1;;;114899:26:0;;-1:-1:-1;;;;;114899:18:0;::::2;::::0;::::2;::::0;:26:::2;::::0;114918:6;;114899:26:::2;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;114818:119;125508:24:::3;125535:42;125551:6;125559:5;125566:2;125570:6;125535:15;:42::i;:::-;125508:69;;125616:1;125594:19;:23;:59;;;;;125622:31;125642:6;125650:2;125622:19;:31::i;:::-;125621:32;125594:59;125590:139;;;125670:47;125682:6;125690:5;125697:19;125670:11;:47::i;:::-;-1:-1:-1::0;;82137:1:0;82123:11;:15;-1:-1:-1;;;;;;125240:496:0:o;114018:45::-;114062:1;114018:45;:::o;126921:847::-;82043:11;;:16;82035:39;;;;-1:-1:-1;;;82035:39:0;;;;;;;:::i;:::-;82099:1;82085:15;;127212:14:::1;::::0;;;::::1;::::0;::::1;;:::i;:::-;115389:22;115404:6;115389:14;:22::i;:::-;115388:23;115380:42;;;;-1:-1:-1::0;;;115380:42:0::1;;;;;;;:::i;:::-;127244:12:::2;:10;:12::i;:::-;-1:-1:-1::0;;;;;127244:26:0::2;;;;;127285:25;;127325:13;:11;:13::i;:::-;127353:42;127410:7:::0;121712:143:::2;127503:14;::::0;;;::::2;::::0;::::2;;:::i;:::-;127536:7;:18;;;127573:5;127597:2;127618:6;127653:7;;127643:18;;;;;;;:::i;:::-;;::::0;;;;::::2;::::0;;127432:244:::2;::::0;;;;;;;::::2;;;:::i;:::-;;;;;;;;;;;;;127244:443;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;-1:-1:-1::0;127700:60:0::2;::::0;-1:-1:-1;127717:14:0::2;::::0;-1:-1:-1;;127717:14:0;;;::::2;::::0;::::2;;:::i;:::-;127733:5;127740:2;127744:6;127752:7;;127700:16;:60::i;:::-;-1:-1:-1::0;;82137:1:0;82123:11;:15;-1:-1:-1;;;;;126921:847:0:o;124168:515::-;82043:11;;:16;82035:39;;;;-1:-1:-1;;;82035:39:0;;;;;;;:::i;:::-;82099:1;82085:15;;89159:13:::1;:11;:13::i;:::-;:18:::0;89151:52:::1;;;;-1:-1:-1::0;;;89151:52:0::1;;;;;;;:::i;:::-;124474:6:::2;114401:30;114434:15;:13;:15::i;:::-;114401:48;;114587:6;-1:-1:-1::0;;;;;114569:24:0::2;:14;-1:-1:-1::0;;;;;114569:24:0::2;;:110;;;;114636:6;-1:-1:-1::0;;;;;114629:20:0::2;;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1::0;;;;;114611:40:0::2;:14;-1:-1:-1::0;;;;;114611:40:0::2;;:67;;;;;114656:22;114671:6;114656:14;:22::i;:::-;114655:23;114611:67;114547:202;;;;-1:-1:-1::0;;;114547:202:0::2;;;;;;;:::i;:::-;114760:16;114779:12;:10;:12::i;:::-;-1:-1:-1::0;;;;;114779:26:0::2;;:28;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;114760:47;;114114:6;114840:2;-1:-1:-1::0;;;;;114840:13:0::2;;114854:6;114840:21;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:42;114822:15;:60;114818:119;;;114899:26;::::0;-1:-1:-1;;;114899:26:0;;-1:-1:-1;;;;;114899:18:0;::::2;::::0;::::2;::::0;:26:::2;::::0;114918:6;;114899:26:::2;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;114818:119;124511:1:::3;124502:6;:10;:46;;;;;124517:31;124537:6;124545:2;124517:19;:31::i;:::-;124516:32;124502:46;124498:113;;;124565:34;124577:6;124585:5;124592:6;124565:11;:34::i;:::-;124623:52;124640:6;124648:5;124655:2;124659:6;124667:7;;124623:16;:52::i;:::-;-1:-1:-1::0;;82137:1:0;82123:11;:15;-1:-1:-1;;;;;;;124168:515:0:o;127776:744::-;82043:11;;:16;82035:39;;;;-1:-1:-1;;;82035:39:0;;;;;;;:::i;:::-;82099:1;82085:15;;128012:14:::1;::::0;;;::::1;::::0;::::1;;:::i;:::-;115389:22;115404:6;115389:14;:22::i;:::-;115388:23;115380:42;;;;-1:-1:-1::0;;;115380:42:0::1;;;;;;;:::i;:::-;128044:12:::2;:10;:12::i;:::-;-1:-1:-1::0;;;;;128044:26:0::2;;;;;128085:25;;128125:13;:11;:13::i;:::-;128153:42;128210:7:::0;121913:128:::2;128302:14;::::0;;;::::2;::::0;::::2;;:::i;:::-;128335:7;:18;;;128372:5;128396:2;128417:6;128232:206;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;128044:405;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;-1:-1:-1::0;128462:50:0::2;::::0;-1:-1:-1;128478:14:0::2;::::0;-1:-1:-1;;128478:14:0;;;::::2;::::0;::::2;;:::i;:::-;128494:5;128501:2;128505:6;128462:15;:50::i;:::-;-1:-1:-1::0;;82137:1:0;82123:11;:15;-1:-1:-1;;;;127776:744:0:o;122050:173::-;122099:124;122050:173;:::o;83829:198::-;83920:14;83937:15;:13;:15::i;:::-;83920:32;;83963:21;83977:6;83963:13;:21::i;:::-;84000:19;84012:6;84000:19;;;;;;:::i;121479:174::-;121545:108;121479:174;:::o;125744:772::-;126114:23;82043:11;;82058:1;82043:16;82035:39;;;;-1:-1:-1;;;82035:39:0;;;;;;;:::i;:::-;82099:1;82085:15;;89159:13:::1;:11;:13::i;:::-;:18:::0;89151:52:::1;;;;-1:-1:-1::0;;;89151:52:0::1;;;;;;;:::i;:::-;126088:6:::2;114401:30;114434:15;:13;:15::i;:::-;114401:48;;114587:6;-1:-1:-1::0;;;;;114569:24:0::2;:14;-1:-1:-1::0;;;;;114569:24:0::2;;:110;;;;114636:6;-1:-1:-1::0;;;;;114629:20:0::2;;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1::0;;;;;114611:40:0::2;:14;-1:-1:-1::0;;;;;114611:40:0::2;;:67;;;;;114656:22;114671:6;114656:14;:22::i;:::-;114655:23;114611:67;114547:202;;;;-1:-1:-1::0;;;114547:202:0::2;;;;;;;:::i;:::-;114760:16;114779:12;:10;:12::i;:::-;-1:-1:-1::0;;;;;114779:26:0::2;;:28;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;114760:47;;114114:6;114840:2;-1:-1:-1::0;;;;;114840:13:0::2;;114854:6;114840:21;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:42;114822:15;:60;114818:119;;;114899:26;::::0;-1:-1:-1;;;114899:26:0;;-1:-1:-1;;;;;114899:18:0;::::2;::::0;::::2;::::0;:26:::2;::::0;114918:6;;114899:26:::2;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;114818:119;126155:24:::3;126182:42;126198:6;126206:5;126213:2;126217:6;126182:15;:42::i;:::-;126155:69;;126264:1;126242:19;:23;:36;;;;126277:1;126269:5;:9;126242:36;126241:74;;;;;126284:31;126304:6;126312:2;126284:19;:31::i;:::-;126283:32;126241:74;126237:207;;;126332:47;126344:6;126352:5;126359:19;126332:11;:47::i;:::-;126394:38;126406:6;126422:1;126426:5;126394:11;:38::i;:::-;126463:45;126484:6;126492:2;126496:5;126503:4;;126463:20;:45::i;:::-;82137:1:::0;82123:11;:15;126456:52;125744:772;-1:-1:-1;;;;;;;;;;;;125744:772:0:o;88955:31::-;;;-1:-1:-1;;;;;88955:31:0;;:::o;122814:633::-;82043:11;;:16;82035:39;;;;-1:-1:-1;;;82035:39:0;;;;;;;:::i;:::-;82099:1;82085:15;;89159:13:::1;:11;:13::i;:::-;:18:::0;89151:52:::1;;;;-1:-1:-1::0;;;89151:52:0::1;;;;;;;:::i;:::-;123019:6:::2;114401:30;114434:15;:13;:15::i;:::-;114401:48;;114587:6;-1:-1:-1::0;;;;;114569:24:0::2;:14;-1:-1:-1::0;;;;;114569:24:0::2;;:110;;;;114636:6;-1:-1:-1::0;;;;;114629:20:0::2;;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1::0;;;;;114611:40:0::2;:14;-1:-1:-1::0;;;;;114611:40:0::2;;:67;;;;;114656:22;114671:6;114656:14;:22::i;:::-;114655:23;114611:67;114547:202;;;;-1:-1:-1::0;;;114547:202:0::2;;;;;;;:::i;:::-;114760:16;114779:12;:10;:12::i;:::-;-1:-1:-1::0;;;;;114779:26:0::2;;:28;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;114760:47;;114114:6;114840:2;-1:-1:-1::0;;;;;114840:13:0::2;;114854:6;114840:21;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:42;114822:15;:60;114818:119;;;114899:26;::::0;-1:-1:-1;;;114899:26:0;;-1:-1:-1;;;;;114899:18:0;::::2;::::0;::::2;::::0;:26:::2;::::0;114918:6;;114899:26:::2;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;114818:119;123043:13:::3;123059:12;:10;:12::i;:::-;-1:-1:-1::0;;;;;123059:23:0::3;;:25;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;123043:41:::0;-1:-1:-1;123095:14:0::3;123112:13:::0;;:43:::3;;123147:8;123112:43;;;123128:2;-1:-1:-1::0;;;;;123128:15:0::3;;:17;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1::0;;;;;123112:43:0::3;;123095:60;;123166:18;123187:2;-1:-1:-1::0;;;;;123187:15:0::3;;123203:6;123187:23;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;123166:44;;123244:9;123227:13;:26;123223:217;;123270:50;::::0;-1:-1:-1;;;123270:50:0;;-1:-1:-1;;;;;123270:14:0;::::3;::::0;::::3;::::0;:50:::3;::::0;123285:6;;123293:9;;123304:15:::3;::::0;123270:50:::3;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;::::0;::::3;;;;;;;;;123223:217;;;123353:2;-1:-1:-1::0;;;;;123353:14:0::3;;123368:6;123376:9;123387:40;123407:19;;123387:15;:19;;:40;;;;:::i;:::-;123353:75;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;::::0;::::3;;;;;;;;;-1:-1:-1::0;;82137:1:0;82123:11;:15;-1:-1:-1;;;;;;122814:633:0:o;114070:50::-;114114:6;114070:50;:::o;122232:226::-;122294:164;122232:226;:::o;131144:131::-;131237:23;131144:131;:::o;121864:177::-;121913:128;121864:177;:::o;121430:40::-;;;;:::o;123455:705::-;82043:11;;:16;82035:39;;;;-1:-1:-1;;;82035:39:0;;;;;;;:::i;:::-;82099:1;82085:15;;123648:14:::1;::::0;;;::::1;::::0;::::1;;:::i;:::-;115389:22;115404:6;115389:14;:22::i;:::-;115388:23;115380:42;;;;-1:-1:-1::0;;;115380:42:0::1;;;;;;;:::i;:::-;123680:12:::2;:10;:12::i;:::-;-1:-1:-1::0;;;;;123680:26:0::2;;;;;123721:25;;123761:13;:11;:13::i;:::-;123789:42;123846:7:::0;121545:108:::2;123955:14;::::0;;;::::2;::::0;::::2;;:::i;:::-;123988:7;:18;;;124025:8;123868:180;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;123680:379;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;124072:12;:10;:12::i;:::-;-1:-1:-1::0;;;;;124072:23:0::2;;:25;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1::0;;;;;124072:37:0::2;;124110:14;::::0;;;::::2;::::0;::::2;;:::i;:::-;124126:8;124136:15;124072:80;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;122467:31:::0;;;;:::o;126524:389::-;126625:10;126650;126675:14;126725:12;:10;:12::i;:::-;-1:-1:-1;;;;;126725:23:0;;:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;126725:38:0;;126764:6;126725:46;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;126717:54;;126790:12;:10;:12::i;:::-;-1:-1:-1;;;;;126790:23:0;;:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;126790:36:0;;126827:6;126790:44;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;126782:52;;126857:12;:10;:12::i;:::-;-1:-1:-1;;;;;126857:23:0;;:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;126857:40:0;;126898:6;126857:48;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;126524:389;;;;-1:-1:-1;;126524:389:0:o;128528:825::-;128817:23;82043:11;;82058:1;82043:16;82035:39;;;;-1:-1:-1;;;82035:39:0;;;;;;;:::i;:::-;82099:1;82085:15;;128783:14:::1;::::0;;;::::1;::::0;::::1;;:::i;:::-;115389:22;115404:6;115389:14;:22::i;:::-;115388:23;115380:42;;;;-1:-1:-1::0;;;115380:42:0::1;;;;;;;:::i;:::-;128858:12:::2;:10;:12::i;:::-;-1:-1:-1::0;;;;;128858:26:0::2;;;;;128899:25;;128939:13;:11;:13::i;:::-;128967:42;129024:7:::0;122099:124:::2;129116:14;::::0;;;::::2;::::0;::::2;;:::i;:::-;129149:7;:18;;;129186:2;129207:5;129241:4;;129231:15;;;;;;;:::i;:::-;;::::0;;;;::::2;::::0;;129046:215:::2;::::0;;;;;;::::2;;;:::i;:::-;;;;;;;;;;;;;128858:414;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;-1:-1:-1::0;129292:53:0::2;::::0;-1:-1:-1;129313:14:0::2;::::0;-1:-1:-1;;129313:14:0;;;::::2;::::0;::::2;;:::i;:::-;129329:2;129333:5;129340:4;;129292:20;:53::i;:::-;82137:1:::0;82123:11;:15;129285:60;128528:825;-1:-1:-1;;;;;;;128528:825:0:o;124691:541::-;125003:23;82043:11;;82058:1;82043:16;82035:39;;;;-1:-1:-1;;;82035:39:0;;;;;;;:::i;:::-;82099:1;82085:15;;89159:13:::1;:11;:13::i;:::-;:18:::0;89151:52:::1;;;;-1:-1:-1::0;;;89151:52:0::1;;;;;;;:::i;:::-;124977:6:::2;114401:30;114434:15;:13;:15::i;:::-;114401:48;;114587:6;-1:-1:-1::0;;;;;114569:24:0::2;:14;-1:-1:-1::0;;;;;114569:24:0::2;;:110;;;;114636:6;-1:-1:-1::0;;;;;114629:20:0::2;;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1::0;;;;;114611:40:0::2;:14;-1:-1:-1::0;;;;;114611:40:0::2;;:67;;;;;114656:22;114671:6;114656:14;:22::i;:::-;114655:23;114611:67;114547:202;;;;-1:-1:-1::0;;;114547:202:0::2;;;;;;;:::i;:::-;114760:16;114779:12;:10;:12::i;:::-;-1:-1:-1::0;;;;;114779:26:0::2;;:28;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;114760:47;;114114:6;114840:2;-1:-1:-1::0;;;;;114840:13:0::2;;114854:6;114840:21;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:42;114822:15;:60;114818:119;;;114899:26;::::0;-1:-1:-1;;;114899:26:0;;-1:-1:-1;;;;;114899:18:0;::::2;::::0;::::2;::::0;:26:::2;::::0;114918:6;;114899:26:::2;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;114818:119;125056:1:::3;125048:5;:9;:45;;;;;125062:31;125082:6;125090:2;125062:19;:31::i;:::-;125061:32;125048:45;125044:116;;;125110:38;125122:6;125138:1;125142:5;125110:11;:38::i;:::-;125179:45;125200:6;125208:2;125212:5;125219:4;;125179:20;:45::i;:::-;82137:1:::0;82123:11;:15;125172:52;124691:541;-1:-1:-1;;;;;;;;;124691:541:0:o;60444:466::-;60530:7;60555:13;60602:9;60590:21;;60264:111;60742:6;:11;;;60726:29;;;;;;60790:6;:14;;;60774:32;;;;;;60825:8;60852:6;:24;;;60656:235;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;60632:270;;;;;;60625:277;;;60444:466;;;;:::o;90476:169::-;90586:15;90626:11;:9;:11::i;:::-;90619:18;;90476:169;;:::o;84575:276::-;84664:6;84682:23;84708:17;:15;:17::i;:::-;84682:43;;84741:6;84736:108;84757:7;:14;84753:1;:18;84736:108;;;84793:1;-1:-1:-1;;;;;84793:12:0;;84806:7;84814:1;84806:10;;;;;;;;;;;;;;84826:4;84793:39;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;84773:3:0;;;;;-1:-1:-1;84736:108:0;;-1:-1:-1;84736:108:0;;;84575:276;;;:::o;117418:221::-;117508:4;117531:10;117546:12;:10;:12::i;:::-;-1:-1:-1;;;;;117546:26:0;;:28;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;117546:36:0;;117583:6;117546:44;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;117616:15:0;-1:-1:-1;;117418:221:0;-1:-1:-1;;;117418:221:0:o;130973:163::-;131116:11;;-1:-1:-1;;;;;131116:11:0;130973:163;:::o;89704:281::-;89777:7;89825:2;89806:8;:21;;;;:55;;-1:-1:-1;89845:16:0;;-1:-1:-1;;;;;89845:16:0;89831:10;:30;89806:55;89802:176;;;89885:40;89922:2;89904:8;;:15;;:20;89885:8;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;89885:18:0;;:40;-1:-1:-1;;89885:18:0;:40;-1:-1:-1;89885:40:0:i;:::-;89878:47;;;;89802:176;-1:-1:-1;89965:1:0;89958:8;;119043:875;119215:24;119287:14;119310:5;-1:-1:-1;;;;;119304:22:0;;119327:6;119335:7;119304:39;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;119287:56;;119370:9;119360:6;:19;119356:328;;119458:13;;119454:101;;119492:47;119513:6;119521:5;119528:7;119537:1;119492:20;:47::i;:::-;119620:52;119641:6;119649:5;119656:7;119665:6;119620:20;:52::i;:::-;119774:9;119765:6;:18;119761:94;;;119822:21;:6;119833:9;119822:10;:21::i;:::-;119800:43;;119761:94;119870:40;119879:6;119887:5;119894:7;119903:6;119870:40;;;;;;;;;:::i;:::-;;;;;;;;119043:875;;;;;;;:::o;119926:943::-;120124:23;120460:2;-1:-1:-1;;;;;120450:12:0;:6;-1:-1:-1;;;;;120450:12:0;;;:45;;;;-1:-1:-1;120467:28:0;;-1:-1:-1;;;120467:28:0;;-1:-1:-1;;;;;120467:24:0;;;;;:28;;120492:2;;120467:28;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;120466:29;120450:45;120442:73;;;;-1:-1:-1;;;120442:73:0;;;;;;;:::i;:::-;120667:12;:10;:12::i;:::-;-1:-1:-1;;;;;120667:24:0;;:26;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;120667:37:0;;120705:2;120709:4;120667:47;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:52;120659:80;;;;-1:-1:-1;;;120659:80:0;;;;;;;:::i;:::-;120765:39;120778:6;120786:2;120790:5;120797:6;;120765:39;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;120765:12:0;;-1:-1:-1;;;120765:39:0:i;:::-;120752:52;;120820:41;120835:6;120843:2;120847:5;120854:6;;120820:41;;;;;;;;;;:::i;:::-;;;;;;;;119926:943;;;;;;;:::o;120877:271::-;120984:8;121019:12;:10;:12::i;:::-;-1:-1:-1;;;;;121019:27:0;;:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;121019:43:0;;121063:6;121071:2;121019:55;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;121010:64:0;-1:-1:-1;121010:64:0;;121091:49;;;121098:12;:10;:12::i;:::-;-1:-1:-1;;;;;121098:29:0;;:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;121098:38:0;;121137:2;121098:42;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;121085:55;120877:271;-1:-1:-1;;;120877:271:0:o;117647:358::-;117800:1;117791:6;:10;:40;;;;-1:-1:-1;117829:1:0;117805:12;:10;:12::i;:::-;-1:-1:-1;;;;;117805:26:0;;;117791:40;117787:211;;;117848:10;117861:12;:10;:12::i;:::-;-1:-1:-1;;;;;117861:24:0;;:26;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;117861:37:0;;117899:5;117906:6;117861:52;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;117848:65;;117939:12;:10;:12::i;:::-;-1:-1:-1;;;;;117928:43:0;;117972:6;117980:5;117928:58;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;117787:211;;117647:358;;;:::o;118716:319::-;118917:48;118939:6;118947:5;118954:2;118958:6;118917:21;:48::i;:::-;118981:46;118992:6;119000:5;119007:2;119011:6;119019:7;;118981:46;;;;;;;;;;;:::i;:::-;;;;;;;;118716:319;;;;;;:::o;84916:275::-;85007:6;85025:23;85051:17;:15;:17::i;:::-;85025:43;;85084:6;85079:105;85100:7;:14;85096:1;:18;85079:105;;;85136:1;-1:-1:-1;;;;;85136:12:0;;85149:7;85157:1;85149:10;;;;;;;;;;;;;;85169:1;85136:36;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;85116:3:0;;;;;-1:-1:-1;85079:105:0;;-1:-1:-1;85079:105:0;46569:191;46705:5;;;46729:6;;;;46721:31;;;;-1:-1:-1;;;46721:31:0;;;;;;;:::i;:::-;46569:191;;;;:::o;89388:308::-;89459:15;89515:2;89496:8;:21;;;;:55;;-1:-1:-1;89535:16:0;;-1:-1:-1;;;;;89535:16:0;89521:10;:30;89496:55;89492:197;;;89575:52;:40;89612:2;89594:8;;:15;;:20;89575:8;;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;89575:18:0;;:40;-1:-1:-1;;89575:18:0;:40;-1:-1:-1;89575:40:0:i;:::-;-1:-1:-1;;;;;89575:50:0;;:52::i;89492:197::-;-1:-1:-1;89667:10:0;89660:17;;52545:304;52622:7;52668:6;52677:2;52668:11;52650:6;:13;:30;;52642:39;;;;;;-1:-1:-1;52769:30:0;52785:4;52769:30;52763:37;;52545:304::o;86464:765::-;-1:-1:-1;;;;;86647:19:0;;86639:45;;;;-1:-1:-1;;;86639:45:0;;;;;;;:::i;:::-;86695:19;86754:22;;;86791:7;86813:6;86717:113;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;86717:113:0;;;;;;;;;;;;;;-1:-1:-1;;;;;86717:113:0;-1:-1:-1;;;;;;86717:113:0;;;;;;;;;;;-1:-1:-1;86841:23:0;86867:38;86880:6;86888:5;-1:-1:-1;86717:113:0;86867:12;:38::i;:::-;86841:64;;87092:12;87107:10;:17;87128:1;87107:22;:63;;87151:10;87140:30;;;;;;;;;;;;:::i;:::-;87107:63;;;87132:4;87107:63;87092:78;;87189:7;87181:40;;;;-1:-1:-1;;;87181:40:0;;;;;;;:::i;:::-;86464:765;;;;;;;:::o;46368:193::-;46476:4;46511:1;46506;:6;;46498:32;;;;-1:-1:-1;;;46498:32:0;;;;;;;:::i;:::-;-1:-1:-1;46548:5:0;;;46368:193::o;85199:264::-;85405:50;;-1:-1:-1;;;85405:50:0;;85368:12;;-1:-1:-1;;;;;85405:23:0;;;;;:50;;85435:1;;85439:2;;85443:5;;85450:4;;85405:50;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;85405:50:0;;;;;;;;;;;;:::i;:::-;85398:57;85199:264;-1:-1:-1;;;;;85199:264:0:o;116106:146::-;116178:7;116218:12;:10;:12::i;:::-;-1:-1:-1;;;;;116218:23:0;;:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;85552:823::-;-1:-1:-1;;;;;85727:19:0;;85723:109;;85763:36;85776:6;85784:2;85788:6;85763:36;;;;;;;;;;;;:12;:36::i;:::-;;85814:7;;85723:109;85844:19;85903:23;;;85941:2;85958:6;85866:109;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;85866:109:0;;;;;;;;;;;;;;-1:-1:-1;;;;;85866:109:0;-1:-1:-1;;;;;;85866:109:0;;;;;;;;;;;-1:-1:-1;85986:23:0;86012:38;86025:6;86033:5;-1:-1:-1;85866:109:0;86012:12;:38::i;:::-;85986:64;;86237:12;86252:10;:17;86273:1;86252:22;:63;;86296:10;86285:30;;;;;;;;;;;;:::i;:::-;86252:63;;;86277:4;86252:63;86237:78;;86334:7;86326:41;;;;-1:-1:-1;;;86326:41:0;;;;;;;:::i;49505:338::-;49582:7;49628:6;49637:2;49628:11;49610:6;:13;:30;;49602:39;;;;;;-1:-1:-1;49733:30:0;49749:4;49733:30;49727:37;-1:-1:-1;;;49723:71:0;;;49505:338::o;432:336:-1:-;;;546:3;539:4;531:6;527:17;523:27;513:2;;-1:-1;;554:12;513:2;-1:-1;584:20;;624:18;613:30;;610:2;;;-1:-1;;646:12;610:2;690:4;682:6;678:17;666:29;;741:3;690:4;721:17;682:6;707:32;;704:41;701:2;;;758:1;;748:12;701:2;506:262;;;;;:::o;2187:159::-;;2298:3;2289:6;2284:3;2280:16;2276:26;2273:2;;;-1:-1;;2305:12;2273:2;-1:-1;2325:15;2266:80;-1:-1;2266:80::o;2772:241::-;;2876:2;2864:9;2855:7;2851:23;2847:32;2844:2;;;-1:-1;;2882:12;2844:2;85:6;72:20;97:33;124:5;97:33;:::i;3020:263::-;;3135:2;3123:9;3114:7;3110:23;3106:32;3103:2;;;-1:-1;;3141:12;3103:2;226:6;220:13;238:33;265:5;238:33;:::i;3290:617::-;;;;;3445:3;3433:9;3424:7;3420:23;3416:33;3413:2;;;-1:-1;;3452:12;3413:2;85:6;72:20;97:33;124:5;97:33;:::i;:::-;3504:63;-1:-1;3604:2;3643:22;;72:20;97:33;72:20;97:33;:::i;:::-;3612:63;-1:-1;3712:2;3751:22;;72:20;97:33;72:20;97:33;:::i;:::-;3407:500;;;;-1:-1;3720:63;;3820:2;3859:22;2561:20;;-1:-1;;3407:500::o;3914:867::-;;;;;;;4105:3;4093:9;4084:7;4080:23;4076:33;4073:2;;;-1:-1;;4112:12;4073:2;85:6;72:20;97:33;124:5;97:33;:::i;:::-;4164:63;-1:-1;4264:2;4303:22;;72:20;97:33;72:20;97:33;:::i;:::-;4272:63;-1:-1;4372:2;4411:22;;72:20;97:33;72:20;97:33;:::i;:::-;4380:63;-1:-1;4480:2;4519:22;;2561:20;;-1:-1;4616:3;4601:19;;4588:33;4641:18;4630:30;;4627:2;;;-1:-1;;4663:12;4627:2;4701:64;4757:7;4748:6;4737:9;4733:22;4701:64;:::i;:::-;4067:714;;;;-1:-1;4067:714;;-1:-1;4067:714;;4683:82;;4067:714;-1:-1;;;4067:714::o;4788:993::-;;;;;;;;4996:3;4984:9;4975:7;4971:23;4967:33;4964:2;;;-1:-1;;5003:12;4964:2;85:6;72:20;97:33;124:5;97:33;:::i;:::-;5055:63;-1:-1;5155:2;5194:22;;72:20;97:33;72:20;97:33;:::i;:::-;5163:63;-1:-1;5263:2;5302:22;;72:20;97:33;72:20;97:33;:::i;:::-;5271:63;-1:-1;5371:2;5410:22;;2561:20;;-1:-1;5479:3;5519:22;;2561:20;;-1:-1;5616:3;5601:19;;5588:33;5641:18;5630:30;;5627:2;;;-1:-1;;5663:12;5627:2;5701:64;5757:7;5748:6;5737:9;5733:22;5701:64;:::i;:::-;4958:823;;;;-1:-1;4958:823;;-1:-1;4958:823;;;;5683:82;;-1:-1;;;4958:823::o;5788:741::-;;;;;;5962:3;5950:9;5941:7;5937:23;5933:33;5930:2;;;-1:-1;;5969:12;5930:2;85:6;72:20;97:33;124:5;97:33;:::i;:::-;6021:63;-1:-1;6121:2;6160:22;;72:20;97:33;72:20;97:33;:::i;:::-;6129:63;-1:-1;6229:2;6268:22;;2561:20;;-1:-1;6365:2;6350:18;;6337:32;6389:18;6378:30;;6375:2;;;-1:-1;;6411:12;6375:2;6449:64;6505:7;6496:6;6485:9;6481:22;6449:64;:::i;:::-;5924:605;;;;-1:-1;5924:605;;-1:-1;6431:82;;;5924:605;-1:-1;;;5924:605::o;6536:366::-;;;6657:2;6645:9;6636:7;6632:23;6628:32;6625:2;;;-1:-1;;6663:12;6625:2;85:6;72:20;97:33;124:5;97:33;:::i;:::-;6715:63;6815:2;6854:22;;;;2561:20;;-1:-1;;;6619:283::o;6909:257::-;;7021:2;7009:9;7000:7;6996:23;6992:32;6989:2;;;-1:-1;;7027:12;6989:2;364:6;358:13;51345:5;48083:13;48076:21;51323:5;51320:32;51310:2;;-1:-1;;51356:12;7173:393;;;7302:2;7290:9;7281:7;7277:23;7273:32;7270:2;;;-1:-1;;7308:12;7270:2;364:6;358:13;51345:5;48083:13;48076:21;51323:5;51320:32;51310:2;;-1:-1;;51356:12;51310:2;7468;7518:22;;;;2709:13;7360:71;;2709:13;;-1:-1;;;7264:302::o;7573:360::-;;7697:2;7685:9;7676:7;7672:23;7668:32;7665:2;;;-1:-1;;7703:12;7665:2;7754:17;7748:24;7792:18;;7784:6;7781:30;7778:2;;;-1:-1;;7814:12;7778:2;7900:6;7889:9;7885:22;;;889:3;882:4;874:6;870:17;866:27;856:2;;-1:-1;;897:12;856:2;937:6;931:13;7792:18;43440:6;43437:30;43434:2;;;-1:-1;;43470:12;43434:2;43104;43098:9;43543;43524:17;;-1:-1;;43520:33;43130:17;;7697:2;43130:17;43190:34;;;43226:22;;;43187:62;43184:2;;;-1:-1;;43252:12;43184:2;43104;43271:22;1029:21;;;1129:16;;;7697:2;1129:16;1126:25;-1:-1;1123:2;;;-1:-1;;1154:12;1123:2;1174:39;1206:6;7697:2;1105:5;1101:16;7697:2;1071:6;1067:17;1174:39;:::i;:::-;7834:83;7659:274;-1:-1;;;;;;7659:274::o;9508:757::-;;;;;9690:3;9678:9;9669:7;9665:23;9661:33;9658:2;;;-1:-1;;9697:12;9658:2;9755:17;9742:31;9793:18;9785:6;9782:30;9779:2;;;-1:-1;;9815:12;9779:2;9845:80;9917:7;9908:6;9897:9;9893:22;9845:80;:::i;:::-;9835:90;;;9962:2;10005:9;10001:22;72:20;97:33;124:5;97:33;:::i;10272:1007::-;;;;;;;10490:3;10478:9;10469:7;10465:23;10461:33;10458:2;;;-1:-1;;10497:12;10458:2;10555:17;10542:31;10593:18;;10585:6;10582:30;10579:2;;;-1:-1;;10615:12;10579:2;10645:80;10717:7;10708:6;10697:9;10693:22;10645:80;:::i;:::-;10635:90;;10762:2;10805:9;10801:22;72:20;63:29;;97:33;124:5;97:33;:::i;:::-;10770:63;;-1:-1;10870:2;10909:22;;72:20;;97:33;72:20;97:33;:::i;:::-;10878:63;;-1:-1;10978:2;11017:22;;2561:20;;-1:-1;11114:3;11099:19;;11086:33;;11128:30;;;11125:2;;;-1:-1;;11161:12;11125:2;;11199:64;11255:7;11246:6;11235:9;11231:22;11199:64;:::i;11286:1133::-;;;;;;;;11521:3;11509:9;11500:7;11496:23;11492:33;11489:2;;;-1:-1;;11528:12;11489:2;11586:17;11573:31;11624:18;;11616:6;11613:30;11610:2;;;-1:-1;;11646:12;11610:2;11676:80;11748:7;11739:6;11728:9;11724:22;11676:80;:::i;:::-;11666:90;;11793:2;11836:9;11832:22;72:20;63:29;;97:33;124:5;97:33;:::i;:::-;11801:63;;-1:-1;11901:2;11940:22;;72:20;;97:33;72:20;97:33;:::i;:::-;11909:63;;-1:-1;12009:2;12048:22;;2561:20;;-1:-1;12117:3;12157:22;;2561:20;;-1:-1;12254:3;12239:19;;12226:33;;12268:30;;;12265:2;;;-1:-1;;12301:12;12265:2;;12339:64;12395:7;12386:6;12375:9;12371:22;12339:64;:::i;12426:881::-;;;;;;12627:3;12615:9;12606:7;12602:23;12598:33;12595:2;;;-1:-1;;12634:12;12595:2;12692:17;12679:31;12730:18;;12722:6;12719:30;12716:2;;;-1:-1;;12752:12;12716:2;12782:80;12854:7;12845:6;12834:9;12830:22;12782:80;:::i;:::-;12772:90;;12899:2;12942:9;12938:22;72:20;63:29;;97:33;124:5;97:33;:::i;:::-;12907:63;;-1:-1;13007:2;13046:22;;2561:20;;-1:-1;13143:2;13128:18;;13115:32;;13156:30;;;13153:2;;;-1:-1;;13189:12;13153:2;;13227:64;13283:7;13274:6;13263:9;13259:22;13227:64;:::i;13314:506::-;;;13462:2;13450:9;13441:7;13437:23;13433:32;13430:2;;;-1:-1;;13468:12;13430:2;13526:17;13513:31;13564:18;13556:6;13553:30;13550:2;;;-1:-1;;13586:12;13550:2;13616:80;13688:7;13679:6;13668:9;13664:22;13616:80;:::i;:::-;13606:90;13733:2;13772:22;;;;2561:20;;-1:-1;;;;13424:396::o;13827:263::-;;13942:2;13930:9;13921:7;13917:23;13913:32;13910:2;;;-1:-1;;13948:12;13910:2;2437:6;2431:13;-1:-1;;;;;52307:5;49151:46;52282:5;52279:35;52269:2;;-1:-1;;52318:12;14097:263;;14212:2;14200:9;14191:7;14187:23;14183:32;14180:2;;;-1:-1;;14218:12;14180:2;-1:-1;2709:13;;14174:186;-1:-1;14174:186::o;14367:399::-;;;14499:2;14487:9;14478:7;14474:23;14470:32;14467:2;;;-1:-1;;14505:12;14467:2;2715:6;2709:13;14557:74;;14668:2;14722:9;14718:22;220:13;238:33;265:5;238:33;:::i;:::-;14676:74;;;;14461:305;;;;;:::o;15532:113::-;-1:-1;;;;;49271:54;15603:37;;15597:48::o;15801:669::-;;44810:6;44805:3;44798:19;44847:4;;44842:3;44838:14;15946:91;;16122:21;-1:-1;16149:299;16174:6;16171:1;16168:13;16149:299;;;85:6;72:20;97:33;124:5;97:33;:::i;:::-;-1:-1;;;;;49271:54;15603:37;;14943:14;;;;46191:12;;;;624:18;16189:9;16149:299;;;-1:-1;16454:10;;15933:537;-1:-1;;;;;15933:537::o;17224:939::-;44798:19;;;44847:4;44838:14;;;;17224:939;;44847:4;17542:17;;;17533:27;;;44838:14;17656:21;17224:939;17683:441;17708:6;17705:1;17702:13;17683:441;;;17770:9;17764:4;17760:20;17755:3;17748:33;47406:3;47393:17;47450:48;;47474:8;47458:14;47454:29;47450:48;47430:18;47426:73;47416:2;;-1:-1;;47503:12;47416:2;47532:33;;47587:19;;47657:18;47646:30;;47643:2;;;-1:-1;;47679:12;47643:2;47736:17;47458:14;47716:38;47706:8;47702:53;47699:2;;;-1:-1;;47758:12;47699:2;15287:82;17994:4;17979:13;44847:4;47625:5;47621:16;15287:82;:::i;:::-;18103:14;;;;17884:115;-1:-1;;;44413:14;;;;17730:1;17723:9;17683:441;;;-1:-1;18147:10;;17376:787;-1:-1;;;;;;;17376:787::o;18666:297::-;;44810:6;44805:3;44798:19;50569:6;50564:3;44847:4;44842:3;44838:14;50546:30;-1:-1;44847:4;50616:6;44842:3;50607:16;;50600:27;44847:4;43543:9;;51006:2;18949:6;50986:14;50982:28;44842:3;18918:39;;18911:46;;18766:197;;;;;:::o;19636:343::-;;19778:5;44153:12;44810:6;44805:3;44798:19;19871:52;19916:6;44847:4;44842:3;44838:14;44847:4;19897:5;19893:16;19871:52;:::i;:::-;43543:9;50986:14;-1:-1;;50982:28;19935:39;;;;44847:4;19935:39;;19726:253;-1:-1;;19726:253::o;25950:291::-;;50569:6;50564:3;50559;50546:30;50607:16;;50600:27;;;50607:16;26094:147;-1:-1;26094:147::o;26248:222::-;-1:-1;;;;;49271:54;;;;15603:37;;26375:2;26360:18;;26346:124::o;26477:333::-;-1:-1;;;;;49271:54;;;15603:37;;49271:54;;26796:2;26781:18;;15603:37;26632:2;26617:18;;26603:207::o;26817:556::-;-1:-1;;;;;49271:54;;;15603:37;;49271:54;;;27193:2;27178:18;;15603:37;49271:54;;27276:2;27261:18;;15603:37;27359:2;27344:18;;18242:37;;;;27028:3;27013:19;;26999:374::o;27380:772::-;-1:-1;;;;;49271:54;;;15603:37;;49271:54;;;27812:2;27797:18;;15603:37;49271:54;;27895:2;27880:18;;15603:37;27978:2;27963:18;;18242:37;;;49282:42;28015:3;28000:19;;27993:49;;;27380:772;;28056:86;;27632:19;;28128:6;28120;28056:86;:::i;:::-;28048:94;27618:534;-1:-1;;;;;;;;27618:534::o;28159:660::-;-1:-1;;;;;49271:54;;;15603:37;;49271:54;;28563:2;28548:18;;15603:37;28646:2;28631:18;;18242:37;;;28398:3;28683:2;28668:18;;28661:48;;;28159:660;;28723:86;;28383:19;;28795:6;28787;28723:86;:::i;:::-;28715:94;28369:450;-1:-1;;;;;;;28369:450::o;28826:385::-;-1:-1;;;;;49271:54;;;;15603:37;;29197:2;29182:18;;20811:76;29007:2;28992:18;;28978:233::o;29558:444::-;-1:-1;;;;;49271:54;;;;15603:37;;29905:2;29890:18;;18242:37;;;;29988:2;29973:18;;18242:37;29741:2;29726:18;;29712:290::o;30009:366::-;30184:2;30198:47;;;44153:12;;30169:18;;;44798:19;;;30009:366;;30184:2;43872:14;;;;44838;;;;30009:366;16910:257;16935:6;16932:1;16929:13;16910:257;;;16996:13;;-1:-1;;;;;;48249:78;18478:36;;44413:14;;;;15121;;;;16957:1;16950:9;16910:257;;;-1:-1;30251:114;;30155:220;-1:-1;;;;;;30155:220::o;30382:222::-;18242:37;;;30509:2;30494:18;;30480:124::o;30611:780::-;18242:37;;;-1:-1;;;;;49271:54;;;31043:2;31028:18;;15603:37;31126:2;31111:18;;18242:37;;;;49271:54;;;31209:2;31194:18;;15603:37;49271:54;;;31292:3;31277:19;;15603:37;49282:42;31361:19;;18242:37;30878:3;30863:19;;30849:542::o;31398:892::-;18242:37;;;-1:-1;;;;;49271:54;;;31858:2;31843:18;;15603:37;31941:2;31926:18;;18242:37;;;;49271:54;;;32024:2;32009:18;;15603:37;49271:54;;;32107:3;32092:19;;15603:37;49282:42;32176:19;;18242:37;;;;32275:3;32260:19;;18242:37;31693:3;31678:19;;31664:626::o;32297:1004::-;18242:37;;;-1:-1;;;;;49271:54;;;32785:2;32770:18;;15603:37;32868:2;32853:18;;18242:37;;;;49271:54;;;32951:2;32936:18;;15603:37;49271:54;;33034:3;33019:19;;15603:37;49282:42;33103:19;;18242:37;;;;33202:3;33187:19;;18242:37;;;;33286:3;33271:19;;18242:37;32620:3;32605:19;;32591:710::o;33308:780::-;18242:37;;;-1:-1;;;;;49271:54;;;33740:2;33725:18;;15603:37;33823:2;33808:18;;18242:37;;;;49271:54;;33906:2;33891:18;;15603:37;33989:3;33974:19;;18242:37;;;;49282:42;34058:19;;18242:37;33575:3;33560:19;;33546:542::o;34095:556::-;18242:37;;;-1:-1;;;;;49271:54;;;;34471:2;34456:18;;15603:37;34554:2;34539:18;;18242:37;34637:2;34622:18;;18242:37;34306:3;34291:19;;34277:374::o;34658:668::-;18242:37;;;35062:2;35047:18;;18242:37;;;;35145:2;35130:18;;18242:37;;;;35228:2;35213:18;;18242:37;-1:-1;;;;;49271:54;35311:3;35296:19;;15603:37;34897:3;34882:19;;34868:458::o;35333:329::-;-1:-1;;;;;;48249:78;;;;18478:36;;-1:-1;;;;;49271:54;35648:2;35633:18;;15603:37;35486:2;35471:18;;35457:205::o;36021:306::-;;36166:2;36187:17;36180:47;36241:76;36166:2;36155:9;36151:18;36303:6;36241:76;:::i;36334:1152::-;-1:-1;;;;;49271:54;;20454:73;;36923:2;36908:18;;18242:37;;;37014:2;36999:18;;18242:37;;;36334:1152;50048:43;20695:5;50048:43;:::i;:::-;37122:2;37111:9;37107:18;20635:67;36719:3;37159;37148:9;37144:19;37137:49;24508:77;24568:16;24561:5;24508:77;:::i;:::-;37159:3;36719;36708:9;36704:19;24598:38;24651:127;24416:14;36708:9;24416:14;24759:12;24745;24651:127;:::i;:::-;24643:135;;;24866:88;36923:2;24941:5;24937:16;24930:5;24866:88;:::i;:::-;24990:14;;;-1:-1;;24990:14;24974;;;24967:38;25020:147;24994:4;25148:12;25134;25020:147;:::i;:::-;25012:155;;;;37014:2;25278:5;25274:16;2561:20;25353:14;36708:9;25353:14;18242:37;25432:50;37122:2;25469:5;25465:16;25458:5;25432:50;:::i;:::-;25488:71;25544:14;36708:9;25544:14;25530:12;25488:71;:::i;:::-;;37367:9;37361:4;37357:20;37351:3;37340:9;37336:19;37329:49;37392:84;37471:4;37462:6;37392:84;:::i;:::-;37384:92;36690:796;-1:-1;;;;;;;;;36690:796::o;37493:416::-;37693:2;37707:47;;;21124:2;37678:18;;;44798:19;-1:-1;;;44838:14;;;21140:44;21203:12;;;37664:245::o;37916:416::-;38116:2;38130:47;;;21454:2;38101:18;;;44798:19;-1:-1;;;44838:14;;;21470:33;21522:12;;;38087:245::o;38339:416::-;38539:2;38553:47;;;21773:2;38524:18;;;44798:19;-1:-1;;;44838:14;;;21789:38;21846:12;;;38510:245::o;38762:416::-;38962:2;38976:47;;;22097:2;38947:18;;;44798:19;-1:-1;;;44838:14;;;22113:36;22168:12;;;38933:245::o;39185:416::-;39385:2;39399:47;;;22419:2;39370:18;;;44798:19;-1:-1;;;44838:14;;;22435:36;22490:12;;;39356:245::o;39608:416::-;39808:2;39822:47;;;22741:2;39793:18;;;44798:19;22777:34;44838:14;;;22757:55;-1:-1;;;22832:12;;;22825:33;22877:12;;;39779:245::o;40031:416::-;40231:2;40245:47;;;23128:2;40216:18;;;44798:19;-1:-1;;;44838:14;;;23144:35;23198:12;;;40202:245::o;40454:416::-;40654:2;40668:47;;;23449:2;40639:18;;;44798:19;-1:-1;;;44838:14;;;23465:44;23528:12;;;40625:245::o;40877:416::-;41077:2;41091:47;;;23779:2;41062:18;;;44798:19;-1:-1;;;44838:14;;;23795:43;23857:12;;;41048:245::o;41300:416::-;41500:2;41514:47;;;24108:1;41485:18;;;44798:19;-1:-1;;;44838:14;;;24123:29;24171:12;;;41471:245::o;41952:444::-;18242:37;;;42299:2;42284:18;;18242:37;;;;42382:2;42367:18;;18242:37;42135:2;42120:18;;42106:290::o;42403:632::-;;49487:4;25931:5;49476:16;25910:3;25903:35;624:18;;49282:42;;;47999:5;49271:54;42789:2;42778:9;42774:18;15603:37;18272:5;42872:2;42861:9;42857:18;18242:37;42628:3;42909:2;42898:9;42894:18;42887:48;42949:76;42628:3;42617:9;42613:19;43011:6;42949:76;:::i;46091:119::-;;85:6;72:20;97:33;124:5;97:33;:::i;46219:517::-;;;46359:3;46346:17;47450:48;;46427:8;46411:14;46407:29;46403:48;46383:18;46379:73;46369:2;;-1:-1;;46456:12;46369:2;46485:33;;46442:4;46574:16;;;-1:-1;46540:19;;-1:-1;46610:18;46599:30;;46596:2;;;46642:1;;46632:12;46596:2;46442:4;46693:6;46689:17;46411:14;46669:38;46659:8;46655:53;46652:2;;;46721:1;;46711:12;48938:144;49008:16;51111:1;51101:12;;51091:2;;51117:9;50642:268;50707:1;50714:101;50728:6;50725:1;50722:13;50714:101;;;50795:11;;;50789:18;50776:11;;;50769:39;50750:2;50743:10;50714:101;;;50830:6;50827:1;50824:13;50821:2;;;-1:-1;;50707:1;50877:16;;50870:27;50691:219::o;51140:117::-;-1:-1;;;;;49271:54;;51199:35;;51189:2;;51248:1;;51238:12;51189:2;51183:74;:::o
Swarm Source
ipfs://2090042fc21907ac63f77ceec11558a7d53960443485b6181dd7961035147f5f
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 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.