More Info
Private Name Tags
ContractCreator
Multichain Info
Latest 15 from a total of 15 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Transfer | 10211263 | 1538 days ago | IN | 0 ETH | 0.00116853 | ||||
Confirm Whitelis... | 10211257 | 1538 days ago | IN | 0 ETH | 0.0018537 | ||||
Submit Whitelist... | 10211252 | 1538 days ago | IN | 0 ETH | 0.00262864 | ||||
Initialize Spend... | 9985633 | 1573 days ago | IN | 0 ETH | 0.00057356 | ||||
Transfer | 8697203 | 1781 days ago | IN | 0 ETH | 0.00041292 | ||||
Transfer | 8302245 | 1842 days ago | IN | 0 ETH | 0.00053626 | ||||
Transfer | 8277071 | 1846 days ago | IN | 0 ETH | 0.00107252 | ||||
Transfer | 8277035 | 1846 days ago | IN | 0 ETH | 0.000747 | ||||
Transfer | 8239699 | 1852 days ago | IN | 0 ETH | 0.00012794 | ||||
Transfer | 8239688 | 1852 days ago | IN | 0 ETH | 0.00007911 | ||||
Initialize White... | 8234930 | 1853 days ago | IN | 0 ETH | 0.00011423 | ||||
Transfer | 8234908 | 1853 days ago | IN | 5 ETH | 0.00002239 | ||||
Transfer | 8234888 | 1853 days ago | IN | 0 ETH | 0.00016532 | ||||
Top Up Gas | 8234877 | 1853 days ago | IN | 0 ETH | 0.0002964 | ||||
Transfer | 8234870 | 1853 days ago | IN | 0.15 ETH | 0.00022397 |
Latest 7 internal transactions
Advanced mode:
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0x18c80Cc7...F986f162a The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
Wallet
Compiler Version
v0.4.25+commit.59dbf8f1
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Multiple files format)
/** * The Consumer Contract Wallet * Copyright (C) 2018 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.4.25; import "./oracle.sol"; import "./ownable.sol"; import "./controllable.sol"; import "./PublicResolver.sol"; import "./SafeMath.sol"; import "./Address.sol"; /// @title ERC20 interface is a subset of the ERC20 specification. interface ERC20 { function transfer(address, uint) external returns (bool); function balanceOf(address) external view returns (uint); } /// @title ERC165 interface specifies a standard way of querying if a contract implements an interface. interface ERC165 { function supportsInterface(bytes4) external view returns (bool); } /// @title Whitelist provides payee-whitelist functionality. contract Whitelist is Controllable, Ownable { 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 isWhitelisted; address[] private _pendingWhitelistAddition; address[] private _pendingWhitelistRemoval; bool public submittedWhitelistAddition; bool public submittedWhitelistRemoval; bool public initializedWhitelist; /// @dev Check if the provided addresses contain the owner or the zero-address address. modifier hasNoOwnerOrZeroAddress(address[] _addresses) { for (uint i = 0; i < _addresses.length; i++) { require(_addresses[i] != owner(), "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[]) { return _pendingWhitelistAddition; } /// @dev Getter for pending removal array. function pendingWhitelistRemoval() external view returns(address[]) { return _pendingWhitelistRemoval; } /// @dev Getter for pending addition/removal array hash. function pendingWhitelistHash(address[] _pendingWhitelist) public pure returns(bytes32) { return keccak256(abi.encodePacked(_pendingWhitelist)); } /// @dev Add initial addresses to the whitelist. /// @param _addresses are the Ethereum addresses to be whitelisted. function initializeWhitelist(address[] _addresses) external onlyOwner hasNoOwnerOrZeroAddress(_addresses) { // Require that the whitelist has not been initialized. require(!initializedWhitelist, "whitelist has already been initialized"); // Add each of the provided addresses to the whitelist. for (uint i = 0; i < _addresses.length; i++) { isWhitelisted[_addresses[i]] = true; } initializedWhitelist = 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[] _addresses) external onlyOwner noActiveSubmission hasNoOwnerOrZeroAddress(_addresses) { // Require that the whitelist has been initialized. require(initializedWhitelist, "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, pendingWhitelistHash(_pendingWhitelistAddition)); } /// @dev Confirm pending whitelist addition. 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 == pendingWhitelistHash(_pendingWhitelistAddition), "hash of the pending whitelist addition do not match"); // Whitelist pending addresses. for (uint i = 0; i < _pendingWhitelistAddition.length; i++) { isWhitelisted[_pendingWhitelistAddition[i]] = true; } // 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 onlyController { // 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 == pendingWhitelistHash(_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[] _addresses) external onlyOwner noActiveSubmission { // Require that the whitelist has been initialized. require(initializedWhitelist, "whitelist has not been initialized"); // Require that the array of addresses is not empty require(_addresses.length > 0, "submitted 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, pendingWhitelistHash(_pendingWhitelistRemoval)); } /// @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 == pendingWhitelistHash(_pendingWhitelistRemoval), "hash of the pending whitelist removal does not match the confirmed hash"); // Remove pending addresses. for (uint i = 0; i < _pendingWhitelistRemoval.length; i++) { isWhitelisted[_pendingWhitelistRemoval[i]] = false; } // 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 onlyController { // 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 == pendingWhitelistHash(_pendingWhitelistRemoval), "hash of the pending whitelist removal does not match"); // Reset pending addresses. delete _pendingWhitelistRemoval; // Reset the submitted operation flag. submittedWhitelistRemoval = false; // Emit the cancellation event. emit CancelledWhitelistRemoval(msg.sender, _hash); } } //// @title SpendLimit provides daily spend limit functionality. contract SpendLimit is Controllable, Ownable { event SetSpendLimit(address _sender, uint _amount); event SubmittedSpendLimitChange(uint _amount); event CancelledSpendLimitChange(address _sender, uint _amount); event UpdatedSpendAvailable(); using SafeMath for uint256; uint public spendLimit; uint private _spendLimitDay; uint private _spendAvailable; uint public pendingSpendLimit; bool public submittedSpendLimit; bool public initializedSpendLimit; /// @dev Constructor initializes the daily spend limit in wei. constructor(uint _spendLimit) internal { spendLimit = _spendLimit; _spendLimitDay = now; _spendAvailable = spendLimit; } /// @dev Returns the available daily balance - accounts for daily limit reset. /// @return amount of ether in wei. function spendAvailable() public view returns (uint) { if (now > _spendLimitDay + 24 hours) { return spendLimit; } else { return _spendAvailable; } } /// @dev Initialize a daily spend (aka transfer) limit for non-whitelisted addresses. /// @param _amount is the daily limit amount in wei. function initializeSpendLimit(uint _amount) external onlyOwner { // Require that the spend limit has not been initialized. require(!initializedSpendLimit, "spend limit has already been initialized"); // Modify spend limit based on the provided value. _modifySpendLimit(_amount); // Flag the operation as initialized. initializedSpendLimit = true; // Emit the set limit event. emit SetSpendLimit(msg.sender, _amount); } /// @dev Set a daily transfer limit for non-whitelisted addresses. /// @param _amount is the daily limit amount in wei. function submitSpendLimit(uint _amount) external onlyOwner { // Require that the spend limit has been initialized. require(initializedSpendLimit, "spend limit has not been initialized"); // Assign the provided amount to pending daily limit change. pendingSpendLimit = _amount; // Flag the operation as submitted. submittedSpendLimit = true; // Emit the submission event. emit SubmittedSpendLimitChange(_amount); } /// @dev Confirm pending set daily limit operation. function confirmSpendLimit(uint _amount) external onlyController { // Require that the operation has been submitted. require(submittedSpendLimit, "spend limit has not been submitted"); // Require that pending and confirmed spend limit are the same require(pendingSpendLimit == _amount, "confirmed and submitted spend limits dont match"); // Modify spend limit based on the pending value. _modifySpendLimit(pendingSpendLimit); // Emit the set limit event. emit SetSpendLimit(msg.sender, pendingSpendLimit); // Reset the submission flag. submittedSpendLimit = false; // Reset pending daily limit. pendingSpendLimit = 0; } /// @dev Cancel pending set daily limit operation. function cancelSpendLimit(uint _amount) external onlyController { // Require a spendlimit has been submitted require(submittedSpendLimit, "a spendlimit needs to be submitted"); // Require that pending and confirmed spend limit are the same require(pendingSpendLimit == _amount, "pending and cancelled spend limits dont match"); // Reset pending daily limit. pendingSpendLimit = 0; // Reset the submitted operation flag. submittedSpendLimit = false; // Emit the cancellation event. emit CancelledSpendLimitChange(msg.sender, _amount); } // @dev Setter method for the available daily spend limit. function _setSpendAvailable(uint _amount) internal { _spendAvailable = _amount; } /// @dev Update available spend limit based on the daily reset. function _updateSpendAvailable() internal { if (now > _spendLimitDay.add(24 hours)) { // Advance the current timestamp. _spendLimitDay = now; // Set the available limit to the current spend limit. _spendAvailable = spendLimit; emit UpdatedSpendAvailable(); } } /// @dev Modify the spend limit and spend available based on the provided value. /// @dev _amount is the daily limit amount in wei. function _modifySpendLimit(uint _amount) private { // Account for the spend limit daily reset. _updateSpendAvailable(); // Set the daily limit to the provided amount. spendLimit = _amount; // Lower the available limit if it's higher than the new daily limit. if (_spendAvailable > spendLimit) { _spendAvailable = spendLimit; } } } //// @title Asset store with extra security features. contract Vault is Whitelist, SpendLimit, ERC165 { event Received(address _from, uint _amount); event Transferred(address _to, address _asset, uint _amount); event BulkTransferred(address _to, address[] _assets); using SafeMath for uint256; /// @dev Supported ERC165 interface ID. bytes4 private constant _ERC165_INTERFACE_ID = 0x01ffc9a7; // solium-disable-line uppercase /// @dev ENS points to the ENS registry smart contract. ENS internal _ENS; /// @dev Is the registered ENS name of the oracle contract. bytes32 internal _oracleNode; /// @dev Constructor initializes the vault with an owner address and spend limit. It also sets up the oracle and controller contracts. /// @param _owner is the owner account of the wallet contract. /// @param _transferable indicates whether the contract ownership can be transferred. /// @param _ens is the ENS public registry contract address. /// @param _oracleName is the ENS name of the Oracle. /// @param _controllerName is the ENS name of the controller. /// @param _spendLimit is the initial spend limit. constructor(address _owner, bool _transferable, address _ens, bytes32 _oracleName, bytes32 _controllerName, uint _spendLimit) SpendLimit(_spendLimit) Ownable(_owner, _transferable) Controllable(_ens, _controllerName) public { _ENS = ENS(_ens); _oracleNode = _oracleName; } /// @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() public payable { //TODO question: Why is this check here, is it necessary or are we building into a corner? require(msg.data.length == 0, "data in fallback"); emit Received(msg.sender, msg.value); } /// @dev Returns the amount of an asset owned by the contract. /// @param _asset address of an ERC20 token or 0x0 for ether. /// @return balance associated with the wallet address in wei. function balance(address _asset) external view returns (uint) { if (_asset != address(0)) { return ERC20(_asset).balanceOf(this); } else { return address(this).balance; } } /// @dev This is a bulk transfer convenience function, used to migrate contracts. /// 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 _to, address[] _assets) public 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; // Get amount based on whether eth or erc20 if (_assets[i] == address(0)) { amount = address(this).balance; } else { amount = ERC20(_assets[i]).balanceOf(address(this)); } // 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 tokens to be transferred in base units. function transfer(address _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 (!isWhitelisted[_to]) { // Update the available spend limit. _updateSpendAvailable(); // Convert token amount to ether value. uint etherValue; bool tokenExists; if (_asset != address(0)) { (tokenExists, etherValue) = IOracle(PublicResolver(_ENS.resolver(_oracleNode)).addr(_oracleNode)).convert(_asset, _amount); } else { etherValue = _amount; } // If token is supported by our oracle or is ether // Check against the daily spent limit and update accordingly if (tokenExists || _asset == address(0)) { // Require that the value is under remaining limit. require(etherValue <= spendAvailable(), "transfer amount exceeds available spend limit"); // Update the available limit. _setSpendAvailable(spendAvailable().sub(etherValue)); } } // Transfer token or ether based on the provided address. if (_asset != address(0)) { require(ERC20(_asset).transfer(_to, _amount), "ERC20 token transfer was unsuccessful"); } else { _to.transfer(_amount); } // Emit the transfer event. emit Transferred(_to, _asset, _amount); } /// @dev Checks for interface support based on ERC165. function supportsInterface(bytes4 interfaceID) external view returns (bool) { return interfaceID == _ERC165_INTERFACE_ID; } } //// @title Asset wallet with extra security features and gas top up management. contract Wallet is Vault { using Address for address; event SetTopUpLimit(address _sender, uint _amount); event SubmittedTopUpLimitChange(uint _amount); event CancelledTopUpLimitChange(address _sender, uint _amount); event ToppedUpGas(address _sender, address _owner, uint _amount); event ExecutedTransaction(address _destination, uint _value, bytes _data); using SafeMath for uint256; string constant public WALLET_VERSION = "1.5.8"; uint constant private MINIMUM_TOPUP_LIMIT = 1 finney; // solium-disable-line uppercase uint constant private MAXIMUM_TOPUP_LIMIT = 500 finney; // solium-disable-line uppercase /// @dev these are needed to allow for the executeTransaction method uint32 private constant _TRANSFER= 0xa9059cbb; uint32 private constant _APPROVE = 0x095ea7b3; uint public topUpLimit; uint private _topUpLimitDay; uint private _topUpAvailable; uint public pendingTopUpLimit; bool public submittedTopUpLimit; bool public initializedTopUpLimit; /// @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. /// @param _oracleName is the ENS name of the Oracle. /// @param _controllerName is the ENS name of the Controller. /// @param _spendLimit is the initial spend limit. constructor(address _owner, bool _transferable, address _ens, bytes32 _oracleName, bytes32 _controllerName, uint _spendLimit) Vault(_owner, _transferable, _ens, _oracleName, _controllerName, _spendLimit) public { _topUpLimitDay = now; topUpLimit = MAXIMUM_TOPUP_LIMIT; _topUpAvailable = topUpLimit; } /// @dev Returns the available daily gas top up balance - accounts for daily limit reset. /// @return amount of gas in wei. function topUpAvailable() external view returns (uint) { if (now > _topUpLimitDay + 24 hours) { return topUpLimit; } else { return _topUpAvailable; } } /// @dev Initialize a daily gas top up limit. /// @param _amount is the gas top up amount in wei. function initializeTopUpLimit(uint _amount) external onlyOwner { // Require that the top up limit has not been initialized. require(!initializedTopUpLimit, "top up limit has already been initialized"); // Require that the limit amount is within the acceptable range. require(MINIMUM_TOPUP_LIMIT <= _amount && _amount <= MAXIMUM_TOPUP_LIMIT, "top up amount is outside of the min/max range"); // Modify spend limit based on the provided value. _modifyTopUpLimit(_amount); // Flag operation as initialized. initializedTopUpLimit = true; // Emit the set limit event. emit SetTopUpLimit(msg.sender, _amount); } /// @dev Set a daily top up top up limit. /// @param _amount is the daily top up limit amount in wei. function submitTopUpLimit(uint _amount) external onlyOwner { // Require that the top up limit has been initialized. require(initializedTopUpLimit, "top up limit has not been initialized"); // Require that the limit amount is within the acceptable range. require(MINIMUM_TOPUP_LIMIT <= _amount && _amount <= MAXIMUM_TOPUP_LIMIT, "top up amount is outside of the min/max range"); // Assign the provided amount to pending daily limit change. pendingTopUpLimit = _amount; // Flag the operation as submitted. submittedTopUpLimit = true; // Emit the submission event. emit SubmittedTopUpLimitChange(_amount); } /// @dev Confirm pending set top up limit operation. function confirmTopUpLimit(uint _amount) external onlyController { // Require that the operation has been submitted. require(submittedTopUpLimit, "top up limit has not been submitted"); // Assert that the pending top up limit amount is within the acceptable range. require(MINIMUM_TOPUP_LIMIT <= pendingTopUpLimit && pendingTopUpLimit <= MAXIMUM_TOPUP_LIMIT, "top up amount is outside the min/max range"); // Assert that confirmed and pending topup limit are the same. require(_amount == pendingTopUpLimit, "confirmed and pending topup limit are not same"); // Modify top up limit based on the pending value. _modifyTopUpLimit(pendingTopUpLimit); // Emit the set limit event. emit SetTopUpLimit(msg.sender, pendingTopUpLimit); // Reset pending daily limit. pendingTopUpLimit = 0; // Reset the submission flag. submittedTopUpLimit = false; } /// @dev Cancel pending set top up limit operation. function cancelTopUpLimit(uint _amount) external onlyController { // Make sure a topup limit update has been submitted require(submittedTopUpLimit, "a topup limit has to be submitted"); // Require that pending and confirmed spend limit are the same require(pendingTopUpLimit == _amount, "pending and cancelled top up limits dont match"); // Reset pending daily limit. pendingTopUpLimit = 0; // Reset the submitted operation flag. submittedTopUpLimit = false; // Emit the cancellation event. emit CancelledTopUpLimitChange(msg.sender, _amount); } /// @dev Refill owner's gas balance. /// @dev 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) { // Require that the sender is either the owner or a controller. require(_isOwner() || _isController(msg.sender), "sender is neither an owner nor a controller"); // Account for the top up limit daily reset. _updateTopUpAvailable(); // Make sure the available top up amount is not zero. require(_topUpAvailable != 0, "available top up limit cannot be zero"); // Fail if there isn't enough in the daily top up limit to perform topUp require(_amount <= _topUpAvailable, "available top up limit less than amount passed in"); // Reduce the top up amount from available balance and transfer corresponding // ether to the owner's account. _topUpAvailable = _topUpAvailable.sub(_amount); owner().transfer(_amount); // Emit the gas top up event. emit ToppedUpGas(tx.origin, owner(), _amount); } /// @dev Modify the top up limit and top up available based on the provided value. /// @dev _amount is the daily limit amount in wei. function _modifyTopUpLimit(uint _amount) private { // Account for the top up limit daily reset. _updateTopUpAvailable(); // Set the daily limit to the provided amount. topUpLimit = _amount; // Lower the available limit if it's higher than the new daily limit. if (_topUpAvailable > topUpLimit) { _topUpAvailable = topUpLimit; } } /// @dev Update available top up limit based on the daily reset. function _updateTopUpAvailable() private { if (now > _topUpLimitDay.add(24 hours)) { // Advance the current timestamp. _topUpLimitDay = now; // Set the available limit to the current top up limit. _topUpAvailable = topUpLimit; } } /// @dev This function allows for the owner to send transaction from the Wallet to arbitrary contracts /// @dev This function does not allow for the sending of data to arbitrary (non-contract) 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 _data, bool _destinationIsContract) external onlyOwner { // Check if the Address is a Contract // This should avoid users accidentally sending Value and Data to a plain old address if (_destinationIsContract) { require(address(_destination).isContract(), "executeTransaction for a contract: call to non-contract"); } else { require(!address(_destination).isContract(), "executeTransaction for a non-contract: call to contract"); } // Update the available spend limit. _updateSpendAvailable(); // Check if there exists at least a method signature in the transaction payload if (_data.length >= 4) { // Get method signature uint32 signature = bytesToUint32(_data, 0); // Check if method is either ERC20 transfer or approve if (signature == _TRANSFER || signature == _APPROVE) { require(_data.length >= 4 + 32 + 32, "invalid transfer / approve transaction data"); uint amount = sliceUint(_data, 4 + 32); // The "toOrSpender" is the '_to' address for a ERC20 transfer or the '_spender; in ERC20 approve // + 12 because address 20 bytes and this is padded to 32 address toOrSpender = bytesToAddress(_data, 4 + 12); // Check if the toOrSpender is in the whitelist if (!isWhitelisted[toOrSpender]) { (bool tokenExists,uint etherValue) = IOracle(PublicResolver(_ENS.resolver(_oracleNode)).addr(_oracleNode)).convert(_destination, amount); // If token is supported by our oracle or is ether // Check against the daily spent limit and update accordingly if (tokenExists) { // Require that the value is under remaining limit. require(etherValue <= spendAvailable(), "transfer amount exceeds available spend limit"); // Update the available limit. _setSpendAvailable(spendAvailable().sub(etherValue)); } } } } // 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 (!isWhitelisted[_destination]) { // Require that the value is under remaining limit. require(_value <= spendAvailable(), "transfer amount exceeds available spend limit"); // Update the available limit. _setSpendAvailable(spendAvailable().sub(_value)); } require(externalCall(_destination, _value, _data.length, _data), "executing transaction failed"); emit ExecutedTransaction(_destination, _value, _data); } /// @dev This function is taken from the Gnosis MultisigWallet: https://github.com/gnosis/MultiSigWallet/ /// @dev License: https://github.com/gnosis/MultiSigWallet/blob/master/LICENSE /// @dev thanks :) /// @dev This calls proxies arbitrary transactions to addresses /// @param _destination address of the transaction /// @param _value ETH amount in wei /// @param _dataLength length of the transaction data /// @param _data transaction payload binary // call has been separated into its own function in order to take advantage // of the Solidity's code generator to produce a loop that copies tx.data into memory. function externalCall(address _destination, uint _value, uint _dataLength, bytes _data) private returns (bool) { bool result; assembly { let x := mload(0x40) // "Allocate" memory for output (0x40 is where "free memory" pointer is stored by convention) let d := add(_data, 32) // First 32 bytes are the padded length of data, so exclude that result := call( sub(gas, 34710), // 34710 is the value that solidity is currently emitting // It includes callGas (700) + callVeryLow (3, to pay for SUB) + callValueTransferGas (9000) + // callNewAccountGas (25000, in case the destination address does not exist and needs creating) _destination, _value, d, _dataLength, // Size of the input (in bytes) - this is what fixes the padding problem x, 0 // Output is ignored, therefore the output size is zero ) } return result; } /// @dev This function converts to an address /// @param _bts bytes /// @param _from start position function bytesToAddress(bytes _bts, uint _from) private pure returns (address) { require(_bts.length >= _from + 20, "slicing out of range"); uint160 m = 0; uint160 b = 0; for (uint8 i = 0; i < 20; i++) { m *= 256; b = uint160 (_bts[_from + i]); m += (b); } return address(m); } /// @dev This function slicing bytes into uint32 /// @param _bts some bytes /// @param _from a start position function bytesToUint32(bytes _bts, uint _from) private pure returns (uint32) { require(_bts.length >= _from + 4, "slicing out of range"); uint32 m = 0; uint32 b = 0; for (uint8 i = 0; i < 4; i++) { m *= 256; b = uint32 (_bts[_from + i]); m += (b); } return m; } /// @dev This function slices a uint /// @param _bts some bytes /// @param _from a 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 sliceUint(bytes _bts, uint _from) private pure returns (uint) { require(_bts.length >= _from + 32, "slicing out of range"); uint x; assembly { x := mload(add(_bts, add(0x20, _from))) } return x; } }
/** * The MIT License (MIT) * * Copyright (c) 2016-2019 zOS Global Limited * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ pragma solidity ^0.4.25; /** * Utility library of inline functions on addresses */ library Address { /** * Returns whether the target address is a contract * @dev This function will return false if invoked during the constructor of a contract, * as the code is not actually created until after the constructor finishes. * @param account address of the account to check * @return whether the target address is a contract */ function isContract(address account) internal view returns (bool) { uint256 size; // XXX Currently there is no better way to check if there is a contract in an address // than to check the size of the code at that address. // See https://ethereum.stackexchange.com/a/14016/36603 // for more details about how this works. // TODO Check this again before the Serenity release, because all addresses will be // contracts then. // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } }
pragma solidity ^0.4.25; /** * This method was modified from the GPLv3 solidity code found in this repository * https://github.com/vcealicu/melonport-price-feed/blob/master/pricefeed/PriceFeed.sol */ /// @title Base64 provides base 64 decoding functionality. contract Base64 { bytes constant BASE64_DECODE_CHAR = hex"000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e003e003f3435363738393a3b3c3d00000000000000000102030405060708090a0b0c0d0e0f10111213141516171819000000003f001a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233"; /// @return decoded array of bytes. /// @param _encoded base 64 encoded array of bytes. function _base64decode(bytes _encoded) internal pure returns (bytes) { byte v1; byte v2; byte v3; byte v4; uint length = _encoded.length; bytes memory result = new bytes(length); uint index; // base64 encoded strings can't be length 0 and they must be divisble by 4 require(length > 0 && length % 4 == 0, "invalid base64 encoding"); if (keccak256(abi.encodePacked(_encoded[length - 2])) == keccak256("=")) { length -= 2; } else if (keccak256(abi.encodePacked(_encoded[length - 1])) == keccak256("=")) { length -= 1; } uint count = length >> 2 << 2; for (uint i = 0; i < count;) { v1 = BASE64_DECODE_CHAR[uint(_encoded[i++])]; v2 = BASE64_DECODE_CHAR[uint(_encoded[i++])]; v3 = BASE64_DECODE_CHAR[uint(_encoded[i++])]; v4 = BASE64_DECODE_CHAR[uint(_encoded[i++])]; result[index++] = (v1 << 2 | v2 >> 4) & 255; result[index++] = (v2 << 4 | v3 >> 2) & 255; result[index++] = (v3 << 6 | v4) & 255; } if (length - count == 2) { v1 = BASE64_DECODE_CHAR[uint(_encoded[i++])]; v2 = BASE64_DECODE_CHAR[uint(_encoded[i++])]; result[index++] = (v1 << 2 | v2 >> 4) & 255; } else if (length - count == 3) { v1 = BASE64_DECODE_CHAR[uint(_encoded[i++])]; v2 = BASE64_DECODE_CHAR[uint(_encoded[i++])]; v3 = BASE64_DECODE_CHAR[uint(_encoded[i++])]; result[index++] = (v1 << 2 | v2 >> 4) & 255; result[index++] = (v2 << 4 | v3 >> 2) & 255; } // Set to correct length. assembly { mstore(result, index) } return result; } }
/** * Controller Interface * Copyright (C) 2018 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.4.25; import "./controller.sol"; import "./ENS.sol"; /// @title Resolver returns the controller contract address. interface IResolver { function addr(bytes32) external view returns (address); } /// @title Controllable implements access control functionality based on a controller set in ENS. contract Controllable { /// @dev _ENS points to the ENS registry smart contract. ENS private _ENS; /// @dev Is the registered ENS name of the controller contract. bytes32 private _node; /// @dev Constructor initializes the controller contract object. /// @param _ens is the address of the ENS. /// @param _controllerName is the ENS name of the Controller. constructor(address _ens, bytes32 _controllerName) internal { _ENS = ENS(_ens); _node = _controllerName; } /// @dev Checks if message sender is the controller. modifier onlyController() { require(_isController(msg.sender), "sender is not a controller"); _; } /// @return true if the provided account is the controller. function _isController(address _account) internal view returns (bool) { return IController(IResolver(_ENS.resolver(_node)).addr(_node)).isController(_account); } }
/** * Controller * Copyright (C) 2018 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.4.25; /// @title The Controller interface provides access to an external list of controllers. interface IController { function isController(address) external view returns (bool); } /// @title Controller stores a list of controller addresses that can be used for authentication in other contracts. contract Controller is IController { event AddedController(address _sender, address _controller); event RemovedController(address _sender, address _controller); mapping (address => bool) private _isController; uint private _controllerCount; /// @dev Constructor initializes the list of controllers with the provided address. /// @param _account address to add to the list of controllers. constructor(address _account) public { _addController(_account); } /// @dev Checks if message sender is a controller. modifier onlyController() { require(isController(msg.sender), "sender is not a controller"); _; } /// @dev Add a new controller to the list of controllers. /// @param _account address to add to the list of controllers. function addController(address _account) external onlyController { _addController(_account); } /// @dev Remove a controller from the list of controllers. /// @param _account address to remove from the list of controllers. function removeController(address _account) external onlyController { _removeController(_account); } /// @return true if the provided account is a controller. function isController(address _account) public view returns (bool) { return _isController[_account]; } /// @return the current number of controllers. function controllerCount() public view returns (uint) { return _controllerCount; } /// @dev Internal-only function that adds a new controller. function _addController(address _account) internal { require(!_isController[_account], "provided account is already a controller"); _isController[_account] = true; _controllerCount++; emit AddedController(msg.sender, _account); } /// @dev Internal-only function that removes an existing controller. function _removeController(address _account) internal { require(_isController[_account], "provided account is not a controller"); require(_controllerCount > 1, "cannot remove the last controller"); _isController[_account] = false; _controllerCount--; emit RemovedController(msg.sender, _account); } }
/** * Month Parser * Copyright (C) 2018 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.4.25; /// @title Date provides date parsing functionality. contract Date { bytes32 constant private JANUARY = keccak256("Jan"); bytes32 constant private FEBRUARY = keccak256("Feb"); bytes32 constant private MARCH = keccak256("Mar"); bytes32 constant private APRIL = keccak256("Apr"); bytes32 constant private MAY = keccak256("May"); bytes32 constant private JUNE = keccak256("Jun"); bytes32 constant private JULY = keccak256("Jul"); bytes32 constant private AUGUST = keccak256("Aug"); bytes32 constant private SEPTEMBER = keccak256("Sep"); bytes32 constant private OCTOBER = keccak256("Oct"); bytes32 constant private NOVEMBER = keccak256("Nov"); bytes32 constant private DECEMBER = keccak256("Dec"); /// @return the number of the month based on its name. /// @param _month the first three letters of a month's name e.g. "Jan". function _monthToNumber(string _month) internal pure returns (uint8) { bytes32 month = keccak256(abi.encodePacked(_month)); if (month == JANUARY) { return 1; } else if (month == FEBRUARY) { return 2; } else if (month == MARCH) { return 3; } else if (month == APRIL) { return 4; } else if (month == MAY) { return 5; } else if (month == JUNE) { return 6; } else if (month == JULY) { return 7; } else if (month == AUGUST) { return 8; } else if (month == SEPTEMBER) { return 9; } else if (month == OCTOBER) { return 10; } else if (month == NOVEMBER) { return 11; } else if (month == DECEMBER) { return 12; } else { revert("not a valid month"); } } }
/** * BSD 2-Clause License * * Copyright (c) 2018, True Names Limited * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ pragma solidity ^0.4.24; 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) public; function setResolver(bytes32 node, address resolver) public; function setOwner(bytes32 node, address owner) public; function setTTL(bytes32 node, uint64 ttl) public; function owner(bytes32 node) public view returns (address); function resolver(bytes32 node) public view returns (address); function ttl(bytes32 node) public view returns (uint64); }
/** * JSON Parser * Copyright (C) 2018 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.4.25; import "./strings.sol"; import "./oraclizeAPI_0.4.25.sol"; /// @title JSON provides JSON parsing functionality. contract JSON is usingOraclize{ using strings for *; bytes32 constant private prefixHash = keccak256("{\"ETH\":"); /// @dev Extracts JSON rate value from the response object. /// @param _json body of the JSON response from the CryptoCompare API. function parseRate(string _json) public pure returns (string) { uint json_len = abi.encodePacked(_json).length; //{"ETH":}.length = 8, assuming a (maximum of) 18 digit prevision require(json_len > 8 && json_len <= 28, "misformatted input"); bytes memory jsonPrefix = new bytes(7); copyBytes(abi.encodePacked(_json), 0, 7, jsonPrefix, 0); require(keccak256(jsonPrefix) == prefixHash, "prefix mismatch"); strings.slice memory body = _json.toSlice(); body.split(":".toSlice()); //we are sure that ':' is included in the string, body now contains the rate+'}' json_len = body._len; body.until("}".toSlice()); require(body._len == json_len-1,"not json format"); //ensure that the json is properly terminated with a '}' return body.toString(); } }
/** * Token Exchange Rate Oracle * Copyright (C) 2018 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.4.25; import "./controllable.sol"; import "./date.sol"; import "./json.sol"; import "./parseIntScientific.sol"; import "./strings.sol"; import "./SafeMath.sol"; import "./base64.sol"; /// @title Oracle converts ERC20 token amounts into equivalent ether amounts based on cryptocurrency exchange rates. interface IOracle { function convert(address, uint) external view returns (bool, uint); } /// @title Oracle provides asset exchange rates and conversion functionality. contract Oracle is usingOraclize, Base64, Date, JSON, Controllable, ParseIntScientific, IOracle { using strings for *; using SafeMath for uint256; /*******************/ /* Events */ /*****************/ event AddedToken(address _sender, address _token, string _symbol, uint _magnitude); event RemovedToken(address _sender, address _token); event UpdatedTokenRate(address _sender, address _token, uint _rate); event SetGasPrice(address _sender, uint _gasPrice); event Converted(address _sender, address _token, uint _amount, uint _ether); event RequestedUpdate(string _symbol); event FailedUpdateRequest(string _reason); event VerifiedProof(bytes _publicKey, string _result); event SetCryptoComparePublicKey(address _sender, bytes _publicKey); /**********************/ /* Constants */ /********************/ uint constant private PROOF_LEN = 165; uint constant private ECDSA_SIG_LEN = 65; uint constant private ENCODING_BYTES = 2; uint constant private HEADERS_LEN = PROOF_LEN - 2 * ENCODING_BYTES - ECDSA_SIG_LEN; // 2 bytes encoding headers length + 2 for signature. uint constant private DIGEST_BASE64_LEN = 44; //base64 encoding of the SHA256 hash (32-bytes) of the result: fixed length. uint constant private DIGEST_OFFSET = HEADERS_LEN - DIGEST_BASE64_LEN; // the starting position of the result hash in the headers string. uint constant private MAX_BYTE_SIZE = 256; //for calculating length encoding struct Token { string symbol; // Token symbol uint magnitude; // 10^decimals uint rate; // Token exchange rate in wei uint lastUpdate; // Time of the last rate update bool exists; // Flags if the struct is empty or not } mapping(address => Token) public tokens; address[] private _tokenAddresses; bytes public APIPublicKey; mapping(bytes32 => address) private _queryToToken; /// @dev Construct the oracle with multiple controllers, address resolver and custom gas price. /// @dev _resolver is the oraclize address resolver contract address. /// @param _ens is the address of the ENS. /// @param _controllerName is the ENS name of the Controller. constructor(address _resolver, address _ens, bytes32 _controllerName) Controllable(_ens, _controllerName) public { APIPublicKey = hex"a0f4f688350018ad1b9785991c0bde5f704b005dc79972b114dbed4a615a983710bfc647ebe5a320daa28771dce6a2d104f5efa2e4a85ba3760b76d46f8571ca"; OAR = OraclizeAddrResolverI(_resolver); oraclize_setCustomGasPrice(10000000000); oraclize_setProof(proofType_Native); } /// @dev Updates the Crypto Compare public API key. function updateAPIPublicKey(bytes _publicKey) external onlyController { APIPublicKey = _publicKey; emit SetCryptoComparePublicKey(msg.sender, _publicKey); } /// @dev Sets the gas price used by oraclize query. function setCustomGasPrice(uint _gasPrice) external onlyController { oraclize_setCustomGasPrice(_gasPrice); emit SetGasPrice(msg.sender, _gasPrice); } /// @dev Convert ERC20 token amount to the corresponding ether amount (used by the wallet contract). /// @param _token ERC20 token contract address. /// @param _amount amount of token in base units. function convert(address _token, uint _amount) external view returns (bool, uint) { // Store the token in memory to save map entry lookup gas. Token storage token = tokens[_token]; // If the token exists require that its rate is not zero if (token.exists) { require(token.rate != 0, "token rate is 0"); // Safely convert the token amount to ether based on the exchange rate. // return the value and a 'true' implying that the token is protected return (true, _amount.mul(token.rate).div(token.magnitude)); } // this returns a 'false' to imply that a card is not protected return (false, 0); } /// @dev Add ERC20 tokens to the list of supported 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 _updateDate date for the token updates. This will be compared to when oracle updates are received. function addTokens(address[] _tokens, bytes32[] _symbols, uint[] _magnitude, uint _updateDate) external onlyController { // Require that all parameters have the same length. require(_tokens.length == _symbols.length && _tokens.length == _magnitude.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 doesn't already exist. address token = _tokens[i]; require(!tokens[token].exists, "token already exists"); // Store the intermediate values. string memory symbol = _symbols[i].toSliceB32().toString(); uint magnitude = _magnitude[i]; // Add the token to the token list. tokens[token] = Token({ symbol : symbol, magnitude : magnitude, rate : 0, exists : true, lastUpdate: _updateDate }); // Add the token address to the address list. _tokenAddresses.push(token); // Emit token addition event. emit AddedToken(msg.sender, token, symbol, magnitude); } } /// @dev Remove ERC20 tokens from the list of supported tokens. /// @param _tokens ERC20 token contract addresses. function removeTokens(address[] _tokens) external onlyController { // Delete each token object from the list of supported tokens based on the addresses provided. for (uint i = 0; i < _tokens.length; i++) { //token must exist, reverts on duplicates as well require(tokens[_tokens[i]].exists, "token does not exist"); // Store the token address. address token = _tokens[i]; // Delete the token object. delete tokens[token]; // Remove the token address from the address list. for (uint j = 0; j < _tokenAddresses.length.sub(1); j++) { if (_tokenAddresses[j] == token) { _tokenAddresses[j] = _tokenAddresses[_tokenAddresses.length.sub(1)]; break; } } _tokenAddresses.length--; // Emit token removal event. emit RemovedToken(msg.sender, token); } } /// @dev Update ERC20 token exchange rate manually. /// @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 onlyController { // Require that the token exists. require(tokens[_token].exists, "token does not exist"); // Update the token's rate. tokens[_token].rate = _rate; // Update the token's last update timestamp. tokens[_token].lastUpdate = _updateDate; // Emit the rate update event. emit UpdatedTokenRate(msg.sender, _token, _rate); } /// @dev Update ERC20 token exchange rates for all supported tokens. //// @param _gasLimit the gas limit is passed, this is used for the Oraclize callback function updateTokenRates(uint _gasLimit) external payable onlyController { _updateTokenRates(_gasLimit); } //// @dev Withdraw ether from the smart contract to the specified account. function withdraw(address _to, uint _amount) external onlyController { _to.transfer(_amount); } //// @dev Handle Oraclize query callback and verifiy the provided origin proof. //// @param _queryID Oraclize query ID. //// @param _result query result in JSON format. //// @param _proof origin proof from crypto compare. // solium-disable-next-line mixedcase function __callback(bytes32 _queryID, string _result, bytes _proof) public { // Require that the caller is the Oraclize contract. require(msg.sender == oraclize_cbAddress(), "sender is not oraclize"); // Use the query ID to find the matching token address. address _token = _queryToToken[_queryID]; require(_token != address(0), "queryID matches to address 0"); // Get the corresponding token object. Token storage token = tokens[_token]; bool valid; uint timestamp; (valid, timestamp) = _verifyProof(_result, _proof, APIPublicKey, token.lastUpdate); // Require that the proof is valid. if (valid) { // Parse the JSON result to get the rate in wei. token.rate = _parseIntScientificWei(parseRate(_result)); // Set the update time of the token rate. token.lastUpdate = timestamp; // Remove query from the list. delete _queryToToken[_queryID]; // Emit the rate update event. emit UpdatedTokenRate(msg.sender, _token, token.rate); } } /// @dev Re-usable helper function that performs the Oraclize Query. //// @param _gasLimit the gas limit is passed, this is used for the Oraclize callback function _updateTokenRates(uint _gasLimit) private { // Check if there are any existing tokens. if (_tokenAddresses.length == 0) { // Emit a query failure event. emit FailedUpdateRequest("no tokens"); // Check if the contract has enough Ether to pay for the query. } else if (oraclize_getPrice("URL") * _tokenAddresses.length > address(this).balance) { // Emit a query failure event. emit FailedUpdateRequest("insufficient balance"); } else { // Set up the cryptocompare API query strings. strings.slice memory apiPrefix = "https://min-api.cryptocompare.com/data/price?fsym=".toSlice(); strings.slice memory apiSuffix = "&tsyms=ETH&sign=true".toSlice(); // Create a new oraclize query for each supported token. for (uint i = 0; i < _tokenAddresses.length; i++) { // Store the token symbol used in the query. strings.slice memory symbol = tokens[_tokenAddresses[i]].symbol.toSlice(); // Create a new oraclize query from the component strings. bytes32 queryID = oraclize_query("URL", apiPrefix.concat(symbol).toSlice().concat(apiSuffix), _gasLimit); // Store the query ID together with the associated token address. _queryToToken[queryID] = _tokenAddresses[i]; // Emit the query success event. emit RequestedUpdate(symbol.toString()); } } } /// @dev Verify the origin proof returned by the cryptocompare API. /// @param _result query result in JSON format. /// @param _proof origin proof from cryptocompare. /// @param _publicKey cryptocompare public key. /// @param _lastUpdate timestamp of the last time the requested token was updated. function _verifyProof(string _result, bytes _proof, bytes _publicKey, uint _lastUpdate) private returns (bool, uint) { //expecting fixed length proofs if (_proof.length != PROOF_LEN) revert("invalid proof length"); //proof should be 65 bytes long: R (32 bytes) + S (32 bytes) + v (1 byte) if (uint(_proof[1]) != ECDSA_SIG_LEN) revert("invalid signature length"); bytes memory signature = new bytes(ECDSA_SIG_LEN); signature = copyBytes(_proof, 2, ECDSA_SIG_LEN, signature, 0); // Extract the headers, big endian encoding of headers length if (uint(_proof[ENCODING_BYTES + ECDSA_SIG_LEN]) * MAX_BYTE_SIZE + uint(_proof[ENCODING_BYTES + ECDSA_SIG_LEN + 1]) != HEADERS_LEN) revert("invalid headers length"); bytes memory headers = new bytes(HEADERS_LEN); headers = copyBytes(_proof, 2*ENCODING_BYTES + ECDSA_SIG_LEN, HEADERS_LEN, headers, 0); // Check if the signature is valid and if the signer address is matching. if (!_verifySignature(headers, signature, _publicKey)) { revert("invalid signature"); } // Check if the date is valid. bytes memory dateHeader = new bytes(20); //keep only the relevant string(e.g. "16 Nov 2018 16:22:18") dateHeader = copyBytes(headers, 11, 20, dateHeader, 0); bool dateValid; uint timestamp; (dateValid, timestamp) = _verifyDate(string(dateHeader), _lastUpdate); // Check whether the date returned is valid or not if (!dateValid) revert("invalid date"); // Check if the signed digest hash matches the result hash. bytes memory digest = new bytes(DIGEST_BASE64_LEN); digest = copyBytes(headers, DIGEST_OFFSET, DIGEST_BASE64_LEN, digest, 0); if (keccak256(abi.encodePacked(sha256(abi.encodePacked(_result)))) != keccak256(_base64decode(digest))) revert("result hash not matching"); emit VerifiedProof(_publicKey, _result); return (true, timestamp); } /// @dev Verify the HTTP headers and the signature /// @param _headers HTTP headers provided by the cryptocompare api /// @param _signature signature provided by the cryptocompare api /// @param _publicKey cryptocompare public key. function _verifySignature(bytes _headers, bytes _signature, bytes _publicKey) private returns (bool) { address signer; bool signatureOK; // Checks if the signature is valid by hashing the headers (signatureOK, signer) = ecrecovery(sha256(_headers), _signature); return signatureOK && signer == address(keccak256(_publicKey)); } /// @dev Verify the signed HTTP date header. /// @param _dateHeader extracted date string e.g. Wed, 12 Sep 2018 15:18:14 GMT. /// @param _lastUpdate timestamp of the last time the requested token was updated. function _verifyDate(string _dateHeader, uint _lastUpdate) private pure returns (bool, uint) { //called by verifyProof(), _dateHeader is always a string of length = 20 assert(abi.encodePacked(_dateHeader).length == 20); //Split the date string and get individual date components. strings.slice memory date = _dateHeader.toSlice(); strings.slice memory timeDelimiter = ":".toSlice(); strings.slice memory dateDelimiter = " ".toSlice(); uint day = _parseIntScientific(date.split(dateDelimiter).toString()); require(day > 0 && day < 32, "day error"); uint month = _monthToNumber(date.split(dateDelimiter).toString()); require(month > 0 && month < 13, "month error"); uint year = _parseIntScientific(date.split(dateDelimiter).toString()); require(year > 2017 && year < 3000, "year error"); uint hour = _parseIntScientific(date.split(timeDelimiter).toString()); require(hour < 25, "hour error"); uint minute = _parseIntScientific(date.split(timeDelimiter).toString()); require(minute < 60, "minute error"); uint second = _parseIntScientific(date.split(timeDelimiter).toString()); require(second < 60, "second error"); uint timestamp = year * (10 ** 10) + month * (10 ** 8) + day * (10 ** 6) + hour * (10 ** 4) + minute * (10 ** 2) + second; return (timestamp > _lastUpdate, timestamp); } }
// <ORACLIZE_API> // Release targetted at solc 0.4.25 to silence compiler warning/error messages, compatible down to 0.4.22 /* Copyright (c) 2015-2016 Oraclize SRL Copyright (c) 2016 Oraclize LTD Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // This api is currently targeted at 0.4.22 to 0.4.25 (stable builds), please import oraclizeAPI_pre0.4.sol or oraclizeAPI_0.4 where necessary pragma solidity >=0.4.22;// Incompatible compiler version... please select one stated within pragma solidity or use different oraclizeAPI version contract OraclizeI { address public cbAddress; function query(uint _timestamp, string _datasource, string _arg) external payable returns (bytes32 _id); function query_withGasLimit(uint _timestamp, string _datasource, string _arg, uint _gaslimit) external payable returns (bytes32 _id); function query2(uint _timestamp, string _datasource, string _arg1, string _arg2) public payable returns (bytes32 _id); function query2_withGasLimit(uint _timestamp, string _datasource, string _arg1, string _arg2, uint _gaslimit) external payable returns (bytes32 _id); function queryN(uint _timestamp, string _datasource, bytes _argN) public payable returns (bytes32 _id); function queryN_withGasLimit(uint _timestamp, string _datasource, bytes _argN, uint _gaslimit) external payable returns (bytes32 _id); function getPrice(string _datasource) public returns (uint _dsprice); function getPrice(string _datasource, uint gaslimit) public returns (uint _dsprice); function setProofType(byte _proofType) external; function setCustomGasPrice(uint _gasPrice) external; function randomDS_getSessionPubKeyHash() external constant returns(bytes32); } contract OraclizeAddrResolverI { function getAddress() public returns (address _addr); } /* Begin solidity-cborutils https://github.com/smartcontractkit/solidity-cborutils MIT License Copyright (c) 2018 SmartContract ChainLink, Ltd. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ library Buffer { struct buffer { bytes buf; uint capacity; } function init(buffer memory buf, uint _capacity) internal pure { uint capacity = _capacity; if(capacity % 32 != 0) capacity += 32 - (capacity % 32); // Allocate space for the buffer data buf.capacity = capacity; assembly { let ptr := mload(0x40) mstore(buf, ptr) mstore(ptr, 0) mstore(0x40, add(ptr, capacity)) } } function resize(buffer memory buf, uint capacity) private pure { bytes memory oldbuf = buf.buf; init(buf, capacity); append(buf, oldbuf); } function max(uint a, uint b) private pure returns(uint) { if(a > b) { return a; } return b; } /** * @dev Appends a byte array to the end of the buffer. Resizes if doing so * would exceed the capacity of the buffer. * @param buf The buffer to append to. * @param data The data to append. * @return The original buffer. */ function append(buffer memory buf, bytes data) internal pure returns(buffer memory) { if(data.length + buf.buf.length > buf.capacity) { resize(buf, max(buf.capacity, data.length) * 2); } uint dest; uint src; uint len = data.length; assembly { // Memory address of the buffer data let bufptr := mload(buf) // Length of existing buffer data let buflen := mload(bufptr) // Start address = buffer address + buffer length + sizeof(buffer length) dest := add(add(bufptr, buflen), 32) // Update buffer length mstore(bufptr, add(buflen, mload(data))) src := add(data, 32) } // 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)) } return buf; } /** * @dev Appends a byte to the end of the buffer. Resizes if doing so would * exceed the capacity of the buffer. * @param buf The buffer to append to. * @param data The data to append. * @return The original buffer. */ function append(buffer memory buf, uint8 data) internal pure { if(buf.buf.length + 1 > buf.capacity) { resize(buf, buf.capacity * 2); } assembly { // Memory address of the buffer data let bufptr := mload(buf) // Length of existing buffer data let buflen := mload(bufptr) // Address = buffer address + buffer length + sizeof(buffer length) let dest := add(add(bufptr, buflen), 32) mstore8(dest, data) // Update buffer length mstore(bufptr, add(buflen, 1)) } } /** * @dev Appends a byte to the end of the buffer. Resizes if doing so would * exceed the capacity of the buffer. * @param buf The buffer to append to. * @param data The data to append. * @return The original buffer. */ function appendInt(buffer memory buf, uint data, uint len) internal pure returns(buffer memory) { if(len + buf.buf.length > buf.capacity) { resize(buf, max(buf.capacity, len) * 2); } uint mask = 256 ** len - 1; assembly { // Memory address of the buffer data let bufptr := mload(buf) // Length of existing buffer data let buflen := mload(bufptr) // Address = buffer address + buffer length + sizeof(buffer length) + len let dest := add(add(bufptr, buflen), len) mstore(dest, or(and(mload(dest), not(mask)), data)) // Update buffer length mstore(bufptr, add(buflen, len)) } return buf; } } library CBOR { using Buffer for Buffer.buffer; uint8 private constant MAJOR_TYPE_INT = 0; uint8 private constant MAJOR_TYPE_NEGATIVE_INT = 1; uint8 private constant MAJOR_TYPE_BYTES = 2; uint8 private constant MAJOR_TYPE_STRING = 3; uint8 private constant MAJOR_TYPE_ARRAY = 4; uint8 private constant MAJOR_TYPE_MAP = 5; uint8 private constant MAJOR_TYPE_CONTENT_FREE = 7; function encodeType(Buffer.buffer memory buf, uint8 major, uint value) private pure { if(value <= 23) { buf.append(uint8((major << 5) | value)); } else if(value <= 0xFF) { buf.append(uint8((major << 5) | 24)); buf.appendInt(value, 1); } else if(value <= 0xFFFF) { buf.append(uint8((major << 5) | 25)); buf.appendInt(value, 2); } else if(value <= 0xFFFFFFFF) { buf.append(uint8((major << 5) | 26)); buf.appendInt(value, 4); } else if(value <= 0xFFFFFFFFFFFFFFFF) { buf.append(uint8((major << 5) | 27)); buf.appendInt(value, 8); } } function encodeIndefiniteLengthType(Buffer.buffer memory buf, uint8 major) private pure { buf.append(uint8((major << 5) | 31)); } function encodeUInt(Buffer.buffer memory buf, uint value) internal pure { encodeType(buf, MAJOR_TYPE_INT, value); } function encodeInt(Buffer.buffer memory buf, int value) internal pure { if(value >= 0) { encodeType(buf, MAJOR_TYPE_INT, uint(value)); } else { encodeType(buf, MAJOR_TYPE_NEGATIVE_INT, uint(-1 - value)); } } function encodeBytes(Buffer.buffer memory buf, bytes value) internal pure { encodeType(buf, MAJOR_TYPE_BYTES, value.length); buf.append(value); } function encodeString(Buffer.buffer memory buf, string value) internal pure { encodeType(buf, MAJOR_TYPE_STRING, bytes(value).length); buf.append(bytes(value)); } function startArray(Buffer.buffer memory buf) internal pure { encodeIndefiniteLengthType(buf, MAJOR_TYPE_ARRAY); } function startMap(Buffer.buffer memory buf) internal pure { encodeIndefiniteLengthType(buf, MAJOR_TYPE_MAP); } function endSequence(Buffer.buffer memory buf) internal pure { encodeIndefiniteLengthType(buf, MAJOR_TYPE_CONTENT_FREE); } } /* End solidity-cborutils */ contract usingOraclize { uint constant day = 60*60*24; uint constant week = 60*60*24*7; uint constant month = 60*60*24*30; byte constant proofType_NONE = 0x00; byte constant proofType_TLSNotary = 0x10; byte constant proofType_Ledger = 0x30; byte constant proofType_Android = 0x40; byte constant proofType_Native = 0xF0; byte constant proofStorage_IPFS = 0x01; uint8 constant networkID_auto = 0; uint8 constant networkID_mainnet = 1; uint8 constant networkID_testnet = 2; uint8 constant networkID_morden = 2; uint8 constant networkID_consensys = 161; OraclizeAddrResolverI OAR; OraclizeI oraclize; modifier oraclizeAPI { if((address(OAR)==0)||(getCodeSize(address(OAR))==0)) oraclize_setNetwork(networkID_auto); if(address(oraclize) != OAR.getAddress()) oraclize = OraclizeI(OAR.getAddress()); _; } modifier coupon(string code){ oraclize = OraclizeI(OAR.getAddress()); _; } function oraclize_setNetwork(uint8 networkID) internal returns(bool){ return oraclize_setNetwork(); networkID; // silence the warning and remain backwards compatible } function oraclize_setNetwork() internal returns(bool){ if (getCodeSize(0x1d3B2638a7cC9f2CB3D298A3DA7a90B67E5506ed)>0){ //mainnet OAR = OraclizeAddrResolverI(0x1d3B2638a7cC9f2CB3D298A3DA7a90B67E5506ed); oraclize_setNetworkName("eth_mainnet"); return true; } if (getCodeSize(0xc03A2615D5efaf5F49F60B7BB6583eaec212fdf1)>0){ //ropsten testnet OAR = OraclizeAddrResolverI(0xc03A2615D5efaf5F49F60B7BB6583eaec212fdf1); oraclize_setNetworkName("eth_ropsten3"); return true; } if (getCodeSize(0xB7A07BcF2Ba2f2703b24C0691b5278999C59AC7e)>0){ //kovan testnet OAR = OraclizeAddrResolverI(0xB7A07BcF2Ba2f2703b24C0691b5278999C59AC7e); oraclize_setNetworkName("eth_kovan"); return true; } if (getCodeSize(0x146500cfd35B22E4A392Fe0aDc06De1a1368Ed48)>0){ //rinkeby testnet OAR = OraclizeAddrResolverI(0x146500cfd35B22E4A392Fe0aDc06De1a1368Ed48); oraclize_setNetworkName("eth_rinkeby"); return true; } if (getCodeSize(0x6f485C8BF6fc43eA212E93BBF8ce046C7f1cb475)>0){ //ethereum-bridge OAR = OraclizeAddrResolverI(0x6f485C8BF6fc43eA212E93BBF8ce046C7f1cb475); return true; } if (getCodeSize(0x20e12A1F859B3FeaE5Fb2A0A32C18F5a65555bBF)>0){ //ether.camp ide OAR = OraclizeAddrResolverI(0x20e12A1F859B3FeaE5Fb2A0A32C18F5a65555bBF); return true; } if (getCodeSize(0x51efaF4c8B3C9AfBD5aB9F4bbC82784Ab6ef8fAA)>0){ //browser-solidity OAR = OraclizeAddrResolverI(0x51efaF4c8B3C9AfBD5aB9F4bbC82784Ab6ef8fAA); return true; } return false; } function __callback(bytes32 myid, string result) public { __callback(myid, result, new bytes(0)); } function __callback(bytes32 myid, string result, bytes proof) public { return; // Following should never be reached with a preceding return, however // this is just a placeholder function, ideally meant to be defined in // child contract when proofs are used myid; result; proof; // Silence compiler warnings oraclize = OraclizeI(0); // Additional compiler silence about making function pure/view. } function oraclize_getPrice(string datasource) oraclizeAPI internal returns (uint){ return oraclize.getPrice(datasource); } function oraclize_getPrice(string datasource, uint gaslimit) oraclizeAPI internal returns (uint){ return oraclize.getPrice(datasource, gaslimit); } function oraclize_query(string datasource, string arg) oraclizeAPI internal returns (bytes32 id){ uint price = oraclize.getPrice(datasource); if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price return oraclize.query.value(price)(0, datasource, arg); } function oraclize_query(uint timestamp, string datasource, string arg) oraclizeAPI internal returns (bytes32 id){ uint price = oraclize.getPrice(datasource); if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price return oraclize.query.value(price)(timestamp, datasource, arg); } function oraclize_query(uint timestamp, string datasource, string arg, uint gaslimit) oraclizeAPI internal returns (bytes32 id){ uint price = oraclize.getPrice(datasource, gaslimit); if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price return oraclize.query_withGasLimit.value(price)(timestamp, datasource, arg, gaslimit); } function oraclize_query(string datasource, string arg, uint gaslimit) oraclizeAPI internal returns (bytes32 id){ uint price = oraclize.getPrice(datasource, gaslimit); if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price return oraclize.query_withGasLimit.value(price)(0, datasource, arg, gaslimit); } function oraclize_query(string datasource, string arg1, string arg2) oraclizeAPI internal returns (bytes32 id){ uint price = oraclize.getPrice(datasource); if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price return oraclize.query2.value(price)(0, datasource, arg1, arg2); } function oraclize_query(uint timestamp, string datasource, string arg1, string arg2) oraclizeAPI internal returns (bytes32 id){ uint price = oraclize.getPrice(datasource); if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price return oraclize.query2.value(price)(timestamp, datasource, arg1, arg2); } function oraclize_query(uint timestamp, string datasource, string arg1, string arg2, uint gaslimit) oraclizeAPI internal returns (bytes32 id){ uint price = oraclize.getPrice(datasource, gaslimit); if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price return oraclize.query2_withGasLimit.value(price)(timestamp, datasource, arg1, arg2, gaslimit); } function oraclize_query(string datasource, string arg1, string arg2, uint gaslimit) oraclizeAPI internal returns (bytes32 id){ uint price = oraclize.getPrice(datasource, gaslimit); if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price return oraclize.query2_withGasLimit.value(price)(0, datasource, arg1, arg2, gaslimit); } function oraclize_query(string datasource, string[] argN) oraclizeAPI internal returns (bytes32 id){ uint price = oraclize.getPrice(datasource); if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price bytes memory args = stra2cbor(argN); return oraclize.queryN.value(price)(0, datasource, args); } function oraclize_query(uint timestamp, string datasource, string[] argN) oraclizeAPI internal returns (bytes32 id){ uint price = oraclize.getPrice(datasource); if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price bytes memory args = stra2cbor(argN); return oraclize.queryN.value(price)(timestamp, datasource, args); } function oraclize_query(uint timestamp, string datasource, string[] argN, uint gaslimit) oraclizeAPI internal returns (bytes32 id){ uint price = oraclize.getPrice(datasource, gaslimit); if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price bytes memory args = stra2cbor(argN); return oraclize.queryN_withGasLimit.value(price)(timestamp, datasource, args, gaslimit); } function oraclize_query(string datasource, string[] argN, uint gaslimit) oraclizeAPI internal returns (bytes32 id){ uint price = oraclize.getPrice(datasource, gaslimit); if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price bytes memory args = stra2cbor(argN); return oraclize.queryN_withGasLimit.value(price)(0, datasource, args, gaslimit); } function oraclize_query(string datasource, string[1] args) oraclizeAPI internal returns (bytes32 id) { string[] memory dynargs = new string[](1); dynargs[0] = args[0]; return oraclize_query(datasource, dynargs); } function oraclize_query(uint timestamp, string datasource, string[1] args) oraclizeAPI internal returns (bytes32 id) { string[] memory dynargs = new string[](1); dynargs[0] = args[0]; return oraclize_query(timestamp, datasource, dynargs); } function oraclize_query(uint timestamp, string datasource, string[1] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { string[] memory dynargs = new string[](1); dynargs[0] = args[0]; return oraclize_query(timestamp, datasource, dynargs, gaslimit); } function oraclize_query(string datasource, string[1] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { string[] memory dynargs = new string[](1); dynargs[0] = args[0]; return oraclize_query(datasource, dynargs, gaslimit); } function oraclize_query(string datasource, string[2] args) oraclizeAPI internal returns (bytes32 id) { string[] memory dynargs = new string[](2); dynargs[0] = args[0]; dynargs[1] = args[1]; return oraclize_query(datasource, dynargs); } function oraclize_query(uint timestamp, string datasource, string[2] args) oraclizeAPI internal returns (bytes32 id) { string[] memory dynargs = new string[](2); dynargs[0] = args[0]; dynargs[1] = args[1]; return oraclize_query(timestamp, datasource, dynargs); } function oraclize_query(uint timestamp, string datasource, string[2] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { string[] memory dynargs = new string[](2); dynargs[0] = args[0]; dynargs[1] = args[1]; return oraclize_query(timestamp, datasource, dynargs, gaslimit); } function oraclize_query(string datasource, string[2] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { string[] memory dynargs = new string[](2); dynargs[0] = args[0]; dynargs[1] = args[1]; return oraclize_query(datasource, dynargs, gaslimit); } function oraclize_query(string datasource, string[3] args) oraclizeAPI internal returns (bytes32 id) { string[] memory dynargs = new string[](3); dynargs[0] = args[0]; dynargs[1] = args[1]; dynargs[2] = args[2]; return oraclize_query(datasource, dynargs); } function oraclize_query(uint timestamp, string datasource, string[3] args) oraclizeAPI internal returns (bytes32 id) { string[] memory dynargs = new string[](3); dynargs[0] = args[0]; dynargs[1] = args[1]; dynargs[2] = args[2]; return oraclize_query(timestamp, datasource, dynargs); } function oraclize_query(uint timestamp, string datasource, string[3] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { string[] memory dynargs = new string[](3); dynargs[0] = args[0]; dynargs[1] = args[1]; dynargs[2] = args[2]; return oraclize_query(timestamp, datasource, dynargs, gaslimit); } function oraclize_query(string datasource, string[3] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { string[] memory dynargs = new string[](3); dynargs[0] = args[0]; dynargs[1] = args[1]; dynargs[2] = args[2]; return oraclize_query(datasource, dynargs, gaslimit); } function oraclize_query(string datasource, string[4] args) oraclizeAPI internal returns (bytes32 id) { string[] memory dynargs = new string[](4); dynargs[0] = args[0]; dynargs[1] = args[1]; dynargs[2] = args[2]; dynargs[3] = args[3]; return oraclize_query(datasource, dynargs); } function oraclize_query(uint timestamp, string datasource, string[4] args) oraclizeAPI internal returns (bytes32 id) { string[] memory dynargs = new string[](4); dynargs[0] = args[0]; dynargs[1] = args[1]; dynargs[2] = args[2]; dynargs[3] = args[3]; return oraclize_query(timestamp, datasource, dynargs); } function oraclize_query(uint timestamp, string datasource, string[4] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { string[] memory dynargs = new string[](4); dynargs[0] = args[0]; dynargs[1] = args[1]; dynargs[2] = args[2]; dynargs[3] = args[3]; return oraclize_query(timestamp, datasource, dynargs, gaslimit); } function oraclize_query(string datasource, string[4] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { string[] memory dynargs = new string[](4); dynargs[0] = args[0]; dynargs[1] = args[1]; dynargs[2] = args[2]; dynargs[3] = args[3]; return oraclize_query(datasource, dynargs, gaslimit); } function oraclize_query(string datasource, string[5] args) oraclizeAPI internal returns (bytes32 id) { string[] memory dynargs = new string[](5); dynargs[0] = args[0]; dynargs[1] = args[1]; dynargs[2] = args[2]; dynargs[3] = args[3]; dynargs[4] = args[4]; return oraclize_query(datasource, dynargs); } function oraclize_query(uint timestamp, string datasource, string[5] args) oraclizeAPI internal returns (bytes32 id) { string[] memory dynargs = new string[](5); dynargs[0] = args[0]; dynargs[1] = args[1]; dynargs[2] = args[2]; dynargs[3] = args[3]; dynargs[4] = args[4]; return oraclize_query(timestamp, datasource, dynargs); } function oraclize_query(uint timestamp, string datasource, string[5] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { string[] memory dynargs = new string[](5); dynargs[0] = args[0]; dynargs[1] = args[1]; dynargs[2] = args[2]; dynargs[3] = args[3]; dynargs[4] = args[4]; return oraclize_query(timestamp, datasource, dynargs, gaslimit); } function oraclize_query(string datasource, string[5] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { string[] memory dynargs = new string[](5); dynargs[0] = args[0]; dynargs[1] = args[1]; dynargs[2] = args[2]; dynargs[3] = args[3]; dynargs[4] = args[4]; return oraclize_query(datasource, dynargs, gaslimit); } function oraclize_query(string datasource, bytes[] argN) oraclizeAPI internal returns (bytes32 id){ uint price = oraclize.getPrice(datasource); if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price bytes memory args = ba2cbor(argN); return oraclize.queryN.value(price)(0, datasource, args); } function oraclize_query(uint timestamp, string datasource, bytes[] argN) oraclizeAPI internal returns (bytes32 id){ uint price = oraclize.getPrice(datasource); if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price bytes memory args = ba2cbor(argN); return oraclize.queryN.value(price)(timestamp, datasource, args); } function oraclize_query(uint timestamp, string datasource, bytes[] argN, uint gaslimit) oraclizeAPI internal returns (bytes32 id){ uint price = oraclize.getPrice(datasource, gaslimit); if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price bytes memory args = ba2cbor(argN); return oraclize.queryN_withGasLimit.value(price)(timestamp, datasource, args, gaslimit); } function oraclize_query(string datasource, bytes[] argN, uint gaslimit) oraclizeAPI internal returns (bytes32 id){ uint price = oraclize.getPrice(datasource, gaslimit); if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price bytes memory args = ba2cbor(argN); return oraclize.queryN_withGasLimit.value(price)(0, datasource, args, gaslimit); } function oraclize_query(string datasource, bytes[1] args) oraclizeAPI internal returns (bytes32 id) { bytes[] memory dynargs = new bytes[](1); dynargs[0] = args[0]; return oraclize_query(datasource, dynargs); } function oraclize_query(uint timestamp, string datasource, bytes[1] args) oraclizeAPI internal returns (bytes32 id) { bytes[] memory dynargs = new bytes[](1); dynargs[0] = args[0]; return oraclize_query(timestamp, datasource, dynargs); } function oraclize_query(uint timestamp, string datasource, bytes[1] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { bytes[] memory dynargs = new bytes[](1); dynargs[0] = args[0]; return oraclize_query(timestamp, datasource, dynargs, gaslimit); } function oraclize_query(string datasource, bytes[1] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { bytes[] memory dynargs = new bytes[](1); dynargs[0] = args[0]; return oraclize_query(datasource, dynargs, gaslimit); } function oraclize_query(string datasource, bytes[2] args) oraclizeAPI internal returns (bytes32 id) { bytes[] memory dynargs = new bytes[](2); dynargs[0] = args[0]; dynargs[1] = args[1]; return oraclize_query(datasource, dynargs); } function oraclize_query(uint timestamp, string datasource, bytes[2] args) oraclizeAPI internal returns (bytes32 id) { bytes[] memory dynargs = new bytes[](2); dynargs[0] = args[0]; dynargs[1] = args[1]; return oraclize_query(timestamp, datasource, dynargs); } function oraclize_query(uint timestamp, string datasource, bytes[2] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { bytes[] memory dynargs = new bytes[](2); dynargs[0] = args[0]; dynargs[1] = args[1]; return oraclize_query(timestamp, datasource, dynargs, gaslimit); } function oraclize_query(string datasource, bytes[2] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { bytes[] memory dynargs = new bytes[](2); dynargs[0] = args[0]; dynargs[1] = args[1]; return oraclize_query(datasource, dynargs, gaslimit); } function oraclize_query(string datasource, bytes[3] args) oraclizeAPI internal returns (bytes32 id) { bytes[] memory dynargs = new bytes[](3); dynargs[0] = args[0]; dynargs[1] = args[1]; dynargs[2] = args[2]; return oraclize_query(datasource, dynargs); } function oraclize_query(uint timestamp, string datasource, bytes[3] args) oraclizeAPI internal returns (bytes32 id) { bytes[] memory dynargs = new bytes[](3); dynargs[0] = args[0]; dynargs[1] = args[1]; dynargs[2] = args[2]; return oraclize_query(timestamp, datasource, dynargs); } function oraclize_query(uint timestamp, string datasource, bytes[3] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { bytes[] memory dynargs = new bytes[](3); dynargs[0] = args[0]; dynargs[1] = args[1]; dynargs[2] = args[2]; return oraclize_query(timestamp, datasource, dynargs, gaslimit); } function oraclize_query(string datasource, bytes[3] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { bytes[] memory dynargs = new bytes[](3); dynargs[0] = args[0]; dynargs[1] = args[1]; dynargs[2] = args[2]; return oraclize_query(datasource, dynargs, gaslimit); } function oraclize_query(string datasource, bytes[4] args) oraclizeAPI internal returns (bytes32 id) { bytes[] memory dynargs = new bytes[](4); dynargs[0] = args[0]; dynargs[1] = args[1]; dynargs[2] = args[2]; dynargs[3] = args[3]; return oraclize_query(datasource, dynargs); } function oraclize_query(uint timestamp, string datasource, bytes[4] args) oraclizeAPI internal returns (bytes32 id) { bytes[] memory dynargs = new bytes[](4); dynargs[0] = args[0]; dynargs[1] = args[1]; dynargs[2] = args[2]; dynargs[3] = args[3]; return oraclize_query(timestamp, datasource, dynargs); } function oraclize_query(uint timestamp, string datasource, bytes[4] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { bytes[] memory dynargs = new bytes[](4); dynargs[0] = args[0]; dynargs[1] = args[1]; dynargs[2] = args[2]; dynargs[3] = args[3]; return oraclize_query(timestamp, datasource, dynargs, gaslimit); } function oraclize_query(string datasource, bytes[4] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { bytes[] memory dynargs = new bytes[](4); dynargs[0] = args[0]; dynargs[1] = args[1]; dynargs[2] = args[2]; dynargs[3] = args[3]; return oraclize_query(datasource, dynargs, gaslimit); } function oraclize_query(string datasource, bytes[5] args) oraclizeAPI internal returns (bytes32 id) { bytes[] memory dynargs = new bytes[](5); dynargs[0] = args[0]; dynargs[1] = args[1]; dynargs[2] = args[2]; dynargs[3] = args[3]; dynargs[4] = args[4]; return oraclize_query(datasource, dynargs); } function oraclize_query(uint timestamp, string datasource, bytes[5] args) oraclizeAPI internal returns (bytes32 id) { bytes[] memory dynargs = new bytes[](5); dynargs[0] = args[0]; dynargs[1] = args[1]; dynargs[2] = args[2]; dynargs[3] = args[3]; dynargs[4] = args[4]; return oraclize_query(timestamp, datasource, dynargs); } function oraclize_query(uint timestamp, string datasource, bytes[5] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { bytes[] memory dynargs = new bytes[](5); dynargs[0] = args[0]; dynargs[1] = args[1]; dynargs[2] = args[2]; dynargs[3] = args[3]; dynargs[4] = args[4]; return oraclize_query(timestamp, datasource, dynargs, gaslimit); } function oraclize_query(string datasource, bytes[5] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { bytes[] memory dynargs = new bytes[](5); dynargs[0] = args[0]; dynargs[1] = args[1]; dynargs[2] = args[2]; dynargs[3] = args[3]; dynargs[4] = args[4]; return oraclize_query(datasource, dynargs, gaslimit); } function oraclize_cbAddress() oraclizeAPI internal returns (address){ return oraclize.cbAddress(); } function oraclize_setProof(byte proofP) oraclizeAPI internal { return oraclize.setProofType(proofP); } function oraclize_setCustomGasPrice(uint gasPrice) oraclizeAPI internal { return oraclize.setCustomGasPrice(gasPrice); } function oraclize_randomDS_getSessionPubKeyHash() oraclizeAPI internal returns (bytes32){ return oraclize.randomDS_getSessionPubKeyHash(); } function getCodeSize(address _addr) view internal returns(uint _size) { assembly { _size := extcodesize(_addr) } } function parseAddr(string _a) internal pure returns (address){ bytes memory tmp = bytes(_a); uint160 iaddr = 0; uint160 b1; uint160 b2; for (uint i=2; i<2+2*20; i+=2){ iaddr *= 256; b1 = uint160(tmp[i]); b2 = uint160(tmp[i+1]); if ((b1 >= 97)&&(b1 <= 102)) b1 -= 87; else if ((b1 >= 65)&&(b1 <= 70)) b1 -= 55; else if ((b1 >= 48)&&(b1 <= 57)) b1 -= 48; if ((b2 >= 97)&&(b2 <= 102)) b2 -= 87; else if ((b2 >= 65)&&(b2 <= 70)) b2 -= 55; else if ((b2 >= 48)&&(b2 <= 57)) b2 -= 48; iaddr += (b1*16+b2); } return address(iaddr); } function strCompare(string _a, string _b) internal pure returns (int) { bytes memory a = bytes(_a); bytes memory b = bytes(_b); uint minLength = a.length; if (b.length < minLength) minLength = b.length; for (uint i = 0; i < minLength; i ++) if (a[i] < b[i]) return -1; else if (a[i] > b[i]) return 1; if (a.length < b.length) return -1; else if (a.length > b.length) return 1; else return 0; } function indexOf(string _haystack, string _needle) internal pure returns (int) { bytes memory h = bytes(_haystack); bytes memory n = bytes(_needle); if(h.length < 1 || n.length < 1 || (n.length > h.length)) return -1; else if(h.length > (2**128 -1)) return -1; else { uint subindex = 0; for (uint i = 0; i < h.length; i ++) { if (h[i] == n[0]) { subindex = 1; while(subindex < n.length && (i + subindex) < h.length && h[i + subindex] == n[subindex]) { subindex++; } if(subindex == n.length) return int(i); } } return -1; } } function strConcat(string _a, string _b, string _c, string _d, string _e) internal pure returns (string) { bytes memory _ba = bytes(_a); bytes memory _bb = bytes(_b); bytes memory _bc = bytes(_c); bytes memory _bd = bytes(_d); bytes memory _be = bytes(_e); string memory abcde = new string(_ba.length + _bb.length + _bc.length + _bd.length + _be.length); bytes memory babcde = bytes(abcde); uint k = 0; for (uint i = 0; i < _ba.length; i++) babcde[k++] = _ba[i]; for (i = 0; i < _bb.length; i++) babcde[k++] = _bb[i]; for (i = 0; i < _bc.length; i++) babcde[k++] = _bc[i]; for (i = 0; i < _bd.length; i++) babcde[k++] = _bd[i]; for (i = 0; i < _be.length; i++) babcde[k++] = _be[i]; return string(babcde); } function strConcat(string _a, string _b, string _c, string _d) internal pure returns (string) { return strConcat(_a, _b, _c, _d, ""); } function strConcat(string _a, string _b, string _c) internal pure returns (string) { return strConcat(_a, _b, _c, "", ""); } function strConcat(string _a, string _b) internal pure returns (string) { return strConcat(_a, _b, "", "", ""); } // parseInt function parseInt(string _a) internal pure returns (uint) { return parseInt(_a, 0); } // parseInt(parseFloat*10^_b) function parseInt(string _a, uint _b) internal pure returns (uint) { bytes memory bresult = bytes(_a); uint mint = 0; bool decimals = false; for (uint i=0; i<bresult.length; i++){ if ((bresult[i] >= 48)&&(bresult[i] <= 57)){ if (decimals){ if (_b == 0) break; else _b--; } mint *= 10; mint += uint(bresult[i]) - 48; } else if (bresult[i] == 46) decimals = true; } if (_b > 0) mint *= 10**_b; return mint; } function uint2str(uint i) internal pure returns (string){ if (i == 0) return "0"; uint j = i; uint len; while (j != 0){ len++; j /= 10; } bytes memory bstr = new bytes(len); uint k = len - 1; while (i != 0){ bstr[k--] = byte(48 + i % 10); i /= 10; } return string(bstr); } using CBOR for Buffer.buffer; function stra2cbor(string[] arr) internal pure returns (bytes) { safeMemoryCleaner(); Buffer.buffer memory buf; Buffer.init(buf, 1024); buf.startArray(); for (uint i = 0; i < arr.length; i++) { buf.encodeString(arr[i]); } buf.endSequence(); return buf.buf; } function ba2cbor(bytes[] arr) internal pure returns (bytes) { safeMemoryCleaner(); Buffer.buffer memory buf; Buffer.init(buf, 1024); buf.startArray(); for (uint i = 0; i < arr.length; i++) { buf.encodeBytes(arr[i]); } buf.endSequence(); return buf.buf; } string oraclize_network_name; function oraclize_setNetworkName(string _network_name) internal { oraclize_network_name = _network_name; } function oraclize_getNetworkName() internal view returns (string) { return oraclize_network_name; } function oraclize_newRandomDSQuery(uint _delay, uint _nbytes, uint _customGasLimit) internal returns (bytes32){ require((_nbytes > 0) && (_nbytes <= 32)); // Convert from seconds to ledger timer ticks _delay *= 10; bytes memory nbytes = new bytes(1); nbytes[0] = byte(_nbytes); bytes memory unonce = new bytes(32); bytes memory sessionKeyHash = new bytes(32); bytes32 sessionKeyHash_bytes32 = oraclize_randomDS_getSessionPubKeyHash(); assembly { mstore(unonce, 0x20) // the following variables can be relaxed // check relaxed random contract under ethereum-examples repo // for an idea on how to override and replace comit hash vars mstore(add(unonce, 0x20), xor(blockhash(sub(number, 1)), xor(coinbase, timestamp))) mstore(sessionKeyHash, 0x20) mstore(add(sessionKeyHash, 0x20), sessionKeyHash_bytes32) } bytes memory delay = new bytes(32); assembly { mstore(add(delay, 0x20), _delay) } bytes memory delay_bytes8 = new bytes(8); copyBytes(delay, 24, 8, delay_bytes8, 0); bytes[4] memory args = [unonce, nbytes, sessionKeyHash, delay]; bytes32 queryId = oraclize_query("random", args, _customGasLimit); bytes memory delay_bytes8_left = new bytes(8); assembly { let x := mload(add(delay_bytes8, 0x20)) mstore8(add(delay_bytes8_left, 0x27), div(x, 0x100000000000000000000000000000000000000000000000000000000000000)) mstore8(add(delay_bytes8_left, 0x26), div(x, 0x1000000000000000000000000000000000000000000000000000000000000)) mstore8(add(delay_bytes8_left, 0x25), div(x, 0x10000000000000000000000000000000000000000000000000000000000)) mstore8(add(delay_bytes8_left, 0x24), div(x, 0x100000000000000000000000000000000000000000000000000000000)) mstore8(add(delay_bytes8_left, 0x23), div(x, 0x1000000000000000000000000000000000000000000000000000000)) mstore8(add(delay_bytes8_left, 0x22), div(x, 0x10000000000000000000000000000000000000000000000000000)) mstore8(add(delay_bytes8_left, 0x21), div(x, 0x100000000000000000000000000000000000000000000000000)) mstore8(add(delay_bytes8_left, 0x20), div(x, 0x1000000000000000000000000000000000000000000000000)) } oraclize_randomDS_setCommitment(queryId, keccak256(abi.encodePacked(delay_bytes8_left, args[1], sha256(args[0]), args[2]))); return queryId; } function oraclize_randomDS_setCommitment(bytes32 queryId, bytes32 commitment) internal { oraclize_randomDS_args[queryId] = commitment; } mapping(bytes32=>bytes32) oraclize_randomDS_args; mapping(bytes32=>bool) oraclize_randomDS_sessionKeysHashVerified; function verifySig(bytes32 tosignh, bytes dersig, bytes pubkey) internal returns (bool){ bool sigok; address signer; bytes32 sigr; bytes32 sigs; bytes memory sigr_ = new bytes(32); uint offset = 4+(uint(dersig[3]) - 0x20); sigr_ = copyBytes(dersig, offset, 32, sigr_, 0); bytes memory sigs_ = new bytes(32); offset += 32 + 2; sigs_ = copyBytes(dersig, offset+(uint(dersig[offset-1]) - 0x20), 32, sigs_, 0); assembly { sigr := mload(add(sigr_, 32)) sigs := mload(add(sigs_, 32)) } (sigok, signer) = safer_ecrecover(tosignh, 27, sigr, sigs); if (address(keccak256(pubkey)) == signer) return true; else { (sigok, signer) = safer_ecrecover(tosignh, 28, sigr, sigs); return (address(keccak256(pubkey)) == signer); } } function oraclize_randomDS_proofVerify__sessionKeyValidity(bytes proof, uint sig2offset) internal returns (bool) { bool sigok; // Step 6: verify the attestation signature, APPKEY1 must sign the sessionKey from the correct ledger app (CODEHASH) bytes memory sig2 = new bytes(uint(proof[sig2offset+1])+2); copyBytes(proof, sig2offset, sig2.length, sig2, 0); bytes memory appkey1_pubkey = new bytes(64); copyBytes(proof, 3+1, 64, appkey1_pubkey, 0); bytes memory tosign2 = new bytes(1+65+32); tosign2[0] = byte(1); //role copyBytes(proof, sig2offset-65, 65, tosign2, 1); bytes memory CODEHASH = hex"fd94fa71bc0ba10d39d464d0d8f465efeef0a2764e3887fcc9df41ded20f505c"; copyBytes(CODEHASH, 0, 32, tosign2, 1+65); sigok = verifySig(sha256(tosign2), sig2, appkey1_pubkey); if (sigok == false) return false; // Step 7: verify the APPKEY1 provenance (must be signed by Ledger) bytes memory LEDGERKEY = hex"7fb956469c5c9b89840d55b43537e66a98dd4811ea0a27224272c2e5622911e8537a2f8e86a46baec82864e98dd01e9ccc2f8bc5dfc9cbe5a91a290498dd96e4"; bytes memory tosign3 = new bytes(1+65); tosign3[0] = 0xFE; copyBytes(proof, 3, 65, tosign3, 1); bytes memory sig3 = new bytes(uint(proof[3+65+1])+2); copyBytes(proof, 3+65, sig3.length, sig3, 0); sigok = verifySig(sha256(tosign3), sig3, LEDGERKEY); return sigok; } modifier oraclize_randomDS_proofVerify(bytes32 _queryId, string _result, bytes _proof) { // Step 1: the prefix has to match 'LP\x01' (Ledger Proof version 1) require((_proof[0] == "L") && (_proof[1] == "P") && (_proof[2] == 1)); bool proofVerified = oraclize_randomDS_proofVerify__main(_proof, _queryId, bytes(_result), oraclize_getNetworkName()); require(proofVerified); _; } function oraclize_randomDS_proofVerify__returnCode(bytes32 _queryId, string _result, bytes _proof) internal returns (uint8){ // Step 1: the prefix has to match 'LP\x01' (Ledger Proof version 1) if ((_proof[0] != "L")||(_proof[1] != "P")||(_proof[2] != 1)) return 1; bool proofVerified = oraclize_randomDS_proofVerify__main(_proof, _queryId, bytes(_result), oraclize_getNetworkName()); if (proofVerified == false) return 2; return 0; } function matchBytes32Prefix(bytes32 content, bytes prefix, uint n_random_bytes) internal pure returns (bool){ bool match_ = true; require(prefix.length == n_random_bytes); for (uint256 i=0; i< n_random_bytes; i++) { if (content[i] != prefix[i]) match_ = false; } return match_; } function oraclize_randomDS_proofVerify__main(bytes proof, bytes32 queryId, bytes result, string context_name) internal returns (bool){ // Step 2: the unique keyhash has to match with the sha256 of (context name + queryId) uint ledgerProofLength = 3+65+(uint(proof[3+65+1])+2)+32; bytes memory keyhash = new bytes(32); copyBytes(proof, ledgerProofLength, 32, keyhash, 0); if (!(keccak256(keyhash) == keccak256(abi.encodePacked(sha256(abi.encodePacked(context_name, queryId)))))) return false; bytes memory sig1 = new bytes(uint(proof[ledgerProofLength+(32+8+1+32)+1])+2); copyBytes(proof, ledgerProofLength+(32+8+1+32), sig1.length, sig1, 0); // Step 3: we assume sig1 is valid (it will be verified during step 5) and we verify if 'result' is the prefix of sha256(sig1) if (!matchBytes32Prefix(sha256(sig1), result, uint(proof[ledgerProofLength+32+8]))) return false; // Step 4: commitment match verification, keccak256(delay, nbytes, unonce, sessionKeyHash) == commitment in storage. // This is to verify that the computed args match with the ones specified in the query. bytes memory commitmentSlice1 = new bytes(8+1+32); copyBytes(proof, ledgerProofLength+32, 8+1+32, commitmentSlice1, 0); bytes memory sessionPubkey = new bytes(64); uint sig2offset = ledgerProofLength+32+(8+1+32)+sig1.length+65; copyBytes(proof, sig2offset-64, 64, sessionPubkey, 0); bytes32 sessionPubkeyHash = sha256(sessionPubkey); if (oraclize_randomDS_args[queryId] == keccak256(abi.encodePacked(commitmentSlice1, sessionPubkeyHash))){ //unonce, nbytes and sessionKeyHash match delete oraclize_randomDS_args[queryId]; } else return false; // Step 5: validity verification for sig1 (keyhash and args signed with the sessionKey) bytes memory tosign1 = new bytes(32+8+1+32); copyBytes(proof, ledgerProofLength, 32+8+1+32, tosign1, 0); if (!verifySig(sha256(tosign1), sig1, sessionPubkey)) return false; // verify if sessionPubkeyHash was verified already, if not.. let's do it! if (oraclize_randomDS_sessionKeysHashVerified[sessionPubkeyHash] == false){ oraclize_randomDS_sessionKeysHashVerified[sessionPubkeyHash] = oraclize_randomDS_proofVerify__sessionKeyValidity(proof, sig2offset); } return oraclize_randomDS_sessionKeysHashVerified[sessionPubkeyHash]; } // the following function has been written by Alex Beregszaszi (@axic), use it under the terms of the MIT license function copyBytes(bytes from, uint fromOffset, uint length, bytes to, uint toOffset) internal pure returns (bytes) { uint minLength = length + toOffset; // Buffer too small require(to.length >= minLength); // Should be a better way? // NOTE: the offset 32 is added to skip the `size` field of both bytes variables uint i = 32 + fromOffset; uint j = 32 + toOffset; while (i < (32 + fromOffset + length)) { assembly { let tmp := mload(add(from, i)) mstore(add(to, j), tmp) } i += 32; j += 32; } return to; } // the following function has been written by Alex Beregszaszi (@axic), use it under the terms of the MIT license // Duplicate Solidity's ecrecover, but catching the CALL return value function safer_ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal returns (bool, address) { // We do our own memory management here. Solidity uses memory offset // 0x40 to store the current end of memory. We write past it (as // writes are memory extensions), but don't update the offset so // Solidity will reuse it. The memory used here is only needed for // this context. // FIXME: inline assembly can't access return values bool ret; address addr; assembly { let size := mload(0x40) mstore(size, hash) mstore(add(size, 32), v) mstore(add(size, 64), r) mstore(add(size, 96), s) // NOTE: we can reuse the request memory because we deal with // the return code ret := call(3000, 1, 0, size, 128, size, 32) addr := mload(size) } return (ret, addr); } // the following function has been written by Alex Beregszaszi (@axic), use it under the terms of the MIT license function ecrecovery(bytes32 hash, bytes sig) internal returns (bool, address) { bytes32 r; bytes32 s; uint8 v; if (sig.length != 65) return (false, 0); // The signature format is a compact form of: // {bytes32 r}{bytes32 s}{uint8 v} // Compact means, uint8 is not padded to 32 bytes. assembly { r := mload(add(sig, 32)) s := mload(add(sig, 64)) // Here we are loading the last 32 bytes. We exploit the fact that // 'mload' will pad with zeroes if we overread. // There is no 'mload8' to do this, but that would be nicer. v := byte(0, mload(add(sig, 96))) // Alternative solution: // 'byte' is not working due to the Solidity parser, so lets // use the second best option, 'and' // v := and(mload(add(sig, 65)), 255) } // albeit non-transactional signatures are not specified by the YP, one would expect it // to match the YP range of [27, 28] // // geth uses [0, 1] and some clients have followed. This might change, see: // https://github.com/ethereum/go-ethereum/issues/2053 if (v < 27) v += 27; if (v != 27 && v != 28) return (false, 0); return safer_ecrecover(hash, v, r, s); } function safeMemoryCleaner() internal pure { assembly { let fmem := mload(0x40) codecopy(fmem, codesize, sub(msize, fmem)) } } } // </ORACLIZE_API>
/** * Ownable * Copyright (C) 2018 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.4.25; /// @title Ownable has an owner address and provides basic authorization control functions. /// This contract is modified version of the MIT OpenZepplin Ownable contract /// This contract doesn't allow for multiple changeOwner operations /// https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ownership/Ownable.sol contract Ownable { event TransferredOwnership(address _from, address _to); address private _owner; bool private _isTransferable; /// @dev Constructor sets the original owner of the contract and whether or not it is one time transferable. constructor(address _account, bool _transferable) internal { _owner = _account; _isTransferable = _transferable; emit TransferredOwnership(address(0), _account); } /// @dev Reverts if called by any account other than the owner. modifier onlyOwner() { require(_isOwner(), "sender is not an owner"); _; } /// @dev Allows the current owner to transfer control of the contract to a new address. /// @param _account address to transfer ownership to. function transferOwnership(address _account) 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 false. _isTransferable = false; // Emit the ownership transfer event. emit TransferredOwnership(_owner, _account); // Set the owner to the provided address. _owner = _account; } /// @dev Allows the current owner to relinquish control of the contract. /// @notice Renouncing to ownership will leave the contract without an owner and unusable. /// It will not be possible to call the functions with the `onlyOwner` modifier anymore. function renounceOwnership() public onlyOwner { // Require that the ownership is transferable. require(_isTransferable, "ownership is not transferable"); emit TransferredOwnership(_owner, address(0)); // note that this could be terminal _owner = address(0); } /// @return the address of the owner. function owner() public view returns (address) { return _owner; } /// @return true if the ownership is transferable. function isTransferable() public view returns (bool) { return _isTransferable; } /// @return true if sender is the owner of the contract. function _isOwner() internal view returns (bool) { return msg.sender == _owner; } }
/** * ParseIntScientific * Copyright (C) 2018 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.4.25; import "./SafeMath.sol"; /// @title ParseIntScientific provides floating point in scientific notation (e.g. e-5) parsing functionality. contract ParseIntScientific { using SafeMath for uint256; byte constant private PLUS_ASCII = byte(43); //decimal value of '+' byte constant private DASH_ASCII = byte(45); //decimal value of '-' byte constant private DOT_ASCII = byte(46); //decimal value of '.' byte constant private ZERO_ASCII = byte(48); //decimal value of '0' byte constant private NINE_ASCII = byte(57); //decimal value of '9' byte constant private E_ASCII = byte(69); //decimal value of 'E' byte constant private e_ASCII = byte(101); //decimal value of 'e' /// @dev ParseIntScientific delegates the call to _parseIntScientific(string, uint) with the 2nd argument being 0. function _parseIntScientific(string _inString) internal pure returns (uint) { return _parseIntScientific(_inString, 0); } /// @dev ParseIntScientificWei parses a rate expressed in ETH and returns its wei denomination function _parseIntScientificWei(string _inString) internal pure returns (uint) { return _parseIntScientific(_inString, 18); } /// @dev ParseIntScientific parses a JSON standard - floating point number. /// @param _inString is input string. /// @param _magnitudeMult multiplies the number with 10^_magnitudeMult. function _parseIntScientific(string _inString, uint _magnitudeMult) internal pure returns (uint) { bytes memory inBytes = bytes(_inString); uint mint = 0; // the final uint returned uint mintDec = 0; // the uint following the decimal point uint mintExp = 0; // the exponent uint decMinted = 0; // how many decimals were 'minted'. uint expIndex = 0; // the position in the byte array that 'e' was found (if found) bool integral = false; // indicates the existence of the integral part, it should always exist (even if 0) e.g. 'e+1' or '.1' is not valid bool decimals = false; // indicates a decimal number, set to true if '.' is found bool exp = false; // indicates if the number being parsed has an exponential representation bool minus = false; // indicated if the exponent is negative bool plus = false; // indicated if the exponent is positive for (uint i = 0; i < inBytes.length; i++) { if ((inBytes[i] >= ZERO_ASCII) && (inBytes[i] <= NINE_ASCII) && (!exp)) { // 'e' not encountered yet, minting integer part or decimals if (decimals) { // '.' encountered //use safeMath in case there is an overflow mintDec = mintDec.mul(10); mintDec = mintDec.add(uint(inBytes[i]) - uint(ZERO_ASCII)); decMinted++; //keep track of the #decimals } else { // integral part (before '.') integral = true; //use safeMath in case there is an overflow mint = mint.mul(10); mint = mint.add(uint(inBytes[i]) - uint(ZERO_ASCII)); } } else if ((inBytes[i] >= ZERO_ASCII) && (inBytes[i] <= NINE_ASCII) && (exp)) { //exponential notation (e-/+) has been detected, mint the exponent mintExp = mintExp.mul(10); mintExp = mintExp.add(uint(inBytes[i]) - uint(ZERO_ASCII)); } else if (inBytes[i] == DOT_ASCII) { //an integral part before should always exist before '.' require(integral, "missing integral part"); // an extra decimal point makes the format invalid require(!decimals, "duplicate decimal point"); //the decimal point should always be before the exponent require(!exp, "decimal after exponent"); decimals = true; } else if (inBytes[i] == DASH_ASCII) { // an extra '-' should be considered an invalid character require(!minus, "duplicate -"); require(!plus, "extra sign"); require(expIndex + 1 == i, "- sign not immediately after e"); minus = true; } else if (inBytes[i] == PLUS_ASCII) { // an extra '+' should be considered an invalid character require(!plus, "duplicate +"); require(!minus, "extra sign"); require(expIndex + 1 == i, "+ sign not immediately after e"); plus = true; } else if ((inBytes[i] == E_ASCII) || (inBytes[i] == e_ASCII)) { //an integral part before should always exist before 'e' require(integral, "missing integral part"); // an extra 'e' or 'E' should be considered an invalid character require(!exp, "duplicate exponent symbol"); exp = true; expIndex = i; } else { revert("invalid digit"); } } if (minus || plus) { // end of string e[x|-] without specifying the exponent require(i > expIndex + 2); } else if (exp) { // end of string (e) without specifying the exponent require(i > expIndex + 1); } if (minus) { // e^(-x) if (mintExp >= _magnitudeMult) { // the (negative) exponent is bigger than the given parameter for "shifting left". // use integer division to reduce the precision. require(mintExp - _magnitudeMult < 78, "exponent > 77"); // mint /= 10 ** (mintExp - _magnitudeMult); return mint; } else { // the (negative) exponent is smaller than the given parameter for "shifting left". //no need for underflow check _magnitudeMult = _magnitudeMult - mintExp; } } else { // e^(+x), positive exponent or no exponent // just shift left as many times as indicated by the exponent and the shift parameter _magnitudeMult = _magnitudeMult.add(mintExp); } if (_magnitudeMult >= decMinted) { // the decimals are fewer or equal than the shifts: use all of them // shift number and add the decimals at the end // include decimals if present in the original input require(decMinted < 78, "more than 77 decimal digits parsed"); // mint = mint.mul(10 ** (decMinted)); mint = mint.add(mintDec); //// add zeros at the end if the decimals were fewer than #_magnitudeMult require(_magnitudeMult - decMinted < 78, "exponent > 77"); // mint = mint.mul(10 ** (_magnitudeMult - decMinted)); } else { // the decimals are more than the #_magnitudeMult shifts // use only the ones needed, discard the rest decMinted -= _magnitudeMult; require(decMinted < 78, "more than 77 decimal digits parsed"); // mintDec /= 10 ** (decMinted); // shift number and add the decimals at the end require(_magnitudeMult < 78, "more than 77 decimal digits parsed"); // mint = mint.mul(10 ** (_magnitudeMult)); mint = mint.add(mintDec); } return mint; } }
/** * BSD 2-Clause License * * Copyright (c) 2018, True Names Limited * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ pragma solidity ^0.4.24; import "./ENS.sol"; /** * A simple resolver anyone can use; only allows the owner of a node to set its * address. */ contract PublicResolver { bytes4 constant INTERFACE_META_ID = 0x01ffc9a7; bytes4 constant ADDR_INTERFACE_ID = 0x3b3b57de; bytes4 constant CONTENT_INTERFACE_ID = 0xd8389dc5; bytes4 constant NAME_INTERFACE_ID = 0x691f3431; bytes4 constant ABI_INTERFACE_ID = 0x2203ab56; bytes4 constant PUBKEY_INTERFACE_ID = 0xc8690233; bytes4 constant TEXT_INTERFACE_ID = 0x59d1d43c; bytes4 constant MULTIHASH_INTERFACE_ID = 0xe89401a1; event AddrChanged(bytes32 indexed node, address a); event ContentChanged(bytes32 indexed node, bytes32 hash); event NameChanged(bytes32 indexed node, string name); event ABIChanged(bytes32 indexed node, uint256 indexed contentType); event PubkeyChanged(bytes32 indexed node, bytes32 x, bytes32 y); event TextChanged(bytes32 indexed node, string indexedKey, string key); event MultihashChanged(bytes32 indexed node, bytes hash); struct PublicKey { bytes32 x; bytes32 y; } struct Record { address addr; bytes32 content; string name; PublicKey pubkey; mapping(string=>string) text; mapping(uint256=>bytes) abis; bytes multihash; } ENS ens; mapping (bytes32 => Record) records; modifier only_owner(bytes32 node) { require(ens.owner(node) == msg.sender); _; } /** * Constructor. * @param ensAddr The ENS registrar contract. */ constructor(ENS ensAddr) public { ens = ensAddr; } /** * 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) public only_owner(node) { records[node].addr = addr; emit AddrChanged(node, addr); } /** * Sets the content hash associated with an ENS node. * May only be called by the owner of that node in the ENS registry. * Note that this resource type is not standardized, and will likely change * in future to a resource type based on multihash. * @param node The node to update. * @param hash The content hash to set */ function setContent(bytes32 node, bytes32 hash) public only_owner(node) { records[node].content = hash; emit ContentChanged(node, hash); } /** * Sets the multihash 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 multihash to set */ function setMultihash(bytes32 node, bytes hash) public only_owner(node) { records[node].multihash = hash; emit MultihashChanged(node, hash); } /** * 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 name) public only_owner(node) { records[node].name = name; emit NameChanged(node, name); } /** * 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 data) public only_owner(node) { // Content types must be powers of 2 require(((contentType - 1) & contentType) == 0); records[node].abis[contentType] = data; emit ABIChanged(node, contentType); } /** * 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) public only_owner(node) { records[node].pubkey = PublicKey(x, y); emit PubkeyChanged(node, x, y); } /** * 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 key, string value) public only_owner(node) { records[node].text[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 key) public view returns (string) { return records[node].text[key]; } /** * 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) public view returns (bytes32 x, bytes32 y) { return (records[node].pubkey.x, records[node].pubkey.y); } /** * 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) public view returns (uint256 contentType, bytes data) { Record storage record = records[node]; for (contentType = 1; contentType <= contentTypes; contentType <<= 1) { if ((contentType & contentTypes) != 0 && record.abis[contentType].length > 0) { data = record.abis[contentType]; return; } } contentType = 0; } /** * 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) public view returns (string) { return records[node].name; } /** * Returns the content hash associated with an ENS node. * Note that this resource type is not standardized, and will likely change * in future to a resource type based on multihash. * @param node The ENS node to query. * @return The associated content hash. */ function content(bytes32 node) public view returns (bytes32) { return records[node].content; } /** * Returns the multihash associated with an ENS node. * @param node The ENS node to query. * @return The associated multihash. */ function multihash(bytes32 node) public view returns (bytes) { return records[node].multihash; } /** * 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 records[node].addr; } /** * Returns true if the resolver implements the interface specified by the provided hash. * @param interfaceID The ID of the interface to check for. * @return True if the contract implements the requested interface. */ function supportsInterface(bytes4 interfaceID) public pure returns (bool) { return interfaceID == ADDR_INTERFACE_ID || interfaceID == CONTENT_INTERFACE_ID || interfaceID == NAME_INTERFACE_ID || interfaceID == ABI_INTERFACE_ID || interfaceID == PUBKEY_INTERFACE_ID || interfaceID == TEXT_INTERFACE_ID || interfaceID == MULTIHASH_INTERFACE_ID || interfaceID == INTERFACE_META_ID; } }
/** * The MIT License (MIT) * * Copyright (c) 2016 Smart Contract Solutions, Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ pragma solidity ^0.4.24; /** * @title SafeMath * @dev Math operations with safety checks that revert on error */ library SafeMath { /** * @dev Multiplies two numbers, reverts on 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); return c; } /** * @dev Integer division of two numbers truncating the quotient, reverts on division by zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0); // Solidity only automatically asserts when dividing by 0 uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a); uint256 c = a - b; return c; } /** * @dev Adds two numbers, reverts on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a); return c; } /** * @dev Divides two numbers and returns the remainder (unsigned integer modulo), * reverts when dividing by zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0); return a % b; } }
/* * Copyright 2016 Nick Johnson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * @title String & slice utility library for Solidity contracts. * @author Nick Johnson <[email protected]> * * @dev Functionality in this library is largely implemented using an * abstraction called a 'slice'. A slice represents a part of a string - * anything from the entire string to a single character, or even no * characters at all (a 0-length slice). Since a slice only has to specify * an offset and a length, copying and manipulating slices is a lot less * expensive than copying and manipulating the strings they reference. * * To further reduce gas costs, most functions on slice that need to return * a slice modify the original one instead of allocating a new one; for * instance, `s.split(".")` will return the text up to the first '.', * modifying s to only contain the remainder of the string after the '.'. * In situations where you do not want to modify the original slice, you * can make a copy first with `.copy()`, for example: * `s.copy().split(".")`. Try and avoid using this idiom in loops; since * Solidity has no memory management, it will result in allocating many * short-lived slices that are later discarded. * * Functions that return two slices come in two versions: a non-allocating * version that takes the second slice as an argument, modifying it in * place, and an allocating version that allocates and returns the second * slice; see `nextRune` for example. * * Functions that have to copy string data will return strings rather than * slices; these can be cast back to slices for further processing if * required. * * For convenience, some functions are provided with non-modifying * variants that create a new slice and return both; for instance, * `s.splitNew('.')` leaves s unmodified, and returns two values * corresponding to the left and right parts of the string. */ pragma solidity ^0.4.14; 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 (self & 0xffffffffffffffffffffffffffffffff == 0) { ret += 16; self = bytes32(uint(self) / 0x100000000000000000000000000000000); } if (self & 0xffffffffffffffff == 0) { ret += 8; self = bytes32(uint(self) / 0x10000000000000000); } if (self & 0xffffffff == 0) { ret += 4; self = bytes32(uint(self) / 0x100000000); } if (self & 0xffff == 0) { ret += 2; self = bytes32(uint(self) / 0x10000); } if (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(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; } }
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":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":false,"inputs":[{"name":"_destination","type":"address"},{"name":"_value","type":"uint256"},{"name":"_data","type":"bytes"},{"name":"_destinationIsContract","type":"bool"}],"name":"executeTransaction","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"isTransferable","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"submittedWhitelistAddition","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"confirmTopUpLimit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"pendingWhitelistRemoval","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"submitTopUpLimit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"isWhitelisted","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pendingWhitelistAddition","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"initializedSpendLimit","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"initializeSpendLimit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_hash","type":"bytes32"}],"name":"confirmWhitelistAddition","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_addresses","type":"address[]"}],"name":"submitWhitelistRemoval","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"initializeTopUpLimit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"pendingTopUpLimit","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"cancelTopUpLimit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_pendingWhitelist","type":"address[]"}],"name":"pendingWhitelistHash","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"name":"_addresses","type":"address[]"}],"name":"submitWhitelistAddition","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"topUpLimit","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"submittedTopUpLimit","outputs":[{"name":"","type":"bool"}],"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":"topUpAvailable","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"spendLimit","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"initializedWhitelist","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":"pendingSpendLimit","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_hash","type":"bytes32"}],"name":"confirmWhitelistRemoval","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_hash","type":"bytes32"}],"name":"cancelWhitelistRemoval","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"submittedSpendLimit","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"submitSpendLimit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"spendAvailable","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":"_asset","type":"address"}],"name":"balance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"cancelSpendLimit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"topUpGas","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_account","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"confirmSpendLimit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_addresses","type":"address[]"}],"name":"initializeWhitelist","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"initializedTopUpLimit","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_owner","type":"address"},{"name":"_transferable","type":"bool"},{"name":"_ens","type":"address"},{"name":"_oracleName","type":"bytes32"},{"name":"_controllerName","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":"_amount","type":"uint256"}],"name":"SetTopUpLimit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_amount","type":"uint256"}],"name":"SubmittedTopUpLimitChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_sender","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"CancelledTopUpLimitChange","type":"event"},{"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":"_destination","type":"address"},{"indexed":false,"name":"_value","type":"uint256"},{"indexed":false,"name":"_data","type":"bytes"}],"name":"ExecutedTransaction","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":"SubmittedSpendLimitChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_sender","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"CancelledSpendLimitChange","type":"event"},{"anonymous":false,"inputs":[],"name":"UpdatedSpendAvailable","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"}]
Deployed Bytecode
0x60806040526004361061020e5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166301ffc9a781146102a0578063100f23fd146102eb5780631127b57e146103055780631aa21fba1461038f5780631e8fb354146103f25780632121dc751461042857806326d05ab21461043d578063278ffa1314610452578063294f40251461046a578063295b3299146104cf5780633af32abf146104e757806347b55a9d146105085780635658eff01461051d57806358453569146105325780635adc02ab1461054a5780636137d670146105625780636ea6f0c31461058257806370332d851461059a578063715018a6146105c1578063754628d0146105d65780637c955583146105ee5780637fd004fa1461064357806383d028031461066357806385cd9919146106785780638da5cb5b1461068d578063a8321459146106be578063aceaf92d146106d3578063afa0fd9b146106e8578063beabacc8146106fd578063c8ecaddb14610727578063cbd2ac681461073c578063ce0b5bd514610754578063d56665901461076c578063d9ec301814610781578063dae37fac14610799578063de212bf3146107ae578063e3d670d7146107c3578063e4682f00146107e4578063e61c51ca146107fc578063f2fde38b14610814578063f349291514610835578063f4199bb81461084d578063f9c470a51461086d575b3615610264576040805160e560020a62461bcd02815260206004820152601060248201527f6461746120696e2066616c6c6261636b00000000000000000000000000000000604482015290519081900360640190fd5b6040805133815234602082015281517f88a5966d370b9919b20f3e2c13ff65706f196a4e32cc2c12bf57088f88525874929181900390910190a1005b3480156102ac57600080fd5b506102d77bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1960043516610882565b604080519115158252519081900360200190f35b3480156102f757600080fd5b506103036004356108ca565b005b34801561031157600080fd5b5061031a610acc565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561035457818101518382015260200161033c565b50505050905090810190601f1680156103815780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561039b57600080fd5b50604080516020600460248035828101358481028087018601909752808652610303968435600160a060020a031696369660449591949091019291829185019084908082843750949750610b039650505050505050565b3480156103fe57600080fd5b5061030360048035600160a060020a03169060248035916044359182019101356064351515610d51565b34801561043457600080fd5b506102d761149f565b34801561044957600080fd5b506102d76114c1565b34801561045e57600080fd5b506103036004356114ca565b34801561047657600080fd5b5061047f611712565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156104bb5781810151838201526020016104a3565b505050509050019250505060405180910390f35b3480156104db57600080fd5b50610303600435611774565b3480156104f357600080fd5b506102d7600160a060020a036004351661192a565b34801561051457600080fd5b5061047f61193f565b34801561052957600080fd5b506102d761199f565b34801561053e57600080fd5b506103036004356119ad565b34801561055657600080fd5b50610303600435611ad5565b34801561056e57600080fd5b506103036004803560248101910135611d96565b34801561058e57600080fd5b50610303600435612042565b3480156105a657600080fd5b506105af612205565b60408051918252519081900360200190f35b3480156105cd57600080fd5b5061030361220b565b3480156105e257600080fd5b50610303600435612331565b3480156105fa57600080fd5b50604080516020600480358082013583810280860185019096528085526105af953695939460249493850192918291850190849080828437509497506124cc9650505050505050565b34801561064f57600080fd5b506103036004803560248101910135612579565b34801561066f57600080fd5b506105af612992565b34801561068457600080fd5b506102d7612998565b34801561069957600080fd5b506106a26129a1565b60408051600160a060020a039092168252519081900360200190f35b3480156106ca57600080fd5b506105af6129b0565b3480156106df57600080fd5b506105af6129d3565b3480156106f457600080fd5b506102d76129d9565b34801561070957600080fd5b50610303600160a060020a03600435811690602435166044356129e8565b34801561073357600080fd5b506105af612f6c565b34801561074857600080fd5b50610303600435612f72565b34801561076057600080fd5b5061030360043561325f565b34801561077857600080fd5b506102d7613465565b34801561078d57600080fd5b5061030360043561346e565b3480156107a557600080fd5b506105af613588565b3480156107ba57600080fd5b506102d76135ab565b3480156107cf57600080fd5b506105af600160a060020a03600435166135b9565b3480156107f057600080fd5b50610303600435613666565b34801561080857600080fd5b506103036004356137e9565b34801561082057600080fd5b50610303600160a060020a0360043516613a8b565b34801561084157600080fd5b50610303600435613c65565b34801561085957600080fd5b506103036004803560248101910135613df6565b34801561087957600080fd5b506102d7614108565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1981167f01ffc9a700000000000000000000000000000000000000000000000000000000145b919050565b6108d333614116565b1515610917576040805160e560020a62461bcd02815260206004820152601a6024820152600080516020614711833981519152604482015290519081900360640190fd5b60065460ff161515610999576040805160e560020a62461bcd02815260206004820152602960248201527f77686974656c697374206164646974696f6e20686173206e6f74206265656e2060448201527f7375626d69747465640000000000000000000000000000000000000000000000606482015290519081900360840190fd5b6109fc60048054806020026020016040519081016040528092919081815260200182805480156109f257602002820191906000526020600020905b8154600160a060020a031681526001909101906020018083116109d4575b50505050506124cc565b8114610a78576040805160e560020a62461bcd02815260206004820152603560248201527f68617368206f66207468652070656e64696e672077686974656c69737420616460448201527f646974696f6e20646f6573206e6f74206d617463680000000000000000000000606482015290519081900360840190fd5b610a8460046000614617565b6006805460ff19169055604080513381526020810183905281517f7794eff834d760583543e6e510e717a5e66d2c064e225f4db448343c3e66afcf929181900390910190a150565b60408051808201909152600581527f312e352e38000000000000000000000000000000000000000000000000000000602082015281565b600080610b0e6142d6565b1515610b52576040805160e560020a62461bcd02815260206004820152601660248201526000805160206146f1833981519152604482015290519081900360640190fd5b82511515610baa576040805160e560020a62461bcd02815260206004820152601f60248201527f61737365742061727261792073686f756c64206265206e6f6e2d656d70747900604482015290519081900360640190fd5b600091505b8251821015610cbc578251600090849084908110610bc957fe5b90602001906020020151600160a060020a03161415610bea57503031610c8f565b8282815181101515610bf857fe5b6020908102909101810151604080517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529051600160a060020a03909216926370a08231926024808401938290030181600087803b158015610c6057600080fd5b505af1158015610c74573d6000803e3d6000fd5b505050506040513d6020811015610c8a57600080fd5b505190505b610cb1848484815181101515610ca157fe5b90602001906020020151836129e8565b600190910190610baf565b7fd4f62f23021706247dcffea245d104ae7ddaec7f23acf3d11d7136d5de6a69ad84846040518083600160a060020a0316600160a060020a0316815260200180602001828103825283818151815260200191508051906020019060200280838360005b83811015610d37578181015183820152602001610d1f565b50505050905001935050505060405180910390a150505050565b6000806000806000610d616142d6565b1515610da5576040805160e560020a62461bcd02815260206004820152601660248201526000805160206146f1833981519152604482015290519081900360640190fd5b8515610e3e57610dbd8a600160a060020a03166142e7565b1515610e39576040805160e560020a62461bcd02815260206004820152603760248201527f657865637574655472616e73616374696f6e20666f72206120636f6e7472616360448201527f743a2063616c6c20746f206e6f6e2d636f6e7472616374000000000000000000606482015290519081900360840190fd5b610ecb565b610e508a600160a060020a03166142e7565b15610ecb576040805160e560020a62461bcd02815260206004820152603760248201527f657865637574655472616e73616374696f6e20666f722061206e6f6e2d636f6e60448201527f74726163743a2063616c6c20746f20636f6e7472616374000000000000000000606482015290519081900360840190fd5b610ed36142ef565b600487106112d657610f1788888080601f01602080910402602001604051908101604052809392919081815260200183838082843750600094506143429350505050565b945063ffffffff851663a9059cbb1480610f3a575063ffffffff851663095ea7b3145b156112d6576044871015610fbe576040805160e560020a62461bcd02815260206004820152602b60248201527f696e76616c6964207472616e73666572202f20617070726f7665207472616e7360448201527f616374696f6e2064617461000000000000000000000000000000000000000000606482015290519081900360840190fd5b610ffa88888080601f016020809104026020016040519081016040528093929190818152602001838380828437506024945061441b9350505050565b935061103888888080601f01602080910402602001604051908101604052809392919081815260200183838082843750601094506144869350505050565b600160a060020a03811660009081526003602052604090205490935060ff1615156112d657600b54600c54604080517f0178b8bf00000000000000000000000000000000000000000000000000000000815260048101929092525162010000909204600160a060020a031691630178b8bf916024808201926020929091908290030181600087803b1580156110cc57600080fd5b505af11580156110e0573d6000803e3d6000fd5b505050506040513d60208110156110f657600080fd5b5051600c54604080517f3b3b57de000000000000000000000000000000000000000000000000000000008152600481019290925251600160a060020a0390921691633b3b57de916024808201926020929091908290030181600087803b15801561115f57600080fd5b505af1158015611173573d6000803e3d6000fd5b505050506040513d602081101561118957600080fd5b5051604080517f67c6e39c000000000000000000000000000000000000000000000000000000008152600160a060020a038d811660048301526024820188905282519316926367c6e39c926044808401939192918290030181600087803b1580156111f357600080fd5b505af1158015611207573d6000803e3d6000fd5b505050506040513d604081101561121d57600080fd5b508051602090910151909250905081156112d657611239613588565b8111156112b6576040805160e560020a62461bcd02815260206004820152602d60248201527f7472616e7366657220616d6f756e74206578636565647320617661696c61626c60448201527f65207370656e64206c696d697400000000000000000000000000000000000000606482015290519081900360840190fd5b6112d66112d1826112c5613588565b9063ffffffff61455516565b61456c565b600160a060020a038a1660009081526003602052604090205460ff16151561138c57611300613588565b89111561137d576040805160e560020a62461bcd02815260206004820152602d60248201527f7472616e7366657220616d6f756e74206578636565647320617661696c61626c60448201527f65207370656e64206c696d697400000000000000000000000000000000000000606482015290519081900360840190fd5b61138c6112d18a6112c5613588565b6113cb8a8a8a8a90508b8b8080601f01602080910402602001604051908101604052809392919081815260200183838082843750614571945050505050565b1515611421576040805160e560020a62461bcd02815260206004820152601c60248201527f657865637574696e67207472616e73616374696f6e206661696c656400000000604482015290519081900360640190fd5b7faf022f6b53b11c364e2dfc0aea08eb9416c94f2661451ea82ead8831385617a68a8a8a8a6040518085600160a060020a0316600160a060020a03168152602001848152602001806020018281038252848482818152602001925080828437604051920182900397509095505050505050a150505050505050505050565b60025474010000000000000000000000000000000000000000900460ff165b90565b60065460ff1681565b6114d333614116565b1515611517576040805160e560020a62461bcd02815260206004820152601a6024820152600080516020614711833981519152604482015290519081900360640190fd5b60115460ff161515611599576040805160e560020a62461bcd02815260206004820152602360248201527f746f70207570206c696d697420686173206e6f74206265656e207375626d697460448201527f7465640000000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b60105466038d7ea4c68000111580156115bc57506706f05b59d3b2000060105411155b1515611638576040805160e560020a62461bcd02815260206004820152602a60248201527f746f7020757020616d6f756e74206973206f75747369646520746865206d696e60448201527f2f6d61782072616e676500000000000000000000000000000000000000000000606482015290519081900360840190fd5b60105481146116b7576040805160e560020a62461bcd02815260206004820152602e60248201527f636f6e6669726d656420616e642070656e64696e6720746f707570206c696d6960448201527f7420617265206e6f742073616d65000000000000000000000000000000000000606482015290519081900360840190fd5b6116c2601054614594565b60105460408051338152602081019290925280517fb0b775ba506691c666928dfe7120eb5ccedd17f4554dc85ce86eb2b4cc437e369281900390910190a15060006010556011805460ff19169055565b6060600580548060200260200160405190810160405280929190818152602001828054801561176a57602002820191906000526020600020905b8154600160a060020a0316815260019091019060200180831161174c575b5050505050905090565b61177c6142d6565b15156117c0576040805160e560020a62461bcd02815260206004820152601660248201526000805160206146f1833981519152604482015290519081900360640190fd5b601154610100900460ff161515611847576040805160e560020a62461bcd02815260206004820152602560248201527f746f70207570206c696d697420686173206e6f74206265656e20696e6974696160448201527f6c697a6564000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b8066038d7ea4c680001115801561186657506706f05b59d3b200008111155b15156118e2576040805160e560020a62461bcd02815260206004820152602d60248201527f746f7020757020616d6f756e74206973206f757473696465206f66207468652060448201527f6d696e2f6d61782072616e676500000000000000000000000000000000000000606482015290519081900360840190fd5b60108190556011805460ff191660011790556040805182815290517f9312377d84b68703929715872be9f0309065e102db6152de9c0587d9e294216d9181900360200190a150565b60036020526000908152604090205460ff1681565b6060600480548060200260200160405190810160405280929190818152602001828054801561176a57602002820191906000526020600020908154600160a060020a0316815260019091019060200180831161174c575050505050905090565b600b54610100900460ff1681565b6119b56142d6565b15156119f9576040805160e560020a62461bcd02815260206004820152601660248201526000805160206146f1833981519152604482015290519081900360640190fd5b600b54610100900460ff1615611a7f576040805160e560020a62461bcd02815260206004820152602860248201527f7370656e64206c696d69742068617320616c7265616479206265656e20696e6960448201527f7469616c697a6564000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b611a88816145b5565b600b805461ff001916610100179055604080513381526020810183905281517f068f112e5ec923d412be64779fe69e0fcbb6784c6617e94cccc8fd348f2e0f21929181900390910190a150565b6000611ae033614116565b1515611b24576040805160e560020a62461bcd02815260206004820152601a6024820152600080516020614711833981519152604482015290519081900360640190fd5b60065460ff161515611ba6576040805160e560020a62461bcd02815260206004820152602960248201527f77686974656c697374206164646974696f6e20686173206e6f74206265656e2060448201527f7375626d69747465640000000000000000000000000000000000000000000000606482015290519081900360840190fd5b611c0760048054806020026020016040519081016040528092919081815260200182805480156109f257602002820191906000526020600020908154600160a060020a031681526001909101906020018083116109d45750505050506124cc565b8214611c83576040805160e560020a62461bcd02815260206004820152603360248201527f68617368206f66207468652070656e64696e672077686974656c69737420616460448201527f646974696f6e20646f206e6f74206d6174636800000000000000000000000000606482015290519081900360840190fd5b5060005b600454811015611ce257600160036000600484815481101515611ca657fe5b600091825260208083209190910154600160a060020a031683528201929092526040019020805460ff1916911515919091179055600101611c87565b7fb2f6cccee7a369e23e293c25aa19bef80af11eb26deba3ea0f2a02783f752e4a3360046040518083600160a060020a0316600160a060020a03168152602001806020018281038252838181548152602001915080548015611d6d57602002820191906000526020600020905b8154600160a060020a03168152600190910190602001808311611d4f575b5050935050505060405180910390a1611d8860046000614617565b50506006805460ff19169055565b611d9e6142d6565b1515611de2576040805160e560020a62461bcd02815260206004820152601660248201526000805160206146f1833981519152604482015290519081900360640190fd5b60065460ff16158015611dfd5750600654610100900460ff16155b1515611e79576040805160e560020a62461bcd02815260206004820152602e60248201527f77686974656c697374206f7065726174696f6e2068617320616c72656164792060448201527f6265656e207375626d6974746564000000000000000000000000000000000000606482015290519081900360840190fd5b60065462010000900460ff161515611ee9576040805160e560020a62461bcd02815260206004820152602260248201527f77686974656c69737420686173206e6f74206265656e20696e697469616c697a604482015260f260020a61195902606482015290519081900360840190fd5b60008111611f66576040805160e560020a62461bcd028152602060048201526024808201527f7375626d69747465642077686974656c6973742072656d6f76616c206973206560448201527f6d70747900000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b611f7260058383614635565b506006805461ff00191661010017905560058054604080516020808402820181019092528281527ffbc0e5ca6c7e4858daf0fdb185ef5186203e74ec9c64737e93c0aeaec596e1d19386938693612007939291908301828280156109f257602002820191906000526020600020908154600160a060020a031681526001909101906020018083116109d45750505050506124cc565b604080516020808201849052828252918101849052908190606082019086908602808284376040519201829003965090945050505050a15050565b61204a6142d6565b151561208e576040805160e560020a62461bcd02815260206004820152601660248201526000805160206146f1833981519152604482015290519081900360640190fd5b601154610100900460ff1615612114576040805160e560020a62461bcd02815260206004820152602960248201527f746f70207570206c696d69742068617320616c7265616479206265656e20696e60448201527f697469616c697a65640000000000000000000000000000000000000000000000606482015290519081900360840190fd5b8066038d7ea4c680001115801561213357506706f05b59d3b200008111155b15156121af576040805160e560020a62461bcd02815260206004820152602d60248201527f746f7020757020616d6f756e74206973206f757473696465206f66207468652060448201527f6d696e2f6d61782072616e676500000000000000000000000000000000000000606482015290519081900360840190fd5b6121b881614594565b6011805461ff001916610100179055604080513381526020810183905281517fb0b775ba506691c666928dfe7120eb5ccedd17f4554dc85ce86eb2b4cc437e36929181900390910190a150565b60105481565b6122136142d6565b1515612257576040805160e560020a62461bcd02815260206004820152601660248201526000805160206146f1833981519152604482015290519081900360640190fd5b60025474010000000000000000000000000000000000000000900460ff1615156122cb576040805160e560020a62461bcd02815260206004820152601d60248201527f6f776e657273686970206973206e6f74207472616e7366657261626c65000000604482015290519081900360640190fd5b60025460408051600160a060020a0390921682526000602083015280517f850b3df64837d7d518b45f5aa64d104652c3b80eb5b34a8e3d9eb666cb7cdea59281900390910190a16002805473ffffffffffffffffffffffffffffffffffffffff19169055565b61233a33614116565b151561237e576040805160e560020a62461bcd02815260206004820152601a6024820152600080516020614711833981519152604482015290519081900360640190fd5b60115460ff161515612400576040805160e560020a62461bcd02815260206004820152602160248201527f6120746f707570206c696d69742068617320746f206265207375626d6974746560448201527f6400000000000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b601054811461247f576040805160e560020a62461bcd02815260206004820152602e60248201527f70656e64696e6720616e642063616e63656c6c656420746f70207570206c696d60448201527f69747320646f6e74206d61746368000000000000000000000000000000000000606482015290519081900360840190fd5b60006010556011805460ff19169055604080513381526020810183905281517f25d36a8351febb568fa2c8f5a167990291168552d0f3381a618eb120f42d91f4929181900390910190a150565b60008160405160200180828051906020019060200280838360005b838110156124ff5781810151838201526020016124e7565b505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106125475780518252601f199092019160209182019101612528565b5181516020939093036101000a6000190180199091169216919091179052604051920182900390912095945050505050565b6125816142d6565b15156125c5576040805160e560020a62461bcd02815260206004820152601660248201526000805160206146f1833981519152604482015290519081900360640190fd5b60065460ff161580156125e05750600654610100900460ff16155b151561265c576040805160e560020a62461bcd02815260206004820152602e60248201527f77686974656c697374206f7065726174696f6e2068617320616c72656164792060448201527f6265656e207375626d6974746564000000000000000000000000000000000000606482015290519081900360840190fd5b8181808060200260200160405190810160405280939291908181526020018383602002808284375060009450505050505b81518110156127c85761269e6129a1565b600160a060020a031682828151811015156126b557fe5b60209081029091010151600160a060020a03161415612732576040805160e560020a62461bcd02815260206004820152602d602482015260008051602061473183398151915260448201527f6f776e6572206164647265737300000000000000000000000000000000000000606482015290519081900360840190fd5b815160009083908390811061274357fe5b60209081029091010151600160a060020a031614156127c0576040805160e560020a62461bcd02815260206004820152602c602482015260008051602061473183398151915260448201527f7a65726f20616464726573730000000000000000000000000000000000000000606482015290519081900360840190fd5b60010161268d565b60065462010000900460ff161515612838576040805160e560020a62461bcd02815260206004820152602260248201527f77686974656c69737420686173206e6f74206265656e20696e697469616c697a604482015260f260020a61195902606482015290519081900360840190fd5b600083116128b6576040805160e560020a62461bcd02815260206004820152602360248201527f70656e64696e672077686974656c697374206164646974696f6e20697320656d60448201527f7074790000000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b6128c260048585614635565b506006805460ff1916600117905560048054604080516020808402820181019092528281527f9c80b3b5f68b3e017766d59e8d09b34efe6462b05c398f35cab9e271d9bc3b9c9388938893612955939291908301828280156109f257602002820191906000526020600020908154600160a060020a031681526001909101906020018083116109d45750505050506124cc565b604080516020808201849052828252918101849052908190606082019086908602808284376040519201829003965090945050505050a150505050565b600d5481565b60115460ff1681565b600254600160a060020a031690565b6000600e5462015180014211156129ca5750600d546114be565b50600f546114be565b60075481565b60065462010000900460ff1681565b6000806129f36142d6565b1515612a37576040805160e560020a62461bcd02815260206004820152601660248201526000805160206146f1833981519152604482015290519081900360640190fd5b82801515612a8f576040805160e560020a62461bcd02815260206004820152601d60248201527f70726f76696465642076616c75652063616e6e6f74206265207a65726f000000604482015290519081900360640190fd5b600160a060020a0386161515612aef576040805160e560020a62461bcd02815260206004820181905260248201527f5f746f20616464726573732063616e6e6f742062652073657420746f20307830604482015290519081900360640190fd5b600160a060020a03861660009081526003602052604090205460ff161515612daa57612b196142ef565b600160a060020a03851615612cfa57600b54600c54604080517f0178b8bf00000000000000000000000000000000000000000000000000000000815260048101929092525162010000909204600160a060020a031691630178b8bf916024808201926020929091908290030181600087803b158015612b9757600080fd5b505af1158015612bab573d6000803e3d6000fd5b505050506040513d6020811015612bc157600080fd5b5051600c54604080517f3b3b57de000000000000000000000000000000000000000000000000000000008152600481019290925251600160a060020a0390921691633b3b57de916024808201926020929091908290030181600087803b158015612c2a57600080fd5b505af1158015612c3e573d6000803e3d6000fd5b505050506040513d6020811015612c5457600080fd5b5051604080517f67c6e39c000000000000000000000000000000000000000000000000000000008152600160a060020a0388811660048301526024820188905282519316926367c6e39c926044808401939192918290030181600087803b158015612cbe57600080fd5b505af1158015612cd2573d6000803e3d6000fd5b505050506040513d6040811015612ce857600080fd5b50805160209091015193509150612cfe565b8392505b8180612d115750600160a060020a038516155b15612daa57612d1e613588565b831115612d9b576040805160e560020a62461bcd02815260206004820152602d60248201527f7472616e7366657220616d6f756e74206578636565647320617661696c61626c60448201527f65207370656e64206c696d697400000000000000000000000000000000000000606482015290519081900360840190fd5b612daa6112d1846112c5613588565b600160a060020a03851615612ee25784600160a060020a031663a9059cbb87866040518363ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050602060405180830381600087803b158015612e3557600080fd5b505af1158015612e49573d6000803e3d6000fd5b505050506040513d6020811015612e5f57600080fd5b50511515612edd576040805160e560020a62461bcd02815260206004820152602560248201527f455243323020746f6b656e207472616e736665722077617320756e737563636560448201527f737366756c000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b612f1a565b604051600160a060020a0387169085156108fc029086906000818181858888f19350505050158015612f18573d6000803e3d6000fd5b505b60408051600160a060020a0380891682528716602082015280820186905290517fd1ba4ac2e2a11b5101f6cb4d978f514a155b421e8ec396d2d9abaf0bb02917ee9181900360600190a1505050505050565b600a5481565b6000612f7d33614116565b1515612fc1576040805160e560020a62461bcd02815260206004820152601a6024820152600080516020614711833981519152604482015290519081900360640190fd5b600654610100900460ff161515613048576040805160e560020a62461bcd02815260206004820152602860248201527f77686974656c6973742072656d6f76616c20686173206e6f74206265656e207360448201527f75626d6974746564000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b6130a960058054806020026020016040519081016040528092919081815260200182805480156109f257602002820191906000526020600020908154600160a060020a031681526001909101906020018083116109d45750505050506124cc565b821461314b576040805160e560020a62461bcd02815260206004820152604760248201527f68617368206f66207468652070656e64696e672077686974656c69737420726560448201527f6d6f76616c20646f6573206e6f74206d617463682074686520636f6e6669726d60648201527f6564206861736800000000000000000000000000000000000000000000000000608482015290519081900360a40190fd5b5060005b6005548110156131aa5760006003600060058481548110151561316e57fe5b600091825260208083209190910154600160a060020a031683528201929092526040019020805460ff191691151591909117905560010161314f565b7fd218c430fa348f4ce67791021b6b89c0c3eacd4ead1d8f5b83c60038ec28249b3360056040518083600160a060020a0316600160a060020a0316815260200180602001828103825283818154815260200191508054801561323557602002820191906000526020600020905b8154600160a060020a03168152600190910190602001808311613217575b5050935050505060405180910390a161325060056000614617565b50506006805461ff0019169055565b61326833614116565b15156132ac576040805160e560020a62461bcd02815260206004820152601a6024820152600080516020614711833981519152604482015290519081900360640190fd5b600654610100900460ff161515613333576040805160e560020a62461bcd02815260206004820152602860248201527f77686974656c6973742072656d6f76616c20686173206e6f74206265656e207360448201527f75626d6974746564000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b61339460058054806020026020016040519081016040528092919081815260200182805480156109f257602002820191906000526020600020908154600160a060020a031681526001909101906020018083116109d45750505050506124cc565b8114613410576040805160e560020a62461bcd02815260206004820152603460248201527f68617368206f66207468652070656e64696e672077686974656c69737420726560448201527f6d6f76616c20646f6573206e6f74206d61746368000000000000000000000000606482015290519081900360840190fd5b61341c60056000614617565b6006805461ff0019169055604080513381526020810183905281517f13c935eb475aa0f6e931fece83e2ac44569ce2d53460d29a6dedab40b965c8a3929181900390910190a150565b600b5460ff1681565b6134766142d6565b15156134ba576040805160e560020a62461bcd02815260206004820152601660248201526000805160206146f1833981519152604482015290519081900360640190fd5b600b54610100900460ff161515613540576040805160e560020a62461bcd028152602060048201526024808201527f7370656e64206c696d697420686173206e6f74206265656e20696e697469616c60448201527f697a656400000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b600a819055600b805460ff191660011790556040805182815290517ff7155f467a69de148f41fc3c39089f07e9bff6d91519f3d69d46936643d5197a9181900360200190a150565b600060085462015180014211156135a257506007546114be565b506009546114be565b600654610100900460ff1681565b6000600160a060020a0382161561365e57604080517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529051600160a060020a038416916370a082319160248083019260209291908290030181600087803b15801561362b57600080fd5b505af115801561363f573d6000803e3d6000fd5b505050506040513d602081101561365557600080fd5b505190506108c5565b5030316108c5565b61366f33614116565b15156136b3576040805160e560020a62461bcd02815260206004820152601a6024820152600080516020614711833981519152604482015290519081900360640190fd5b600b5460ff16151561371d576040805160e560020a62461bcd02815260206004820152602260248201527f61207370656e646c696d6974206e6565647320746f206265207375626d697474604482015260f260020a61195902606482015290519081900360840190fd5b600a54811461379c576040805160e560020a62461bcd02815260206004820152602d60248201527f70656e64696e6720616e642063616e63656c6c6564207370656e64206c696d6960448201527f747320646f6e74206d6174636800000000000000000000000000000000000000606482015290519081900360840190fd5b6000600a55600b805460ff19169055604080513381526020810183905281517f83e15565cfc238cf8d4e7a0a2cefeda3c9d6621e44a4d2ee96034ea403648b6a929181900390910190a150565b80801515613841576040805160e560020a62461bcd02815260206004820152601d60248201527f70726f76696465642076616c75652063616e6e6f74206265207a65726f000000604482015290519081900360640190fd5b6138496142d6565b80613858575061385833614116565b15156138d4576040805160e560020a62461bcd02815260206004820152602b60248201527f73656e646572206973206e65697468657220616e206f776e6572206e6f72206160448201527f20636f6e74726f6c6c6572000000000000000000000000000000000000000000606482015290519081900360840190fd5b6138dc6145d5565b600f54151561395b576040805160e560020a62461bcd02815260206004820152602560248201527f617661696c61626c6520746f70207570206c696d69742063616e6e6f7420626560448201527f207a65726f000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b600f548211156139db576040805160e560020a62461bcd02815260206004820152603160248201527f617661696c61626c6520746f70207570206c696d6974206c657373207468616e60448201527f20616d6f756e742070617373656420696e000000000000000000000000000000606482015290519081900360840190fd5b600f546139ee908363ffffffff61455516565b600f556139f96129a1565b600160a060020a03166108fc839081150290604051600060405180830381858888f19350505050158015613a31573d6000803e3d6000fd5b507f611b7c0d84fda988026215bef9b3e4d81cbceced7e679be6d5e044b588467c0e32613a5c6129a1565b60408051600160a060020a03938416815291909216602082015280820185905290519081900360600190a15050565b613a936142d6565b1515613ad7576040805160e560020a62461bcd02815260206004820152601660248201526000805160206146f1833981519152604482015290519081900360640190fd5b60025474010000000000000000000000000000000000000000900460ff161515613b4b576040805160e560020a62461bcd02815260206004820152601d60248201527f6f776e657273686970206973206e6f74207472616e7366657261626c65000000604482015290519081900360640190fd5b600160a060020a0381161515613bd1576040805160e560020a62461bcd02815260206004820152602360248201527f6f776e65722063616e6e6f742062652073657420746f207a65726f206164647260448201527f6573730000000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b6002805474ff000000000000000000000000000000000000000019811690915560408051600160a060020a039283168152918316602083015280517f850b3df64837d7d518b45f5aa64d104652c3b80eb5b34a8e3d9eb666cb7cdea59281900390910190a16002805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b613c6e33614116565b1515613cb2576040805160e560020a62461bcd02815260206004820152601a6024820152600080516020614711833981519152604482015290519081900360640190fd5b600b5460ff161515613d1c576040805160e560020a62461bcd02815260206004820152602260248201527f7370656e64206c696d697420686173206e6f74206265656e207375626d697474604482015260f260020a61195902606482015290519081900360840190fd5b600a548114613d9b576040805160e560020a62461bcd02815260206004820152602f60248201527f636f6e6669726d656420616e64207375626d6974746564207370656e64206c6960448201527f6d69747320646f6e74206d617463680000000000000000000000000000000000606482015290519081900360840190fd5b613da6600a546145b5565b600a5460408051338152602081019290925280517f068f112e5ec923d412be64779fe69e0fcbb6784c6617e94cccc8fd348f2e0f219281900390910190a150600b805460ff191690556000600a55565b6000613e006142d6565b1515613e44576040805160e560020a62461bcd02815260206004820152601660248201526000805160206146f1833981519152604482015290519081900360640190fd5b8282808060200260200160405190810160405280939291908181526020018383602002808284375060009450505050505b8151811015613fb057613e866129a1565b600160a060020a03168282815181101515613e9d57fe5b60209081029091010151600160a060020a03161415613f1a576040805160e560020a62461bcd02815260206004820152602d602482015260008051602061473183398151915260448201527f6f776e6572206164647265737300000000000000000000000000000000000000606482015290519081900360840190fd5b8151600090839083908110613f2b57fe5b60209081029091010151600160a060020a03161415613fa8576040805160e560020a62461bcd02815260206004820152602c602482015260008051602061473183398151915260448201527f7a65726f20616464726573730000000000000000000000000000000000000000606482015290519081900360840190fd5b600101613e75565b60065462010000900460ff1615614037576040805160e560020a62461bcd02815260206004820152602660248201527f77686974656c6973742068617320616c7265616479206265656e20696e69746960448201527f616c697a65640000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b600092505b838310156140935760016003600087878781811061405657fe5b60209081029290920135600160a060020a0316835250810191909152604001600020805460ff19169115159190911790556001929092019161403c565b6006805462ff0000191662010000179055604080513380825260208083018481529383018890527fb2f6cccee7a369e23e293c25aa19bef80af11eb26deba3ea0f2a02783f752e4a93919289928992606083019085908502808284376040519201829003965090945050505050a15050505050565b601154610100900460ff1681565b60008054600154604080517f0178b8bf000000000000000000000000000000000000000000000000000000008152600481019290925251600160a060020a0390921691630178b8bf9160248082019260209290919082900301818787803b15801561418057600080fd5b505af1158015614194573d6000803e3d6000fd5b505050506040513d60208110156141aa57600080fd5b5051600154604080517f3b3b57de000000000000000000000000000000000000000000000000000000008152600481019290925251600160a060020a0390921691633b3b57de916024808201926020929091908290030181600087803b15801561421357600080fd5b505af1158015614227573d6000803e3d6000fd5b505050506040513d602081101561423d57600080fd5b5051604080517fb429afeb000000000000000000000000000000000000000000000000000000008152600160a060020a0385811660048301529151919092169163b429afeb9160248083019260209291908290030181600087803b1580156142a457600080fd5b505af11580156142b8573d6000803e3d6000fd5b505050506040513d60208110156142ce57600080fd5b505192915050565b600254600160a060020a0316331490565b6000903b1190565b600854614305906201518063ffffffff6145fe16565b42111561434057426008556007546009556040517fec917ce84be0ee78b1b222c78ca6608b5a89a6c4835b1a041cba7343f209269890600090a15b565b600080600080846004018651101515156143a6576040805160e560020a62461bcd02815260206004820152601460248201527f736c6963696e67206f7574206f662072616e6765000000000000000000000000604482015290519081900360640190fd5b5060009150819050805b60048160ff1610156144115761010083029250858160ff1686018151811015156143d657fe5b01602001517f0100000000000000000000000000000000000000000000000000000000000000908190048102049283019291506001016143b0565b5090949350505050565b6000808260200184511015151561447c576040805160e560020a62461bcd02815260206004820152601460248201527f736c6963696e67206f7574206f662072616e6765000000000000000000000000604482015290519081900360640190fd5b5050016020015190565b600080600080846014018651101515156144ea576040805160e560020a62461bcd02815260206004820152601460248201527f736c6963696e67206f7574206f662072616e6765000000000000000000000000604482015290519081900360640190fd5b5060009150819050805b60148160ff1610156144115761010083029250858160ff16860181518110151561451a57fe5b01602001517f0100000000000000000000000000000000000000000000000000000000000000908190048102049283019291506001016144f4565b6000808383111561456557600080fd5b5050900390565b600955565b6000806040516020840160008287838a8c6187965a03f198975050505050505050565b61459c6145d5565b600d819055600f548110156145b257600d54600f555b50565b6145bd6142ef565b60078190556009548110156145b25760075460095550565b600e546145eb906201518063ffffffff6145fe16565b4211156143405742600e55600d54600f55565b60008282018381101561461057600080fd5b9392505050565b50805460008255906000526020600020908101906145b291906146a5565b828054828255906000526020600020908101928215614695579160200282015b8281111561469557815473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03843516178255602090920191600190910190614655565b506146a19291506146bf565b5090565b6114be91905b808211156146a157600081556001016146ab565b6114be91905b808211156146a157805473ffffffffffffffffffffffffffffffffffffffff191681556001016146c5560073656e646572206973206e6f7420616e206f776e65720000000000000000000073656e646572206973206e6f74206120636f6e74726f6c6c657200000000000070726f76696465642077686974656c69737420636f6e7461696e732074686520a165627a7a72305820938e1617a273c4151bc1777bc995eea2c1a6de99b117631bc25ebafb061b0a310029
Deployed Bytecode Sourcemap
20043:14239:14:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;16212:8;:20;16204:49;;;;;-1:-1:-1;;;;;16204:49:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;16268:31;;;16277:10;16268:31;;16289:9;16268:31;;;;;;;;;;;;;;;;;20043:14239;19822:135;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;19822:135:14;-1:-1:-1;;19822:135:14;;;;;;;;;;;;;;;;;;;;;;;6020:715;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;6020:715:14;;;;;;;20467:47;;8:9:-1;5:2;;;30:1;27;20:12;5:2;20467:47:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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;20467:47:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;17070:758;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;17070:758:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;17070:758:14;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;17070:758:14;;-1:-1:-1;17070:758:14;;-1:-1:-1;;;;;;;17070:758:14;27978:2948;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;27978:2948:14;;;;-1:-1:-1;;;;;27978:2948:14;;;;;;;;;;;;;;;;;;;;3231:92:11;;8:9:-1;5:2;;;30:1;27;20:12;5:2;3231:92:11;;;;2036:38:14;;8:9:-1;5:2;;;30:1;27;20:12;5:2;2036:38:14;;;;23894:956;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;23894:956:14;;;;;3084:116;;8:9:-1;5:2;;;30:1;27;20:12;5:2;3084:116:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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;3084:116:14;;;;;;;;;;;;;;;;;23143:688;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;23143:688:14;;;;;1888:45;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1888:45:14;-1:-1:-1;;;;;1888:45:14;;;;;2913:118;;8:9:-1;5:2;;;30:1;27;20:12;5:2;2913:118:14;;;;9903:33;;8:9:-1;5:2;;;30:1;27;20:12;5:2;9903:33:14;;;;10641:486;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;10641:486:14;;;;;5052:914;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;5052:914:14;;;;;6859:715;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;6859:715:14;;;;;;;;;;;;22338:689;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;22338:689:14;;;;;20977:29;;8:9:-1;5:2;;;30:1;27;20:12;5:2;20977:29:14;;;;;;;;;;;;;;;;;;;;2742:303:11;;8:9:-1;5:2;;;30:1;27;20:12;5:2;2742:303:11;;;;24912:629:14;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;24912:629:14;;;;;3267:158;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;3267:158:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;3267:158:14;;-1:-1:-1;3267:158:14;;-1:-1:-1;;;;;;;3267:158:14;4244:753;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;4244:753:14;;;;;;;;;;;;20881:22;;8:9:-1;5:2;;;30:1;27;20:12;5:2;20881:22:14;;;;21012:31;;8:9:-1;5:2;;;30:1;27;20:12;5:2;21012:31:14;;;;3093:77:11;;8:9:-1;5:2;;;30:1;27;20:12;5:2;3093:77:11;;;;;;;;-1:-1:-1;;;;;3093:77:11;;;;;;;;;;;;;;22023:203:14;;8:9:-1;5:2;;;30:1;27;20:12;5:2;22023:203:14;;;;9735:22;;8:9:-1;5:2;;;30:1;27;20:12;5:2;9735:22:14;;;;2123:32;;8:9:-1;5:2;;;30:1;27;20:12;5:2;2123:32:14;;;;18105:1652;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;18105:1652:14;-1:-1:-1;;;;;18105:1652:14;;;;;;;;;;;;9831:29;;8:9:-1;5:2;;;30:1;27;20:12;5:2;9831:29:14;;;;7643:953;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;7643:953:14;;;;;8664:706;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;8664:706:14;;;;;9866:31;;8:9:-1;5:2;;;30:1;27;20:12;5:2;9866:31:14;;;;11261:481;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;11261:481:14;;;;;10287:201;;8:9:-1;5:2;;;30:1;27;20:12;5:2;10287:201:14;;;;2080:37;;8:9:-1;5:2;;;30:1;27;20:12;5:2;2080:37:14;;;;16512:224;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;16512:224:14;-1:-1:-1;;;;;16512:224:14;;;;;12584:619;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;12584:619:14;;;;;25734:962;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;25734:962:14;;;;;1879:592:11;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1879:592:11;-1:-1:-1;;;;;1879:592:11;;;;;11804:719:14;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;11804:719:14;;;;;3556:565;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;3556:565:14;;;;;;;;;;;;21049:33;;8:9:-1;5:2;;;30:1;27;20:12;5:2;21049:33:14;;;;19822:135;-1:-1:-1;;19915:35:14;;19930:20;19915:35;19822:135;;;;:::o;6020:715::-;1686:25:5;1700:10;1686:13;:25::i;:::-;1678:64;;;;;;;-1:-1:-1;;;;;1678:64:5;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1678:64:5;;;;;;;;;;;;;;;6160:26:14;;;;6152:80;;;;;;;-1:-1:-1;;;;;6152:80:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6354:47;6375:25;6354:47;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;6354:47:14;;;;;;;;;;;;;;;;;;;;;:20;:47::i;:::-;6345:56;;6337:122;;;;;-1:-1:-1;;;;;6337:122:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6505:32;6512:25;;6505:32;:::i;:::-;6594:26;:34;;-1:-1:-1;;6594:34:14;;;6683:45;;;6710:10;6683:45;;;;;;;;;;;;;;;;;;;;;6020:715;:::o;20467:47::-;;;;;;;;;;;;;;;;;;;:::o;17070:758::-;17346:6;17397:11;1668:10:11;:8;:10::i;:::-;1660:45;;;;;;;-1:-1:-1;;;;;1660:45:11;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1660:45:11;;;;;;;;;;;;;;;17214:14:14;;:19;;17206:63;;;;;-1:-1:-1;;;;;17206:63:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;17355:1;17346:10;;17341:437;17362:7;:14;17358:1;:18;17341:437;;;17482:10;;17504:1;;17482:7;;17490:1;;17482:10;;;;;;;;;;;;;;-1:-1:-1;;;;;17482:24:14;;17478:183;;;-1:-1:-1;17543:4:14;17535:21;17478:183;;;17610:7;17618:1;17610:10;;;;;;;;;;;;;;;;;;;;17604:42;;;;;;17640:4;17604:42;;;;;;-1:-1:-1;;;;;17604:27:14;;;;;;:42;;;;;;;;;;-1:-1:-1;17604:27:14;:42;;;5:2:-1;;;;30:1;27;20:12;5:2;17604:42:14;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;17604:42:14;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;17604:42:14;;-1:-1:-1;17478:183:14;17734:33;17743:3;17748:7;17756:1;17748:10;;;;;;;;;;;;;;;;;;17760:6;17734:8;:33::i;:::-;17378:3;;;;;17341:437;;;17792:29;17808:3;17813:7;17792:29;;;;-1:-1:-1;;;;;17792:29:14;-1:-1:-1;;;;;17792:29:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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;17792:29:14;;;;;;;;;;;;;;;;;;17070:758;;;;:::o;27978:2948::-;28792:16;29088:11;29332:19;29522:16;29539:15;1668:10:11;:8;:10::i;:::-;1660:45;;;;;;;-1:-1:-1;;;;;1660:45:11;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1660:45:11;;;;;;;;;;;;;;;28257:22:14;28253:289;;;28303:34;28311:12;-1:-1:-1;;;;;28303:32:14;;:34::i;:::-;28295:102;;;;;;;-1:-1:-1;;;;;28295:102:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;28253:289;;;28437:34;28445:12;-1:-1:-1;;;;;28437:32:14;;:34::i;:::-;28436:35;28428:103;;;;;-1:-1:-1;;;;;28428:103:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;28597:23;:21;:23::i;:::-;28739:1;28723:17;;28719:1518;;28811:23;28825:5;;28811:23;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;28832:1:14;;-1:-1:-1;28811:13:14;;-1:-1:-1;;;;28811:23:14:i;:::-;28792:42;-1:-1:-1;28920:22:14;;;20813:10;28920:22;;:47;;-1:-1:-1;28946:21:14;;;20864:10;28946:21;28920:47;28916:1311;;;29011:11;28995:27;;;28987:83;;;;;-1:-1:-1;;;;;28987:83:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;29102:24;29112:5;;29102:24;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;29119:6:14;;-1:-1:-1;29102:9:14;;-1:-1:-1;;;;29102:24:14:i;:::-;29088:38;;29354:29;29369:5;;29354:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;29376:6:14;;-1:-1:-1;29354:14:14;;-1:-1:-1;;;;29354:29:14:i;:::-;-1:-1:-1;;;;;29471:26:14;;;;;;:13;:26;;;;;;29332:51;;-1:-1:-1;29471:26:14;;29470:27;29466:747;;;29581:4;;29595:11;;29581:26;;;;;;;;;;;;;;:4;;;;-1:-1:-1;;;;;29581:4:14;;:13;;:26;;;;;;;;;;;;;;;-1:-1:-1;29581:4:14;:26;;;5:2:-1;;;;30:1;27;20:12;5:2;29581:26:14;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;29581:26:14;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;29581:26:14;29614:11;;29566:60;;;;;;;;;;;;;;-1:-1:-1;;;;;29566:47:14;;;;;;:60;;;;;29581:26;;29566:60;;;;;;;;;:47;:60;;;5:2:-1;;;;30:1;27;20:12;5:2;29566:60:14;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;29566:60:14;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;29566:60:14;29558:99;;;;;;-1:-1:-1;;;;;29558:99:14;;;;;;;;;;;;;;;:77;;;;;:99;;;;;;;;;;;;;-1:-1:-1;29558:77:14;:99;;;5:2:-1;;;;30:1;27;20:12;5:2;29558:99:14;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;29558:99:14;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;29558:99:14;;;;;;;;;-1:-1:-1;29558:99:14;-1:-1:-1;29832:363:14;;;;29973:16;:14;:16::i;:::-;29959:30;;;29951:88;;;;;-1:-1:-1;;;;;29951:88:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30120:52;30139:32;30160:10;30139:16;:14;:16::i;:::-;:20;:32;:20;:32;:::i;:::-;30120:18;:52::i;:::-;-1:-1:-1;;;;;30442:27:14;;;;;;:13;:27;;;;;;;;30441:28;30437:312;;;30567:16;:14;:16::i;:::-;30557:26;;;30549:84;;;;;-1:-1:-1;;;;;30549:84:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30690:48;30709:28;30730:6;30709:16;:14;:16::i;30690:48::-;30767:55;30780:12;30794:6;30802:5;;:12;;30816:5;;30767:55;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;30767:12:14;;-1:-1:-1;;;;;30767:55:14:i;:::-;30759:96;;;;;;;-1:-1:-1;;;;;30759:96:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;30871:48;30891:12;30905:6;30913:5;;30871:48;;;;-1:-1:-1;;;;;30871:48:14;-1:-1:-1;;;;;30871:48:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;30871:48:14;;-1:-1:-1;;;;;;30871:48:14;27978:2948;;;;;;;;;;:::o;3231:92:11:-;3301:15;;;;;;;3231:92;;:::o;2036:38:14:-;;;;;;:::o;23894:956::-;1686:25:5;1700:10;1686:13;:25::i;:::-;1678:64;;;;;;;-1:-1:-1;;;;;1678:64:5;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1678:64:5;;;;;;;;;;;;;;;24035:19:14;;;;24027:67;;;;;;;-1:-1:-1;;;;;24027:67:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;24222:17;;20564:8;24199:40;;:84;;;;;20655:10;24243:17;;:40;;24199:84;24191:139;;;;;;;-1:-1:-1;;;;;24191:139:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;24430:17;;24419:28;;24411:87;;;;;-1:-1:-1;;;;;24411:87:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;24567:36;24585:17;;24567;:36::i;:::-;24681:17;;24655:44;;;24669:10;24655:44;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;24767:1:14;24747:17;:21;24816:19;:27;;-1:-1:-1;;24816:27:14;;;23894:956::o;3084:116::-;3141:9;3169:24;3162:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;3162:31:14;;;;;;;;;;;;;;;;;;;;;;;3084:116;:::o;23143:688::-;1668:10:11;:8;:10::i;:::-;1660:45;;;;;;;-1:-1:-1;;;;;1660:45:11;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1660:45:11;;;;;;;;;;;;;;;23283:21:14;;;;;;;23275:71;;;;;;;-1:-1:-1;;;;;23275:71:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23460:7;20564:8;23437:30;;:64;;;;;20655:10;23471:7;:30;;23437:64;23429:122;;;;;;;-1:-1:-1;;;;;23429:122:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23630:17;:27;;;23711:19;:26;;-1:-1:-1;;23711:26:14;23733:4;23711:26;;;23790:34;;;;;;;;;;;;;;;;;23143:688;:::o;1888:45::-;;;;;;;;;;;;;;;:::o;2913:118::-;2971:9;2999:25;2992:32;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;2992:32:14;;;;;;;;;;;;;;;;;;;;;;2913:118;:::o;9903:33::-;;;;;;;;;:::o;10641:486::-;1668:10:11;:8;:10::i;:::-;1660:45;;;;;;;-1:-1:-1;;;;;1660:45:11;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1660:45:11;;;;;;;;;;;;;;;10789:21:14;;;;;;;10788:22;10780:75;;;;;-1:-1:-1;;;;;10780:75:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10924:26;10942:7;10924:17;:26::i;:::-;11006:21;:28;;-1:-1:-1;;11006:28:14;;;;;11086:34;;;11100:10;11086:34;;;;;;;;;;;;;;;;;;;;;10641:486;:::o;5052:914::-;5564:6;1686:25:5;1700:10;1686:13;:25::i;:::-;1678:64;;;;;;;-1:-1:-1;;;;;1678:64:5;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1678:64:5;;;;;;;;;;;;;;;5210:26:14;;;;5202:80;;;;;;;-1:-1:-1;;;;;5202:80:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5405:47;5426:25;5405:47;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;5405:47:14;;;;;;;;;;;;;;;;;;;;:20;:47::i;:::-;5396:56;;5388:120;;;;;-1:-1:-1;;;;;5388:120:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;5573:1:14;5559:135;5580:25;:32;5576:36;;5559:135;;;5679:4;5633:13;:43;5647:25;5673:1;5647:28;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;5647:28:14;5633:43;;;;;;;;;;;;:50;;-1:-1:-1;;5633:50:14;;;;;;;;;;-1:-1:-1;5614:3:14;5559:135;;;5744:55;5761:10;5773:25;5744:55;;;;-1:-1:-1;;;;;5744:55:14;-1:-1:-1;;;;;5744:55:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;5744:55:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5845:32;5852:25;;5845:32;:::i;:::-;-1:-1:-1;;5925:26:14;:34;;-1:-1:-1;;5925:34:14;;;5052:914::o;6859:715::-;1668:10:11;:8;:10::i;:::-;1660:45;;;;;;;-1:-1:-1;;;;;1660:45:11;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1660:45:11;;;;;;;;;;;;;;;2734:26:14;;;;2733:27;:57;;;;-1:-1:-1;2765:25:14;;;;;;;2764:26;2733:57;2725:116;;;;;;;-1:-1:-1;;;;;2725:116:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7029:20;;;;;;;7021:67;;;;;;;-1:-1:-1;;;;;7021:67:14;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;7021:67:14;;;;;;;;;;;;;;;7186:1;7166:21;;7158:70;;;;;-1:-1:-1;;;;;7158:70:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7306:37;:24;7333:10;;7306:37;:::i;:::-;-1:-1:-1;7397:25:14;:32;;-1:-1:-1;;7397:32:14;;;;;7541:24;7520:46;;;;;;;;;;;;;;;;;;;7482:85;;7508:10;;;;7520:46;;;7541:24;7520:46;;;7541:24;7520:46;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;7520:46:14;;;;;;;;;;;;;;;;;;;;:20;:46::i;:::-;7482:85;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;7482:85:14;;-1:-1:-1;;;;;7482:85:14;6859:715;;:::o;22338:689::-;1668:10:11;:8;:10::i;:::-;1660:45;;;;;;;-1:-1:-1;;;;;1660:45:11;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1660:45:11;;;;;;;;;;;;;;;22487:21:14;;;;;;;22486:22;22478:76;;;;;-1:-1:-1;;;;;22478:76:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;22668:7;20564:8;22645:30;;:64;;;;;20655:10;22679:7;:30;;22645:64;22637:122;;;;;;;-1:-1:-1;;;;;22637:122:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;22828:26;22846:7;22828:17;:26::i;:::-;22906:21;:28;;-1:-1:-1;;22906:28:14;;;;;22986:34;;;23000:10;22986:34;;;;;;;;;;;;;;;;;;;;;22338:689;:::o;20977:29::-;;;;:::o;2742:303:11:-;1668:10;:8;:10::i;:::-;1660:45;;;;;;;-1:-1:-1;;;;;1660:45:11;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1660:45:11;;;;;;;;;;;;;;;2861:15;;;;;;;2853:57;;;;;;;-1:-1:-1;;;;;2853:57:11;;;;;;;;;;;;;;;;;;;;;;;;;;;;2946:6;;2925:40;;;-1:-1:-1;;;;;2946:6:11;;;2925:40;;2946:6;2925:40;;;;;;;;;;;;;;;;3019:6;:19;;-1:-1:-1;;3019:19:11;;;2742:303::o;24912:629:14:-;1686:25:5;1700:10;1686:13;:25::i;:::-;1678:64;;;;;;;-1:-1:-1;;;;;1678:64:5;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1678:64:5;;;;;;;;;;;;;;;25055:19:14;;;;25047:65;;;;;;;-1:-1:-1;;;;;25047:65:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;25201:17;;:28;;25193:87;;;;;-1:-1:-1;;;;;25193:87:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;25348:1;25328:17;:21;25406:19;:27;;-1:-1:-1;;25406:27:14;;;25488:46;;;25514:10;25488:46;;;;;;;;;;;;;;;;;;;;;24912:629;:::o;3267:158::-;3346:7;3399:17;3382:35;;;;;;;;;;;;;;;;;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;3382:35:14;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;3382:35:14;;;3372:46;;;;;;;;;;;;;36:153:-1;66:2;58:11;;36:153;;176:10;;164:23;;-1:-1;;139:12;;;;98:2;89:12;;;;114;36:153;;;299:10;344;;263:2;259:12;;;;254:3;250:22;-1:-1;;246:30;311:9;;295:26;;;340:21;;377:20;;;;365:33;;3372:46:14;;;;;;;;;;;;-1:-1:-1;;;;;3267:158:14:o;4244:753::-;1668:10:11;:8;:10::i;:::-;1660:45;;;;;;;-1:-1:-1;;;;;1660:45:11;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1660:45:11;;;;;;;;;;;;;;;2734:26:14;;;;2733:27;:57;;;;-1:-1:-1;2765:25:14;;;;;;;2764:26;2733:57;2725:116;;;;;;;-1:-1:-1;;;;;2725:116:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4361:10;;2254:332;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;2324:6:14;;-1:-1:-1;;;;;2319:250:14;2340:10;:17;2336:1;:21;2319:250;;;2403:7;:5;:7::i;:::-;-1:-1:-1;;;;;2386:24:14;:10;2397:1;2386:13;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;2386:24:14;;;2378:82;;;;;-1:-1:-1;;;;;2378:82:14;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;2378:82:14;;;;;;;;;;;;;;;;;;;;2482:13;;2507:1;;2482:10;;2493:1;;2482:13;;;;;;;;;;;;;;;-1:-1:-1;;;;;2482:27:14;;;2474:84;;;;;-1:-1:-1;;;;;2474:84:14;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;2474:84:14;;;;;;;;;;;;;;;;;;;;2359:3;;2319:250;;;4451:20;;;;;;;4443:67;;;;;;;-1:-1:-1;;;;;4443:67:14;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;4443:67:14;;;;;;;;;;;;;;;4601:1;4581:21;;4573:69;;;;;-1:-1:-1;;;;;4573:69:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4725:38;:25;4753:10;;4725:38;:::i;:::-;-1:-1:-1;4817:26:14;:33;;-1:-1:-1;;4817:33:14;4846:4;4817:33;;;4963:25;4942:47;;;;;;;;;;;;;;;;;;;4903:87;;4930:10;;;;4942:47;;;4963:25;4942:47;;;4963:25;4942:47;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;4942:47:14;;;;;;;;;;;;;;;;;;;;:20;:47::i;:::-;4903:87;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;4903:87:14;;-1:-1:-1;;;;;4903:87:14;2851:1;;4244:753;;:::o;20881:22::-;;;;:::o;21012:31::-;;;;;;:::o;3093:77:11:-;3157:6;;-1:-1:-1;;;;;3157:6:11;3093:77;:::o;22023:203:14:-;22072:4;22098:14;;22115:8;22098:25;22092:3;:31;22088:132;;;-1:-1:-1;22146:10:14;;22139:17;;22088:132;-1:-1:-1;22194:15:14;;22187:22;;9735;;;;:::o;2123:32::-;;;;;;;;;:::o;18105:1652::-;18596:15;18625:16;1668:10:11;:8;:10::i;:::-;1660:45;;;;;;;-1:-1:-1;;;;;1660:45:11;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1660:45:11;;;;;;;;;;;;;;;18193:7:14;15903:11;;;15895:53;;;;;-1:-1:-1;;;;;15895:53:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;18281:17:14;;;;18273:62;;;;;-1:-1:-1;;;;;18273:62:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;18424:18:14;;;;;;:13;:18;;;;;;;;18423:19;18419:984;;;18507:23;:21;:23::i;:::-;-1:-1:-1;;;;;18659:20:14;;;18655:240;;18750:4;;18764:11;;18750:26;;;;;;;;;;;;;;:4;;;;-1:-1:-1;;;;;18750:4:14;;:13;;:26;;;;;;;;;;;;;;;-1:-1:-1;18750:4:14;:26;;;5:2:-1;;;;30:1;27;20:12;5:2;18750:26:14;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;18750:26:14;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;18750:26:14;18783:11;;18735:60;;;;;;;;;;;;;;-1:-1:-1;;;;;18735:47:14;;;;;;:60;;;;;18750:26;;18735:60;;;;;;;;;:47;:60;;;5:2:-1;;;;30:1;27;20:12;5:2;18735:60:14;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;18735:60:14;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;18735:60:14;18727:94;;;;;;-1:-1:-1;;;;;18727:94:14;;;;;;;;;;;;;;;:77;;;;;:94;;;;;;;;;;;;;-1:-1:-1;18727:77:14;:94;;;5:2:-1;;;;30:1;27;20:12;5:2;18727:94:14;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;18727:94:14;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;18727:94:14;;;;;;;;-1:-1:-1;18727:94:14;-1:-1:-1;18655:240:14;;;18873:7;18860:20;;18655:240;19050:11;:35;;;-1:-1:-1;;;;;;19065:20:14;;;19050:35;19046:347;;;19195:16;:14;:16::i;:::-;19181:30;;;19173:88;;;;;-1:-1:-1;;;;;19173:88:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;19326:52;19345:32;19366:10;19345:16;:14;:16::i;19326:52::-;-1:-1:-1;;;;;19482:20:14;;;19478:189;;19532:6;-1:-1:-1;;;;;19526:22:14;;19549:3;19554:7;19526:36;;;;;;;;;;;;;-1:-1:-1;;;;;19526:36:14;-1:-1:-1;;;;;19526:36:14;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;19526:36:14;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;19526:36:14;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;19526:36:14;19518:86;;;;;;;-1:-1:-1;;;;;19518:86:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;19478:189;;;19635:21;;-1:-1:-1;;;;;19635:12:14;;;:21;;;;;19648:7;;19635:21;;;;19648:7;19635:12;:21;;;;;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;19635:21:14;19478:189;19717:33;;;-1:-1:-1;;;;;19717:33:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;1715:1:11;18105:1652:14;;;;;:::o;9831:29::-;;;;:::o;7643:953::-;8195:6;1686:25:5;1700:10;1686:13;:25::i;:::-;1678:64;;;;;;;-1:-1:-1;;;;;1678:64:5;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1678:64:5;;;;;;;;;;;;;;;7830:25:14;;;;;;;7822:78;;;;;;;-1:-1:-1;;;;;7822:78:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8021:46;8042:24;8021:46;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;8021:46:14;;;;;;;;;;;;;;;;;;;;:20;:46::i;:::-;8012:55;;8004:139;;;;;-1:-1:-1;;;;;8004:139:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;8204:1:14;8190:134;8211:24;:31;8207:35;;8190:134;;;8308:5;8263:13;:42;8277:24;8302:1;8277:27;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;8277:27:14;8263:42;;;;;;;;;;;;:50;;-1:-1:-1;;8263:50:14;;;;;;;;;;-1:-1:-1;8244:3:14;8190:134;;;8373:58;8394:10;8406:24;8373:58;;;;-1:-1:-1;;;;;8373:58:14;-1:-1:-1;;;;;8373:58:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;8373:58:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8477:31;8484:24;;8477:31;:::i;:::-;-1:-1:-1;;8556:25:14;:33;;-1:-1:-1;;8556:33:14;;;7643:953::o;8664:706::-;1686:25:5;1700:10;1686:13;:25::i;:::-;1678:64;;;;;;;-1:-1:-1;;;;;1678:64:5;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1678:64:5;;;;;;;;;;;;;;;8803:25:14;;;;;;;8795:78;;;;;;;-1:-1:-1;;;;;8795:78:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8994:46;9015:24;8994:46;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;8994:46:14;;;;;;;;;;;;;;;;;;;;:20;:46::i;:::-;8985:55;;8977:120;;;;;-1:-1:-1;;;;;8977:120:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;9143:31;9150:24;;9143:31;:::i;:::-;9231:25;:33;;-1:-1:-1;;9231:33:14;;;9319:44;;;9345:10;9319:44;;;;;;;;;;;;;;;;;;;;;8664:706;:::o;9866:31::-;;;;;;:::o;11261:481::-;1668:10:11;:8;:10::i;:::-;1660:45;;;;;;;-1:-1:-1;;;;;1660:45:11;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1660:45:11;;;;;;;;;;;;;;;11400:21:14;;;;;;;11392:70;;;;;;;-1:-1:-1;;;;;11392:70:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;11541:17;:27;;;11622:19;:26;;-1:-1:-1;;11622:26:14;11644:4;11622:26;;;11701:34;;;;;;;;;;;;;;;;;11261:481;:::o;10287:201::-;10334:4;10360:14;;10377:8;10360:25;10354:3;:31;10350:132;;;-1:-1:-1;10408:10:14;;10401:17;;10350:132;-1:-1:-1;10456:15:14;;10449:22;;2080:37;;;;;;;;;:::o;16512:224::-;16568:4;-1:-1:-1;;;;;16588:20:14;;;16584:146;;16631:29;;;;;;16655:4;16631:29;;;;;;-1:-1:-1;;;;;16631:23:14;;;;;:29;;;;;;;;;;;;;;-1:-1:-1;16631:23:14;:29;;;5:2:-1;;;;30:1;27;20:12;5:2;16631:29:14;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;16631:29:14;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;16631:29:14;;-1:-1:-1;16624:36:14;;16584:146;-1:-1:-1;16706:4:14;16698:21;16691:28;;12584:619;1686:25:5;1700:10;1686:13;:25::i;:::-;1678:64;;;;;;;-1:-1:-1;;;;;1678:64:5;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1678:64:5;;;;;;;;;;;;;;;12717:19:14;;;;12709:66;;;;;;;-1:-1:-1;;;;;12709:66:14;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;12709:66:14;;;;;;;;;;;;;;;12864:17;;:28;;12856:86;;;;;-1:-1:-1;;;;;12856:86:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;13010:1;12990:17;:21;13068:19;:27;;-1:-1:-1;;13068:27:14;;;13150:46;;;13176:10;13150:46;;;;;;;;;;;;;;;;;;;;;12584:619;:::o;25734:962::-;25785:7;15903:11;;;15895:53;;;;;-1:-1:-1;;;;;15895:53:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;25884:10;:8;:10::i;:::-;:39;;;;25898:25;25912:10;25898:13;:25::i;:::-;25876:95;;;;;;;-1:-1:-1;;;;;25876:95:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;26034:23;:21;:23::i;:::-;26137:15;;:20;;26129:70;;;;;-1:-1:-1;;;;;26129:70:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;26309:15;;26298:26;;;26290:88;;;;;-1:-1:-1;;;;;26290:88:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;26533:15;;:28;;26553:7;26533:28;:19;:28;:::i;:::-;26515:15;:46;26571:7;:5;:7::i;:::-;-1:-1:-1;;;;;26571:16:14;:25;26588:7;26571:25;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;26571:25:14;26649:40;26661:9;26672:7;:5;:7::i;:::-;26649:40;;;-1:-1:-1;;;;;26649:40:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;25734:962;;:::o;1879:592:11:-;1668:10;:8;:10::i;:::-;1660:45;;;;;;;-1:-1:-1;;;;;1660:45:11;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1660:45:11;;;;;;;;;;;;;;;2016:15;;;;;;;2008:57;;;;;;;-1:-1:-1;;;;;2008:57:11;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;2146:22:11;;;;2138:70;;;;;-1:-1:-1;;;;;2138:70:11;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2265:15;:23;;-1:-1:-1;;2265:23:11;;;;;2349:38;;;-1:-1:-1;;;;;2370:6:11;;;2349:38;;;;;;;;;;;;;;;;;;;;;2447:6;:17;;-1:-1:-1;;2447:17:11;-1:-1:-1;;;;;2447:17:11;;;;;;;;;;1879:592::o;11804:719:14:-;1686:25:5;1700:10;1686:13;:25::i;:::-;1678:64;;;;;;;-1:-1:-1;;;;;1678:64:5;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1678:64:5;;;;;;;;;;;;;;;11945:19:14;;;;11937:66;;;;;;;-1:-1:-1;;;;;11937:66:14;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;11937:66:14;;;;;;;;;;;;;;;12092:17;;:28;;12084:88;;;;;-1:-1:-1;;;;;12084:88:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;12240:36;12258:17;;12240;:36::i;:::-;12354:17;;12328:44;;;12342:10;12328:44;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;12420:19:14;:27;;-1:-1:-1;;12420:27:14;;;12442:5;12495:17;:21;11804:719::o;3556:565::-;3887:6;1668:10:11;:8;:10::i;:::-;1660:45;;;;;;;-1:-1:-1;;;;;1660:45:11;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;1660:45:11;;;;;;;;;;;;;;;3650:10:14;;2254:332;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;2324:6:14;;-1:-1:-1;;;;;2319:250:14;2340:10;:17;2336:1;:21;2319:250;;;2403:7;:5;:7::i;:::-;-1:-1:-1;;;;;2386:24:14;:10;2397:1;2386:13;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;2386:24:14;;;2378:82;;;;;-1:-1:-1;;;;;2378:82:14;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;2378:82:14;;;;;;;;;;;;;;;;;;;;2482:13;;2507:1;;2482:10;;2493:1;;2482:13;;;;;;;;;;;;;;;-1:-1:-1;;;;;2482:27:14;;;2474:84;;;;;-1:-1:-1;;;;;2474:84:14;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;2474:84:14;;;;;;;;;;;;;;;;;;;;2359:3;;2319:250;;;3745:20;;;;;;;3744:21;3736:72;;;;;-1:-1:-1;;;;;3736:72:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3896:1;3887:10;;3882:105;3899:21;;;3882:105;;;3972:4;3941:13;:28;3955:10;;3966:1;3955:13;;;;;;;;;;;;;;;;-1:-1:-1;;;;;3955:13:14;3941:28;;-1:-1:-1;3941:28:14;;;;;;;;-1:-1:-1;3941:28:14;:35;;-1:-1:-1;;3941:35:14;;;;;;;;;;-1:-1:-1;3922:3:14;;;;;3882:105;;;3996:20;:27;;-1:-1:-1;;3996:27:14;;;;;4074:40;;;4091:10;4074:40;;;;;;;;;;;;;;;;;;4091:10;;4103;;;;4074:40;;;;4103:10;;4074:40;;;4103:10;4074:40;;;;;;;;;;-1:-1:-1;4074:40:14;;-1:-1:-1;;;;;4074:40:14;1715:1:11;;3556:565:14;;;:::o;21049:33::-;;;;;;;;;:::o;1830:173:5:-;1894:4;1939;;;1953:5;1939:20;;;;;;;;;;;;;;-1:-1:-1;;;;;1939:4:5;;;;:13;;:20;;;;;;;;;;;;;;;1894:4;1939;:20;;;5:2:-1;;;;30:1;27;20:12;5:2;1939:20:5;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;1939:20:5;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;1939:20:5;1966:5;;1929:43;;;;;;;;;;;;;;-1:-1:-1;;;;;1929:36:5;;;;;;:43;;;;;1939:20;;1929:43;;;;;;;;;:36;:43;;;5:2:-1;;;;30:1;27;20:12;5:2;1929:43:5;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;1929:43:5;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;1929:43:5;1917:79;;;;;;-1:-1:-1;;;;;1917:79:5;;;;;;;;;:69;;;;;;;:79;;;;;1929:43;;1917:79;;;;;;;-1:-1:-1;1917:69:5;:79;;;5:2:-1;;;;30:1;27;20:12;5:2;1917:79:5;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;1917:79:5;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;1917:79:5;;1830:173;-1:-1:-1;;1830:173:5:o;3390:93:11:-;3470:6;;-1:-1:-1;;;;;3470:6:11;3456:10;:20;;3390:93::o;1625:616:0:-;1685:4;2188:20;;2226:8;;1625:616::o;13439:340:14:-;13501:14;;:28;;13520:8;13501:28;:18;:28;:::i;:::-;13495:3;:34;13491:282;;;13608:3;13591:14;:20;13710:10;;13692:15;:28;13739:23;;;;-1:-1:-1;;13739:23:14;13491:282;13439:340::o;33313:352::-;33382:6;33468:8;33490;33518:7;33423:5;33431:1;33423:9;33408:4;:11;:24;;33400:57;;;;;;;-1:-1:-1;;;;;33400:57:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;33479:1:14;;-1:-1:-1;33479:1:14;;-1:-1:-1;33479:1:14;33513:127;33535:1;33531;:5;;;33513:127;;;33562:3;33557:8;;;;33591:4;33604:1;33596:9;;:5;:9;33591:15;;;;;;;;;;;;;;;;;;;;;33583:24;33621:8;;;;33583:24;-1:-1:-1;33538:3:14;;33513:127;;;-1:-1:-1;33657:1:14;;33313:352;-1:-1:-1;;;;33313:352:14:o;34018:262::-;34083:4;34168:6;34122:5;34130:2;34122:10;34107:4;:11;:25;;34099:58;;;;;;;-1:-1:-1;;;;;34099:58:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;34217:27:14;34231:4;34217:27;34211:34;;34018:262::o;32816:368::-;32886:7;32974:9;32997;33026:7;32928:5;32936:2;32928:10;32913:4;:11;:25;;32905:58;;;;;;;-1:-1:-1;;;;;32905:58:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;32986:1:14;;-1:-1:-1;32986:1:14;;-1:-1:-1;32986:1:14;33021:129;33043:2;33039:1;:6;;;33021:129;;;33071:3;33066:8;;;;33101:4;33114:1;33106:9;;:5;:9;33101:15;;;;;;;;;;;;;;;;;;;;;33092:25;33131:8;;;;33092:25;-1:-1:-1;33047:3:14;;33021:129;;2251:131:3;2309:7;;2332:6;;;;2324:15;;;;;;-1:-1:-1;;2357:5:3;;;2251:131::o;13272:93:14:-;13333:15;:25;13272:93::o;31588:1110::-;31693:4;31709:11;31768:4;31762:11;31902:2;31895:5;31891:14;32567:1;32549;32441:11;32423:1;32400:6;32371:12;32024:5;32019:3;32015:15;31993:666;31983:676;31588:1110;-1:-1:-1;;;;;;;;31588:1110:14:o;26844:401::-;26956:23;:21;:23::i;:::-;27044:10;:20;;;27156:15;;:28;-1:-1:-1;27152:87:14;;;27218:10;;27200:15;:28;27152:87;26844:401;:::o;13925:400::-;14036:23;:21;:23::i;:::-;14124:10;:20;;;14236:15;;:28;-1:-1:-1;14232:87:14;;;14298:10;;14280:15;:28;13925:400;:::o;27320:298::-;27381:14;;:28;;27400:8;27381:28;:18;:28;:::i;:::-;27375:3;:34;27371:241;;;27488:3;27471:14;:20;27591:10;;27573:15;:28;27320:298::o;2445:131:3:-;2503:7;2530:5;;;2549:6;;;;2541:15;;;;;;2570:1;2445:131;-1:-1:-1;;;2445:131:3:o;20043:14239:14:-;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;20043:14239:14;-1:-1:-1;;;;;20043:14239:14;;;;;;;;;;;;;;;;;;;-1:-1:-1;20043:14239:14;;;-1:-1:-1;20043:14239:14;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;20043:14239:14;;;;;;
Swarm Source
bzzr://938e1617a273c4151bc1777bc995eea2c1a6de99b117631bc25ebafb061b0a31
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.