Overview
ETH Balance
0.00198 ETH
Eth Value
$6.32 (@ $3,190.97/ETH)More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 59 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Load Token Card | 12100721 | 1330 days ago | IN | 0 ETH | 0.02089265 | ||||
Top Up Gas | 12100594 | 1330 days ago | IN | 0 ETH | 0.00687592 | ||||
Transfer | 12061100 | 1337 days ago | IN | 0.35 ETH | 0.00288676 | ||||
Load Token Card | 11798171 | 1377 days ago | IN | 0 ETH | 0.03424808 | ||||
Transfer | 11796996 | 1377 days ago | IN | 0.25 ETH | 0.01089488 | ||||
Load Token Card | 11666597 | 1397 days ago | IN | 0 ETH | 0.01401659 | ||||
Transfer | 11666585 | 1397 days ago | IN | 0.15 ETH | 0.00351654 | ||||
Load Token Card | 11658362 | 1398 days ago | IN | 0 ETH | 0.00793392 | ||||
Transfer | 11658347 | 1399 days ago | IN | 0.25 ETH | 0.00218665 | ||||
Load Token Card | 11648593 | 1400 days ago | IN | 0 ETH | 0.0079332 | ||||
Load Token Card | 11308648 | 1452 days ago | IN | 0 ETH | 0.00700829 | ||||
Transfer | 11308641 | 1452 days ago | IN | 0.25 ETH | 0.00246158 | ||||
Load Token Card | 11295748 | 1454 days ago | IN | 0 ETH | 0.01243093 | ||||
Top Up Gas | 11295729 | 1454 days ago | IN | 0 ETH | 0.00465081 | ||||
Transfer | 11295715 | 1454 days ago | IN | 1 ETH | 0.0033567 | ||||
Load Token Card | 11257615 | 1460 days ago | IN | 0 ETH | 0.0031865 | ||||
Transfer | 11257598 | 1460 days ago | IN | 1 ETH | 0.00089512 | ||||
Load Token Card | 11250884 | 1461 days ago | IN | 0 ETH | 0.00634713 | ||||
Transfer | 11250875 | 1461 days ago | IN | 3 ETH | 0.0022378 | ||||
Load Token Card | 11214832 | 1467 days ago | IN | 0 ETH | 0.0025124 | ||||
Transfer | 11214804 | 1467 days ago | IN | 0.4 ETH | 0.00089512 | ||||
Load Token Card | 11125740 | 1480 days ago | IN | 0 ETH | 0.00276339 | ||||
Transfer | 11120484 | 1481 days ago | IN | 0.82112277 ETH | 0.00134268 | ||||
Load Token Card | 10351067 | 1600 days ago | IN | 0 ETH | 0.00396732 | ||||
Top Up Gas | 10351061 | 1600 days ago | IN | 0 ETH | 0.0013263 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
12100721 | 1330 days ago | 0.20402 ETH | ||||
12100594 | 1330 days ago | 0.144 ETH | ||||
11798171 | 1377 days ago | 0.2522365 ETH | ||||
11666597 | 1397 days ago | 0.153015 ETH | ||||
11658362 | 1398 days ago | 0.244824 ETH | ||||
11648593 | 1400 days ago | 0.0153015 ETH | ||||
11308648 | 1452 days ago | 0.234623 ETH | ||||
11295748 | 1454 days ago | 0.90868 ETH | ||||
11295729 | 1454 days ago | 0.09131999 ETH | ||||
11257615 | 1460 days ago | 1 ETH | ||||
11250884 | 1461 days ago | 3.01740477 ETH | ||||
11214832 | 1467 days ago | 0.387638 ETH | ||||
11125740 | 1480 days ago | 0.81608 ETH | ||||
10351067 | 1600 days ago | 24.10513 ETH | ||||
10351061 | 1600 days ago | 0.01 ETH | ||||
10311238 | 1606 days ago | 0.01 ETH | ||||
10311233 | 1606 days ago | 8.87487 ETH | ||||
10264065 | 1613 days ago | 6.190805 ETH | ||||
10264058 | 1613 days ago | 0.01 ETH | ||||
10256497 | 1614 days ago | 0.005 ETH | ||||
10083370 | 1641 days ago | 1.989195 ETH | ||||
10083363 | 1641 days ago | 0.005 ETH | ||||
10049771 | 1647 days ago | 16.003322 ETH | ||||
9941809 | 1663 days ago | 0.999698 ETH | ||||
9780266 | 1688 days ago | 9.99698 ETH |
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0xA8E7213d...DcF722dC4 The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
Wallet
Compiler Version
v0.5.10+commit.5a6ea5b1
Contract Source Code (Solidity)
/** *Submitted for verification at Etherscan.io on 2019-08-14 */ /** * The Consumer Contract Wallet * Copyright (C) 2019 The Contract Wallet Company Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ pragma solidity ^0.5.10; contract Ownable { event TransferredOwnership(address _from, address _to); event LockedOwnership(address _locked); address payable private _owner; bool private _isTransferable; /// @notice Constructor sets the original owner of the contract and whether or not it is one time transferable. constructor(address payable _account_, bool _transferable_) internal { _owner = _account_; _isTransferable = _transferable_; // Emit the LockedOwnership event if no longer transferable. if (!_isTransferable) { emit LockedOwnership(_account_); } emit TransferredOwnership(address(0), _account_); } /// @notice Reverts if called by any account other than the owner. modifier onlyOwner() { require(_isOwner(msg.sender), "sender is not an owner"); _; } /// @notice Allows the current owner to transfer control of the contract to a new address. /// @param _account address to transfer ownership to. /// @param _transferable indicates whether to keep the ownership transferable. function transferOwnership(address payable _account, bool _transferable) external onlyOwner { // Require that the ownership is transferable. require(_isTransferable, "ownership is not transferable"); // Require that the new owner is not the zero address. require(_account != address(0), "owner cannot be set to zero address"); // Set the transferable flag to the value _transferable passed in. _isTransferable = _transferable; // Emit the LockedOwnership event if no longer transferable. if (!_transferable) { emit LockedOwnership(_account); } // Emit the ownership transfer event. emit TransferredOwnership(_owner, _account); // Set the owner to the provided address. _owner = _account; } /// @notice check if the ownership is transferable. /// @return true if the ownership is transferable. function isTransferable() external view returns (bool) { return _isTransferable; } /// @notice Allows the current owner to relinquish control of the contract. /// @dev Renouncing to ownership will leave the contract without an owner and unusable. /// @dev It will not be possible to call the functions with the `onlyOwner` modifier anymore. function renounceOwnership() external onlyOwner { // Require that the ownership is transferable. require(_isTransferable, "ownership is not transferable"); // note that this could be terminal _owner = address(0); emit TransferredOwnership(_owner, address(0)); } /// @notice Find out owner address /// @return address of the owner. function owner() public view returns (address payable) { return _owner; } /// @notice Check if owner address /// @return true if sender is the owner of the contract. function _isOwner(address _address) internal view returns (bool) { return _address == _owner; } } library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SafeMath: subtraction overflow"); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0, "SafeMath: division by zero"); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0, "SafeMath: modulo by zero"); return a % b; } } contract ResolverBase { bytes4 private constant INTERFACE_META_ID = 0x01ffc9a7; function supportsInterface(bytes4 interfaceID) public pure returns(bool) { return interfaceID == INTERFACE_META_ID; } function isAuthorised(bytes32 node) internal view returns(bool); modifier authorised(bytes32 node) { require(isAuthorised(node)); _; } } 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 (uint(self) & 0xffffffffffffffffffffffffffffffff == 0) { ret += 16; self = bytes32(uint(self) / 0x100000000000000000000000000000000); } if (uint(self) & 0xffffffffffffffff == 0) { ret += 8; self = bytes32(uint(self) / 0x10000000000000000); } if (uint(self) & 0xffffffff == 0) { ret += 4; self = bytes32(uint(self) / 0x100000000); } if (uint(self) & 0xffff == 0) { ret += 2; self = bytes32(uint(self) / 0x10000); } if (uint(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 know 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; } } interface ERC165 { function supportsInterface(bytes4) external view returns (bool); } interface ENS { // 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); } library Address { /** * @dev Returns true if `account` is a contract. * * This test is non-exhaustive, and there may be false-negatives: during the * execution of a contract's constructor, its address will be reported as * not containing a contract. * * > It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. */ function isContract(address account) internal view returns (bool) { // This method relies in extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } } interface ERC20 { function allowance(address _owner, address _spender) external view returns (uint256); function approve(address _spender, uint256 _value) external returns (bool); function balanceOf(address _who) external view returns (uint256); function totalSupply() external view returns (uint256); function transfer(address _to, uint256 _value) external returns (bool); function transferFrom(address _from, address _to, uint256 _value) external returns (bool); } contract AddrResolver is ResolverBase { bytes4 constant private ADDR_INTERFACE_ID = 0x3b3b57de; event AddrChanged(bytes32 indexed node, address a); mapping(bytes32=>address) addresses; /** * Sets the address associated with an ENS node. * May only be called by the owner of that node in the ENS registry. * @param node The node to update. * @param addr The address to set. */ function setAddr(bytes32 node, address addr) external authorised(node) { addresses[node] = addr; emit AddrChanged(node, addr); } /** * Returns the address associated with an ENS node. * @param node The ENS node to query. * @return The associated address. */ function addr(bytes32 node) public view returns (address) { return addresses[node]; } function supportsInterface(bytes4 interfaceID) public pure returns(bool) { return interfaceID == ADDR_INTERFACE_ID || super.supportsInterface(interfaceID); } } contract ContentHashResolver is ResolverBase { bytes4 constant private CONTENT_HASH_INTERFACE_ID = 0xbc1c58d1; event ContenthashChanged(bytes32 indexed node, bytes hash); mapping(bytes32=>bytes) hashes; /** * Sets the contenthash associated with an ENS node. * May only be called by the owner of that node in the ENS registry. * @param node The node to update. * @param hash The contenthash to set */ function setContenthash(bytes32 node, bytes calldata hash) external authorised(node) { hashes[node] = hash; emit ContenthashChanged(node, hash); } /** * Returns the contenthash associated with an ENS node. * @param node The ENS node to query. * @return The associated contenthash. */ function contenthash(bytes32 node) external view returns (bytes memory) { return hashes[node]; } function supportsInterface(bytes4 interfaceID) public pure returns(bool) { return interfaceID == CONTENT_HASH_INTERFACE_ID || super.supportsInterface(interfaceID); } } contract NameResolver is ResolverBase { bytes4 constant private NAME_INTERFACE_ID = 0x691f3431; event NameChanged(bytes32 indexed node, string name); mapping(bytes32=>string) names; /** * Sets the name associated with an ENS node, for reverse records. * May only be called by the owner of that node in the ENS registry. * @param node The node to update. * @param name The name to set. */ function setName(bytes32 node, string calldata name) external authorised(node) { names[node] = name; emit NameChanged(node, name); } /** * Returns the name associated with an ENS node, for reverse records. * Defined in EIP181. * @param node The ENS node to query. * @return The associated name. */ function name(bytes32 node) external view returns (string memory) { return names[node]; } function supportsInterface(bytes4 interfaceID) public pure returns(bool) { return interfaceID == NAME_INTERFACE_ID || super.supportsInterface(interfaceID); } } contract ABIResolver is ResolverBase { bytes4 constant private ABI_INTERFACE_ID = 0x2203ab56; event ABIChanged(bytes32 indexed node, uint256 indexed contentType); mapping(bytes32=>mapping(uint256=>bytes)) abis; /** * Sets the ABI associated with an ENS node. * Nodes may have one ABI of each content type. To remove an ABI, set it to * the empty string. * @param node The node to update. * @param contentType The content type of the ABI * @param data The ABI data. */ function setABI(bytes32 node, uint256 contentType, bytes calldata data) external authorised(node) { // Content types must be powers of 2 require(((contentType - 1) & contentType) == 0); abis[node][contentType] = data; emit ABIChanged(node, contentType); } /** * Returns the ABI associated with an ENS node. * Defined in EIP205. * @param node The ENS node to query * @param contentTypes A bitwise OR of the ABI formats accepted by the caller. * @return contentType The content type of the return value * @return data The ABI data */ function ABI(bytes32 node, uint256 contentTypes) external view returns (uint256, bytes memory) { mapping(uint256=>bytes) storage abiset = abis[node]; for (uint256 contentType = 1; contentType <= contentTypes; contentType <<= 1) { if ((contentType & contentTypes) != 0 && abiset[contentType].length > 0) { return (contentType, abiset[contentType]); } } return (0, bytes("")); } function supportsInterface(bytes4 interfaceID) public pure returns(bool) { return interfaceID == ABI_INTERFACE_ID || super.supportsInterface(interfaceID); } } library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer(ERC20 token, address to, uint256 value) internal { callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(ERC20 token, address from, address to, uint256 value) internal { callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } function safeApprove(ERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length require((value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(ERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(ERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function callOptionalReturn(ERC20 token, bytes memory data) internal { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. // A Solidity high level call has three parts: // 1. The target address is checked to verify it contains contract code // 2. The call itself is made, and success asserted // 3. The return value is decoded, which in turn checks the size of the returned data. // solhint-disable-next-line max-line-length require(address(token).isContract(), "SafeERC20: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = address(token).call(data); require(success, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } library BytesUtils { using SafeMath for uint256; /// @dev This function converts to an address /// @param _bts bytes /// @param _from start position function _bytesToAddress(bytes memory _bts, uint _from) internal pure returns (address) { require(_bts.length >= _from.add(20), "slicing out of range"); bytes20 convertedAddress; uint startByte = _from.add(32); //first 32 bytes denote the array length assembly { convertedAddress := mload(add(_bts, startByte)) } return address(convertedAddress); } /// @dev This function slices bytes into bytes4 /// @param _bts some bytes /// @param _from start position function _bytesToBytes4(bytes memory _bts, uint _from) internal pure returns (bytes4) { require(_bts.length >= _from.add(4), "slicing out of range"); bytes4 slicedBytes4; uint startByte = _from.add(32); //first 32 bytes denote the array length assembly { slicedBytes4 := mload(add(_bts, startByte)) } return slicedBytes4; } /// @dev This function slices a uint /// @param _bts some bytes /// @param _from start position // credit to https://ethereum.stackexchange.com/questions/51229/how-to-convert-bytes-to-uint-in-solidity // and Nick Johnson https://ethereum.stackexchange.com/questions/4170/how-to-convert-a-uint-to-bytes-in-solidity/4177#4177 function _bytesToUint256(bytes memory _bts, uint _from) internal pure returns (uint) { require(_bts.length >= _from.add(32), "slicing out of range"); uint convertedUint256; uint startByte = _from.add(32); //first 32 bytes denote the array length assembly { convertedUint256 := mload(add(_bts, startByte)) } return convertedUint256; } } contract Balanceable { /// @dev This function is used to get a balance /// @param _address of which balance we are trying to ascertain /// @param _asset is the address of an ERC20 token or 0x0 for ether. /// @return balance associated with an address, for any token, in the wei equivalent function _balance(address _address, address _asset) internal view returns (uint) { if (_asset != address(0)) { return ERC20(_asset).balanceOf(_address); } else { return _address.balance; } } } contract PubkeyResolver is ResolverBase { bytes4 constant private PUBKEY_INTERFACE_ID = 0xc8690233; event PubkeyChanged(bytes32 indexed node, bytes32 x, bytes32 y); struct PublicKey { bytes32 x; bytes32 y; } mapping(bytes32=>PublicKey) pubkeys; /** * Sets the SECP256k1 public key associated with an ENS node. * @param node The ENS node to query * @param x the X coordinate of the curve point for the public key. * @param y the Y coordinate of the curve point for the public key. */ function setPubkey(bytes32 node, bytes32 x, bytes32 y) external authorised(node) { pubkeys[node] = PublicKey(x, y); emit PubkeyChanged(node, x, y); } /** * Returns the SECP256k1 public key associated with an ENS node. * Defined in EIP 619. * @param node The ENS node to query * @return x, y the X and Y coordinates of the curve point for the public key. */ function pubkey(bytes32 node) external view returns (bytes32 x, bytes32 y) { return (pubkeys[node].x, pubkeys[node].y); } function supportsInterface(bytes4 interfaceID) public pure returns(bool) { return interfaceID == PUBKEY_INTERFACE_ID || super.supportsInterface(interfaceID); } } contract TextResolver is ResolverBase { bytes4 constant private TEXT_INTERFACE_ID = 0x59d1d43c; event TextChanged(bytes32 indexed node, string indexedKey, string key); mapping(bytes32=>mapping(string=>string)) texts; /** * Sets the text data associated with an ENS node and key. * May only be called by the owner of that node in the ENS registry. * @param node The node to update. * @param key The key to set. * @param value The text data value to set. */ function setText(bytes32 node, string calldata key, string calldata value) external authorised(node) { texts[node][key] = value; emit TextChanged(node, key, key); } /** * Returns the text data associated with an ENS node and key. * @param node The ENS node to query. * @param key The text data key to query. * @return The associated text data. */ function text(bytes32 node, string calldata key) external view returns (string memory) { return texts[node][key]; } function supportsInterface(bytes4 interfaceID) public pure returns(bool) { return interfaceID == TEXT_INTERFACE_ID || super.supportsInterface(interfaceID); } } contract Transferrable { using SafeERC20 for ERC20; /// @dev This function is used to move tokens sent accidentally to this contract method. /// @dev The owner can chose the new destination address /// @param _to is the recipient's address. /// @param _asset is the address of an ERC20 token or 0x0 for ether. /// @param _amount is the amount to be transferred in base units. function _safeTransfer(address payable _to, address _asset, uint _amount) internal { // address(0) is used to denote ETH if (_asset == address(0)) { _to.transfer(_amount); } else { ERC20(_asset).safeTransfer(_to, _amount); } } } contract InterfaceResolver is ResolverBase, AddrResolver { bytes4 constant private INTERFACE_INTERFACE_ID = bytes4(keccak256("interfaceImplementer(bytes32,bytes4)")); bytes4 private constant INTERFACE_META_ID = 0x01ffc9a7; event InterfaceChanged(bytes32 indexed node, bytes4 indexed interfaceID, address implementer); mapping(bytes32=>mapping(bytes4=>address)) interfaces; /** * Sets an interface associated with a name. * Setting the address to 0 restores the default behaviour of querying the contract at `addr()` for interface support. * @param node The node to update. * @param interfaceID The EIP 168 interface ID. * @param implementer The address of a contract that implements this interface for this node. */ function setInterface(bytes32 node, bytes4 interfaceID, address implementer) external authorised(node) { interfaces[node][interfaceID] = implementer; emit InterfaceChanged(node, interfaceID, implementer); } /** * Returns the address of a contract that implements the specified interface for this name. * If an implementer has not been set for this interfaceID and name, the resolver will query * the contract at `addr()`. If `addr()` is set, a contract exists at that address, and that * contract implements EIP168 and returns `true` for the specified interfaceID, its address * will be returned. * @param node The ENS node to query. * @param interfaceID The EIP 168 interface ID to check for. * @return The address that implements this interface, or 0 if the interface is unsupported. */ function interfaceImplementer(bytes32 node, bytes4 interfaceID) external view returns (address) { address implementer = interfaces[node][interfaceID]; if(implementer != address(0)) { return implementer; } address a = addr(node); if(a == address(0)) { return address(0); } (bool success, bytes memory returnData) = a.staticcall(abi.encodeWithSignature("supportsInterface(bytes4)", INTERFACE_META_ID)); if(!success || returnData.length < 32 || returnData[31] == 0) { // EIP 168 not supported by target return address(0); } (success, returnData) = a.staticcall(abi.encodeWithSignature("supportsInterface(bytes4)", interfaceID)); if(!success || returnData.length < 32 || returnData[31] == 0) { // Specified interface not supported by target return address(0); } return a; } function supportsInterface(bytes4 interfaceID) public pure returns(bool) { return interfaceID == INTERFACE_INTERFACE_ID || super.supportsInterface(interfaceID); } } interface IController { function isController(address) external view returns (bool); function isAdmin(address) external view returns (bool); } /// @title Controller stores a list of controller addresses that can be used for authentication in other contracts. /// @notice The Controller implements a hierarchy of concepts, Owner, Admin, and the Controllers. /// @dev Owner can change the Admins /// @dev Admins and can the Controllers /// @dev Controllers are used by the application. contract Controller is IController, Ownable, Transferrable { event AddedController(address _sender, address _controller); event RemovedController(address _sender, address _controller); event AddedAdmin(address _sender, address _admin); event RemovedAdmin(address _sender, address _admin); event Claimed(address _to, address _asset, uint _amount); event Stopped(address _sender); event Started(address _sender); mapping (address => bool) private _isAdmin; uint private _adminCount; mapping (address => bool) private _isController; uint private _controllerCount; bool private _stopped; /// @notice Constructor initializes the owner with the provided address. /// @param _ownerAddress_ address of the owner. constructor(address payable _ownerAddress_) Ownable(_ownerAddress_, false) public {} /// @notice Checks if message sender is an admin. modifier onlyAdmin() { require(isAdmin(msg.sender), "sender is not an admin"); _; } /// @notice Check if Owner or Admin modifier onlyAdminOrOwner() { require(_isOwner(msg.sender) || isAdmin(msg.sender), "sender is not an admin"); _; } /// @notice Check if controller is stopped modifier notStopped() { require(!isStopped(), "controller is stopped"); _; } /// @notice Add a new admin to the list of admins. /// @param _account address to add to the list of admins. function addAdmin(address _account) external onlyOwner notStopped { _addAdmin(_account); } /// @notice Remove a admin from the list of admins. /// @param _account address to remove from the list of admins. function removeAdmin(address _account) external onlyOwner { _removeAdmin(_account); } /// @return the current number of admins. function adminCount() external view returns (uint) { return _adminCount; } /// @notice Add a new controller to the list of controllers. /// @param _account address to add to the list of controllers. function addController(address _account) external onlyAdminOrOwner notStopped { _addController(_account); } /// @notice Remove a controller from the list of controllers. /// @param _account address to remove from the list of controllers. function removeController(address _account) external onlyAdminOrOwner { _removeController(_account); } /// @notice count the Controllers /// @return the current number of controllers. function controllerCount() external view returns (uint) { return _controllerCount; } /// @notice is an address an Admin? /// @return true if the provided account is an admin. function isAdmin(address _account) public view notStopped returns (bool) { return _isAdmin[_account]; } /// @notice is an address a Controller? /// @return true if the provided account is a controller. function isController(address _account) public view notStopped returns (bool) { return _isController[_account]; } /// @notice this function can be used to see if the controller has been stopped /// @return true is the Controller has been stopped function isStopped() public view returns (bool) { return _stopped; } /// @notice Internal-only function that adds a new admin. function _addAdmin(address _account) private { require(!_isAdmin[_account], "provided account is already an admin"); require(!_isController[_account], "provided account is already a controller"); require(!_isOwner(_account), "provided account is already the owner"); require(_account != address(0), "provided account is the zero address"); _isAdmin[_account] = true; _adminCount++; emit AddedAdmin(msg.sender, _account); } /// @notice Internal-only function that removes an existing admin. function _removeAdmin(address _account) private { require(_isAdmin[_account], "provided account is not an admin"); _isAdmin[_account] = false; _adminCount--; emit RemovedAdmin(msg.sender, _account); } /// @notice Internal-only function that adds a new controller. function _addController(address _account) private { require(!_isAdmin[_account], "provided account is already an admin"); require(!_isController[_account], "provided account is already a controller"); require(!_isOwner(_account), "provided account is already the owner"); require(_account != address(0), "provided account is the zero address"); _isController[_account] = true; _controllerCount++; emit AddedController(msg.sender, _account); } /// @notice Internal-only function that removes an existing controller. function _removeController(address _account) private { require(_isController[_account], "provided account is not a controller"); _isController[_account] = false; _controllerCount--; emit RemovedController(msg.sender, _account); } /// @notice stop our controllers and admins from being useable function stop() external onlyAdminOrOwner { _stopped = true; emit Stopped(msg.sender); } /// @notice start our controller again function start() external onlyOwner { _stopped = false; emit Started(msg.sender); } //// @notice Withdraw tokens from the smart contract to the specified account. function claim(address payable _to, address _asset, uint _amount) external onlyAdmin notStopped { _safeTransfer(_to, _asset, _amount); emit Claimed(_to, _asset, _amount); } } contract PublicResolver is ABIResolver, AddrResolver, ContentHashResolver, InterfaceResolver, NameResolver, PubkeyResolver, TextResolver { ENS ens; /** * A mapping of authorisations. An address that is authorised for a name * may make any changes to the name that the owner could, but may not update * the set of authorisations. * (node, owner, caller) => isAuthorised */ mapping(bytes32=>mapping(address=>mapping(address=>bool))) public authorisations; event AuthorisationChanged(bytes32 indexed node, address indexed owner, address indexed target, bool isAuthorised); constructor(ENS _ens) public { ens = _ens; } /** * @dev Sets or clears an authorisation. * Authorisations are specific to the caller. Any account can set an authorisation * for any name, but the authorisation that is checked will be that of the * current owner of a name. Thus, transferring a name effectively clears any * existing authorisations, and new authorisations can be set in advance of * an ownership transfer if desired. * * @param node The name to change the authorisation on. * @param target The address that is to be authorised or deauthorised. * @param isAuthorised True if the address should be authorised, or false if it should be deauthorised. */ function setAuthorisation(bytes32 node, address target, bool isAuthorised) external { authorisations[node][msg.sender][target] = isAuthorised; emit AuthorisationChanged(node, msg.sender, target, isAuthorised); } function isAuthorised(bytes32 node) internal view returns(bool) { address owner = ens.owner(node); return owner == msg.sender || authorisations[node][owner][msg.sender]; } } contract ENSResolvable { /// @notice _ens is an instance of ENS ENS private _ens; /// @notice _ensRegistry points to the ENS registry smart contract. address private _ensRegistry; /// @param _ensReg_ is the ENS registry used constructor(address _ensReg_) internal { _ensRegistry = _ensReg_; _ens = ENS(_ensRegistry); } /// @notice this is used to that one can observe which ENS registry is being used function ensRegistry() external view returns (address) { return _ensRegistry; } /// @notice helper function used to get the address of a node /// @param _node of the ENS entry that needs resolving /// @return the address of the said node function _ensResolve(bytes32 _node) internal view returns (address) { return PublicResolver(_ens.resolver(_node)).addr(_node); } } contract Controllable is ENSResolvable { /// @dev Is the registered ENS node identifying the controller contract. bytes32 private _controllerNode; /// @notice Constructor initializes the controller contract object. /// @param _controllerNode_ is the ENS node of the Controller. constructor(bytes32 _controllerNode_) internal { _controllerNode = _controllerNode_; } /// @notice Checks if message sender is a controller. modifier onlyController() { require(_isController(msg.sender), "sender is not a controller"); _; } /// @notice Checks if message sender is an admin. modifier onlyAdmin() { require(_isAdmin(msg.sender), "sender is not an admin"); _; } /// @return the controller node registered in ENS. function controllerNode() external view returns (bytes32) { return _controllerNode; } /// @return true if the provided account is a controller. function _isController(address _account) internal view returns (bool) { return IController(_ensResolve(_controllerNode)).isController(_account); } /// @return true if the provided account is an admin. function _isAdmin(address _account) internal view returns (bool) { return IController(_ensResolve(_controllerNode)).isAdmin(_account); } } interface ILicence { function load(address, uint) external payable; function updateLicenceAmount(uint) external; } /// @title Licence loads the TokenCard and transfers the licence amout to the TKN Holder Contract. /// @notice the rest of the amount gets sent to the CryptoFloat contract Licence is Transferrable, ENSResolvable, Controllable { using SafeMath for uint256; using SafeERC20 for ERC20; /*******************/ /* Events */ /*****************/ event UpdatedLicenceDAO(address _newDAO); event UpdatedCryptoFloat(address _newFloat); event UpdatedTokenHolder(address _newHolder); event UpdatedTKNContractAddress(address _newTKN); event UpdatedLicenceAmount(uint _newAmount); event TransferredToTokenHolder(address _from, address _to, address _asset, uint _amount); event TransferredToCryptoFloat(address _from, address _to, address _asset, uint _amount); event Claimed(address _to, address _asset, uint _amount); /// @notice This is 100% scaled up by a factor of 10 to give us an extra 1 decimal place of precision uint constant public MAX_AMOUNT_SCALE = 1000; uint constant public MIN_AMOUNT_SCALE = 1; address private _tknContractAddress = 0xaAAf91D9b90dF800Df4F55c205fd6989c977E73a; // solium-disable-line uppercase address payable private _cryptoFloat; address payable private _tokenHolder; address private _licenceDAO; bool private _lockedCryptoFloat; bool private _lockedTokenHolder; bool private _lockedLicenceDAO; bool private _lockedTKNContractAddress; /// @notice This is the _licenceAmountScaled by a factor of 10 /// @dev i.e. 1% is 10 _licenceAmountScaled, 0.1% is 1 _licenceAmountScaled uint private _licenceAmountScaled; /// @notice Reverts if called by any address other than the DAO contract. modifier onlyDAO() { require(msg.sender == _licenceDAO, "the sender isn't the DAO"); _; } /// @notice Constructor initializes the card licence contract. /// @param _licence_ is the initial card licence amount. this number is scaled 10 = 1%, 9 = 0.9% /// @param _float_ is the address of the multi-sig cryptocurrency float contract. /// @param _holder_ is the address of the token holder contract /// @param _tknAddress_ is the address of the TKN ERC20 contract /// @param _ens_ is the address of the ENS Registry /// @param _controllerNode_ is the ENS node corresponding to the controller constructor(uint _licence_, address payable _float_, address payable _holder_, address _tknAddress_, address _ens_, bytes32 _controllerNode_) ENSResolvable(_ens_) Controllable(_controllerNode_) public { require(MIN_AMOUNT_SCALE <= _licence_ && _licence_ <= MAX_AMOUNT_SCALE, "licence amount out of range"); _licenceAmountScaled = _licence_; _cryptoFloat = _float_; _tokenHolder = _holder_; if (_tknAddress_ != address(0)) { _tknContractAddress = _tknAddress_; } } /// @notice Ether can be deposited from any source, so this contract should be payable by anyone. function() external payable {} /// @notice this allows for people to see the scaled licence amount /// @return the scaled licence amount, used to calculate the split when loading. function licenceAmountScaled() external view returns (uint) { return _licenceAmountScaled; } /// @notice allows one to see the address of the CryptoFloat /// @return the address of the multi-sig cryptocurrency float contract. function cryptoFloat() external view returns (address) { return _cryptoFloat; } /// @notice allows one to see the address TKN holder contract /// @return the address of the token holder contract. function tokenHolder() external view returns (address) { return _tokenHolder; } /// @notice allows one to see the address of the DAO /// @return the address of the DAO contract. function licenceDAO() external view returns (address) { return _licenceDAO; } /// @notice The address of the TKN token /// @return the address of the TKN contract. function tknContractAddress() external view returns (address) { return _tknContractAddress; } /// @notice This locks the cryptoFloat address /// @dev so that it can no longer be updated function lockFloat() external onlyAdmin { _lockedCryptoFloat = true; } /// @notice This locks the TokenHolder address /// @dev so that it can no longer be updated function lockHolder() external onlyAdmin { _lockedTokenHolder = true; } /// @notice This locks the DAO address /// @dev so that it can no longer be updated function lockLicenceDAO() external onlyAdmin { _lockedLicenceDAO = true; } /// @notice This locks the TKN address /// @dev so that it can no longer be updated function lockTKNContractAddress() external onlyAdmin { _lockedTKNContractAddress = true; } /// @notice Updates the address of the cyptoFloat. /// @param _newFloat This is the new address for the CryptoFloat function updateFloat(address payable _newFloat) external onlyAdmin { require(!floatLocked(), "float is locked"); _cryptoFloat = _newFloat; emit UpdatedCryptoFloat(_newFloat); } /// @notice Updates the address of the Holder contract. /// @param _newHolder This is the new address for the TokenHolder function updateHolder(address payable _newHolder) external onlyAdmin { require(!holderLocked(), "holder contract is locked"); _tokenHolder = _newHolder; emit UpdatedTokenHolder(_newHolder); } /// @notice Updates the address of the DAO contract. /// @param _newDAO This is the new address for the Licence DAO function updateLicenceDAO(address _newDAO) external onlyAdmin { require(!licenceDAOLocked(), "DAO is locked"); _licenceDAO = _newDAO; emit UpdatedLicenceDAO(_newDAO); } /// @notice Updates the address of the TKN contract. /// @param _newTKN This is the new address for the TKN contract function updateTKNContractAddress(address _newTKN) external onlyAdmin { require(!tknContractAddressLocked(), "TKN is locked"); _tknContractAddress = _newTKN; emit UpdatedTKNContractAddress(_newTKN); } /// @notice Updates the TKN licence amount /// @param _newAmount is a number between MIN_AMOUNT_SCALE (1) and MAX_AMOUNT_SCALE function updateLicenceAmount(uint _newAmount) external onlyDAO { require(MIN_AMOUNT_SCALE <= _newAmount && _newAmount <= MAX_AMOUNT_SCALE, "licence amount out of range"); _licenceAmountScaled = _newAmount; emit UpdatedLicenceAmount(_newAmount); } /// @notice Load the holder and float contracts based on the licence amount and asset amount. /// @param _asset is the address of an ERC20 token or 0x0 for ether. /// @param _amount is the amount of assets to be transferred including the licence amount. function load(address _asset, uint _amount) external payable { uint loadAmount = _amount; // If TKN then no licence to be paid if (_asset == _tknContractAddress) { ERC20(_asset).safeTransferFrom(msg.sender, _cryptoFloat, loadAmount); } else { loadAmount = _amount.mul(MAX_AMOUNT_SCALE).div(_licenceAmountScaled + MAX_AMOUNT_SCALE); uint licenceAmount = _amount.sub(loadAmount); if (_asset != address(0)) { ERC20(_asset).safeTransferFrom(msg.sender, _tokenHolder, licenceAmount); ERC20(_asset).safeTransferFrom(msg.sender, _cryptoFloat, loadAmount); } else { require(msg.value == _amount, "ETH sent is not equal to amount"); _tokenHolder.transfer(licenceAmount); _cryptoFloat.transfer(loadAmount); } emit TransferredToTokenHolder(msg.sender, _tokenHolder, _asset, licenceAmount); } emit TransferredToCryptoFloat(msg.sender, _cryptoFloat, _asset, loadAmount); } //// @notice Withdraw tokens from the smart contract to the specified account. function claim(address payable _to, address _asset, uint _amount) external onlyAdmin { _safeTransfer(_to, _asset, _amount); emit Claimed(_to, _asset, _amount); } /// @notice returns whether or not the CryptoFloat address is locked function floatLocked() public view returns (bool) { return _lockedCryptoFloat; } /// @notice returns whether or not the TokenHolder address is locked function holderLocked() public view returns (bool) { return _lockedTokenHolder; } /// @notice returns whether or not the Licence DAO address is locked function licenceDAOLocked() public view returns (bool) { return _lockedLicenceDAO; } /// @notice returns whether or not the TKN address is locked function tknContractAddressLocked() public view returns (bool) { return _lockedTKNContractAddress; } } interface ITokenWhitelist { function getTokenInfo(address) external view returns (string memory, uint256, uint256, bool, bool, bool, uint256); function getStablecoinInfo() external view returns (string memory, uint256, uint256, bool, bool, bool, uint256); function tokenAddressArray() external view returns (address[] memory); function redeemableTokens() external view returns (address[] memory); function methodIdWhitelist(bytes4) external view returns (bool); function getERC20RecipientAndAmount(address, bytes calldata) external view returns (address, uint); function stablecoin() external view returns (address); function updateTokenRate(address, uint, uint) external; } /// @title TokenWhitelist stores a list of tokens used by the Consumer Contract Wallet, the Oracle, the TKN Holder and the TKN Licence Contract contract TokenWhitelist is ENSResolvable, Controllable, Transferrable { using strings for *; using SafeMath for uint256; using BytesUtils for bytes; event UpdatedTokenRate(address _sender, address _token, uint _rate); event UpdatedTokenLoadable(address _sender, address _token, bool _loadable); event UpdatedTokenRedeemable(address _sender, address _token, bool _redeemable); event AddedToken(address _sender, address _token, string _symbol, uint _magnitude, bool _loadable, bool _redeemable); event RemovedToken(address _sender, address _token); event AddedMethodId(bytes4 _methodId); event RemovedMethodId(bytes4 _methodId); event AddedExclusiveMethod(address _token, bytes4 _methodId); event RemovedExclusiveMethod(address _token, bytes4 _methodId); event Claimed(address _to, address _asset, uint _amount); /// @dev these are the methods whitelisted by default in executeTransaction() for protected tokens bytes4 private constant _APPROVE = 0x095ea7b3; // keccak256(approve(address,uint256)) => 0x095ea7b3 bytes4 private constant _BURN = 0x42966c68; // keccak256(burn(uint256)) => 0x42966c68 bytes4 private constant _TRANSFER= 0xa9059cbb; // keccak256(transfer(address,uint256)) => 0xa9059cbb bytes4 private constant _TRANSFER_FROM = 0x23b872dd; // keccak256(transferFrom(address,address,uint256)) => 0x23b872dd struct Token { string symbol; // Token symbol uint magnitude; // 10^decimals uint rate; // Token exchange rate in wei bool available; // Flags if the token is available or not bool loadable; // Flags if token is loadable to the TokenCard bool redeemable; // Flags if token is redeemable in the TKN Holder contract uint lastUpdate; // Time of the last rate update } mapping(address => Token) private _tokenInfoMap; // @notice specifies whitelisted methodIds for protected tokens in wallet's excuteTranaction() e.g. keccak256(transfer(address,uint256)) => 0xa9059cbb mapping(bytes4 => bool) private _methodIdWhitelist; address[] private _tokenAddressArray; /// @notice keeping track of how many redeemable tokens are in the tokenWhitelist uint private _redeemableCounter; /// @notice Address of the stablecoin. address private _stablecoin; /// @notice is registered ENS node identifying the oracle contract. bytes32 private _oracleNode; /// @notice Constructor initializes ENSResolvable, and Controllable. /// @param _ens_ is the ENS registry address. /// @param _oracleNode_ is the ENS node of the Oracle. /// @param _controllerNode_ is our Controllers node. /// @param _stablecoinAddress_ is the address of the stablecoint used by the wallet for the card load limit. constructor(address _ens_, bytes32 _oracleNode_, bytes32 _controllerNode_, address _stablecoinAddress_) ENSResolvable(_ens_) Controllable(_controllerNode_) public { _oracleNode = _oracleNode_; _stablecoin = _stablecoinAddress_; //a priori ERC20 whitelisted methods _methodIdWhitelist[_APPROVE] = true; _methodIdWhitelist[_BURN] = true; _methodIdWhitelist[_TRANSFER] = true; _methodIdWhitelist[_TRANSFER_FROM] = true; } modifier onlyAdminOrOracle() { address oracleAddress = _ensResolve(_oracleNode); require (_isAdmin(msg.sender) || msg.sender == oracleAddress, "either oracle or admin"); _; } /// @notice Add ERC20 tokens to the list of whitelisted tokens. /// @param _tokens ERC20 token contract addresses. /// @param _symbols ERC20 token names. /// @param _magnitude 10 to the power of number of decimal places used by each ERC20 token. /// @param _loadable is a bool that states whether or not a token is loadable to the TokenCard. /// @param _redeemable is a bool that states whether or not a token is redeemable in the TKN Holder Contract. /// @param _lastUpdate is a unit representing an ISO datetime e.g. 20180913153211. function addTokens(address[] calldata _tokens, bytes32[] calldata _symbols, uint[] calldata _magnitude, bool[] calldata _loadable, bool[] calldata _redeemable, uint _lastUpdate) external onlyAdmin { // Require that all parameters have the same length. require(_tokens.length == _symbols.length && _tokens.length == _magnitude.length && _tokens.length == _loadable.length && _tokens.length == _loadable.length, "parameter lengths do not match"); // Add each token to the list of supported tokens. for (uint i = 0; i < _tokens.length; i++) { // Require that the token isn't already available. require(!_tokenInfoMap[_tokens[i]].available, "token already available"); // Store the intermediate values. string memory symbol = _symbols[i].toSliceB32().toString(); // Add the token to the token list. _tokenInfoMap[_tokens[i]] = Token({ symbol : symbol, magnitude : _magnitude[i], rate : 0, available : true, loadable : _loadable[i], redeemable: _redeemable[i], lastUpdate : _lastUpdate }); // Add the token address to the address list. _tokenAddressArray.push(_tokens[i]); //if the token is redeemable increase the redeemableCounter if (_redeemable[i]){ _redeemableCounter = _redeemableCounter.add(1); } // Emit token addition event. emit AddedToken(msg.sender, _tokens[i], symbol, _magnitude[i], _loadable[i], _redeemable[i]); } } /// @notice Remove ERC20 tokens from the whitelist of tokens. /// @param _tokens ERC20 token contract addresses. function removeTokens(address[] calldata _tokens) external onlyAdmin { // Delete each token object from the list of supported tokens based on the addresses provided. for (uint i = 0; i < _tokens.length; i++) { // Store the token address. address token = _tokens[i]; //token must be available, reverts on duplicates as well require(_tokenInfoMap[token].available, "token is not available"); //if the token is redeemable decrease the redeemableCounter if (_tokenInfoMap[token].redeemable){ _redeemableCounter = _redeemableCounter.sub(1); } // Delete the token object. delete _tokenInfoMap[token]; // Remove the token address from the address list. for (uint j = 0; j < _tokenAddressArray.length.sub(1); j++) { if (_tokenAddressArray[j] == token) { _tokenAddressArray[j] = _tokenAddressArray[_tokenAddressArray.length.sub(1)]; break; } } _tokenAddressArray.length--; // Emit token removal event. emit RemovedToken(msg.sender, token); } } /// @notice based on the method it returns the recipient address and amount/value, ERC20 specific. /// @param _data is the transaction payload. function getERC20RecipientAndAmount(address _token, bytes calldata _data) external view returns (address, uint) { // Require that there exist enough bytes for encoding at least a method signature + data in the transaction payload: // 4 (signature) + 32(address or uint256) require(_data.length >= 4 + 32, "not enough method-encoding bytes"); // Get the method signature bytes4 signature = _data._bytesToBytes4(0); // Check if method Id is supported require(isERC20MethodSupported(_token, signature), "unsupported method"); // returns the recipient's address and amount is the value to be transferred if (signature == _BURN) { // 4 (signature) + 32(uint256) return (_token, _data._bytesToUint256(4)); } else if (signature == _TRANSFER_FROM) { // 4 (signature) + 32(address) + 32(address) + 32(uint256) require(_data.length >= 4 + 32 + 32 + 32, "not enough data for transferFrom"); return ( _data._bytesToAddress(4 + 32 + 12), _data._bytesToUint256(4 + 32 + 32)); } else { //transfer or approve // 4 (signature) + 32(address) + 32(uint) require(_data.length >= 4 + 32 + 32, "not enough data for transfer/appprove"); return (_data._bytesToAddress(4 + 12), _data._bytesToUint256(4 + 32)); } } /// @notice Toggles whether or not a token is loadable or not. function setTokenLoadable(address _token, bool _loadable) external onlyAdmin { // Require that the token exists. require(_tokenInfoMap[_token].available, "token is not available"); // this sets the loadable flag to the value passed in _tokenInfoMap[_token].loadable = _loadable; emit UpdatedTokenLoadable(msg.sender, _token, _loadable); } /// @notice Toggles whether or not a token is redeemable or not. function setTokenRedeemable(address _token, bool _redeemable) external onlyAdmin { // Require that the token exists. require(_tokenInfoMap[_token].available, "token is not available"); // this sets the redeemable flag to the value passed in _tokenInfoMap[_token].redeemable = _redeemable; emit UpdatedTokenRedeemable(msg.sender, _token, _redeemable); } /// @notice Update ERC20 token exchange rate. /// @param _token ERC20 token contract address. /// @param _rate ERC20 token exchange rate in wei. /// @param _updateDate date for the token updates. This will be compared to when oracle updates are received. function updateTokenRate(address _token, uint _rate, uint _updateDate) external onlyAdminOrOracle { // Require that the token exists. require(_tokenInfoMap[_token].available, "token is not available"); // Update the token's rate. _tokenInfoMap[_token].rate = _rate; // Update the token's last update timestamp. _tokenInfoMap[_token].lastUpdate = _updateDate; // Emit the rate update event. emit UpdatedTokenRate(msg.sender, _token, _rate); } //// @notice Withdraw tokens from the smart contract to the specified account. function claim(address payable _to, address _asset, uint _amount) external onlyAdmin { _safeTransfer(_to, _asset, _amount); emit Claimed(_to, _asset, _amount); } /// @notice This returns all of the fields for a given token. /// @param _a is the address of a given token. /// @return string of the token's symbol. /// @return uint of the token's magnitude. /// @return uint of the token's exchange rate to ETH. /// @return bool whether the token is available. /// @return bool whether the token is loadable to the TokenCard. /// @return bool whether the token is redeemable to the TKN Holder Contract. /// @return uint of the lastUpdated time of the token's exchange rate. function getTokenInfo(address _a) external view returns (string memory, uint256, uint256, bool, bool, bool, uint256) { Token storage tokenInfo = _tokenInfoMap[_a]; return (tokenInfo.symbol, tokenInfo.magnitude, tokenInfo.rate, tokenInfo.available, tokenInfo.loadable, tokenInfo.redeemable, tokenInfo.lastUpdate); } /// @notice This returns all of the fields for our StableCoin. /// @return string of the token's symbol. /// @return uint of the token's magnitude. /// @return uint of the token's exchange rate to ETH. /// @return bool whether the token is available. /// @return bool whether the token is loadable to the TokenCard. /// @return bool whether the token is redeemable to the TKN Holder Contract. /// @return uint of the lastUpdated time of the token's exchange rate. function getStablecoinInfo() external view returns (string memory, uint256, uint256, bool, bool, bool, uint256) { Token storage stablecoinInfo = _tokenInfoMap[_stablecoin]; return (stablecoinInfo.symbol, stablecoinInfo.magnitude, stablecoinInfo.rate, stablecoinInfo.available, stablecoinInfo.loadable, stablecoinInfo.redeemable, stablecoinInfo.lastUpdate); } /// @notice This returns an array of all whitelisted token addresses. /// @return address[] of whitelisted tokens. function tokenAddressArray() external view returns (address[] memory) { return _tokenAddressArray; } /// @notice This returns an array of all redeemable token addresses. /// @return address[] of redeemable tokens. function redeemableTokens() external view returns (address[] memory) { address[] memory redeemableAddresses = new address[](_redeemableCounter); uint redeemableIndex = 0; for (uint i = 0; i < _tokenAddressArray.length; i++) { address token = _tokenAddressArray[i]; if (_tokenInfoMap[token].redeemable){ redeemableAddresses[redeemableIndex] = token; redeemableIndex += 1; } } return redeemableAddresses; } /// @notice This returns true if a method Id is supported for the specific token. /// @return true if _methodId is supported in general or just for the specific token. function isERC20MethodSupported(address _token, bytes4 _methodId) public view returns (bool) { require(_tokenInfoMap[_token].available, "non-existing token"); return (_methodIdWhitelist[_methodId]); } /// @notice This returns true if the method is supported for all protected tokens. /// @return true if _methodId is in the method whitelist. function isERC20MethodWhitelisted(bytes4 _methodId) external view returns (bool) { return (_methodIdWhitelist[_methodId]); } /// @notice This returns the number of redeemable tokens. /// @return current # of redeemables. function redeemableCounter() external view returns (uint) { return _redeemableCounter; } /// @notice This returns the address of our stablecoin of choice. /// @return the address of the stablecoin contract. function stablecoin() external view returns (address) { return _stablecoin; } /// @notice this returns the node hash of our Oracle. /// @return the oracle node registered in ENS. function oracleNode() external view returns (bytes32) { return _oracleNode; } } contract TokenWhitelistable is ENSResolvable { /// @notice Is the registered ENS node identifying the tokenWhitelist contract bytes32 private _tokenWhitelistNode; /// @notice Constructor initializes the TokenWhitelistable object. /// @param _tokenWhitelistNode_ is the ENS node of the TokenWhitelist. constructor(bytes32 _tokenWhitelistNode_) internal { _tokenWhitelistNode = _tokenWhitelistNode_; } /// @notice This shows what TokenWhitelist is being used /// @return TokenWhitelist's node registered in ENS. function tokenWhitelistNode() external view returns (bytes32) { return _tokenWhitelistNode; } /// @notice This returns all of the fields for a given token. /// @param _a is the address of a given token. /// @return string of the token's symbol. /// @return uint of the token's magnitude. /// @return uint of the token's exchange rate to ETH. /// @return bool whether the token is available. /// @return bool whether the token is loadable to the TokenCard. /// @return bool whether the token is redeemable to the TKN Holder Contract. /// @return uint of the lastUpdated time of the token's exchange rate. function _getTokenInfo(address _a) internal view returns (string memory, uint256, uint256, bool, bool, bool, uint256) { return ITokenWhitelist(_ensResolve(_tokenWhitelistNode)).getTokenInfo(_a); } /// @notice This returns all of the fields for our stablecoin token. /// @return string of the token's symbol. /// @return uint of the token's magnitude. /// @return uint of the token's exchange rate to ETH. /// @return bool whether the token is available. /// @return bool whether the token is loadable to the TokenCard. /// @return bool whether the token is redeemable to the TKN Holder Contract. /// @return uint of the lastUpdated time of the token's exchange rate. function _getStablecoinInfo() internal view returns (string memory, uint256, uint256, bool, bool, bool, uint256) { return ITokenWhitelist(_ensResolve(_tokenWhitelistNode)).getStablecoinInfo(); } /// @notice This returns an array of our whitelisted addresses. /// @return address[] of our whitelisted tokens. function _tokenAddressArray() internal view returns (address[] memory) { return ITokenWhitelist(_ensResolve(_tokenWhitelistNode)).tokenAddressArray(); } /// @notice This returns an array of all redeemable token addresses. /// @return address[] of redeemable tokens. function _redeemableTokens() internal view returns (address[] memory) { return ITokenWhitelist(_ensResolve(_tokenWhitelistNode)).redeemableTokens(); } /// @notice Update ERC20 token exchange rate. /// @param _token ERC20 token contract address. /// @param _rate ERC20 token exchange rate in wei. /// @param _updateDate date for the token updates. This will be compared to when oracle updates are received. function _updateTokenRate(address _token, uint _rate, uint _updateDate) internal { ITokenWhitelist(_ensResolve(_tokenWhitelistNode)).updateTokenRate(_token, _rate, _updateDate); } /// @notice based on the method it returns the recipient address and amount/value, ERC20 specific. /// @param _data is the transaction payload. function _getERC20RecipientAndAmount(address _destination, bytes memory _data) internal view returns (address, uint) { return ITokenWhitelist(_ensResolve(_tokenWhitelistNode)).getERC20RecipientAndAmount(_destination, _data); } /// @notice Checks whether a token is available. /// @return bool available or not. function _isTokenAvailable(address _a) internal view returns (bool) { ( , , , bool available, , , ) = _getTokenInfo(_a); return available; } /// @notice Checks whether a token is redeemable. /// @return bool redeemable or not. function _isTokenRedeemable(address _a) internal view returns (bool) { ( , , , , , bool redeemable, ) = _getTokenInfo(_a); return redeemable; } /// @notice Checks whether a token is loadable. /// @return bool loadable or not. function _isTokenLoadable(address _a) internal view returns (bool) { ( , , , , bool loadable, , ) = _getTokenInfo(_a); return loadable; } /// @notice This gets the address of the stablecoin. /// @return the address of the stablecoin contract. function _stablecoin() internal view returns (address) { return ITokenWhitelist(_ensResolve(_tokenWhitelistNode)).stablecoin(); } } contract ControllableOwnable is Controllable, Ownable { /// @dev Check if the sender is the Owner or one of the Controllers modifier onlyOwnerOrController() { require (_isOwner(msg.sender) || _isController(msg.sender), "either owner or controller"); _; } } /// @title AddressWhitelist provides payee-whitelist functionality. /// @dev This contract will allow the user to maintain a whitelist of addresses /// @dev These addresses will live outside of the various spend limits contract AddressWhitelist is ControllableOwnable { using SafeMath for uint256; event AddedToWhitelist(address _sender, address[] _addresses); event SubmittedWhitelistAddition(address[] _addresses, bytes32 _hash); event CancelledWhitelistAddition(address _sender, bytes32 _hash); event RemovedFromWhitelist(address _sender, address[] _addresses); event SubmittedWhitelistRemoval(address[] _addresses, bytes32 _hash); event CancelledWhitelistRemoval(address _sender, bytes32 _hash); mapping(address => bool) public whitelistMap; address[] public whitelistArray; address[] private _pendingWhitelistAddition; address[] private _pendingWhitelistRemoval; bool public submittedWhitelistAddition; bool public submittedWhitelistRemoval; bool public isSetWhitelist; /// @dev Check if the provided addresses contain the owner or the zero-address address. modifier hasNoOwnerOrZeroAddress(address[] memory _addresses) { for (uint i = 0; i < _addresses.length; i++) { require(!_isOwner(_addresses[i]), "provided whitelist contains the owner address"); require(_addresses[i] != address(0), "provided whitelist contains the zero address"); } _; } /// @dev Check that neither addition nor removal operations have already been submitted. modifier noActiveSubmission() { require(!submittedWhitelistAddition && !submittedWhitelistRemoval, "whitelist operation has already been submitted"); _; } /// @dev Getter for pending addition array. function pendingWhitelistAddition() external view returns (address[] memory) { return _pendingWhitelistAddition; } /// @dev Getter for pending removal array. function pendingWhitelistRemoval() external view returns (address[] memory) { return _pendingWhitelistRemoval; } /// @dev Add initial addresses to the whitelist. /// @param _addresses are the Ethereum addresses to be whitelisted. function setWhitelist(address[] calldata _addresses) external onlyOwner hasNoOwnerOrZeroAddress(_addresses) { // Require that the whitelist has not been initialized. require(!isSetWhitelist, "whitelist has already been initialized"); // Add each of the provided addresses to the whitelist. for (uint i = 0; i < _addresses.length; i++) { // adds to the whitelist mapping whitelistMap[_addresses[i]] = true; // adds to the whitelist array whitelistArray.push(_addresses[i]); } isSetWhitelist = true; // Emit the addition event. emit AddedToWhitelist(msg.sender, _addresses); } /// @dev Add addresses to the whitelist. /// @param _addresses are the Ethereum addresses to be whitelisted. function submitWhitelistAddition(address[] calldata _addresses) external onlyOwner noActiveSubmission hasNoOwnerOrZeroAddress(_addresses) { // Require that the whitelist has been initialized. require(isSetWhitelist, "whitelist has not been initialized"); // Require this array of addresses not empty require(_addresses.length > 0, "pending whitelist addition is empty"); // Set the provided addresses to the pending addition addresses. _pendingWhitelistAddition = _addresses; // Flag the operation as submitted. submittedWhitelistAddition = true; // Emit the submission event. emit SubmittedWhitelistAddition(_addresses, calculateHash(_addresses)); } /// @dev Confirm pending whitelist addition. /// @dev This will only ever be applied post 2FA, by one of the Controllers /// @param _hash is the hash of the pending whitelist array, a form of lamport lock function confirmWhitelistAddition(bytes32 _hash) external onlyController { // Require that the whitelist addition has been submitted. require(submittedWhitelistAddition, "whitelist addition has not been submitted"); // Require that confirmation hash and the hash of the pending whitelist addition match require(_hash == calculateHash(_pendingWhitelistAddition), "hash of the pending whitelist addition do not match"); // Whitelist pending addresses. for (uint i = 0; i < _pendingWhitelistAddition.length; i++) { // check if it doesn't exist already. if (!whitelistMap[_pendingWhitelistAddition[i]]) { // add to the Map and the Array whitelistMap[_pendingWhitelistAddition[i]] = true; whitelistArray.push(_pendingWhitelistAddition[i]); } } // Emit the addition event. emit AddedToWhitelist(msg.sender, _pendingWhitelistAddition); // Reset pending addresses. delete _pendingWhitelistAddition; // Reset the submission flag. submittedWhitelistAddition = false; } /// @dev Cancel pending whitelist addition. function cancelWhitelistAddition(bytes32 _hash) external onlyOwnerOrController { // Check if operation has been submitted. require(submittedWhitelistAddition, "whitelist addition has not been submitted"); // Require that confirmation hash and the hash of the pending whitelist addition match require(_hash == calculateHash(_pendingWhitelistAddition), "hash of the pending whitelist addition does not match"); // Reset pending addresses. delete _pendingWhitelistAddition; // Reset the submitted operation flag. submittedWhitelistAddition = false; // Emit the cancellation event. emit CancelledWhitelistAddition(msg.sender, _hash); } /// @dev Remove addresses from the whitelist. /// @param _addresses are the Ethereum addresses to be removed. function submitWhitelistRemoval(address[] calldata _addresses) external onlyOwner noActiveSubmission { // Require that the whitelist has been initialized. require(isSetWhitelist, "whitelist has not been initialized"); // Require that the array of addresses is not empty require(_addresses.length > 0, "pending whitelist removal is empty"); // Add the provided addresses to the pending addition list. _pendingWhitelistRemoval = _addresses; // Flag the operation as submitted. submittedWhitelistRemoval = true; // Emit the submission event. emit SubmittedWhitelistRemoval(_addresses, calculateHash(_addresses)); } /// @dev Confirm pending removal of whitelisted addresses. function confirmWhitelistRemoval(bytes32 _hash) external onlyController { // Require that the pending whitelist is not empty and the operation has been submitted. require(submittedWhitelistRemoval, "whitelist removal has not been submitted"); // Require that confirmation hash and the hash of the pending whitelist removal match require(_hash == calculateHash(_pendingWhitelistRemoval), "hash of the pending whitelist removal does not match the confirmed hash"); // Remove pending addresses. for (uint i = 0; i < _pendingWhitelistRemoval.length; i++) { // check if it exists if (whitelistMap[_pendingWhitelistRemoval[i]]) { whitelistMap[_pendingWhitelistRemoval[i]] = false; for (uint j = 0; j < whitelistArray.length.sub(1); j++) { if (whitelistArray[j] == _pendingWhitelistRemoval[i]) { whitelistArray[j] = whitelistArray[whitelistArray.length - 1]; break; } } whitelistArray.length--; } } // Emit the removal event. emit RemovedFromWhitelist(msg.sender, _pendingWhitelistRemoval); // Reset pending addresses. delete _pendingWhitelistRemoval; // Reset the submission flag. submittedWhitelistRemoval = false; } /// @dev Cancel pending removal of whitelisted addresses. function cancelWhitelistRemoval(bytes32 _hash) external onlyOwnerOrController { // Check if operation has been submitted. require(submittedWhitelistRemoval, "whitelist removal has not been submitted"); // Require that confirmation hash and the hash of the pending whitelist removal match require(_hash == calculateHash(_pendingWhitelistRemoval), "hash of the pending whitelist removal do not match"); // Reset pending addresses. delete _pendingWhitelistRemoval; // Reset pending addresses. submittedWhitelistRemoval = false; // Emit the cancellation event. emit CancelledWhitelistRemoval(msg.sender, _hash); } /// @dev Method used to hash our whitelist address arrays. function calculateHash(address[] memory _addresses) public pure returns (bytes32) { return keccak256(abi.encodePacked(_addresses)); } } /// @title DailyLimitTrait This trait allows for daily limits to be included in other contracts. /// This contract will allow for a DailyLimit object to be instantiated and used. library DailyLimitTrait { using SafeMath for uint256; event UpdatedAvailableLimit(); struct DailyLimit { uint value; uint available; uint limitTimestamp; uint pending; bool updateable; } /// @dev Returns the available daily balance - accounts for daily limit reset. /// @return amount of available to spend within the current day in base units. function _getAvailableLimit(DailyLimit storage self) internal view returns (uint) { if (now > self.limitTimestamp.add(24 hours)) { return self.value; } else { return self.available; } } /// @dev Use up amount within the daily limit. Will fail if amount is larger than daily limit. function _enforceLimit(DailyLimit storage self, uint _amount) internal { // Account for the spend limit daily reset. _updateAvailableLimit(self); require(self.available >= _amount, "available has to be greater or equal to use amount"); self.available = self.available.sub(_amount); } /// @dev Set the daily limit. /// @param _amount is the daily limit amount in base units. function _setLimit(DailyLimit storage self, uint _amount) internal { // Require that the spend limit has not been set yet. require(!self.updateable, "daily limit not updateable"); // Modify spend limit based on the provided value. _modifyLimit(self, _amount); // Flag the operation as set. self.updateable = true; } /// @dev Submit a daily limit update, needs to be confirmed. /// @param _amount is the daily limit amount in base units. function _submitLimitUpdate(DailyLimit storage self, uint _amount) internal { // Require that the spend limit has been set. require(self.updateable, "daily limit is still updateable"); // Assign the provided amount to pending daily limit. self.pending = _amount; } /// @dev Confirm pending set daily limit operation. function _confirmLimitUpdate(DailyLimit storage self, uint _amount) internal { // Require that pending and confirmed spend limit are the same require(self.pending == _amount, "confirmed and submitted limits dont match"); // Modify spend limit based on the pending value. _modifyLimit(self, self.pending); } /// @dev Update available spend limit based on the daily reset. function _updateAvailableLimit(DailyLimit storage self) private { if (now > self.limitTimestamp.add(24 hours)) { // Update the current timestamp. self.limitTimestamp = now; // Set the available limit to the current spend limit. self.available = self.value; emit UpdatedAvailableLimit(); } } /// @dev Modify the spend limit and spend available based on the provided value. /// @dev _amount is the daily limit amount in wei. function _modifyLimit(DailyLimit storage self, uint _amount) private { // Account for the spend limit daily reset. _updateAvailableLimit(self); // Set the daily limit to the provided amount. self.value = _amount; // Lower the available limit if it's higher than the new daily limit. if (self.available > self.value) { self.available = self.value; } } } /// @title it provides daily spend limit functionality. contract SpendLimit is ControllableOwnable { event SetSpendLimit(address _sender, uint _amount); event SubmittedSpendLimitUpdate(uint _amount); using DailyLimitTrait for DailyLimitTrait.DailyLimit; DailyLimitTrait.DailyLimit internal _spendLimit; /// @dev Constructor initializes the daily spend limit in wei. constructor(uint _limit_) internal { _spendLimit = DailyLimitTrait.DailyLimit(_limit_, _limit_, now, 0, false); } /// @dev Sets the initial daily spend (aka transfer) limit for non-whitelisted addresses. /// @param _amount is the daily limit amount in wei. function setSpendLimit(uint _amount) external onlyOwner { _spendLimit._setLimit(_amount); emit SetSpendLimit(msg.sender, _amount); } /// @dev Submit a daily transfer limit update for non-whitelisted addresses. /// @param _amount is the daily limit amount in wei. function submitSpendLimitUpdate(uint _amount) external onlyOwner { _spendLimit._submitLimitUpdate(_amount); emit SubmittedSpendLimitUpdate(_amount); } /// @dev Confirm pending set daily limit operation. function confirmSpendLimitUpdate(uint _amount) external onlyController { _spendLimit._confirmLimitUpdate(_amount); emit SetSpendLimit(msg.sender, _amount); } function spendLimitAvailable() external view returns (uint) { return _spendLimit._getAvailableLimit(); } function spendLimitValue() external view returns (uint) { return _spendLimit.value; } function spendLimitUpdateable() external view returns (bool) { return _spendLimit.updateable; } function spendLimitPending() external view returns (uint) { return _spendLimit.pending; } } //// @title GasTopUpLimit provides daily limit functionality. contract GasTopUpLimit is ControllableOwnable { event SetGasTopUpLimit(address _sender, uint _amount); event SubmittedGasTopUpLimitUpdate(uint _amount); uint constant private _MINIMUM_GAS_TOPUP_LIMIT = 1 finney; uint constant private _MAXIMUM_GAS_TOPUP_LIMIT = 500 finney; using DailyLimitTrait for DailyLimitTrait.DailyLimit; DailyLimitTrait.DailyLimit internal _gasTopUpLimit; /// @dev Constructor initializes the daily gas topup limit in wei. constructor() internal { _gasTopUpLimit = DailyLimitTrait.DailyLimit(_MAXIMUM_GAS_TOPUP_LIMIT, _MAXIMUM_GAS_TOPUP_LIMIT, now, 0, false); } /// @dev Sets the daily gas top up limit. /// @param _amount is the gas top up amount in wei. function setGasTopUpLimit(uint _amount) external onlyOwner { require(_MINIMUM_GAS_TOPUP_LIMIT <= _amount && _amount <= _MAXIMUM_GAS_TOPUP_LIMIT, "gas top up amount is outside the min/max range"); _gasTopUpLimit._setLimit(_amount); emit SetGasTopUpLimit(msg.sender, _amount); } /// @dev Submit a daily gas top up limit update. /// @param _amount is the daily top up gas limit amount in wei. function submitGasTopUpLimitUpdate(uint _amount) external onlyOwner { require(_MINIMUM_GAS_TOPUP_LIMIT <= _amount && _amount <= _MAXIMUM_GAS_TOPUP_LIMIT, "gas top up amount is outside the min/max range"); _gasTopUpLimit._submitLimitUpdate(_amount); emit SubmittedGasTopUpLimitUpdate(_amount); } /// @dev Confirm pending set top up gas limit operation. function confirmGasTopUpLimitUpdate(uint _amount) external onlyController { _gasTopUpLimit._confirmLimitUpdate(_amount); emit SetGasTopUpLimit(msg.sender, _amount); } function gasTopUpLimitAvailable() external view returns (uint) { return _gasTopUpLimit._getAvailableLimit(); } function gasTopUpLimitValue() external view returns (uint) { return _gasTopUpLimit.value; } function gasTopUpLimitUpdateable() external view returns (bool) { return _gasTopUpLimit.updateable; } function gasTopUpLimitPending() external view returns (uint) { return _gasTopUpLimit.pending; } } /// @title LoadLimit provides daily load limit functionality. contract LoadLimit is ControllableOwnable { event SetLoadLimit(address _sender, uint _amount); event SubmittedLoadLimitUpdate(uint _amount); uint constant private _MINIMUM_LOAD_LIMIT = 1 finney; uint private _maximumLoadLimit; using DailyLimitTrait for DailyLimitTrait.DailyLimit; DailyLimitTrait.DailyLimit internal _loadLimit; /// @dev Sets a daily card load limit. /// @param _amount is the card load amount in current stablecoin base units. function setLoadLimit(uint _amount) external onlyOwner { require(_MINIMUM_LOAD_LIMIT <= _amount && _amount <= _maximumLoadLimit, "card load amount is outside the min/max range"); _loadLimit._setLimit(_amount); emit SetLoadLimit(msg.sender, _amount); } /// @dev Submit a daily load limit update. /// @param _amount is the daily load limit amount in wei. function submitLoadLimitUpdate(uint _amount) external onlyOwner { require(_MINIMUM_LOAD_LIMIT <= _amount && _amount <= _maximumLoadLimit, "card load amount is outside the min/max range"); _loadLimit._submitLimitUpdate(_amount); emit SubmittedLoadLimitUpdate(_amount); } /// @dev Confirm pending set load limit operation. function confirmLoadLimitUpdate(uint _amount) external onlyController { _loadLimit._confirmLimitUpdate(_amount); emit SetLoadLimit(msg.sender, _amount); } function loadLimitAvailable() external view returns (uint) { return _loadLimit._getAvailableLimit(); } function loadLimitValue() external view returns (uint) { return _loadLimit.value; } function loadLimitUpdateable() external view returns (bool) { return _loadLimit.updateable; } function loadLimitPending() external view returns (uint) { return _loadLimit.pending; } /// @dev initializes the daily load limit. /// @param _maxLimit is the maximum load limit amount in stablecoin base units. function _initializeLoadLimit(uint _maxLimit) internal { _maximumLoadLimit = _maxLimit; _loadLimit = DailyLimitTrait.DailyLimit(_maximumLoadLimit, _maximumLoadLimit, now, 0, false); } } //// @title Asset store with extra security features. contract Vault is AddressWhitelist, SpendLimit, ERC165, Transferrable, Balanceable, TokenWhitelistable { using SafeMath for uint256; using SafeERC20 for ERC20; event Received(address _from, uint _amount); event Transferred(address _to, address _asset, uint _amount); event BulkTransferred(address _to, address[] _assets); /// @dev Supported ERC165 interface ID. bytes4 private constant _ERC165_INTERFACE_ID = 0x01ffc9a7; // solium-disable-line uppercase /// @dev Constructor initializes the vault with an owner address and spend limit. It also sets up the controllable and tokenWhitelist contracts with the right name registered in ENS. /// @param _owner_ is the owner account of the wallet contract. /// @param _transferable_ indicates whether the contract ownership can be transferred. /// @param _tokenWhitelistNode_ is the ENS node of the Token whitelist. /// @param _controllerNode_ is the ENS name node of the controller. /// @param _spendLimit_ is the initial spend limit. constructor(address payable _owner_, bool _transferable_, bytes32 _tokenWhitelistNode_, bytes32 _controllerNode_, uint _spendLimit_) SpendLimit(_spendLimit_) Ownable(_owner_, _transferable_) Controllable(_controllerNode_) TokenWhitelistable(_tokenWhitelistNode_) public {} /// @dev Checks if the value is not zero. modifier isNotZero(uint _value) { require(_value != 0, "provided value cannot be zero"); _; } /// @dev Ether can be deposited from any source, so this contract must be payable by anyone. function() external payable { emit Received(msg.sender, msg.value); } /// @dev Checks for interface support based on ERC165. function supportsInterface(bytes4 _interfaceID) external view returns (bool) { return _interfaceID == _ERC165_INTERFACE_ID; } /// @dev This is a bulk transfer convenience function, used to migrate contracts. /// @notice If any of the transfers fail, this will revert. /// @param _to is the recipient's address, can't be the zero (0x0) address: transfer() will revert. /// @param _assets is an array of addresses of ERC20 tokens or 0x0 for ether. function bulkTransfer(address payable _to, address[] calldata _assets) external onlyOwner { // check to make sure that _assets isn't empty require(_assets.length != 0, "asset array should be non-empty"); // This loops through all of the transfers to be made for (uint i = 0; i < _assets.length; i++) { uint amount = _balance(address(this), _assets[i]); // use our safe, daily limit protected transfer transfer(_to, _assets[i], amount); } emit BulkTransferred(_to, _assets); } /// @dev Transfers the specified asset to the recipient's address. /// @param _to is the recipient's address. /// @param _asset is the address of an ERC20 token or 0x0 for ether. /// @param _amount is the amount of assets to be transferred in base units. function transfer(address payable _to, address _asset, uint _amount) public onlyOwner isNotZero(_amount) { // Checks if the _to address is not the zero-address require(_to != address(0), "_to address cannot be set to 0x0"); // If address is not whitelisted, take daily limit into account. if (!whitelistMap[_to]) { // initialize ether value in case the asset is ETH uint etherValue = _amount; // Convert token amount to ether value if asset is an ERC20 token. if (_asset != address(0)) { etherValue = convertToEther(_asset, _amount); } // Check against the daily spent limit and update accordingly // Check against the daily spent limit and update accordingly, require that the value is under remaining limit. _spendLimit._enforceLimit(etherValue); } // Transfer token or ether based on the provided address. _safeTransfer(_to, _asset, _amount); // Emit the transfer event. emit Transferred(_to, _asset, _amount); } /// @dev Convert ERC20 token amount to the corresponding ether amount. /// @param _token ERC20 token contract address. /// @param _amount amount of token in base units. function convertToEther(address _token, uint _amount) public view returns (uint) { // Store the token in memory to save map entry lookup gas. (,uint256 magnitude, uint256 rate, bool available, , , ) = _getTokenInfo(_token); // If the token exists require that its rate is not zero. if (available) { require(rate != 0, "token rate is 0"); // Safely convert the token amount to ether based on the exchange rate. return _amount.mul(rate).div(magnitude); } return 0; } } //// @title Asset wallet with extra security features, gas top up management and card integration. contract Wallet is ENSResolvable, Vault, GasTopUpLimit, LoadLimit { using SafeERC20 for ERC20; using Address for address; event ToppedUpGas(address _sender, address _owner, uint _amount); event LoadedTokenCard(address _asset, uint _amount); event ExecutedTransaction(address _destination, uint _value, bytes _data, bytes _returndata); event UpdatedAvailableLimit(); string constant public WALLET_VERSION = "2.2.0"; uint constant private _DEFAULT_MAX_STABLECOIN_LOAD_LIMIT = 10000; //10,000 USD /// @dev Is the registered ENS node identifying the licence contract. bytes32 private _licenceNode; /// @dev Constructor initializes the wallet top up limit and the vault contract. /// @param _owner_ is the owner account of the wallet contract. /// @param _transferable_ indicates whether the contract ownership can be transferred. /// @param _ens_ is the address of the ENS registry. /// @param _tokenWhitelistNode_ is the ENS name node of the Token whitelist. /// @param _controllerNode_ is the ENS name node of the Controller contract. /// @param _licenceNode_ is the ENS name node of the Licence contract. /// @param _spendLimit_ is the initial spend limit. constructor(address payable _owner_, bool _transferable_, address _ens_, bytes32 _tokenWhitelistNode_, bytes32 _controllerNode_, bytes32 _licenceNode_, uint _spendLimit_) ENSResolvable(_ens_) Vault(_owner_, _transferable_, _tokenWhitelistNode_, _controllerNode_, _spendLimit_) public { // Get the stablecoin's magnitude. ( ,uint256 stablecoinMagnitude, , , , , ) = _getStablecoinInfo(); require(stablecoinMagnitude > 0, "stablecoin not set"); _initializeLoadLimit(_DEFAULT_MAX_STABLECOIN_LOAD_LIMIT * stablecoinMagnitude); _licenceNode = _licenceNode_; } /// @dev Refill owner's gas balance, revert if the transaction amount is too large /// @param _amount is the amount of ether to transfer to the owner account in wei. function topUpGas(uint _amount) external isNotZero(_amount) onlyOwnerOrController { // Check against the daily spent limit and update accordingly, require that the value is under remaining limit. _gasTopUpLimit._enforceLimit(_amount); // Then perform the transfer owner().transfer(_amount); // Emit the gas top up event. emit ToppedUpGas(msg.sender, owner(), _amount); } /// @dev Load a token card with the specified asset amount. /// @dev the amount send should be inclusive of the percent licence. /// @param _asset is the address of an ERC20 token or 0x0 for ether. /// @param _amount is the amount of assets to be transferred in base units. function loadTokenCard(address _asset, uint _amount) external payable onlyOwner { // check if token is allowed to be used for loading the card require(_isTokenLoadable(_asset), "token not loadable"); // Convert token amount to stablecoin value. uint stablecoinValue = convertToStablecoin(_asset, _amount); // Check against the daily spent limit and update accordingly, require that the value is under remaining limit. _loadLimit._enforceLimit(stablecoinValue); // Get the TKN licenceAddress from ENS address licenceAddress = _ensResolve(_licenceNode); if (_asset != address(0)) { ERC20(_asset).safeApprove(licenceAddress, _amount); ILicence(licenceAddress).load(_asset, _amount); } else { ILicence(licenceAddress).load.value(_amount)(_asset, _amount); } emit LoadedTokenCard(_asset, _amount); } /// @dev This function allows for the owner to send transaction from the Wallet to arbitrary addresses /// @param _destination address of the transaction /// @param _value ETH amount in wei /// @param _data transaction payload binary function executeTransaction(address _destination, uint _value, bytes calldata _data) external onlyOwner returns (bytes memory) { // If value is send across as a part of this executeTransaction, this will be sent to any payable // destination. As a result enforceLimit if destination is not whitelisted. if (!whitelistMap[_destination]) { _spendLimit._enforceLimit(_value); } // Check if the destination is a Contract and it is one of our supported tokens if (address(_destination).isContract() && _isTokenAvailable(_destination)) { // to is the recipient's address and amount is the value to be transferred address to; uint amount; (to, amount) = _getERC20RecipientAndAmount(_destination, _data); if (!whitelistMap[to]) { // If the address (of the token contract, e.g) is not in the TokenWhitelist used by the convert method... // ...then etherValue will be zero uint etherValue = convertToEther(_destination, amount); _spendLimit._enforceLimit(etherValue); } // use callOptionalReturn provided in SafeERC20 in case the ERC20 method // returns flase instead of reverting! ERC20(_destination).callOptionalReturn(_data); // if ERC20 call completes, return a boolean "true" as bytes emulating ERC20 bytes memory b = new bytes(32); b[31] = 0x01; emit ExecutedTransaction(_destination, _value, _data, b); return b; } (bool success, bytes memory returndata) = _destination.call.value(_value)(_data); require(success, "low-level call failed"); emit ExecutedTransaction(_destination, _value, _data, returndata); // returns all of the bytes returned by _destination contract return returndata; } /// @return licence contract node registered in ENS. function licenceNode() external view returns (bytes32) { return _licenceNode; } /// @dev Convert ether or ERC20 token amount to the corresponding stablecoin amount. /// @param _token ERC20 token contract address. /// @param _amount amount of token in base units. function convertToStablecoin(address _token, uint _amount) public view returns (uint) { // avoid the unnecessary calculations if the token to be loaded is the stablecoin itself if (_token == _stablecoin()) { return _amount; } uint amountToSend = _amount; // 0x0 represents ether if (_token != address(0)) { // convert to eth first, same as convertToEther() // Store the token in memory to save map entry lookup gas. (,uint256 magnitude, uint256 rate, bool available, , , ) = _getTokenInfo(_token); // require that token both exists in the whitelist and its rate is not zero. require(available, "token is not available"); require(rate != 0, "token rate is 0"); // Safely convert the token amount to ether based on the exchange rate. amountToSend = _amount.mul(rate).div(magnitude); } // _amountToSend now is in ether // Get the stablecoin's magnitude and its current rate. ( ,uint256 stablecoinMagnitude, uint256 stablecoinRate, bool stablecoinAvailable, , , ) = _getStablecoinInfo(); // Check if the stablecoin rate is set. require(stablecoinAvailable, "token is not available"); require(stablecoinRate != 0, "stablecoin rate is 0"); // Safely convert the token amount to stablecoin based on its exchange rate and the stablecoin exchange rate. return amountToSend.mul(stablecoinMagnitude).div(stablecoinRate); } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"constant":true,"inputs":[{"name":"_interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"spendLimitPending","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"setGasTopUpLimit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_hash","type":"bytes32"}],"name":"cancelWhitelistAddition","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"WALLET_VERSION","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_assets","type":"address[]"}],"name":"bulkTransfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"loadLimitAvailable","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isTransferable","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"submitSpendLimitUpdate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"loadLimitUpdateable","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"gasTopUpLimitValue","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"submittedWhitelistAddition","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pendingWhitelistRemoval","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"whitelistMap","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_asset","type":"address"},{"name":"_amount","type":"uint256"}],"name":"loadTokenCard","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"setLoadLimit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"setSpendLimit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_destination","type":"address"},{"name":"_value","type":"uint256"},{"name":"_data","type":"bytes"}],"name":"executeTransaction","outputs":[{"name":"","type":"bytes"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"pendingWhitelistAddition","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"spendLimitUpdateable","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_hash","type":"bytes32"}],"name":"confirmWhitelistAddition","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"spendLimitAvailable","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_addresses","type":"address[]"}],"name":"submitWhitelistRemoval","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"submitGasTopUpLimitUpdate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"licenceNode","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ensRegistry","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"gasTopUpLimitAvailable","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_addresses","type":"address[]"}],"name":"submitWhitelistAddition","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"tokenWhitelistNode","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"spendLimitValue","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"submitLoadLimitUpdate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_account","type":"address"},{"name":"_transferable","type":"bool"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_token","type":"address"},{"name":"_amount","type":"uint256"}],"name":"convertToEther","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isSetWhitelist","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_asset","type":"address"},{"name":"_amount","type":"uint256"}],"name":"transfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"loadLimitPending","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"gasTopUpLimitUpdateable","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_hash","type":"bytes32"}],"name":"confirmWhitelistRemoval","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"gasTopUpLimitPending","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_addresses","type":"address[]"}],"name":"calculateHash","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"name":"_hash","type":"bytes32"}],"name":"cancelWhitelistRemoval","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"whitelistArray","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"loadLimitValue","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"submittedWhitelistRemoval","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"controllerNode","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"topUpGas","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"confirmSpendLimitUpdate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_token","type":"address"},{"name":"_amount","type":"uint256"}],"name":"convertToStablecoin","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"confirmLoadLimitUpdate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"confirmGasTopUpLimitUpdate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_addresses","type":"address[]"}],"name":"setWhitelist","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_owner_","type":"address"},{"name":"_transferable_","type":"bool"},{"name":"_ens_","type":"address"},{"name":"_tokenWhitelistNode_","type":"bytes32"},{"name":"_controllerNode_","type":"bytes32"},{"name":"_licenceNode_","type":"bytes32"},{"name":"_spendLimit_","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_sender","type":"address"},{"indexed":false,"name":"_owner","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"ToppedUpGas","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_asset","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"LoadedTokenCard","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_destination","type":"address"},{"indexed":false,"name":"_value","type":"uint256"},{"indexed":false,"name":"_data","type":"bytes"},{"indexed":false,"name":"_returndata","type":"bytes"}],"name":"ExecutedTransaction","type":"event"},{"anonymous":false,"inputs":[],"name":"UpdatedAvailableLimit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_sender","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"SetLoadLimit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_amount","type":"uint256"}],"name":"SubmittedLoadLimitUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_sender","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"SetGasTopUpLimit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_amount","type":"uint256"}],"name":"SubmittedGasTopUpLimitUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_from","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"Received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_to","type":"address"},{"indexed":false,"name":"_asset","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"Transferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_to","type":"address"},{"indexed":false,"name":"_assets","type":"address[]"}],"name":"BulkTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_sender","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"SetSpendLimit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_amount","type":"uint256"}],"name":"SubmittedSpendLimitUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_sender","type":"address"},{"indexed":false,"name":"_addresses","type":"address[]"}],"name":"AddedToWhitelist","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_addresses","type":"address[]"},{"indexed":false,"name":"_hash","type":"bytes32"}],"name":"SubmittedWhitelistAddition","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_sender","type":"address"},{"indexed":false,"name":"_hash","type":"bytes32"}],"name":"CancelledWhitelistAddition","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_sender","type":"address"},{"indexed":false,"name":"_addresses","type":"address[]"}],"name":"RemovedFromWhitelist","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_addresses","type":"address[]"},{"indexed":false,"name":"_hash","type":"bytes32"}],"name":"SubmittedWhitelistRemoval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_sender","type":"address"},{"indexed":false,"name":"_hash","type":"bytes32"}],"name":"CancelledWhitelistRemoval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_from","type":"address"},{"indexed":false,"name":"_to","type":"address"}],"name":"TransferredOwnership","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_locked","type":"address"}],"name":"LockedOwnership","type":"event"}]
Deployed Bytecode
0x60806040526004361061031a5760003560e01c80637d73b231116101ab578063cbd2ac68116100f7578063e2b4ce9711610095578063f36febda1161006f578063f36febda14610c97578063f40b51f814610cd0578063f41c431914610cfa578063f421764814610d245761031a565b8063e2b4ce9714610c2e578063e61c51ca14610c43578063eadd3cea14610c6d5761031a565b8063ce0b5bd5116100d1578063ce0b5bd514610bb0578063d251fefc14610bda578063da84b1ed14610c04578063de212bf314610c195761031a565b8063cbd2ac6814610ac3578063cc0e7e5614610aed578063cd7958dd14610b025761031a565b8063b221f31611610164578063be40ba791161013e578063be40ba7914610a41578063beabacc814610a56578063c4856cd914610a99578063c785141814610aae5761031a565b8063b221f316146109a3578063b242e534146109cd578063b87e21ef14610a085761031a565b80637d73b231146108a35780637d7d0046146108d45780637fd004fa146108e9578063877337b0146109645780638da5cb5b146109795780639b0dfd271461098e5761031a565b806332531c3c1161026a5780634aa46fde116102235780636137d670116101fd5780636137d670146107d4578063715018a61461084f57806374624c5514610864578063747c31d61461088e5761031a565b80634aa46fde146107805780635adc02ab146107955780635d2362a8146107bf5761031a565b806332531c3c146106285780633a43199f1461065b5780633bfec254146106875780633c672eb7146106b15780633f579f42146106db57806347b55a9d1461076b5761031a565b80631efd0299116102d7578063227149a3116102b1578063227149a3146105845780632587a6a21461059957806326d05ab2146105ae578063294f4025146105c35761031a565b80631efd0299146105305780632121dc751461054557806321ce918d1461055a5761031a565b806301ffc9a714610356578063027ef3eb1461039e5780630f3a85d8146103c5578063100f23fd146103f15780631127b57e1461041b5780631aa21fba146104a5575b6040805133815234602082015281517f88a5966d370b9919b20f3e2c13ff65706f196a4e32cc2c12bf57088f88525874929181900390910190a1005b34801561036257600080fd5b5061038a6004803603602081101561037957600080fd5b50356001600160e01b031916610d9f565b604080519115158252519081900360200190f35b3480156103aa57600080fd5b506103b3610db9565b60408051918252519081900360200190f35b3480156103d157600080fd5b506103ef600480360360208110156103e857600080fd5b5035610dc0565b005b3480156103fd57600080fd5b506103ef6004803603602081101561041457600080fd5b5035610eb1565b34801561042757600080fd5b5061043061104c565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561046a578181015183820152602001610452565b50505050905090810190601f1680156104975780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156104b157600080fd5b506103ef600480360360408110156104c857600080fd5b6001600160a01b038235169190810190604081016020820135600160201b8111156104f257600080fd5b82018360208201111561050457600080fd5b803590602001918460208302840111600160201b8311171561052557600080fd5b50909250905061106d565b34801561053c57600080fd5b506103b36111ed565b34801561055157600080fd5b5061038a6111fe565b34801561056657600080fd5b506103ef6004803603602081101561057d57600080fd5b503561120e565b34801561059057600080fd5b5061038a61129d565b3480156105a557600080fd5b506103b36112a6565b3480156105ba57600080fd5b5061038a6112ac565b3480156105cf57600080fd5b506105d86112b5565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156106145781810151838201526020016105fc565b505050509050019250505060405180910390f35b34801561063457600080fd5b5061038a6004803603602081101561064b57600080fd5b50356001600160a01b0316611317565b6103ef6004803603604081101561067157600080fd5b506001600160a01b03813516906020013561132c565b34801561069357600080fd5b506103ef600480360360208110156106aa57600080fd5b503561155b565b3480156106bd57600080fd5b506103ef600480360360208110156106d457600080fd5b5035611646565b3480156106e757600080fd5b50610430600480360360608110156106fe57600080fd5b6001600160a01b0382351691602081013591810190606081016040820135600160201b81111561072d57600080fd5b82018360208201111561073f57600080fd5b803590602001918460018302840111600160201b8311171561076057600080fd5b5090925090506116dd565b34801561077757600080fd5b506105d8611b48565b34801561078c57600080fd5b5061038a611ba8565b3480156107a157600080fd5b506103ef600480360360208110156107b857600080fd5b5035611bb1565b3480156107cb57600080fd5b506103b3611e73565b3480156107e057600080fd5b506103ef600480360360208110156107f757600080fd5b810190602081018135600160201b81111561081157600080fd5b82018360208201111561082357600080fd5b803590602001918460208302840111600160201b8311171561084457600080fd5b509092509050611e7f565b34801561085b57600080fd5b506103ef612066565b34801561087057600080fd5b506103ef6004803603602081101561088757600080fd5b5035612159565b34801561089a57600080fd5b506103b3612242565b3480156108af57600080fd5b506108b8612248565b604080516001600160a01b039092168252519081900360200190f35b3480156108e057600080fd5b506103b3612257565b3480156108f557600080fd5b506103ef6004803603602081101561090c57600080fd5b810190602081018135600160201b81111561092657600080fd5b82018360208201111561093857600080fd5b803590602001918460208302840111600160201b8311171561095957600080fd5b509092509050612263565b34801561097057600080fd5b506103b361254c565b34801561098557600080fd5b506108b8612552565b34801561099a57600080fd5b506103b3612561565b3480156109af57600080fd5b506103ef600480360360208110156109c657600080fd5b5035612567565b3480156109d957600080fd5b506103ef600480360360408110156109f057600080fd5b506001600160a01b038135169060200135151561264a565b348015610a1457600080fd5b506103b360048036036040811015610a2b57600080fd5b506001600160a01b0381351690602001356127f9565b348015610a4d57600080fd5b5061038a612894565b348015610a6257600080fd5b506103ef60048036036060811015610a7957600080fd5b506001600160a01b038135811691602081013590911690604001356128a3565b348015610aa557600080fd5b506103b3612a44565b348015610aba57600080fd5b5061038a612a4a565b348015610acf57600080fd5b506103ef60048036036020811015610ae657600080fd5b5035612a53565b348015610af957600080fd5b506103b3612dc2565b348015610b0e57600080fd5b506103b360048036036020811015610b2557600080fd5b810190602081018135600160201b811115610b3f57600080fd5b820183602082011115610b5157600080fd5b803590602001918460208302840111600160201b83111715610b7257600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550612dc8945050505050565b348015610bbc57600080fd5b506103ef60048036036020811015610bd357600080fd5b5035612e22565b348015610be657600080fd5b506108b860048036036020811015610bfd57600080fd5b5035612fc1565b348015610c1057600080fd5b506103b3612fe8565b348015610c2557600080fd5b5061038a612fee565b348015610c3a57600080fd5b506103b3612ffc565b348015610c4f57600080fd5b506103ef60048036036020811015610c6657600080fd5b5035613002565b348015610c7957600080fd5b506103ef60048036036020811015610c9057600080fd5b5035613166565b348015610ca357600080fd5b506103b360048036036040811015610cba57600080fd5b506001600160a01b0381351690602001356131bf565b348015610cdc57600080fd5b506103ef60048036036020811015610cf357600080fd5b5035613384565b348015610d0657600080fd5b506103ef60048036036020811015610d1d57600080fd5b50356133dd565b348015610d3057600080fd5b506103ef60048036036020811015610d4757600080fd5b810190602081018135600160201b811115610d6157600080fd5b820183602082011115610d7357600080fd5b803590602001918460208302840111600160201b83111715610d9457600080fd5b509092509050613436565b6001600160e01b031981166301ffc9a760e01b145b919050565b600c545b90565b610dc9336136f5565b610e08576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b8066038d7ea4c6800011158015610e2757506706f05b59d3b200008111155b610e625760405162461bcd60e51b815260040180806020018281038252602e815260200180614579602e913960400191505060405180910390fd5b610e73600f8263ffffffff61370916565b604080513381526020810183905281517f41ff5d5ce3b7935893a4e7269ec5caae9cca5e3bf0eb4b21d2f443489667112e929181900390910190a150565b610eba336136f5565b80610ec95750610ec93361377e565b610f17576040805162461bcd60e51b815260206004820152601a60248201527932b4ba3432b91037bbb732b91037b91031b7b73a3937b63632b960311b604482015290519081900360640190fd5b60085460ff16610f585760405162461bcd60e51b81526004018080602001828103825260298152602001806147276029913960400191505060405180910390fd5b610fbb6006805480602002602001604051908101604052809291908181526020018280548015610fb157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610f93575b5050505050612dc8565b8114610ff85760405162461bcd60e51b81526004018080602001828103825260358152602001806147c06035913960400191505060405180910390fd5b6110046006600061444a565b6008805460ff19169055604080513381526020810183905281517f7794eff834d760583543e6e510e717a5e66d2c064e225f4db448343c3e66afcf929181900390910190a150565b604051806040016040528060058152602001640322e322e360dc1b81525081565b611076336136f5565b6110b5576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b80611107576040805162461bcd60e51b815260206004820152601f60248201527f61737365742061727261792073686f756c64206265206e6f6e2d656d70747900604482015290519081900360640190fd5b60005b8181101561116a5760006111393085858581811061112457fe5b905060200201356001600160a01b0316613812565b90506111618585858581811061114b57fe5b905060200201356001600160a01b0316836128a3565b5060010161110a565b507fd4f62f23021706247dcffea245d104ae7ddaec7f23acf3d11d7136d5de6a69ad83838360405180846001600160a01b03166001600160a01b03168152602001806020018281038252848482818152602001925060200280828437600083820152604051601f909101601f1916909201829003965090945050505050a1505050565b60006111f960156138bd565b905090565b600354600160a01b900460ff1690565b611217336136f5565b611256576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b61126760098263ffffffff6138f216565b6040805182815290517f4b1b970c8a0fa761e7803ed70c13d7aca71904b13df60fbe03f981da1730da919181900360200190a150565b60195460ff1690565b600f5490565b60085460ff1681565b6060600780548060200260200160405190810160405280929190818152602001828054801561130d57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116112ef575b5050505050905090565b60046020526000908152604090205460ff1681565b611335336136f5565b611374576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b61137d82613953565b6113c3576040805162461bcd60e51b8152602060048201526012602482015271746f6b656e206e6f74206c6f616461626c6560701b604482015290519081900360640190fd5b60006113cf83836131bf565b90506113e260158263ffffffff61396d16565b60006113ef601a546139da565b90506001600160a01b038416156114975761141a6001600160a01b038516828563ffffffff613a9c16565b806001600160a01b0316631b3c96b485856040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561147a57600080fd5b505af115801561148e573d6000803e3d6000fd5b50505050611511565b806001600160a01b0316631b3c96b48486866040518463ffffffff1660e01b815260040180836001600160a01b03166001600160a01b03168152602001828152602001925050506000604051808303818588803b1580156114f757600080fd5b505af115801561150b573d6000803e3d6000fd5b50505050505b604080516001600160a01b03861681526020810185905281517f5f65674bec9af81f71be68674135a0ea3f163fb91984e3893d06da9f6ea2ce8a929181900390910190a150505050565b611564336136f5565b6115a3576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b8066038d7ea4c68000111580156115bc57506014548111155b6115f75760405162461bcd60e51b815260040180806020018281038252602d8152602001806148dd602d913960400191505060405180910390fd5b61160860158263ffffffff61370916565b604080513381526020810183905281517f0b05243483e17c3f3377aee82b7d47e5700b48288695fc08b7ecc2759afa44ef929181900390910190a150565b61164f336136f5565b61168e576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b61169f60098263ffffffff61370916565b604080513381526020810183905281517f068f112e5ec923d412be64779fe69e0fcbb6784c6617e94cccc8fd348f2e0f21929181900390910190a150565b60606116e8336136f5565b611727576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b6001600160a01b03851660009081526004602052604090205460ff166117585761175860098563ffffffff61396d16565b61176a856001600160a01b0316613bb4565b801561177a575061177a85613bba565b15611999576000806117c28786868080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613bd492505050565b6001600160a01b038216600090815260046020526040902054919350915060ff166118085760006117f388836127f9565b905061180660098263ffffffff61396d16565b505b61185885858080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250506001600160a01b038b169291505063ffffffff613cde16565b604080516020808252818301909252606091602082018180388339019050509050600160f81b81601f8151811061188b57fe5b60200101906001600160f81b031916908160001a9053507ff77753fab406ecfff96d6ff2476c64a838fa9f6d37b1bf190f8546e395e3b613888888888560405180866001600160a01b03166001600160a01b03168152602001858152602001806020018060200183810383528686828181526020019250808284376000838201819052601f909101601f191690920185810384528651815286516020918201939188019250908190849084905b83811015611950578181015183820152602001611938565b50505050905090810190601f16801561197d5780820380516001836020036101000a031916815260200191505b5097505050505050505060405180910390a19250611b40915050565b60006060866001600160a01b0316868686604051808383808284376040519201945060009350909150508083038185875af1925050503d80600081146119fb576040519150601f19603f3d011682016040523d82523d6000602084013e611a00565b606091505b509150915081611a4f576040805162461bcd60e51b81526020600482015260156024820152741b1bddcb5b195d995b0818d85b1b0819985a5b1959605a1b604482015290519081900360640190fd5b7ff77753fab406ecfff96d6ff2476c64a838fa9f6d37b1bf190f8546e395e3b613878787878560405180866001600160a01b03166001600160a01b03168152602001858152602001806020018060200183810383528686828181526020019250808284376000838201819052601f909101601f191690920185810384528651815286516020918201939188019250908190849084905b83811015611afd578181015183820152602001611ae5565b50505050905090810190601f168015611b2a5780820380516001836020036101000a031916815260200191505b5097505050505050505060405180910390a19150505b949350505050565b6060600680548060200260200160405190810160405280929190818152602001828054801561130d576020028201919060005260206000209081546001600160a01b031681526001909101906020018083116112ef575050505050905090565b600d5460ff1690565b611bba3361377e565b611bf9576040805162461bcd60e51b815260206004820152601a602482015260008051602061463a833981519152604482015290519081900360640190fd5b60085460ff16611c3a5760405162461bcd60e51b81526004018080602001828103825260298152602001806147276029913960400191505060405180910390fd5b611c9b6006805480602002602001604051908101604052809291908181526020018280548015610fb1576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311610f93575050505050612dc8565b8114611cd85760405162461bcd60e51b81526004018080602001828103825260338152602001806147f56033913960400191505060405180910390fd5b60005b600654811015611dbf576004600060068381548110611cf657fe5b60009182526020808320909101546001600160a01b0316835282019290925260400190205460ff16611db75760016004600060068481548110611d3557fe5b6000918252602080832091909101546001600160a01b031683528201929092526040019020805460ff1916911515919091179055600680546005919083908110611d7b57fe5b60009182526020808320909101548354600181018555938352912090910180546001600160a01b0319166001600160a01b039092169190911790555b600101611cdb565b507fb2f6cccee7a369e23e293c25aa19bef80af11eb26deba3ea0f2a02783f752e4a33600660405180836001600160a01b03166001600160a01b03168152602001806020018281038252838181548152602001915080548015611e4b57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611e2d575b5050935050505060405180910390a1611e666006600061444a565b506008805460ff19169055565b60006111f960096138bd565b611e88336136f5565b611ec7576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b60085460ff16158015611ee25750600854610100900460ff16155b611f1d5760405162461bcd60e51b815260040180806020018281038252602e81526020018061465a602e913960400191505060405180910390fd5b60085462010000900460ff16611f645760405162461bcd60e51b81526004018080602001828103825260228152602001806145d06022913960400191505060405180910390fd5b80611fa05760405162461bcd60e51b815260040180806020018281038252602281526020018061479e6022913960400191505060405180910390fd5b611fac60078383614468565b506008805461ff00191661010017905560408051602080840282810182019093528382527ffbc0e5ca6c7e4858daf0fdb185ef5186203e74ec9c64737e93c0aeaec596e1d1928592859261201b92859185918291850190849080828437600092019190915250612dc892505050565b60405180806020018381526020018281038252858582818152602001925060200280828437600083820152604051601f909101601f1916909201829003965090945050505050a15050565b61206f336136f5565b6120ae576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b600354600160a01b900460ff1661210c576040805162461bcd60e51b815260206004820152601d60248201527f6f776e657273686970206973206e6f74207472616e7366657261626c65000000604482015290519081900360640190fd5b600380546001600160a01b0319169055604080516000808252602082015281517f850b3df64837d7d518b45f5aa64d104652c3b80eb5b34a8e3d9eb666cb7cdea5929181900390910190a1565b612162336136f5565b6121a1576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b8066038d7ea4c68000111580156121c057506706f05b59d3b200008111155b6121fb5760405162461bcd60e51b815260040180806020018281038252602e815260200180614579602e913960400191505060405180910390fd5b61220c600f8263ffffffff6138f216565b6040805182815290517faf2a77cd04c3cc155588dd3bf67b310ab4fb3b1da3cf6b8d7d4d2aa1d09b794c9181900360200190a150565b601a5490565b6001546001600160a01b031690565b60006111f9600f6138bd565b61226c336136f5565b6122ab576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b60085460ff161580156122c65750600854610100900460ff16155b6123015760405162461bcd60e51b815260040180806020018281038252602e81526020018061465a602e913960400191505060405180910390fd5b8181808060200260200160405190810160405280939291908181526020018383602002808284376000920182905250925050505b81518110156124035761235a82828151811061234d57fe5b60200260200101516136f5565b156123965760405162461bcd60e51b815260040180806020018281038252602d815260200180614771602d913960400191505060405180910390fd5b60006001600160a01b03168282815181106123ad57fe5b60200260200101516001600160a01b031614156123fb5760405162461bcd60e51b815260040180806020018281038252602c81526020018061452a602c913960400191505060405180910390fd5b600101612335565b5060085462010000900460ff1661244b5760405162461bcd60e51b81526004018080602001828103825260228152602001806145d06022913960400191505060405180910390fd5b816124875760405162461bcd60e51b81526004018080602001828103825260238152602001806145566023913960400191505060405180910390fd5b61249360068484614468565b506008805460ff1916600117905560408051602080850282810182019093528482527f9c80b3b5f68b3e017766d59e8d09b34efe6462b05c398f35cab9e271d9bc3b9c928692869261250092859185918291850190849080828437600092019190915250612dc892505050565b60405180806020018381526020018281038252858582818152602001925060200280828437600083820152604051601f909101601f1916909201829003965090945050505050a1505050565b600e5490565b6003546001600160a01b031690565b60095490565b612570336136f5565b6125af576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b8066038d7ea4c68000111580156125c857506014548111155b6126035760405162461bcd60e51b815260040180806020018281038252602d8152602001806148dd602d913960400191505060405180910390fd5b61261460158263ffffffff6138f216565b6040805182815290517fc178d379965e5657b6fc57494e392f121a14119215dfb422aad7db4cc03f2d109181900360200190a150565b612653336136f5565b612692576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b600354600160a01b900460ff166126f0576040805162461bcd60e51b815260206004820152601d60248201527f6f776e657273686970206973206e6f74207472616e7366657261626c65000000604482015290519081900360640190fd5b6001600160a01b0382166127355760405162461bcd60e51b81526004018080602001828103825260238152602001806148286023913960400191505060405180910390fd5b6003805460ff60a01b1916600160a01b831515021790558061278e57604080516001600160a01b038416815290517f808639ff9c8e4732d60b6c2330de498035416d229f27a77d259680895efec1229181900360200190a15b600354604080516001600160a01b039283168152918416602083015280517f850b3df64837d7d518b45f5aa64d104652c3b80eb5b34a8e3d9eb666cb7cdea59281900390910190a150600380546001600160a01b0319166001600160a01b0392909216919091179055565b60008060008061280886613e9c565b505050935093509350508015612886578161285c576040805162461bcd60e51b815260206004820152600f60248201526e0746f6b656e2072617465206973203608c1b604482015290519081900360640190fd5b61287c83612870878563ffffffff613fc716565b9063ffffffff61402716565b935050505061288e565b600093505050505b92915050565b60085462010000900460ff1681565b6128ac336136f5565b6128eb576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b808061293e576040805162461bcd60e51b815260206004820152601d60248201527f70726f76696465642076616c75652063616e6e6f74206265207a65726f000000604482015290519081900360640190fd5b6001600160a01b038416612999576040805162461bcd60e51b815260206004820181905260248201527f5f746f20616464726573732063616e6e6f742062652073657420746f20307830604482015290519081900360640190fd5b6001600160a01b03841660009081526004602052604090205460ff166129e957816001600160a01b038416156129d6576129d384846127f9565b90505b6129e760098263ffffffff61396d16565b505b6129f4848484614091565b604080516001600160a01b0380871682528516602082015280820184905290517fd1ba4ac2e2a11b5101f6cb4d978f514a155b421e8ec396d2d9abaf0bb02917ee9181900360600190a150505050565b60185490565b60135460ff1690565b612a5c3361377e565b612a9b576040805162461bcd60e51b815260206004820152601a602482015260008051602061463a833981519152604482015290519081900360640190fd5b600854610100900460ff16612ae15760405162461bcd60e51b81526004018080602001828103825260288152602001806146126028913960400191505060405180910390fd5b612b426007805480602002602001604051908101604052809291908181526020018280548015610fb1576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311610f93575050505050612dc8565b8114612b7f5760405162461bcd60e51b81526004018080602001828103825260478152602001806146e06047913960600191505060405180910390fd5b60005b600754811015612d0d576004600060078381548110612b9d57fe5b60009182526020808320909101546001600160a01b0316835282019290925260400190205460ff1615612d055760006004600060078481548110612bdd57fe5b6000918252602080832091909101546001600160a01b031683528201929092526040018120805460ff1916921515929092179091555b600554612c2790600163ffffffff6140f516565b811015612cef5760078281548110612c3b57fe5b600091825260209091200154600580546001600160a01b039092169183908110612c6157fe5b6000918252602090912001546001600160a01b03161415612ce757600580546000198101908110612c8e57fe5b600091825260209091200154600580546001600160a01b039092169183908110612cb457fe5b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550612cef565b600101612c13565b506005805490612d039060001983016144cb565b505b600101612b82565b507fd218c430fa348f4ce67791021b6b89c0c3eacd4ead1d8f5b83c60038ec28249b33600760405180836001600160a01b03166001600160a01b03168152602001806020018281038252838181548152602001915080548015612d9957602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612d7b575b5050935050505060405180910390a1612db46007600061444a565b506008805461ff0019169055565b60125490565b60008160405160200180828051906020019060200280838360005b83811015612dfb578181015183820152602001612de3565b50505050905001915050604051602081830303815290604052805190602001209050919050565b612e2b336136f5565b80612e3a5750612e3a3361377e565b612e88576040805162461bcd60e51b815260206004820152601a60248201527932b4ba3432b91037bbb732b91037b91031b7b73a3937b63632b960311b604482015290519081900360640190fd5b600854610100900460ff16612ece5760405162461bcd60e51b81526004018080602001828103825260288152602001806146126028913960400191505060405180910390fd5b612f2f6007805480602002602001604051908101604052809291908181526020018280548015610fb1576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311610f93575050505050612dc8565b8114612f6c5760405162461bcd60e51b81526004018080602001828103825260328152602001806148756032913960400191505060405180910390fd5b612f786007600061444a565b6008805461ff0019169055604080513381526020810183905281517f13c935eb475aa0f6e931fece83e2ac44569ce2d53460d29a6dedab40b965c8a3929181900390910190a150565b60058181548110612fce57fe5b6000918252602090912001546001600160a01b0316905081565b60155490565b600854610100900460ff1681565b60025490565b8080613055576040805162461bcd60e51b815260206004820152601d60248201527f70726f76696465642076616c75652063616e6e6f74206265207a65726f000000604482015290519081900360640190fd5b61305e336136f5565b8061306d575061306d3361377e565b6130bb576040805162461bcd60e51b815260206004820152601a60248201527932b4ba3432b91037bbb732b91037b91031b7b73a3937b63632b960311b604482015290519081900360640190fd5b6130cc600f8363ffffffff61396d16565b6130d4612552565b6001600160a01b03166108fc839081150290604051600060405180830381858888f1935050505015801561310c573d6000803e3d6000fd5b507f611b7c0d84fda988026215bef9b3e4d81cbceced7e679be6d5e044b588467c0e33613137612552565b604080516001600160a01b03938416815291909216602082015280820185905290519081900360600190a15050565b61316f3361377e565b6131ae576040805162461bcd60e51b815260206004820152601a602482015260008051602061463a833981519152604482015290519081900360640190fd5b61169f60098263ffffffff61415216565b60006131c96141a6565b6001600160a01b0316836001600160a01b031614156131e957508061288e565b816001600160a01b038416156132ba57600080600061320787613e9c565b505050935093509350508061325c576040805162461bcd60e51b8152602060048201526016602482015275746f6b656e206973206e6f7420617661696c61626c6560501b604482015290519081900360640190fd5b816132a0576040805162461bcd60e51b815260206004820152600f60248201526e0746f6b656e2072617465206973203608c1b604482015290519081900360640190fd5b6132b483612870888563ffffffff613fc716565b93505050505b60008060006132c761421c565b505050935093509350508061331c576040805162461bcd60e51b8152602060048201526016602482015275746f6b656e206973206e6f7420617661696c61626c6560501b604482015290519081900360640190fd5b81613365576040805162461bcd60e51b81526020600482015260146024820152730737461626c65636f696e207261746520697320360641b604482015290519081900360640190fd5b61337982612870868663ffffffff613fc716565b979650505050505050565b61338d3361377e565b6133cc576040805162461bcd60e51b815260206004820152601a602482015260008051602061463a833981519152604482015290519081900360640190fd5b61160860158263ffffffff61415216565b6133e63361377e565b613425576040805162461bcd60e51b815260206004820152601a602482015260008051602061463a833981519152604482015290519081900360640190fd5b610e73600f8263ffffffff61415216565b61343f336136f5565b61347e576040805162461bcd60e51b815260206004820152601660248201526000805160206145f2833981519152604482015290519081900360640190fd5b8181808060200260200160405190810160405280939291908181526020018383602002808284376000920182905250925050505b8151811015613573576134ca82828151811061234d57fe5b156135065760405162461bcd60e51b815260040180806020018281038252602d815260200180614771602d913960400191505060405180910390fd5b60006001600160a01b031682828151811061351d57fe5b60200260200101516001600160a01b0316141561356b5760405162461bcd60e51b815260040180806020018281038252602c81526020018061452a602c913960400191505060405180910390fd5b6001016134b2565b5060085462010000900460ff16156135bc5760405162461bcd60e51b81526004018080602001828103825260268152602001806146ba6026913960400191505060405180910390fd5b60005b82811015613671576001600460008686858181106135d957fe5b905060200201356001600160a01b03166001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff021916908315150217905550600584848381811061362e57fe5b835460018181018655600095865260209586902090910180546001600160a01b0319166001600160a01b03969093029490940135949094161790915550016135bf565b506008805462ff0000191662010000179055604080513380825260208083018481529383018690527fb2f6cccee7a369e23e293c25aa19bef80af11eb26deba3ea0f2a02783f752e4a9391928792879260608301908590850280828437600083820152604051601f909101601f1916909201829003965090945050505050a1505050565b6003546001600160a01b0390811691161490565b600482015460ff1615613763576040805162461bcd60e51b815260206004820152601a60248201527f6461696c79206c696d6974206e6f742075706461746561626c65000000000000604482015290519081900360640190fd5b61376d8282614322565b50600401805460ff19166001179055565b600061378b6002546139da565b6001600160a01b031663b429afeb836040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156137e057600080fd5b505afa1580156137f4573d6000803e3d6000fd5b505050506040513d602081101561380a57600080fd5b505192915050565b60006001600160a01b038216156138ac57816001600160a01b03166370a08231846040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561387957600080fd5b505afa15801561388d573d6000803e3d6000fd5b505050506040513d60208110156138a357600080fd5b5051905061288e565b506001600160a01b0382163161288e565b60028101546000906138d8906201518063ffffffff61434516565b4211156138e757508054610db4565b506001810154610db4565b600482015460ff1661394b576040805162461bcd60e51b815260206004820152601f60248201527f6461696c79206c696d6974206973207374696c6c2075706461746561626c6500604482015290519081900360640190fd5b600390910155565b60008061395f83613e9c565b509098975050505050505050565b6139768261439f565b80826001015410156139b95760405162461bcd60e51b81526004018080602001828103825260328152602001806146886032913960400191505060405180910390fd5b60018201546139ce908263ffffffff6140f516565b82600101819055505050565b6000805460408051630178b8bf60e01b81526004810185905290516001600160a01b0390921691630178b8bf91602480820192602092909190829003018186803b158015613a2757600080fd5b505afa158015613a3b573d6000803e3d6000fd5b505050506040513d6020811015613a5157600080fd5b505160408051631d9dabef60e11b81526004810185905290516001600160a01b0390921691633b3b57de91602480820192602092909190829003018186803b1580156137e057600080fd5b801580613b22575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b158015613af457600080fd5b505afa158015613b08573d6000803e3d6000fd5b505050506040513d6020811015613b1e57600080fd5b5051155b613b5d5760405162461bcd60e51b81526004018080602001828103825260368152602001806148a76036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052613baf908490613cde565b505050565b3b151590565b600080613bc683613e9c565b509198975050505050505050565b600080613be2600e546139da565b6001600160a01b031663afc72e9385856040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200180602001828103825283818151815260200191508051906020019080838360005b83811015613c56578181015183820152602001613c3e565b50505050905090810190601f168015613c835780820380516001836020036101000a031916815260200191505b509350505050604080518083038186803b158015613ca057600080fd5b505afa158015613cb4573d6000803e3d6000fd5b505050506040513d6040811015613cca57600080fd5b508051602090910151909590945092505050565b613cf0826001600160a01b0316613bb4565b613d41576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b60208310613d7f5780518252601f199092019160209182019101613d60565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613de1576040519150601f19603f3d011682016040523d82523d6000602084013e613de6565b606091505b509150915081613e3d576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115613e9657808060200190516020811015613e5957600080fd5b5051613e965760405162461bcd60e51b815260040180806020018281038252602a81526020018061484b602a913960400191505060405180910390fd5b50505050565b6060600080600080600080613eb2600e546139da565b6001600160a01b0316631f69565f896040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060006040518083038186803b158015613f0757600080fd5b505afa158015613f1b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260e0811015613f4457600080fd5b810190808051600160201b811115613f5b57600080fd5b82016020810184811115613f6e57600080fd5b8151600160201b811182820187101715613f8757600080fd5b5050602082015160408301516060840151608085015160a086015160c090960151949e50929c50909a509850965090945092505050919395979092949650565b600082613fd65750600061288e565b82820282848281613fe357fe5b04146140205760405162461bcd60e51b81526004018080602001828103825260218152602001806147506021913960400191505060405180910390fd5b9392505050565b600080821161407d576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b600082848161408857fe5b04949350505050565b6001600160a01b0382166140db576040516001600160a01b0384169082156108fc029083906000818181858888f193505050501580156140d5573d6000803e3d6000fd5b50613baf565b613baf6001600160a01b038316848363ffffffff6143f816565b60008282111561414c576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b808260030154146141945760405162461bcd60e51b81526004018080602001828103825260298152602001806145a76029913960400191505060405180910390fd5b6141a2828360030154614322565b5050565b60006141b3600e546139da565b6001600160a01b031663e9cbd8226040518163ffffffff1660e01b815260040160206040518083038186803b1580156141eb57600080fd5b505afa1580156141ff573d6000803e3d6000fd5b505050506040513d602081101561421557600080fd5b5051905090565b6060600080600080600080614232600e546139da565b6001600160a01b0316633efec5e96040518163ffffffff1660e01b815260040160006040518083038186803b15801561426a57600080fd5b505afa15801561427e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260e08110156142a757600080fd5b810190808051600160201b8111156142be57600080fd5b820160208101848111156142d157600080fd5b8151600160201b8111828201871017156142ea57600080fd5b5050602082015160408301516060840151608085015160a086015160c090960151949f939e50919c509a509850919650945092505050565b61432b8261439f565b80825560018201548110156141a257815460018301555050565b600082820183811015614020576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b60028101546143b7906201518063ffffffff61434516565b4211156143f557426002820155805460018201556040517fe93bc25276d408d390778e7a8b926f2f67209c43ed540081b951fe128f0d3cd290600090a15b50565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052613baf908490613cde565b50805460008255906000526020600020908101906143f591906144eb565b8280548282559060005260206000209081019282156144bb579160200282015b828111156144bb5781546001600160a01b0319166001600160a01b03843516178255602090920191600190910190614488565b506144c7929150614505565b5090565b815481835581811115613baf57600083815260209020613baf9181019083015b610dbd91905b808211156144c757600081556001016144f1565b610dbd91905b808211156144c75780546001600160a01b031916815560010161450b56fe70726f76696465642077686974656c69737420636f6e7461696e7320746865207a65726f206164647265737370656e64696e672077686974656c697374206164646974696f6e20697320656d70747967617320746f7020757020616d6f756e74206973206f75747369646520746865206d696e2f6d61782072616e6765636f6e6669726d656420616e64207375626d6974746564206c696d69747320646f6e74206d6174636877686974656c69737420686173206e6f74206265656e20696e697469616c697a656473656e646572206973206e6f7420616e206f776e65720000000000000000000077686974656c6973742072656d6f76616c20686173206e6f74206265656e207375626d697474656473656e646572206973206e6f74206120636f6e74726f6c6c657200000000000077686974656c697374206f7065726174696f6e2068617320616c7265616479206265656e207375626d6974746564617661696c61626c652068617320746f2062652067726561746572206f7220657175616c20746f2075736520616d6f756e7477686974656c6973742068617320616c7265616479206265656e20696e697469616c697a656468617368206f66207468652070656e64696e672077686974656c6973742072656d6f76616c20646f6573206e6f74206d617463682074686520636f6e6669726d6564206861736877686974656c697374206164646974696f6e20686173206e6f74206265656e207375626d6974746564536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7770726f76696465642077686974656c69737420636f6e7461696e7320746865206f776e6572206164647265737370656e64696e672077686974656c6973742072656d6f76616c20697320656d70747968617368206f66207468652070656e64696e672077686974656c697374206164646974696f6e20646f6573206e6f74206d6174636868617368206f66207468652070656e64696e672077686974656c697374206164646974696f6e20646f206e6f74206d617463686f776e65722063616e6e6f742062652073657420746f207a65726f20616464726573735361666545524332303a204552433230206f7065726174696f6e20646964206e6f74207375636365656468617368206f66207468652070656e64696e672077686974656c6973742072656d6f76616c20646f206e6f74206d617463685361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e636563617264206c6f616420616d6f756e74206973206f75747369646520746865206d696e2f6d61782072616e6765a265627a7a7230582039d1a7c207df4e9874dc1b1a8be5453ec2db2d73fab93e6dc8a826b323ac459264736f6c634300050a0032
Deployed Bytecode Sourcemap
116276:7902:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;112837:31;;;112846:10;112837:31;;112858:9;112837:31;;;;;;;;;;;;;;;;;116276:7902;112944:139;;8:9:-1;5:2;;;30:1;27;20:12;5:2;112944:139:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;112944:139:0;-1:-1:-1;;;;;;112944:139:0;;:::i;:::-;;;;;;;;;;;;;;;;;;106362:103;;8:9:-1;5:2;;;30:1;27;20:12;5:2;106362:103:0;;;:::i;:::-;;;;;;;;;;;;;;;;107297:308;;8:9:-1;5:2;;;30:1;27;20:12;5:2;107297:308:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;107297:308:0;;:::i;:::-;;96856:726;;8:9:-1;5:2;;;30:1;27;20:12;5:2;96856:726:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;96856:726:0;;:::i;116683:47::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;116683:47:0;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:100:-1;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;116683:47:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;113431:575;;8:9:-1;5:2;;;30:1;27;20:12;5:2;113431:575:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;113431:575:0;;;;;;;;;;;;;;;-1:-1:-1;;;5:28;;2:2;;;46:1;43;36:12;2:2;113431:575:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;113431:575:0;;;;;;101:9:-1;95:2;81:12;77:21;67:8;63:36;60:51;-1:-1;;;25:12;22:29;11:108;8:2;;;132:1;129;122:12;8:2;-1:-1;113431:575:0;;-1:-1:-1;113431:575:0;-1:-1:-1;113431:575:0;:::i;110330:116::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;110330:116:0;;;:::i;2873:96::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;2873:96:0;;;:::i;105586:173::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;105586:173:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;105586:173:0;;:::i;110559:107::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;110559:107:0;;;:::i;108461:105::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;108461:105:0;;;:::i;92483:38::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;92483:38:0;;;:::i;93565:126::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;93565:126:0;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:100:-1;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;93565:126:0;;;;;;;;;;;;;;;;;92295:44;;8:9:-1;5:2;;;30:1;27;20:12;5:2;92295:44:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;92295:44:0;-1:-1:-1;;;;;92295:44:0;;:::i;119056:950::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;119056:950:0;;;;;;;;:::i;109378:283::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;109378:283:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;109378:283:0;;:::i;105283:155::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;105283:155:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;105283:155:0;;:::i;120268:1970::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;120268:1970:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;120268:1970:0;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5:28;;2:2;;;46:1;43;36:12;2:2;120268:1970:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;120268:1970:0;;;;;;100:9:-1;95:1;81:12;77:20;67:8;63:35;60:50;-1:-1;;;25:12;22:29;11:107;8:2;;;131:1;128;121:12;8:2;-1:-1;120268:1970:0;;-1:-1:-1;120268:1970:0;-1:-1:-1;120268:1970:0;:::i;93381:128::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;93381:128:0;;;:::i;106245:109::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;106245:109:0;;;:::i;95629:1170::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;95629:1170:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;95629:1170:0;;:::i;106012:118::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;106012:118:0;;;:::i;97710:706::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;97710:706:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;97710:706:0;;;;;;;;-1:-1:-1;;;5:28;;2:2;;;46:1;43;36:12;2:2;97710:706:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;97710:706:0;;;;;;101:9:-1;95:2;81:12;77:21;67:8;63:36;60:51;-1:-1;;;25:12;22:29;11:108;8:2;;;132:1;129;122:12;8:2;-1:-1;97710:706:0;;-1:-1:-1;97710:706:0;-1:-1:-1;97710:706:0;:::i;3250:313::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;3250:313:0;;;:::i;107736:326::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;107736:326:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;107736:326:0;;:::i;122304:93::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;122304:93:0;;;:::i;59565:::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;59565:93:0;;;:::i;:::-;;;;-1:-1:-1;;;;;59565:93:0;;;;;;;;;;;;;;108329:124;;8:9:-1;5:2;;;30:1;27;20:12;5:2;108329:124:0;;;:::i;94656:745::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;94656:745:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;94656:745:0;;;;;;;;-1:-1:-1;;;5:28;;2:2;;;46:1;43;36:12;2:2;94656:745:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;94656:745:0;;;;;;101:9:-1;95:2;81:12;77:21;67:8;63:36;60:51;-1:-1;;;25:12;22:29;11:108;8:2;;;132:1;129;122:12;8:2;-1:-1;94656:745:0;;-1:-1:-1;94656:745:0;-1:-1:-1;94656:745:0;:::i;87104:107::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;87104:107:0;;;:::i;3650:87::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;3650:87:0;;;:::i;106138:99::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;106138:99:0;;;:::i;109780:301::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;109780:301:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;109780:301:0;;:::i;1928:824::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1928:824:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;1928:824:0;;;;;;;;;;:::i;115605:562::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;115605:562:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;115605:562:0;;;;;;;;:::i;92572:26::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;92572:26:0;;;:::i;114289:1124::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;114289:1124:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;114289:1124:0;;;;;;;;;;;;;;;;;:::i;110674:101::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;110674:101:0;;;:::i;108574:115::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;108574:115:0;;;:::i;98488:1432::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;98488:1432:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;98488:1432:0;;:::i;108697:109::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;108697:109:0;;;:::i;100767:147::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;100767:147:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;100767:147:0;;;;;;;;-1:-1:-1;;;5:28;;2:2;;;46:1;43;36:12;2:2;100767:147:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;100767:147:0;;;;;;101:9:-1;95:2;81:12;77:21;67:8;63:36;60:51;-1:-1;;;25:12;22:29;11:108;8:2;;;132:1;129;122:12;8:2;100767:147:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;100767:147:0;;-1:-1:-1;100767:147:0;;-1:-1:-1;;;;;100767:147:0:i;99991:704::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;99991:704:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;99991:704:0;;:::i;92346:31::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;92346:31:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;92346:31:0;;:::i;110454:97::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;110454:97:0;;;:::i;92528:37::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;92528:37:0;;;:::i;60817:99::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;60817:99:0;;;:::i;118325:429::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;118325:429:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;118325:429:0;;:::i;105824:180::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;105824:180:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;105824:180:0;;:::i;122603:1570::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;122603:1570:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;122603:1570:0;;;;;;;;:::i;110145:177::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;110145:177:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;110145:177:0;;:::i;108132:189::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;108132:189:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;108132:189:0;;:::i;93826:703::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;93826:703:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;93826:703:0;;;;;;;;-1:-1:-1;;;5:28;;2:2;;;46:1;43;36:12;2:2;93826:703:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;93826:703:0;;;;;;101:9:-1;95:2;81:12;77:21;67:8;63:36;60:51;-1:-1;;;25:12;22:29;11:108;8:2;;;132:1;129;122:12;8:2;-1:-1;93826:703:0;;-1:-1:-1;93826:703:0;-1:-1:-1;93826:703:0;:::i;112944:139::-;-1:-1:-1;;;;;;113039:36:0;;-1:-1:-1;;;113039:36:0;112944:139;;;;:::o;106362:103::-;106438:19;;106362:103;;:::o;107297:308::-;1614:20;1623:10;1614:8;:20::i;:::-;1606:55;;;;;-1:-1:-1;;;1606:55:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1606:55:0;;;;;;;;;;;;;;;107403:7;106758:8;107375:35;;:74;;;;;106822:10;107414:7;:35;;107375:74;107367:133;;;;-1:-1:-1;;;107367:133:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;107511:33;:14;107536:7;107511:33;:24;:33;:::i;:::-;107560:37;;;107577:10;107560:37;;;;;;;;;;;;;;;;;;;;;107297:308;:::o;96856:726::-;91437:20;91446:10;91437:8;:20::i;:::-;:49;;;;91461:25;91475:10;91461:13;:25::i;:::-;91428:89;;;;;-1:-1:-1;;;91428:89:0;;;;;;;;;;;;-1:-1:-1;;;91428:89:0;;;;;;;;;;;;;;;97005:26;;;;96997:80;;;;-1:-1:-1;;;96997:80:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;97201:40;97215:25;97201:40;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;97201:40:0;;;;;;;;;;;;;;;;;;;;;:13;:40::i;:::-;97192:5;:49;97184:115;;;;-1:-1:-1;;;97184:115:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;97347:32;97354:25;;97347:32;:::i;:::-;97438:26;:34;;-1:-1:-1;;97438:34:0;;;97529:45;;;97556:10;97529:45;;;;;;;;;;;;;;;;;;;;;96856:726;:::o;116683:47::-;;;;;;;;;;;;;;-1:-1:-1;;;116683:47:0;;;;:::o;113431:575::-;1614:20;1623:10;1614:8;:20::i;:::-;1606:55;;;;;-1:-1:-1;;;1606:55:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1606:55:0;;;;;;;;;;;;;;;113596:19;113588:63;;;;;-1:-1:-1;;;113588:63:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;113730:6;113725:227;113742:18;;;113725:227;;;113782:11;113796:35;113813:4;113820:7;;113828:1;113820:10;;;;;;;;;;;;;-1:-1:-1;;;;;113820:10:0;113796:8;:35::i;:::-;113782:49;;113907:33;113916:3;113921:7;;113929:1;113921:10;;;;;;;;;;;;;-1:-1:-1;;;;;113921:10:0;113933:6;113907:8;:33::i;:::-;-1:-1:-1;113762:3:0;;113725:227;;;;113969:29;113985:3;113990:7;;113969:29;;;;-1:-1:-1;;;;;113969:29:0;-1:-1:-1;;;;;113969:29:0;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;;74:27;113969:29:0;;137:4:-1;117:14;;;-1:-1;;113:30;157:16;;;113969:29:0;;;;-1:-1:-1;113969:29:0;;-1:-1:-1;;;;;113969:29:0;113431:575;;;:::o;110330:116::-;110383:4;110407:31;:10;:29;:31::i;:::-;110400:38;;110330:116;:::o;2873:96::-;2946:15;;-1:-1:-1;;;2946:15:0;;;;;2873:96::o;105586:173::-;1614:20;1623:10;1614:8;:20::i;:::-;1606:55;;;;;-1:-1:-1;;;1606:55:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1606:55:0;;;;;;;;;;;;;;;105662:39;:11;105693:7;105662:39;:30;:39;:::i;:::-;105717:34;;;;;;;;;;;;;;;;;105586:173;:::o;110559:107::-;110637:21;;;;110559:107;:::o;108461:105::-;108538:14;:20;108461:105;:::o;92483:38::-;;;;;;:::o;93565:126::-;93623:16;93659:24;93652:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;93652:31:0;;;;;;;;;;;;;;;;;;;;;;;93565:126;:::o;92295:44::-;;;;;;;;;;;;;;;:::o;119056:950::-;1614:20;1623:10;1614:8;:20::i;:::-;1606:55;;;;;-1:-1:-1;;;1606:55:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1606:55:0;;;;;;;;;;;;;;;119225:24;119242:6;119225:16;:24::i;:::-;119217:55;;;;;-1:-1:-1;;;119217:55:0;;;;;;;;;;;;-1:-1:-1;;;119217:55:0;;;;;;;;;;;;;;;119337:20;119360:36;119380:6;119388:7;119360:19;:36::i;:::-;119337:59;-1:-1:-1;119528:41:0;:10;119337:59;119528:41;:24;:41;:::i;:::-;119628:22;119653:25;119665:12;;119653:11;:25::i;:::-;119628:50;-1:-1:-1;;;;;;119693:20:0;;;119689:258;;119730:50;-1:-1:-1;;;;;119730:25:0;;119756:14;119772:7;119730:50;:25;:50;:::i;:::-;119804:14;-1:-1:-1;;;;;119795:29:0;;119825:6;119833:7;119795:46;;;;;;;;;;;;;-1:-1:-1;;;;;119795:46:0;-1:-1:-1;;;;;119795:46:0;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;119795:46:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;119795:46:0;;;;119689:258;;;119883:14;-1:-1:-1;;;;;119874:29:0;;119910:7;119919:6;119927:7;119874:61;;;;;;;;;;;;;-1:-1:-1;;;;;119874:61:0;-1:-1:-1;;;;;119874:61:0;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;119874:61:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;119874:61:0;;;;;119689:258;119964:32;;;-1:-1:-1;;;;;119964:32:0;;;;;;;;;;;;;;;;;;;;;;;1672:1;;119056:950;;:::o;109378:283::-;1614:20;1623:10;1614:8;:20::i;:::-;1606:55;;;;;-1:-1:-1;;;1606:55:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1606:55:0;;;;;;;;;;;;;;;109475:7;109082:8;109452:30;;:62;;;;;109497:17;;109486:7;:28;;109452:62;109444:120;;;;-1:-1:-1;;;109444:120:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;109575:29;:10;109596:7;109575:29;:20;:29;:::i;:::-;109620:33;;;109633:10;109620:33;;;;;;;;;;;;;;;;;;;;;109378:283;:::o;105283:155::-;1614:20;1623:10;1614:8;:20::i;:::-;1606:55;;;;;-1:-1:-1;;;1606:55:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1606:55:0;;;;;;;;;;;;;;;105350:30;:11;105372:7;105350:30;:21;:30;:::i;:::-;105396:34;;;105410:10;105396:34;;;;;;;;;;;;;;;;;;;;;105283:155;:::o;120268:1970::-;120381:12;1614:20;1623:10;1614:8;:20::i;:::-;1606:55;;;;;-1:-1:-1;;;1606:55:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1606:55:0;;;;;;;;;;;;;;;-1:-1:-1;;;;;120603:26:0;;;;;;:12;:26;;;;;;;;120598:93;;120646:33;:11;120672:6;120646:33;:25;:33;:::i;:::-;120794:34;120802:12;-1:-1:-1;;;;;120794:32:0;;:34::i;:::-;:69;;;;;120832:31;120850:12;120832:17;:31::i;:::-;120790:1119;;;120968:10;120993:11;121034:48;121062:12;121076:5;;121034:48;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;121034:27:0;;-1:-1:-1;;;121034:48:0:i;:::-;-1:-1:-1;;;;;121102:16:0;;;;;;:12;:16;;;;;;121019:63;;-1:-1:-1;121019:63:0;-1:-1:-1;121102:16:0;;121097:343;;121314:15;121332:36;121347:12;121361:6;121332:14;:36::i;:::-;121314:54;-1:-1:-1;121387:37:0;:11;121314:54;121387:37;:25;:37;:::i;:::-;121097:343;;121592:45;121631:5;;121592:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;;;;;;;121592:38:0;;;:45;-1:-1:-1;;121592:45:0;:38;:45;:::i;:::-;121761:13;;;121771:2;121761:13;;;;;;;;;121744:14;;121761:13;;;21:6:-1;;104:10;121761:13:0;87:34:-1;135:17;;-1:-1;121761:13:0;121744:30;;121797:4;121789:12;;:1;121791:2;121789:5;;;;;;;;;;;:12;-1:-1:-1;;;;;121789:12:0;;;;;;;;;121823:51;121843:12;121857:6;121865:5;;121872:1;121823:51;;;;-1:-1:-1;;;;;121823:51:0;-1:-1:-1;;;;;121823:51:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;;74:27;;;137:4;117:14;;;-1:-1;;113:30;157:16;;;121823:51:0;;;;;;;;;;;;;;;;;;;;-1:-1:-1;121823:51:0;;;;;;;8:100:-1;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;121823:51:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;121896:1;-1:-1:-1;121889:8:0;;-1:-1:-1;;121889:8:0;120790:1119;121922:12;121936:23;121963:12;-1:-1:-1;;;;;121963:17:0;121987:6;121995:5;;121963:38;;;;;30:3:-1;22:6;14;1:33;121963:38:0;;45:16:-1;;;-1:-1;121963:38:0;;-1:-1:-1;121963:38:0;;-1:-1:-1;;121963:38:0;;;;;;;;;;;;;14:1:-1;21;16:31;;;;75:4;69:11;64:16;;144:4;140:9;133:4;115:16;111:27;107:43;104:1;100:51;94:4;87:65;169:16;166:1;159:27;225:16;222:1;215:4;212:1;208:12;193:49;7:242;;16:31;36:4;31:9;;7:242;;121921:80:0;;;;122020:7;122012:41;;;;;-1:-1:-1;;;122012:41:0;;;;;;;;;;;;-1:-1:-1;;;122012:41:0;;;;;;;;;;;;;;;122071:60;122091:12;122105:6;122113:5;;122120:10;122071:60;;;;-1:-1:-1;;;;;122071:60:0;-1:-1:-1;;;;;122071:60:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;;74:27;;;137:4;117:14;;;-1:-1;;113:30;157:16;;;122071:60:0;;;;;;;;;;;;;;;;;;;;-1:-1:-1;122071:60:0;;;;;;;8:100:-1;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;122071:60:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;122220:10;-1:-1:-1;;1672:1:0;120268:1970;;;;;;:::o;93381:128::-;93440:16;93476:25;93469:32;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;93469:32:0;;;;;;;;;;;;;;;;;;;;;;93381:128;:::o;106245:109::-;106324:22;;;;106245:109;:::o;95629:1170::-;60507:25;60521:10;60507:13;:25::i;:::-;60499:64;;;;;-1:-1:-1;;;60499:64:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;60499:64:0;;;;;;;;;;;;;;;95789:26;;;;95781:80;;;;-1:-1:-1;;;95781:80:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;95985:40;95999:25;95985:40;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;95985:40:0;;;;;;;;;;;;;;;;;;;;:13;:40::i;:::-;95976:5;:49;95968:113;;;;-1:-1:-1;;;95968:113:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;96138:6;96133:387;96154:25;:32;96150:36;;96133:387;;;96264:12;:42;96277:25;96303:1;96277:28;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;96277:28:0;96264:42;;;;;;;;;;;;;;;96259:250;;96421:4;96376:12;:42;96389:25;96415:1;96389:28;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;96389:28:0;96376:42;;;;;;;;;;;;:49;;-1:-1:-1;;96376:49:0;;;;;;;;;;96464:25;:28;;96444:14;;96464:25;96490:1;;96464:28;;;;;;;;;;;;;;;;;;27:10:-1;;96464:28:0;23:18:-1;;45:23;;96444:49:0;;;;;;;;;;-1:-1:-1;;;;;;96444:49:0;-1:-1:-1;;;;;96464:28:0;;;96444:49;;;;;;96259:250;96188:3;;96133:387;;;;96572:55;96589:10;96601:25;96572:55;;;;-1:-1:-1;;;;;96572:55:0;-1:-1:-1;;;;;96572:55:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;96572:55:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;96675:32;96682:25;;96675:32;:::i;:::-;-1:-1:-1;96757:26:0;:34;;-1:-1:-1;;96757:34:0;;;95629:1170::o;106012:118::-;106066:4;106090:32;:11;:30;:32::i;97710:706::-;1614:20;1623:10;1614:8;:20::i;:::-;1606:55;;;;;-1:-1:-1;;;1606:55:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1606:55:0;;;;;;;;;;;;;;;93197:26;;;;93196:27;:57;;;;-1:-1:-1;93228:25:0;;;;;;;93227:26;93196:57;93188:116;;;;-1:-1:-1;;;93188:116:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;97891:14;;;;;;;97883:61;;;;-1:-1:-1;;;97883:61:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;98024:21;98016:68;;;;-1:-1:-1;;;98016:68:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;98164:37;:24;98191:10;;98164:37;:::i;:::-;-1:-1:-1;98257:25:0;:32;;-1:-1:-1;;98257:32:0;;;;;98382:25;;;;;;;;;;;;;;;;;;98344:64;;98370:10;;;;98382:25;;98370:10;;;;;;98382:25;;;98370:10;;98382:25;98370:10;98382:25;1:33:-1;99:1;81:16;;74:27;;;;-1:-1;98382:13:0;;-1:-1:-1;;;98382:25:0:i;:::-;98344:64;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;;74:27;98344:64:0;;137:4:-1;117:14;;;-1:-1;;113:30;157:16;;;98344:64:0;;;;-1:-1:-1;98344:64:0;;-1:-1:-1;;;;;98344:64:0;97710:706;;:::o;3250:313::-;1614:20;1623:10;1614:8;:20::i;:::-;1606:55;;;;;-1:-1:-1;;;1606:55:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1606:55:0;;;;;;;;;;;;;;;3373:15;;-1:-1:-1;;;3373:15:0;;;;3365:57;;;;;-1:-1:-1;;;3365:57:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;3478:6;:19;;-1:-1:-1;;;;;;3478:19:0;;;3515:40;;;3495:1;3515:40;;;;;;;;;;;;;;;;;;;;3250:313::o;107736:326::-;1614:20;1623:10;1614:8;:20::i;:::-;1606:55;;;;;-1:-1:-1;;;1606:55:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1606:55:0;;;;;;;;;;;;;;;107851:7;106758:8;107823:35;;:74;;;;;106822:10;107862:7;:35;;107823:74;107815:133;;;;-1:-1:-1;;;107815:133:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;107959:42;:14;107993:7;107959:42;:33;:42;:::i;:::-;108017:37;;;;;;;;;;;;;;;;;107736:326;:::o;122304:93::-;122377:12;;122304:93;:::o;59565:::-;59638:12;;-1:-1:-1;;;;;59638:12:0;59565:93;:::o;108329:124::-;108386:4;108410:35;:14;:33;:35::i;94656:745::-;1614:20;1623:10;1614:8;:20::i;:::-;1606:55;;;;;-1:-1:-1;;;1606:55:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1606:55:0;;;;;;;;;;;;;;;93197:26;;;;93196:27;:57;;;;-1:-1:-1;93228:25:0;;;;;;;93227:26;93196:57;93188:116;;;;-1:-1:-1;;;93188:116:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;94782:10;;92700:345;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;-1:-1;99:1;-1:-1;;;92773:253:0;92794:10;:17;92790:1;:21;92773:253;;;92842:23;92851:10;92862:1;92851:13;;;;;;;;;;;;;;92842:8;:23::i;:::-;92841:24;92833:82;;;;-1:-1:-1;;;92833:82:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;92963:1;-1:-1:-1;;;;;92938:27:0;:10;92949:1;92938:13;;;;;;;;;;;;;;-1:-1:-1;;;;;92938:27:0;;;92930:84;;;;-1:-1:-1;;;92930:84:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;92813:3;;92773:253;;;-1:-1:-1;94874:14:0;;;;;;;94866:61;;;;-1:-1:-1;;;94866:61:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;95000:21;94992:69;;;;-1:-1:-1;;;94992:69:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;95146:38;:25;95174:10;;95146:38;:::i;:::-;-1:-1:-1;95240:26:0;:33;;-1:-1:-1;;95240:33:0;95269:4;95240:33;;;95367:25;;;;;;;;;;;;;;;;;;95328:65;;95355:10;;;;95367:25;;95355:10;;;;;;95367:25;;;95355:10;;95367:25;95355:10;95367:25;1:33:-1;99:1;81:16;;74:27;;;;-1:-1;95367:13:0;;-1:-1:-1;;;95367:25:0:i;:::-;95328:65;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;;74:27;95328:65:0;;137:4:-1;117:14;;;-1:-1;;113:30;157:16;;;95328:65:0;;;;-1:-1:-1;95328:65:0;;-1:-1:-1;;;;;95328:65:0;93315:1;94656:745;;:::o;87104:107::-;87184:19;;87104:107;:::o;3650:87::-;3723:6;;-1:-1:-1;;;;;3723:6:0;3650:87;:::o;106138:99::-;106212:11;:17;106138:99;:::o;109780:301::-;1614:20;1623:10;1614:8;:20::i;:::-;1606:55;;;;;-1:-1:-1;;;1606:55:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1606:55:0;;;;;;;;;;;;;;;109886:7;109082:8;109863:30;;:62;;;;;109908:17;;109897:7;:28;;109863:62;109855:120;;;;-1:-1:-1;;;109855:120:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;109986:38;:10;110016:7;109986:38;:29;:38;:::i;:::-;110040:33;;;;;;;;;;;;;;;;;109780:301;:::o;1928:824::-;1614:20;1623:10;1614:8;:20::i;:::-;1606:55;;;;;-1:-1:-1;;;1606:55:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1606:55:0;;;;;;;;;;;;;;;2095:15;;-1:-1:-1;;;2095:15:0;;;;2087:57;;;;;-1:-1:-1;;;2087:57:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;2227:22:0;;2219:70;;;;-1:-1:-1;;;2219:70:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2376:15;:31;;-1:-1:-1;;;;2376:31:0;-1:-1:-1;;;2376:31:0;;;;;;;;2488:77;;2528:25;;;-1:-1:-1;;;;;2528:25:0;;;;;;;;;;;;;;;2488:77;2648:6;;2627:38;;;-1:-1:-1;;;;;2648:6:0;;;2627:38;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;2727:6:0;:17;;-1:-1:-1;;;;;;2727:17:0;-1:-1:-1;;;;;2727:17:0;;;;;;;;;;1928:824::o;115605:562::-;115680:4;115767:17;115786:12;115800:14;115824:21;115838:6;115824:13;:21::i;:::-;115765:80;;;;;;;;;;115927:9;115923:218;;;115961:9;115953:37;;;;;-1:-1:-1;;;115953:37:0;;;;;;;;;;;;-1:-1:-1;;;115953:37:0;;;;;;;;;;;;;;;116097:32;116119:9;116097:17;:7;116109:4;116097:17;:11;:17;:::i;:::-;:21;:32;:21;:32;:::i;:::-;116090:39;;;;;;;115923:218;116158:1;116151:8;;;;;115605:562;;;;;:::o;92572:26::-;;;;;;;;;:::o;114289:1124::-;1614:20;1623:10;1614:8;:20::i;:::-;1606:55;;;;;-1:-1:-1;;;1606:55:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1606:55:0;;;;;;;;;;;;;;;114385:7;112622:11;112614:53;;;;;-1:-1:-1;;;112614:53:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;114475:17:0;;114467:62;;;;;-1:-1:-1;;;114467:62:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;114621:17:0;;;;;;:12;:17;;;;;;;;114616:591;;114737:7;-1:-1:-1;;;;;114843:20:0;;;114839:105;;114897:31;114912:6;114920:7;114897:14;:31::i;:::-;114884:44;;114839:105;115158:37;:11;115184:10;115158:37;:25;:37;:::i;:::-;114616:591;;115284:35;115298:3;115303:6;115311:7;115284:13;:35::i;:::-;115372:33;;;-1:-1:-1;;;;;115372:33:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;1672:1;114289:1124;;;:::o;110674:101::-;110749:18;;110674:101;:::o;108574:115::-;108656:25;;;;108574:115;:::o;98488:1432::-;60507:25;60521:10;60507:13;:25::i;:::-;60499:64;;;;;-1:-1:-1;;;60499:64:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;60499:64:0;;;;;;;;;;;;;;;98677:25;;;;;;;98669:78;;;;-1:-1:-1;;;98669:78:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;98870:39;98884:24;98870:39;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;98870:39:0;;;;;;;;;;;;;;;;;;;;:13;:39::i;:::-;98861:5;:48;98853:132;;;;-1:-1:-1;;;98853:132:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;99039:6;99034:607;99055:24;:31;99051:35;;99034:607;;;99147:12;:41;99160:24;99185:1;99160:27;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;99160:27:0;99147:41;;;;;;;;;;;;;;;99143:487;;;99253:5;99209:12;:41;99222:24;99247:1;99222:27;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;99222:27:0;99209:41;;;;;;;;;;;;:49;;-1:-1:-1;;99209:49:0;;;;;;;;;;;99277:296;99298:14;:21;:28;;99324:1;99298:28;:25;:28;:::i;:::-;99294:1;:32;99277:296;;;99381:24;99406:1;99381:27;;;;;;;;;;;;;;;;;;99360:14;:17;;-1:-1:-1;;;;;99381:27:0;;;;99375:1;;99360:17;;;;;;;;;;;;;;;;-1:-1:-1;;;;;99360:17:0;:48;99356:198;;;99457:14;99472:21;;-1:-1:-1;;99472:25:0;;;99457:41;;;;;;;;;;;;;;;;99437:14;:17;;-1:-1:-1;;;;;99457:41:0;;;;99452:1;;99437:17;;;;;;;;;;;;;;:61;;;;;-1:-1:-1;;;;;99437:61:0;;;;;-1:-1:-1;;;;;99437:61:0;;;;;;99525:5;;99356:198;99328:3;;99277:296;;;-1:-1:-1;99591:14:0;:23;;;;;-1:-1:-1;;99591:23:0;;;:::i;:::-;;99143:487;99088:3;;99034:607;;;;99692:58;99713:10;99725:24;99692:58;;;;-1:-1:-1;;;;;99692:58:0;-1:-1:-1;;;;;99692:58:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;99692:58:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;99798:31;99805:24;;99798:31;:::i;:::-;-1:-1:-1;99879:25:0;:33;;-1:-1:-1;;99879:33:0;;;98488:1432::o;108697:109::-;108776:22;;108697:109;:::o;100767:147::-;100840:7;100894:10;100877:28;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;100877:28:0;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;100877:28:0;;;100867:39;;;;;;100860:46;;100767:147;;;:::o;99991:704::-;91437:20;91446:10;91437:8;:20::i;:::-;:49;;;;91461:25;91475:10;91461:13;:25::i;:::-;91428:89;;;;;-1:-1:-1;;;91428:89:0;;;;;;;;;;;;-1:-1:-1;;;91428:89:0;;;;;;;;;;;;;;;100139:25;;;;;;;100131:78;;;;-1:-1:-1;;;100131:78:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;100332:39;100346:24;100332:39;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;100332:39:0;;;;;;;;;;;;;;;;;;;;:13;:39::i;:::-;100323:5;:48;100315:111;;;;-1:-1:-1;;;100315:111:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;100474:31;100481:24;;100474:31;:::i;:::-;100553:25;:33;;-1:-1:-1;;100553:33:0;;;100643:44;;;100669:10;100643:44;;;;;;;;;;;;;;;;;;;;;99991:704;:::o;92346:31::-;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;92346:31:0;;-1:-1:-1;92346:31:0;:::o;110454:97::-;110527:10;:16;110454:97;:::o;92528:37::-;;;;;;;;;:::o;60817:99::-;60893:15;;60817:99;:::o;118325:429::-;118376:7;112622:11;112614:53;;;;;-1:-1:-1;;;112614:53:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;91437:20;91446:10;91437:8;:20::i;:::-;:49;;;;91461:25;91475:10;91461:13;:25::i;:::-;91428:89;;;;;-1:-1:-1;;;91428:89:0;;;;;;;;;;;;-1:-1:-1;;;91428:89:0;;;;;;;;;;;;;;;118539:37;:14;118568:7;118539:37;:28;:37;:::i;:::-;118625:7;:5;:7::i;:::-;-1:-1:-1;;;;;118625:16:0;:25;118642:7;118625:25;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;118625:25:0;118705:41;118717:10;118729:7;:5;:7::i;:::-;118705:41;;;-1:-1:-1;;;;;118705:41:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;118325:429;;:::o;105824:180::-;60507:25;60521:10;60507:13;:25::i;:::-;60499:64;;;;;-1:-1:-1;;;60499:64:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;60499:64:0;;;;;;;;;;;;;;;105906:40;:11;105938:7;105906:40;:31;:40;:::i;122603:1570::-;122683:4;122812:13;:11;:13::i;:::-;-1:-1:-1;;;;;122802:23:0;:6;-1:-1:-1;;;;;122802:23:0;;122798:70;;;-1:-1:-1;122849:7:0;122842:14;;122798:70;122898:7;-1:-1:-1;;;;;122955:20:0;;;122951:616;;123129:17;123148:12;123162:14;123186:21;123200:6;123186:13;:21::i;:::-;123127:80;;;;;;;;;;123320:9;123312:44;;;;;-1:-1:-1;;;123312:44:0;;;;;;;;;;;;-1:-1:-1;;;123312:44:0;;;;;;;;;;;;;;;123379:9;123371:37;;;;;-1:-1:-1;;;123371:37:0;;;;;;;;;;;;-1:-1:-1;;;123371:37:0;;;;;;;;;;;;;;;123523:32;123545:9;123523:17;:7;123535:4;123523:17;:11;:17;:::i;:32::-;123508:47;;122951:616;;;;123687:27;123716:22;123740:24;123774:20;:18;:20::i;:::-;123684:110;;;;;;;;;;123862:19;123854:54;;;;;-1:-1:-1;;;123854:54:0;;;;;;;;;;;;-1:-1:-1;;;123854:54:0;;;;;;;;;;;;;;;123927:19;123919:52;;;;;-1:-1:-1;;;123919:52:0;;;;;;;;;;;;-1:-1:-1;;;123919:52:0;;;;;;;;;;;;;;;124108:57;124150:14;124108:37;:12;124125:19;124108:37;:16;:37;:::i;:57::-;124101:64;122603:1570;-1:-1:-1;;;;;;;122603:1570:0:o;110145:177::-;60507:25;60521:10;60507:13;:25::i;:::-;60499:64;;;;;-1:-1:-1;;;60499:64:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;60499:64:0;;;;;;;;;;;;;;;110226:39;:10;110257:7;110226:39;:30;:39;:::i;108132:189::-;60507:25;60521:10;60507:13;:25::i;:::-;60499:64;;;;;-1:-1:-1;;;60499:64:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;60499:64:0;;;;;;;;;;;;;;;108217:43;:14;108252:7;108217:43;:34;:43;:::i;93826:703::-;1614:20;1623:10;1614:8;:20::i;:::-;1606:55;;;;;-1:-1:-1;;;1606:55:0;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1606:55:0;;;;;;;;;;;;;;;93922:10;;92700:345;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;-1:-1;99:1;-1:-1;;;92773:253:0;92794:10;:17;92790:1;:21;92773:253;;;92842:23;92851:10;92862:1;92851:13;;;;;;;92842:23;92841:24;92833:82;;;;-1:-1:-1;;;92833:82:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;92963:1;-1:-1:-1;;;;;92938:27:0;:10;92949:1;92938:13;;;;;;;;;;;;;;-1:-1:-1;;;;;92938:27:0;;;92930:84;;;;-1:-1:-1;;;92930:84:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;92813:3;;92773:253;;;-1:-1:-1;94019:14:0;;;;;;;94018:15;94010:66;;;;-1:-1:-1;;;94010:66:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;94157:6;94152:245;94169:21;;;94152:245;;;94288:4;94258:12;:27;94271:10;;94282:1;94271:13;;;;;;;;;;;;;-1:-1:-1;;;;;94271:13:0;-1:-1:-1;;;;;94258:27:0;-1:-1:-1;;;;;94258:27:0;;;;;;;;;;;;;:34;;;;;;;;;;;;;;;;;;94351:14;94371:10;;94382:1;94371:13;;;;;;;27:10:-1;;39:1;23:18;;;45:23;;-1:-1;94351:34:0;;;94371:13;94351:34;;;;;;;;;-1:-1:-1;;;;;;94351:34:0;-1:-1:-1;;;;;94371:13:0;;;;;;;;;;;;;94351:34;;;;-1:-1:-1;94192:3:0;94152:245;;;-1:-1:-1;94407:14:0;:21;;-1:-1:-1;;94407:21:0;;;;;94481:40;;;94498:10;94481:40;;;;;;;;;;;;;;;;;;94498:10;;94510;;;;94481:40;;;;94510:10;;94481:40;;;94510:10;94481:40;1:33:-1;99:1;81:16;;;74:27;94481:40:0;;137:4:-1;117:14;;;-1:-1;;113:30;157:16;;;94481:40:0;;;;-1:-1:-1;94481:40:0;;-1:-1:-1;;;;;94481:40:0;1672:1;93826:703;;:::o;3847:109::-;3942:6;;-1:-1:-1;;;;;3942:6:0;;;3930:18;;;;3847:109::o;102316:374::-;102466:15;;;;;;102465:16;102457:55;;;;;-1:-1:-1;;;102457:55:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;102583:27;102596:4;102602:7;102583:12;:27::i;:::-;-1:-1:-1;102660:15:0;;:22;;-1:-1:-1;;102660:22:0;102678:4;102660:22;;;102316:374::o;60987:160::-;61051:4;61087:28;61099:15;;61087:11;:28::i;:::-;-1:-1:-1;;;;;61075:54:0;;61130:8;61075:64;;;;;;;;;;;;;-1:-1:-1;;;;;61075:64:0;-1:-1:-1;;;;;61075:64:0;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;61075:64:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;61075:64:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;61075:64:0;;60987:160;-1:-1:-1;;60987:160:0:o;44502:248::-;44577:4;-1:-1:-1;;;;;44598:20:0;;;44594:149;;44648:6;-1:-1:-1;;;;;44642:23:0;;44666:8;44642:33;;;;;;;;;;;;;-1:-1:-1;;;;;44642:33:0;-1:-1:-1;;;;;44642:33:0;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;44642:33:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;44642:33:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;44642:33:0;;-1:-1:-1;44635:40:0;;44594:149;-1:-1:-1;;;;;;44715:16:0;;;44708:23;;101533:243;101636:19;;;;101609:4;;101636:33;;101660:8;101636:33;:23;:33;:::i;:::-;101630:3;:39;101626:143;;;-1:-1:-1;101693:10:0;;101686:17;;101626:143;-1:-1:-1;101743:14:0;;;;101736:21;;102829:305;102979:15;;;;;;102971:59;;;;;-1:-1:-1;;;102971:59:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;103104:12;;;;:22;102829:305::o;90815:160::-;90876:4;90903:13;90924:17;90938:2;90924:13;:17::i;:::-;-1:-1:-1;90893:48:0;;90815:160;-1:-1:-1;;;;;;;;90815:160:0:o;101884:324::-;102019:27;102041:4;102019:21;:27::i;:::-;102083:7;102065:4;:14;;;:25;;102057:88;;;;-1:-1:-1;;;102057:88:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;102173:14;;;;:27;;102192:7;102173:27;:18;:27;:::i;:::-;102156:4;:14;;:44;;;;101884:324;;:::o;59839:142::-;59898:7;59940:4;;:20;;;-1:-1:-1;;;59940:20:0;;;;;;;;;;-1:-1:-1;;;;;59940:4:0;;;;:13;;:20;;;;;;;;;;;;;;;:4;:20;;;5:2:-1;;;;30:1;27;20:12;5:2;59940:20:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;59940:20:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;59940:20:0;59925:48;;;-1:-1:-1;;;59925:48:0;;;;;;;;;;-1:-1:-1;;;;;59925:41:0;;;;;;:48;;;;;59940:20;;59925:48;;;;;;;;:41;:48;;;5:2:-1;;;;30:1;27;20:12;39548:620:0;39917:10;;;39916:62;;-1:-1:-1;39933:39:0;;;-1:-1:-1;;;39933:39:0;;39957:4;39933:39;;;;-1:-1:-1;;;;;39933:39:0;;;;;;;;;:15;;;;;;:39;;;;;;;;;;;;;;;:15;:39;;;5:2:-1;;;;30:1;27;20:12;5:2;39933:39:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;39933:39:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;39933:39:0;:44;39916:62;39908:152;;;;-1:-1:-1;;;39908:152:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;40097:62;;;-1:-1:-1;;;;;40097:62:0;;;;;;;;;;;;;;;26:21:-1;;;22:32;;;6:49;;40097:62:0;;;;;;;;25:18:-1;;61:17;;-1:-1;;;;;182:15;-1:-1;;;179:29;160:49;;40071:89:0;;40090:5;;40071:18;:89::i;:::-;39548:620;;;:::o;33064:422::-;33431:20;33470:8;;;33064:422::o;90282:163::-;90344:4;90369:14;90393:17;90407:2;90393:13;:17::i;:::-;-1:-1:-1;90361:49:0;;90282:163;-1:-1:-1;;;;;;;;90282:163:0:o;89940:240::-;90042:7;90051:4;90091:32;90103:19;;90091:11;:32::i;:::-;-1:-1:-1;;;;;90075:76:0;;90152:12;90166:5;90075:97;;;;;;;;;;;;;-1:-1:-1;;;;;90075:97:0;-1:-1:-1;;;;;90075:97:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;90075:97:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;90075:97:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;90075:97:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;90075:97:0;;;;;;;;;;;-1:-1:-1;89940:240:0;-1:-1:-1;;;89940:240:0:o;41143:1114::-;41747:27;41755:5;-1:-1:-1;;;;;41747:25:0;;:27::i;:::-;41739:71;;;;;-1:-1:-1;;;41739:71:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;41884:12;41898:23;41933:5;-1:-1:-1;;;;;41925:19:0;41945:4;41925:25;;;;;;;;;;;;;36:153:-1;66:2;61:3;58:11;36:153;;176:10;;164:23;;-1:-1;;139:12;;;;98:2;89:12;;;;114;36:153;;;274:1;267:3;263:2;259:12;254:3;250:22;246:30;315:4;311:9;305:3;299:10;295:26;356:4;350:3;344:10;340:21;389:7;380;377:20;372:3;365:33;3:399;;;41925:25:0;;;;;;;;;;;;;;;;;;;;;;;;14:1:-1;21;16:31;;;;75:4;69:11;64:16;;144:4;140:9;133:4;115:16;111:27;107:43;104:1;100:51;94:4;87:65;169:16;166:1;159:27;225:16;222:1;215:4;212:1;208:12;193:49;7:242;;16:31;36:4;31:9;;7:242;;41883:67:0;;;;41969:7;41961:52;;;;;-1:-1:-1;;;41961:52:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;42030:17;;:21;42026:224;;42172:10;42161:30;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;42161:30:0;42153:85;;;;-1:-1:-1;;;42153:85:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;41143:1114;;;;:::o;87774:210::-;87832:13;87847:7;87856;87865:4;87871;87877;87883:7;87926:32;87938:19;;87926:11;:32::i;:::-;-1:-1:-1;;;;;87910:62:0;;87973:2;87910:66;;;;;;;;;;;;;-1:-1:-1;;;;;87910:66:0;-1:-1:-1;;;;;87910:66:0;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;87910:66:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;87910:66:0;;;;;;39:16:-1;36:1;17:17;2:54;101:4;87910:66:0;80:15:-1;;;-1:-1;;76:31;65:43;;120:4;113:20;13:3;5:12;;2:2;;;30:1;27;20:12;2:2;87910:66:0;;;;;;-1:-1:-1;;;14:3;11:20;8:2;;;44:1;41;34:12;8:2;62:21;;123:4;114:14;;138:31;;;135:2;;;182:1;179;172:12;135:2;213:10;;-1:-1;;;244:29;;285:43;;;282:58;-1:-1;233:115;230:2;;;361:1;358;351:12;230:2;-1:-1;;87910:66:0;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;87910:66:0;;-1:-1:-1;87910:66:0;;-1:-1:-1;87910:66:0;-1:-1:-1;87910:66:0;-1:-1:-1;87910:66:0;;-1:-1:-1;87910:66:0;-1:-1:-1;;;87774:210:0;;;;;;;;;:::o;5109:470::-;5167:7;5411:6;5407:47;;-1:-1:-1;5441:1:0;5434:8;;5407:47;5478:5;;;5482:1;5478;:5;:1;5502:5;;;;;:10;5494:56;;;;-1:-1:-1;;;5494:56:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5570:1;5109:470;-1:-1:-1;;;5109:470:0:o;6047:333::-;6105:7;6204:1;6200;:5;6192:44;;;;;-1:-1:-1;;;6192:44:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;6247:9;6263:1;6259;:5;;;;;;;6047:333;-1:-1:-1;;;;6047:333:0:o;47743:293::-;-1:-1:-1;;;;;47886:20:0;;47882:147;;47923:21;;-1:-1:-1;;;;;47923:12:0;;;:21;;;;;47936:7;;47923:21;;;;47936:7;47923:12;:21;;;;;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;47923:21:0;47882:147;;;47977:40;-1:-1:-1;;;;;47977:26:0;;48004:3;48009:7;47977:40;:26;:40;:::i;4674:184::-;4732:7;4765:1;4760;:6;;4752:49;;;;;-1:-1:-1;;;4752:49:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;4824:5:0;;;4674:184::o;103199:347::-;103383:7;103367:4;:12;;;:23;103359:77;;;;-1:-1:-1;;;103359:77:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;103506:32;103519:4;103525;:12;;;103506;:32::i;:::-;103199:347;;:::o;91098:143::-;91144:7;91187:32;91199:19;;91187:11;:32::i;:::-;-1:-1:-1;;;;;91171:60:0;;:62;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;91171:62:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;91171:62:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;91171:62:0;;-1:-1:-1;91098:143:0;:::o;88502:208::-;88555:13;88570:7;88579;88588:4;88594;88600;88606:7;88649:32;88661:19;;88649:11;:32::i;:::-;-1:-1:-1;;;;;88633:67:0;;:69;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;88633:69:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;88633:69:0;;;;;;39:16:-1;36:1;17:17;2:54;101:4;88633:69:0;80:15:-1;;;-1:-1;;76:31;65:43;;120:4;113:20;13:3;5:12;;2:2;;;30:1;27;20:12;2:2;88633:69:0;;;;;;-1:-1:-1;;;14:3;11:20;8:2;;;44:1;41;34:12;8:2;62:21;;123:4;114:14;;138:31;;;135:2;;;182:1;179;172:12;135:2;213:10;;-1:-1;;;244:29;;285:43;;;282:58;-1:-1;233:115;230:2;;;361:1;358;351:12;230:2;-1:-1;;88633:69:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;88633:69:0;;-1:-1:-1;88633:69:0;-1:-1:-1;88633:69:0;-1:-1:-1;88633:69:0;;-1:-1:-1;88633:69:0;-1:-1:-1;88502:208:0;-1:-1:-1;;;88502:208:0:o;104151:431::-;104284:27;104306:4;104284:21;:27::i;:::-;104378:20;;;104492:14;;;;:27;-1:-1:-1;104488:87:0;;;104553:10;;104536:14;;;:27;104151:431;;:::o;4218:181::-;4276:7;4308:5;;;4332:6;;;;4324:46;;;;;-1:-1:-1;;;4324:46:0;;;;;;;;;;;;;;;;;;;;;;;;;;;103623:378;103708:19;;;;:33;;103732:8;103708:33;:23;:33;:::i;:::-;103702:3;:39;103698:296;;;103826:3;103804:19;;;:25;103929:10;;103912:14;;;:27;103959:23;;;;103929:10;;103959:23;103698:296;103623:378;:::o;39154:175::-;39262:58;;;-1:-1:-1;;;;;39262:58:0;;;;;;;;;;;;;;;26:21:-1;;;22:32;;;6:49;;39262:58:0;;;;;;;;25:18:-1;;61:17;;-1:-1;;;;;182:15;-1:-1;;;179:29;160:49;;39236:85:0;;39255:5;;39236:18;:85::i;116276:7902::-;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;116276:7902:0;-1:-1:-1;;;;;116276:7902:0;;;;;;;;;;;;;;;;;;;-1:-1:-1;116276:7902:0;;;-1:-1:-1;116276:7902:0;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;116276:7902:0;;;;;;
Swarm Source
bzzr://39d1a7c207df4e9874dc1b1a8be5453ec2db2d73fab93e6dc8a826b323ac4592
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
ETH | Ether (ETH) | 100.00% | $3,190.97 | 0.00198 | $6.32 |
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.