Transaction Hash:
Block:
13259060 at Sep-19-2021 11:03:09 PM +UTC
Transaction Fee:
0.0028131 ETH
$6.84
Gas Used:
56,262 Gas / 50 Gwei
Emitted Events:
146 |
Holder.Received( _from=[Receiver] Licence, _amount=429570925278761 )
|
147 |
MultiSigWalletWithDailyLimit.Deposit( sender=[Receiver] Licence, value=42957092527876033 )
|
148 |
Licence.TransferredToTokenHolder( _from=[Sender] 0xfe7eadf2a687da3d05b727b429e6922c2b889842, _to=Holder, _asset=0x00000000...000000000, _amount=429570925278761 )
|
149 |
Licence.TransferredToCryptoFloat( _from=[Sender] 0xfe7eadf2a687da3d05b727b429e6922c2b889842, _to=MultiSigWalletWithDailyLimit, _asset=0x00000000...000000000, _amount=42957092527876033 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x24F0bB6c...F55cDd387 | (Monolith: Community Chest) | 200.124989919436188603 Eth | 200.125419490361467364 Eth | 0.000429570925278761 | |
0x829BD824...93333A830
Miner
| (F2Pool Old) | 2,572.565777381534813553 Eth | 2,572.565902528222885575 Eth | 0.000125146688072022 | |
0xab613544...6fe941257 | 313.481270550810391716 Eth | 313.524227643338267749 Eth | 0.042957092527876033 | ||
0xFe7Eadf2...C2b889842 |
0.107934329106254937 Eth
Nonce: 1
|
0.061734565653100143 Eth
Nonce: 2
| 0.046199763453154794 |
Execution Trace
load[Licence (ln:1385)]
safeTransferFrom[Licence (ln:1389)]
div[Licence (ln:1391)]
mul[Licence (ln:1391)]
sub[Licence (ln:1392)]
safeTransferFrom[Licence (ln:1395)]
safeTransferFrom[Licence (ln:1396)]
transfer[Licence (ln:1399)]
transfer[Licence (ln:1400)]
TransferredToTokenHolder[Licence (ln:1403)]
TransferredToCryptoFloat[Licence (ln:1406)]
File 1 of 3: Licence
File 2 of 3: Holder
File 3 of 3: MultiSigWalletWithDailyLimit
// File: SafeMath.sol pragma solidity ^0.5.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SafeMath: subtraction overflow"); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0, "SafeMath: division by zero"); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0, "SafeMath: modulo by zero"); return a % b; } } // File: ERC20.sol pragma solidity ^0.5.10; /// @title ERC20 interface is a subset of the ERC20 specification. /// @notice see https://github.com/ethereum/EIPs/issues/20 interface ERC20 { function allowance(address _owner, address _spender) external view returns (uint256); function approve(address _spender, uint256 _value) external returns (bool); function balanceOf(address _who) external view returns (uint256); function totalSupply() external view returns (uint256); function transfer(address _to, uint256 _value) external returns (bool); function transferFrom(address _from, address _to, uint256 _value) external returns (bool); } // File: Address.sol pragma solidity ^0.5.0; /** * @dev Collection of functions related to the address type, */ library Address { /** * @dev Returns true if `account` is a contract. * * This test is non-exhaustive, and there may be false-negatives: during the * execution of a contract's constructor, its address will be reported as * not containing a contract. * * > It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. */ function isContract(address account) internal view returns (bool) { // This method relies in extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } } // File: SafeERC20.sol /** * 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.5.0; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer(ERC20 token, address to, uint256 value) internal { callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(ERC20 token, address from, address to, uint256 value) internal { callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } function safeApprove(ERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length require((value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(ERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(ERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function callOptionalReturn(ERC20 token, bytes memory data) internal { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. // A Solidity high level call has three parts: // 1. The target address is checked to verify it contains contract code // 2. The call itself is made, and success asserted // 3. The return value is decoded, which in turn checks the size of the returned data. // solhint-disable-next-line max-line-length require(address(token).isContract(), "SafeERC20: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = address(token).call(data); require(success, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } // File: ownable.sol /** * Ownable - The Consumer Contract Wallet * Copyright (C) 2019 The Contract Wallet Company Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ pragma solidity ^0.5.10; /// @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 allows for the transferOwnership operation to be made impossible /// https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ownership/Ownable.sol contract Ownable { event TransferredOwnership(address _from, address _to); event LockedOwnership(address _locked); address payable private _owner; bool private _isTransferable; /// @notice Constructor sets the original owner of the contract and whether or not it is one time transferable. constructor(address payable _account_, bool _transferable_) internal { _owner = _account_; _isTransferable = _transferable_; // Emit the LockedOwnership event if no longer transferable. if (!_isTransferable) { emit LockedOwnership(_account_); } emit TransferredOwnership(address(0), _account_); } /// @notice Reverts if called by any account other than the owner. modifier onlyOwner() { require(_isOwner(msg.sender), "sender is not an owner"); _; } /// @notice Allows the current owner to transfer control of the contract to a new address. /// @param _account address to transfer ownership to. /// @param _transferable indicates whether to keep the ownership transferable. function transferOwnership(address payable _account, bool _transferable) external onlyOwner { // Require that the ownership is transferable. require(_isTransferable, "ownership is not transferable"); // Require that the new owner is not the zero address. require(_account != address(0), "owner cannot be set to zero address"); // Set the transferable flag to the value _transferable passed in. _isTransferable = _transferable; // Emit the LockedOwnership event if no longer transferable. if (!_transferable) { emit LockedOwnership(_account); } // Emit the ownership transfer event. emit TransferredOwnership(_owner, _account); // Set the owner to the provided address. _owner = _account; } /// @notice check if the ownership is transferable. /// @return true if the ownership is transferable. function isTransferable() external view returns (bool) { return _isTransferable; } /// @notice Allows the current owner to relinquish control of the contract. /// @dev Renouncing to ownership will leave the contract without an owner and unusable. /// @dev It will not be possible to call the functions with the `onlyOwner` modifier anymore. function renounceOwnership() external onlyOwner { // Require that the ownership is transferable. require(_isTransferable, "ownership is not transferable"); // note that this could be terminal _owner = address(0); emit TransferredOwnership(_owner, address(0)); } /// @notice Find out owner address /// @return address of the owner. function owner() public view returns (address payable) { return _owner; } /// @notice Check if owner address /// @return true if sender is the owner of the contract. function _isOwner(address _address) internal view returns (bool) { return _address == _owner; } } // File: transferrable.sol /** * Transferrable - The Consumer Contract Wallet * Copyright (C) 2019 The Contract Wallet Company Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ pragma solidity ^0.5.10; /// @title SafeTransfer, allowing contract to withdraw tokens accidentally sent to itself contract Transferrable { using SafeERC20 for ERC20; /// @dev This function is used to move tokens sent accidentally to this contract method. /// @dev The owner can chose the new destination address /// @param _to is the recipient's address. /// @param _asset is the address of an ERC20 token or 0x0 for ether. /// @param _amount is the amount to be transferred in base units. function _safeTransfer(address payable _to, address _asset, uint _amount) internal { // address(0) is used to denote ETH if (_asset == address(0)) { _to.transfer(_amount); } else { ERC20(_asset).safeTransfer(_to, _amount); } } } // File: controller.sol /** * Controller - The Consumer Contract Wallet * Copyright (C) 2019 The Contract Wallet Company Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ pragma solidity ^0.5.10; /// @title The IController interface provides access to the isController and isAdmin checks. interface IController { function isController(address) external view returns (bool); function isAdmin(address) external view returns (bool); } /// @title Controller stores a list of controller addresses that can be used for authentication in other contracts. /// @notice The Controller implements a hierarchy of concepts, Owner, Admin, and the Controllers. /// @dev Owner can change the Admins /// @dev Admins and can the Controllers /// @dev Controllers are used by the application. contract Controller is IController, Ownable, Transferrable { event AddedController(address _sender, address _controller); event RemovedController(address _sender, address _controller); event AddedAdmin(address _sender, address _admin); event RemovedAdmin(address _sender, address _admin); event Claimed(address _to, address _asset, uint _amount); event Stopped(address _sender); event Started(address _sender); mapping (address => bool) private _isAdmin; uint private _adminCount; mapping (address => bool) private _isController; uint private _controllerCount; bool private _stopped; /// @notice Constructor initializes the owner with the provided address. /// @param _ownerAddress_ address of the owner. constructor(address payable _ownerAddress_) Ownable(_ownerAddress_, false) public {} /// @notice Checks if message sender is an admin. modifier onlyAdmin() { require(isAdmin(msg.sender), "sender is not an admin"); _; } /// @notice Check if Owner or Admin modifier onlyAdminOrOwner() { require(_isOwner(msg.sender) || isAdmin(msg.sender), "sender is not an admin"); _; } /// @notice Check if controller is stopped modifier notStopped() { require(!isStopped(), "controller is stopped"); _; } /// @notice Add a new admin to the list of admins. /// @param _account address to add to the list of admins. function addAdmin(address _account) external onlyOwner notStopped { _addAdmin(_account); } /// @notice Remove a admin from the list of admins. /// @param _account address to remove from the list of admins. function removeAdmin(address _account) external onlyOwner { _removeAdmin(_account); } /// @return the current number of admins. function adminCount() external view returns (uint) { return _adminCount; } /// @notice Add a new controller to the list of controllers. /// @param _account address to add to the list of controllers. function addController(address _account) external onlyAdminOrOwner notStopped { _addController(_account); } /// @notice Remove a controller from the list of controllers. /// @param _account address to remove from the list of controllers. function removeController(address _account) external onlyAdminOrOwner { _removeController(_account); } /// @notice count the Controllers /// @return the current number of controllers. function controllerCount() external view returns (uint) { return _controllerCount; } /// @notice is an address an Admin? /// @return true if the provided account is an admin. function isAdmin(address _account) public view notStopped returns (bool) { return _isAdmin[_account]; } /// @notice is an address a Controller? /// @return true if the provided account is a controller. function isController(address _account) public view notStopped returns (bool) { return _isController[_account]; } /// @notice this function can be used to see if the controller has been stopped /// @return true is the Controller has been stopped function isStopped() public view returns (bool) { return _stopped; } /// @notice Internal-only function that adds a new admin. function _addAdmin(address _account) private { require(!_isAdmin[_account], "provided account is already an admin"); require(!_isController[_account], "provided account is already a controller"); require(!_isOwner(_account), "provided account is already the owner"); require(_account != address(0), "provided account is the zero address"); _isAdmin[_account] = true; _adminCount++; emit AddedAdmin(msg.sender, _account); } /// @notice Internal-only function that removes an existing admin. function _removeAdmin(address _account) private { require(_isAdmin[_account], "provided account is not an admin"); _isAdmin[_account] = false; _adminCount--; emit RemovedAdmin(msg.sender, _account); } /// @notice Internal-only function that adds a new controller. function _addController(address _account) private { require(!_isAdmin[_account], "provided account is already an admin"); require(!_isController[_account], "provided account is already a controller"); require(!_isOwner(_account), "provided account is already the owner"); require(_account != address(0), "provided account is the zero address"); _isController[_account] = true; _controllerCount++; emit AddedController(msg.sender, _account); } /// @notice Internal-only function that removes an existing controller. function _removeController(address _account) private { require(_isController[_account], "provided account is not a controller"); _isController[_account] = false; _controllerCount--; emit RemovedController(msg.sender, _account); } /// @notice stop our controllers and admins from being useable function stop() external onlyAdminOrOwner { _stopped = true; emit Stopped(msg.sender); } /// @notice start our controller again function start() external onlyOwner { _stopped = false; emit Started(msg.sender); } //// @notice Withdraw tokens from the smart contract to the specified account. function claim(address payable _to, address _asset, uint _amount) external onlyAdmin notStopped { _safeTransfer(_to, _asset, _amount); emit Claimed(_to, _asset, _amount); } } // File: ENS.sol /** * 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.5.0; interface ENS { // Logged when the owner of a node assigns a new owner to a subnode. event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner); // Logged when the owner of a node transfers ownership to a new account. event Transfer(bytes32 indexed node, address owner); // Logged when the resolver for a node changes. event NewResolver(bytes32 indexed node, address resolver); // Logged when the TTL of a node changes event NewTTL(bytes32 indexed node, uint64 ttl); function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external; function setResolver(bytes32 node, address resolver) external; function setOwner(bytes32 node, address owner) external; function setTTL(bytes32 node, uint64 ttl) external; function owner(bytes32 node) external view returns (address); function resolver(bytes32 node) external view returns (address); function ttl(bytes32 node) external view returns (uint64); } // File: ResolverBase.sol pragma solidity ^0.5.0; contract ResolverBase { bytes4 private constant INTERFACE_META_ID = 0x01ffc9a7; function supportsInterface(bytes4 interfaceID) public pure returns(bool) { return interfaceID == INTERFACE_META_ID; } function isAuthorised(bytes32 node) internal view returns(bool); modifier authorised(bytes32 node) { require(isAuthorised(node)); _; } } // File: ABIResolver.sol pragma solidity ^0.5.0; contract ABIResolver is ResolverBase { bytes4 constant private ABI_INTERFACE_ID = 0x2203ab56; event ABIChanged(bytes32 indexed node, uint256 indexed contentType); mapping(bytes32=>mapping(uint256=>bytes)) abis; /** * Sets the ABI associated with an ENS node. * Nodes may have one ABI of each content type. To remove an ABI, set it to * the empty string. * @param node The node to update. * @param contentType The content type of the ABI * @param data The ABI data. */ function setABI(bytes32 node, uint256 contentType, bytes calldata data) external authorised(node) { // Content types must be powers of 2 require(((contentType - 1) & contentType) == 0); abis[node][contentType] = data; emit ABIChanged(node, contentType); } /** * Returns the ABI associated with an ENS node. * Defined in EIP205. * @param node The ENS node to query * @param contentTypes A bitwise OR of the ABI formats accepted by the caller. * @return contentType The content type of the return value * @return data The ABI data */ function ABI(bytes32 node, uint256 contentTypes) external view returns (uint256, bytes memory) { mapping(uint256=>bytes) storage abiset = abis[node]; for (uint256 contentType = 1; contentType <= contentTypes; contentType <<= 1) { if ((contentType & contentTypes) != 0 && abiset[contentType].length > 0) { return (contentType, abiset[contentType]); } } return (0, bytes("")); } function supportsInterface(bytes4 interfaceID) public pure returns(bool) { return interfaceID == ABI_INTERFACE_ID || super.supportsInterface(interfaceID); } } // File: AddrResolver.sol pragma solidity ^0.5.0; contract AddrResolver is ResolverBase { bytes4 constant private ADDR_INTERFACE_ID = 0x3b3b57de; event AddrChanged(bytes32 indexed node, address a); mapping(bytes32=>address) addresses; /** * Sets the address associated with an ENS node. * May only be called by the owner of that node in the ENS registry. * @param node The node to update. * @param addr The address to set. */ function setAddr(bytes32 node, address addr) external authorised(node) { addresses[node] = addr; emit AddrChanged(node, addr); } /** * Returns the address associated with an ENS node. * @param node The ENS node to query. * @return The associated address. */ function addr(bytes32 node) public view returns (address) { return addresses[node]; } function supportsInterface(bytes4 interfaceID) public pure returns(bool) { return interfaceID == ADDR_INTERFACE_ID || super.supportsInterface(interfaceID); } } // File: ContentHashResolver.sol pragma solidity ^0.5.0; contract ContentHashResolver is ResolverBase { bytes4 constant private CONTENT_HASH_INTERFACE_ID = 0xbc1c58d1; event ContenthashChanged(bytes32 indexed node, bytes hash); mapping(bytes32=>bytes) hashes; /** * Sets the contenthash associated with an ENS node. * May only be called by the owner of that node in the ENS registry. * @param node The node to update. * @param hash The contenthash to set */ function setContenthash(bytes32 node, bytes calldata hash) external authorised(node) { hashes[node] = hash; emit ContenthashChanged(node, hash); } /** * Returns the contenthash associated with an ENS node. * @param node The ENS node to query. * @return The associated contenthash. */ function contenthash(bytes32 node) external view returns (bytes memory) { return hashes[node]; } function supportsInterface(bytes4 interfaceID) public pure returns(bool) { return interfaceID == CONTENT_HASH_INTERFACE_ID || super.supportsInterface(interfaceID); } } // File: InterfaceResolver.sol pragma solidity ^0.5.0; contract InterfaceResolver is ResolverBase, AddrResolver { bytes4 constant private INTERFACE_INTERFACE_ID = bytes4(keccak256("interfaceImplementer(bytes32,bytes4)")); bytes4 private constant INTERFACE_META_ID = 0x01ffc9a7; event InterfaceChanged(bytes32 indexed node, bytes4 indexed interfaceID, address implementer); mapping(bytes32=>mapping(bytes4=>address)) interfaces; /** * Sets an interface associated with a name. * Setting the address to 0 restores the default behaviour of querying the contract at `addr()` for interface support. * @param node The node to update. * @param interfaceID The EIP 168 interface ID. * @param implementer The address of a contract that implements this interface for this node. */ function setInterface(bytes32 node, bytes4 interfaceID, address implementer) external authorised(node) { interfaces[node][interfaceID] = implementer; emit InterfaceChanged(node, interfaceID, implementer); } /** * Returns the address of a contract that implements the specified interface for this name. * If an implementer has not been set for this interfaceID and name, the resolver will query * the contract at `addr()`. If `addr()` is set, a contract exists at that address, and that * contract implements EIP168 and returns `true` for the specified interfaceID, its address * will be returned. * @param node The ENS node to query. * @param interfaceID The EIP 168 interface ID to check for. * @return The address that implements this interface, or 0 if the interface is unsupported. */ function interfaceImplementer(bytes32 node, bytes4 interfaceID) external view returns (address) { address implementer = interfaces[node][interfaceID]; if(implementer != address(0)) { return implementer; } address a = addr(node); if(a == address(0)) { return address(0); } (bool success, bytes memory returnData) = a.staticcall(abi.encodeWithSignature("supportsInterface(bytes4)", INTERFACE_META_ID)); if(!success || returnData.length < 32 || returnData[31] == 0) { // EIP 168 not supported by target return address(0); } (success, returnData) = a.staticcall(abi.encodeWithSignature("supportsInterface(bytes4)", interfaceID)); if(!success || returnData.length < 32 || returnData[31] == 0) { // Specified interface not supported by target return address(0); } return a; } function supportsInterface(bytes4 interfaceID) public pure returns(bool) { return interfaceID == INTERFACE_INTERFACE_ID || super.supportsInterface(interfaceID); } } // File: NameResolver.sol pragma solidity ^0.5.0; contract NameResolver is ResolverBase { bytes4 constant private NAME_INTERFACE_ID = 0x691f3431; event NameChanged(bytes32 indexed node, string name); mapping(bytes32=>string) names; /** * Sets the name associated with an ENS node, for reverse records. * May only be called by the owner of that node in the ENS registry. * @param node The node to update. * @param name The name to set. */ function setName(bytes32 node, string calldata name) external authorised(node) { names[node] = name; emit NameChanged(node, name); } /** * Returns the name associated with an ENS node, for reverse records. * Defined in EIP181. * @param node The ENS node to query. * @return The associated name. */ function name(bytes32 node) external view returns (string memory) { return names[node]; } function supportsInterface(bytes4 interfaceID) public pure returns(bool) { return interfaceID == NAME_INTERFACE_ID || super.supportsInterface(interfaceID); } } // File: PubkeyResolver.sol pragma solidity ^0.5.0; contract PubkeyResolver is ResolverBase { bytes4 constant private PUBKEY_INTERFACE_ID = 0xc8690233; event PubkeyChanged(bytes32 indexed node, bytes32 x, bytes32 y); struct PublicKey { bytes32 x; bytes32 y; } mapping(bytes32=>PublicKey) pubkeys; /** * Sets the SECP256k1 public key associated with an ENS node. * @param node The ENS node to query * @param x the X coordinate of the curve point for the public key. * @param y the Y coordinate of the curve point for the public key. */ function setPubkey(bytes32 node, bytes32 x, bytes32 y) external authorised(node) { pubkeys[node] = PublicKey(x, y); emit PubkeyChanged(node, x, y); } /** * Returns the SECP256k1 public key associated with an ENS node. * Defined in EIP 619. * @param node The ENS node to query * @return x, y the X and Y coordinates of the curve point for the public key. */ function pubkey(bytes32 node) external view returns (bytes32 x, bytes32 y) { return (pubkeys[node].x, pubkeys[node].y); } function supportsInterface(bytes4 interfaceID) public pure returns(bool) { return interfaceID == PUBKEY_INTERFACE_ID || super.supportsInterface(interfaceID); } } // File: TextResolver.sol pragma solidity ^0.5.0; contract TextResolver is ResolverBase { bytes4 constant private TEXT_INTERFACE_ID = 0x59d1d43c; event TextChanged(bytes32 indexed node, string indexedKey, string key); mapping(bytes32=>mapping(string=>string)) texts; /** * Sets the text data associated with an ENS node and key. * May only be called by the owner of that node in the ENS registry. * @param node The node to update. * @param key The key to set. * @param value The text data value to set. */ function setText(bytes32 node, string calldata key, string calldata value) external authorised(node) { texts[node][key] = value; emit TextChanged(node, key, key); } /** * Returns the text data associated with an ENS node and key. * @param node The ENS node to query. * @param key The text data key to query. * @return The associated text data. */ function text(bytes32 node, string calldata key) external view returns (string memory) { return texts[node][key]; } function supportsInterface(bytes4 interfaceID) public pure returns(bool) { return interfaceID == TEXT_INTERFACE_ID || super.supportsInterface(interfaceID); } } // File: PublicResolver.sol /** * 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.5.0; /** * A simple resolver anyone can use; only allows the owner of a node to set its * address. */ contract PublicResolver is ABIResolver, AddrResolver, ContentHashResolver, InterfaceResolver, NameResolver, PubkeyResolver, TextResolver { ENS ens; /** * A mapping of authorisations. An address that is authorised for a name * may make any changes to the name that the owner could, but may not update * the set of authorisations. * (node, owner, caller) => isAuthorised */ mapping(bytes32=>mapping(address=>mapping(address=>bool))) public authorisations; event AuthorisationChanged(bytes32 indexed node, address indexed owner, address indexed target, bool isAuthorised); constructor(ENS _ens) public { ens = _ens; } /** * @dev Sets or clears an authorisation. * Authorisations are specific to the caller. Any account can set an authorisation * for any name, but the authorisation that is checked will be that of the * current owner of a name. Thus, transferring a name effectively clears any * existing authorisations, and new authorisations can be set in advance of * an ownership transfer if desired. * * @param node The name to change the authorisation on. * @param target The address that is to be authorised or deauthorised. * @param isAuthorised True if the address should be authorised, or false if it should be deauthorised. */ function setAuthorisation(bytes32 node, address target, bool isAuthorised) external { authorisations[node][msg.sender][target] = isAuthorised; emit AuthorisationChanged(node, msg.sender, target, isAuthorised); } function isAuthorised(bytes32 node) internal view returns(bool) { address owner = ens.owner(node); return owner == msg.sender || authorisations[node][owner][msg.sender]; } } // File: ensResolvable.sol /** * ENSResolvable - The Consumer Contract Wallet * Copyright (C) 2019 The Contract Wallet Company Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ pragma solidity ^0.5.10; ///@title ENSResolvable - Ethereum Name Service Resolver ///@notice contract should be used to get an address for an ENS node contract ENSResolvable { /// @notice _ens is an instance of ENS ENS private _ens; /// @notice _ensRegistry points to the ENS registry smart contract. address private _ensRegistry; /// @param _ensReg_ is the ENS registry used constructor(address _ensReg_) internal { _ensRegistry = _ensReg_; _ens = ENS(_ensRegistry); } /// @notice this is used to that one can observe which ENS registry is being used function ensRegistry() external view returns (address) { return _ensRegistry; } /// @notice helper function used to get the address of a node /// @param _node of the ENS entry that needs resolving /// @return the address of the said node function _ensResolve(bytes32 _node) internal view returns (address) { return PublicResolver(_ens.resolver(_node)).addr(_node); } } // File: controllable.sol /** * Controllable - The Consumer Contract Wallet * Copyright (C) 2019 The Contract Wallet Company Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ pragma solidity ^0.5.10; /// @title Controllable implements access control functionality of the Controller found via ENS. contract Controllable is ENSResolvable { /// @dev Is the registered ENS node identifying the controller contract. bytes32 private _controllerNode; /// @notice Constructor initializes the controller contract object. /// @param _controllerNode_ is the ENS node of the Controller. constructor(bytes32 _controllerNode_) internal { _controllerNode = _controllerNode_; } /// @notice Checks if message sender is a controller. modifier onlyController() { require(_isController(msg.sender), "sender is not a controller"); _; } /// @notice Checks if message sender is an admin. modifier onlyAdmin() { require(_isAdmin(msg.sender), "sender is not an admin"); _; } /// @return the controller node registered in ENS. function controllerNode() external view returns (bytes32) { return _controllerNode; } /// @return true if the provided account is a controller. function _isController(address _account) internal view returns (bool) { return IController(_ensResolve(_controllerNode)).isController(_account); } /// @return true if the provided account is an admin. function _isAdmin(address _account) internal view returns (bool) { return IController(_ensResolve(_controllerNode)).isAdmin(_account); } } // File: licence.sol /** * Licence - The Consumer Contract Wallet * Copyright (C) 2019 The Contract Wallet Company Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ pragma solidity ^0.5.10; /// @title ILicence interface describes methods for loading a TokenCard and updating licence amount. interface ILicence { function load(address, uint) external payable; function updateLicenceAmount(uint) external; } /// @title Licence loads the TokenCard and transfers the licence amout to the TKN Holder Contract. /// @notice the rest of the amount gets sent to the CryptoFloat contract Licence is Transferrable, ENSResolvable, Controllable { using SafeMath for uint256; using SafeERC20 for ERC20; /*******************/ /* Events */ /*****************/ event UpdatedLicenceDAO(address _newDAO); event UpdatedCryptoFloat(address _newFloat); event UpdatedTokenHolder(address _newHolder); event UpdatedTKNContractAddress(address _newTKN); event UpdatedLicenceAmount(uint _newAmount); event TransferredToTokenHolder(address _from, address _to, address _asset, uint _amount); event TransferredToCryptoFloat(address _from, address _to, address _asset, uint _amount); event Claimed(address _to, address _asset, uint _amount); /// @notice This is 100% scaled up by a factor of 10 to give us an extra 1 decimal place of precision uint constant public MAX_AMOUNT_SCALE = 1000; uint constant public MIN_AMOUNT_SCALE = 1; address private _tknContractAddress = 0xaAAf91D9b90dF800Df4F55c205fd6989c977E73a; // solium-disable-line uppercase address payable private _cryptoFloat; address payable private _tokenHolder; address private _licenceDAO; bool private _lockedCryptoFloat; bool private _lockedTokenHolder; bool private _lockedLicenceDAO; bool private _lockedTKNContractAddress; /// @notice This is the _licenceAmountScaled by a factor of 10 /// @dev i.e. 1% is 10 _licenceAmountScaled, 0.1% is 1 _licenceAmountScaled uint private _licenceAmountScaled; /// @notice Reverts if called by any address other than the DAO contract. modifier onlyDAO() { require(msg.sender == _licenceDAO, "the sender isn't the DAO"); _; } /// @notice Constructor initializes the card licence contract. /// @param _licence_ is the initial card licence amount. this number is scaled 10 = 1%, 9 = 0.9% /// @param _float_ is the address of the multi-sig cryptocurrency float contract. /// @param _holder_ is the address of the token holder contract /// @param _tknAddress_ is the address of the TKN ERC20 contract /// @param _ens_ is the address of the ENS Registry /// @param _controllerNode_ is the ENS node corresponding to the controller constructor(uint _licence_, address payable _float_, address payable _holder_, address _tknAddress_, address _ens_, bytes32 _controllerNode_) ENSResolvable(_ens_) Controllable(_controllerNode_) public { require(MIN_AMOUNT_SCALE <= _licence_ && _licence_ <= MAX_AMOUNT_SCALE, "licence amount out of range"); _licenceAmountScaled = _licence_; _cryptoFloat = _float_; _tokenHolder = _holder_; if (_tknAddress_ != address(0)) { _tknContractAddress = _tknAddress_; } } /// @notice Ether can be deposited from any source, so this contract should be payable by anyone. function() external payable {} /// @notice this allows for people to see the scaled licence amount /// @return the scaled licence amount, used to calculate the split when loading. function licenceAmountScaled() external view returns (uint) { return _licenceAmountScaled; } /// @notice allows one to see the address of the CryptoFloat /// @return the address of the multi-sig cryptocurrency float contract. function cryptoFloat() external view returns (address) { return _cryptoFloat; } /// @notice allows one to see the address TKN holder contract /// @return the address of the token holder contract. function tokenHolder() external view returns (address) { return _tokenHolder; } /// @notice allows one to see the address of the DAO /// @return the address of the DAO contract. function licenceDAO() external view returns (address) { return _licenceDAO; } /// @notice The address of the TKN token /// @return the address of the TKN contract. function tknContractAddress() external view returns (address) { return _tknContractAddress; } /// @notice This locks the cryptoFloat address /// @dev so that it can no longer be updated function lockFloat() external onlyAdmin { _lockedCryptoFloat = true; } /// @notice This locks the TokenHolder address /// @dev so that it can no longer be updated function lockHolder() external onlyAdmin { _lockedTokenHolder = true; } /// @notice This locks the DAO address /// @dev so that it can no longer be updated function lockLicenceDAO() external onlyAdmin { _lockedLicenceDAO = true; } /// @notice This locks the TKN address /// @dev so that it can no longer be updated function lockTKNContractAddress() external onlyAdmin { _lockedTKNContractAddress = true; } /// @notice Updates the address of the cyptoFloat. /// @param _newFloat This is the new address for the CryptoFloat function updateFloat(address payable _newFloat) external onlyAdmin { require(!floatLocked(), "float is locked"); _cryptoFloat = _newFloat; emit UpdatedCryptoFloat(_newFloat); } /// @notice Updates the address of the Holder contract. /// @param _newHolder This is the new address for the TokenHolder function updateHolder(address payable _newHolder) external onlyAdmin { require(!holderLocked(), "holder contract is locked"); _tokenHolder = _newHolder; emit UpdatedTokenHolder(_newHolder); } /// @notice Updates the address of the DAO contract. /// @param _newDAO This is the new address for the Licence DAO function updateLicenceDAO(address _newDAO) external onlyAdmin { require(!licenceDAOLocked(), "DAO is locked"); _licenceDAO = _newDAO; emit UpdatedLicenceDAO(_newDAO); } /// @notice Updates the address of the TKN contract. /// @param _newTKN This is the new address for the TKN contract function updateTKNContractAddress(address _newTKN) external onlyAdmin { require(!tknContractAddressLocked(), "TKN is locked"); _tknContractAddress = _newTKN; emit UpdatedTKNContractAddress(_newTKN); } /// @notice Updates the TKN licence amount /// @param _newAmount is a number between MIN_AMOUNT_SCALE (1) and MAX_AMOUNT_SCALE function updateLicenceAmount(uint _newAmount) external onlyDAO { require(MIN_AMOUNT_SCALE <= _newAmount && _newAmount <= MAX_AMOUNT_SCALE, "licence amount out of range"); _licenceAmountScaled = _newAmount; emit UpdatedLicenceAmount(_newAmount); } /// @notice Load the holder and float contracts based on the licence amount and asset amount. /// @param _asset is the address of an ERC20 token or 0x0 for ether. /// @param _amount is the amount of assets to be transferred including the licence amount. function load(address _asset, uint _amount) external payable { uint loadAmount = _amount; // If TKN then no licence to be paid if (_asset == _tknContractAddress) { ERC20(_asset).safeTransferFrom(msg.sender, _cryptoFloat, loadAmount); } else { loadAmount = _amount.mul(MAX_AMOUNT_SCALE).div(_licenceAmountScaled + MAX_AMOUNT_SCALE); uint licenceAmount = _amount.sub(loadAmount); if (_asset != address(0)) { ERC20(_asset).safeTransferFrom(msg.sender, _tokenHolder, licenceAmount); ERC20(_asset).safeTransferFrom(msg.sender, _cryptoFloat, loadAmount); } else { require(msg.value == _amount, "ETH sent is not equal to amount"); _tokenHolder.transfer(licenceAmount); _cryptoFloat.transfer(loadAmount); } emit TransferredToTokenHolder(msg.sender, _tokenHolder, _asset, licenceAmount); } emit TransferredToCryptoFloat(msg.sender, _cryptoFloat, _asset, loadAmount); } //// @notice Withdraw tokens from the smart contract to the specified account. function claim(address payable _to, address _asset, uint _amount) external onlyAdmin { _safeTransfer(_to, _asset, _amount); emit Claimed(_to, _asset, _amount); } /// @notice returns whether or not the CryptoFloat address is locked function floatLocked() public view returns (bool) { return _lockedCryptoFloat; } /// @notice returns whether or not the TokenHolder address is locked function holderLocked() public view returns (bool) { return _lockedTokenHolder; } /// @notice returns whether or not the Licence DAO address is locked function licenceDAOLocked() public view returns (bool) { return _lockedLicenceDAO; } /// @notice returns whether or not the TKN address is locked function tknContractAddressLocked() public view returns (bool) { return _lockedTKNContractAddress; } }
File 2 of 3: Holder
// File: ERC20.sol pragma solidity ^0.5.10; /// @title ERC20 interface is a subset of the ERC20 specification. /// @notice see https://github.com/ethereum/EIPs/issues/20 interface ERC20 { function allowance(address _owner, address _spender) external view returns (uint256); function approve(address _spender, uint256 _value) external returns (bool); function balanceOf(address _who) external view returns (uint256); function totalSupply() external view returns (uint256); function transfer(address _to, uint256 _value) external returns (bool); function transferFrom(address _from, address _to, uint256 _value) external returns (bool); } // File: SafeMath.sol pragma solidity ^0.5.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SafeMath: subtraction overflow"); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0, "SafeMath: division by zero"); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0, "SafeMath: modulo by zero"); return a % b; } } // File: Address.sol pragma solidity ^0.5.0; /** * @dev Collection of functions related to the address type, */ library Address { /** * @dev Returns true if `account` is a contract. * * This test is non-exhaustive, and there may be false-negatives: during the * execution of a contract's constructor, its address will be reported as * not containing a contract. * * > It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. */ function isContract(address account) internal view returns (bool) { // This method relies in extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } } // File: SafeERC20.sol /** * 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.5.0; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer(ERC20 token, address to, uint256 value) internal { callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(ERC20 token, address from, address to, uint256 value) internal { callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } function safeApprove(ERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length require((value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(ERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(ERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function callOptionalReturn(ERC20 token, bytes memory data) internal { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. // A Solidity high level call has three parts: // 1. The target address is checked to verify it contains contract code // 2. The call itself is made, and success asserted // 3. The return value is decoded, which in turn checks the size of the returned data. // solhint-disable-next-line max-line-length require(address(token).isContract(), "SafeERC20: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = address(token).call(data); require(success, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } // File: transferrable.sol /** * Transferrable - The Consumer Contract Wallet * Copyright (C) 2019 The Contract Wallet Company Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ pragma solidity ^0.5.10; /// @title SafeTransfer, allowing contract to withdraw tokens accidentally sent to itself contract Transferrable { using SafeERC20 for ERC20; /// @dev This function is used to move tokens sent accidentally to this contract method. /// @dev The owner can chose the new destination address /// @param _to is the recipient's address. /// @param _asset is the address of an ERC20 token or 0x0 for ether. /// @param _amount is the amount to be transferred in base units. function _safeTransfer(address payable _to, address _asset, uint _amount) internal { // address(0) is used to denote ETH if (_asset == address(0)) { _to.transfer(_amount); } else { ERC20(_asset).safeTransfer(_to, _amount); } } } // File: balanceable.sol /** * Balanceable - The Consumer Contract Wallet * Copyright (C) 2019 The Contract Wallet Company Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ pragma solidity ^0.5.10; /// @title Balanceable - This is a contract used to get a balance contract Balanceable { /// @dev This function is used to get a balance /// @param _address of which balance we are trying to ascertain /// @param _asset is the address of an ERC20 token or 0x0 for ether. /// @return balance associated with an address, for any token, in the wei equivalent function _balance(address _address, address _asset) internal view returns (uint) { if (_asset != address(0)) { return ERC20(_asset).balanceOf(_address); } else { return _address.balance; } } } // File: burner.sol /** * IBurner - The Consumer Contract Wallet * Copyright (C) 2019 The Contract Wallet Company Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ pragma solidity ^0.5.10; // The BurnerToken interface is the interface to a token contract which // provides the total burnable supply for the TokenHolder contract. interface IBurner { function currentSupply() external view returns (uint); } // File: ownable.sol /** * Ownable - The Consumer Contract Wallet * Copyright (C) 2019 The Contract Wallet Company Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ pragma solidity ^0.5.10; /// @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 allows for the transferOwnership operation to be made impossible /// https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ownership/Ownable.sol contract Ownable { event TransferredOwnership(address _from, address _to); event LockedOwnership(address _locked); address payable private _owner; bool private _isTransferable; /// @notice Constructor sets the original owner of the contract and whether or not it is one time transferable. constructor(address payable _account_, bool _transferable_) internal { _owner = _account_; _isTransferable = _transferable_; // Emit the LockedOwnership event if no longer transferable. if (!_isTransferable) { emit LockedOwnership(_account_); } emit TransferredOwnership(address(0), _account_); } /// @notice Reverts if called by any account other than the owner. modifier onlyOwner() { require(_isOwner(msg.sender), "sender is not an owner"); _; } /// @notice Allows the current owner to transfer control of the contract to a new address. /// @param _account address to transfer ownership to. /// @param _transferable indicates whether to keep the ownership transferable. function transferOwnership(address payable _account, bool _transferable) external onlyOwner { // Require that the ownership is transferable. require(_isTransferable, "ownership is not transferable"); // Require that the new owner is not the zero address. require(_account != address(0), "owner cannot be set to zero address"); // Set the transferable flag to the value _transferable passed in. _isTransferable = _transferable; // Emit the LockedOwnership event if no longer transferable. if (!_transferable) { emit LockedOwnership(_account); } // Emit the ownership transfer event. emit TransferredOwnership(_owner, _account); // Set the owner to the provided address. _owner = _account; } /// @notice check if the ownership is transferable. /// @return true if the ownership is transferable. function isTransferable() external view returns (bool) { return _isTransferable; } /// @notice Allows the current owner to relinquish control of the contract. /// @dev Renouncing to ownership will leave the contract without an owner and unusable. /// @dev It will not be possible to call the functions with the `onlyOwner` modifier anymore. function renounceOwnership() external onlyOwner { // Require that the ownership is transferable. require(_isTransferable, "ownership is not transferable"); // note that this could be terminal _owner = address(0); emit TransferredOwnership(_owner, address(0)); } /// @notice Find out owner address /// @return address of the owner. function owner() public view returns (address payable) { return _owner; } /// @notice Check if owner address /// @return true if sender is the owner of the contract. function _isOwner(address _address) internal view returns (bool) { return _address == _owner; } } // File: controller.sol /** * Controller - The Consumer Contract Wallet * Copyright (C) 2019 The Contract Wallet Company Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ pragma solidity ^0.5.10; /// @title The IController interface provides access to the isController and isAdmin checks. interface IController { function isController(address) external view returns (bool); function isAdmin(address) external view returns (bool); } /// @title Controller stores a list of controller addresses that can be used for authentication in other contracts. /// @notice The Controller implements a hierarchy of concepts, Owner, Admin, and the Controllers. /// @dev Owner can change the Admins /// @dev Admins and can the Controllers /// @dev Controllers are used by the application. contract Controller is IController, Ownable, Transferrable { event AddedController(address _sender, address _controller); event RemovedController(address _sender, address _controller); event AddedAdmin(address _sender, address _admin); event RemovedAdmin(address _sender, address _admin); event Claimed(address _to, address _asset, uint _amount); event Stopped(address _sender); event Started(address _sender); mapping (address => bool) private _isAdmin; uint private _adminCount; mapping (address => bool) private _isController; uint private _controllerCount; bool private _stopped; /// @notice Constructor initializes the owner with the provided address. /// @param _ownerAddress_ address of the owner. constructor(address payable _ownerAddress_) Ownable(_ownerAddress_, false) public {} /// @notice Checks if message sender is an admin. modifier onlyAdmin() { require(isAdmin(msg.sender), "sender is not an admin"); _; } /// @notice Check if Owner or Admin modifier onlyAdminOrOwner() { require(_isOwner(msg.sender) || isAdmin(msg.sender), "sender is not an admin"); _; } /// @notice Check if controller is stopped modifier notStopped() { require(!isStopped(), "controller is stopped"); _; } /// @notice Add a new admin to the list of admins. /// @param _account address to add to the list of admins. function addAdmin(address _account) external onlyOwner notStopped { _addAdmin(_account); } /// @notice Remove a admin from the list of admins. /// @param _account address to remove from the list of admins. function removeAdmin(address _account) external onlyOwner { _removeAdmin(_account); } /// @return the current number of admins. function adminCount() external view returns (uint) { return _adminCount; } /// @notice Add a new controller to the list of controllers. /// @param _account address to add to the list of controllers. function addController(address _account) external onlyAdminOrOwner notStopped { _addController(_account); } /// @notice Remove a controller from the list of controllers. /// @param _account address to remove from the list of controllers. function removeController(address _account) external onlyAdminOrOwner { _removeController(_account); } /// @notice count the Controllers /// @return the current number of controllers. function controllerCount() external view returns (uint) { return _controllerCount; } /// @notice is an address an Admin? /// @return true if the provided account is an admin. function isAdmin(address _account) public view notStopped returns (bool) { return _isAdmin[_account]; } /// @notice is an address a Controller? /// @return true if the provided account is a controller. function isController(address _account) public view notStopped returns (bool) { return _isController[_account]; } /// @notice this function can be used to see if the controller has been stopped /// @return true is the Controller has been stopped function isStopped() public view returns (bool) { return _stopped; } /// @notice Internal-only function that adds a new admin. function _addAdmin(address _account) private { require(!_isAdmin[_account], "provided account is already an admin"); require(!_isController[_account], "provided account is already a controller"); require(!_isOwner(_account), "provided account is already the owner"); require(_account != address(0), "provided account is the zero address"); _isAdmin[_account] = true; _adminCount++; emit AddedAdmin(msg.sender, _account); } /// @notice Internal-only function that removes an existing admin. function _removeAdmin(address _account) private { require(_isAdmin[_account], "provided account is not an admin"); _isAdmin[_account] = false; _adminCount--; emit RemovedAdmin(msg.sender, _account); } /// @notice Internal-only function that adds a new controller. function _addController(address _account) private { require(!_isAdmin[_account], "provided account is already an admin"); require(!_isController[_account], "provided account is already a controller"); require(!_isOwner(_account), "provided account is already the owner"); require(_account != address(0), "provided account is the zero address"); _isController[_account] = true; _controllerCount++; emit AddedController(msg.sender, _account); } /// @notice Internal-only function that removes an existing controller. function _removeController(address _account) private { require(_isController[_account], "provided account is not a controller"); _isController[_account] = false; _controllerCount--; emit RemovedController(msg.sender, _account); } /// @notice stop our controllers and admins from being useable function stop() external onlyAdminOrOwner { _stopped = true; emit Stopped(msg.sender); } /// @notice start our controller again function start() external onlyOwner { _stopped = false; emit Started(msg.sender); } //// @notice Withdraw tokens from the smart contract to the specified account. function claim(address payable _to, address _asset, uint _amount) external onlyAdmin notStopped { _safeTransfer(_to, _asset, _amount); emit Claimed(_to, _asset, _amount); } } // File: ENS.sol /** * 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.5.0; interface ENS { // Logged when the owner of a node assigns a new owner to a subnode. event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner); // Logged when the owner of a node transfers ownership to a new account. event Transfer(bytes32 indexed node, address owner); // Logged when the resolver for a node changes. event NewResolver(bytes32 indexed node, address resolver); // Logged when the TTL of a node changes event NewTTL(bytes32 indexed node, uint64 ttl); function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external; function setResolver(bytes32 node, address resolver) external; function setOwner(bytes32 node, address owner) external; function setTTL(bytes32 node, uint64 ttl) external; function owner(bytes32 node) external view returns (address); function resolver(bytes32 node) external view returns (address); function ttl(bytes32 node) external view returns (uint64); } // File: ResolverBase.sol pragma solidity ^0.5.0; contract ResolverBase { bytes4 private constant INTERFACE_META_ID = 0x01ffc9a7; function supportsInterface(bytes4 interfaceID) public pure returns(bool) { return interfaceID == INTERFACE_META_ID; } function isAuthorised(bytes32 node) internal view returns(bool); modifier authorised(bytes32 node) { require(isAuthorised(node)); _; } } // File: ABIResolver.sol pragma solidity ^0.5.0; contract ABIResolver is ResolverBase { bytes4 constant private ABI_INTERFACE_ID = 0x2203ab56; event ABIChanged(bytes32 indexed node, uint256 indexed contentType); mapping(bytes32=>mapping(uint256=>bytes)) abis; /** * Sets the ABI associated with an ENS node. * Nodes may have one ABI of each content type. To remove an ABI, set it to * the empty string. * @param node The node to update. * @param contentType The content type of the ABI * @param data The ABI data. */ function setABI(bytes32 node, uint256 contentType, bytes calldata data) external authorised(node) { // Content types must be powers of 2 require(((contentType - 1) & contentType) == 0); abis[node][contentType] = data; emit ABIChanged(node, contentType); } /** * Returns the ABI associated with an ENS node. * Defined in EIP205. * @param node The ENS node to query * @param contentTypes A bitwise OR of the ABI formats accepted by the caller. * @return contentType The content type of the return value * @return data The ABI data */ function ABI(bytes32 node, uint256 contentTypes) external view returns (uint256, bytes memory) { mapping(uint256=>bytes) storage abiset = abis[node]; for (uint256 contentType = 1; contentType <= contentTypes; contentType <<= 1) { if ((contentType & contentTypes) != 0 && abiset[contentType].length > 0) { return (contentType, abiset[contentType]); } } return (0, bytes("")); } function supportsInterface(bytes4 interfaceID) public pure returns(bool) { return interfaceID == ABI_INTERFACE_ID || super.supportsInterface(interfaceID); } } // File: AddrResolver.sol pragma solidity ^0.5.0; contract AddrResolver is ResolverBase { bytes4 constant private ADDR_INTERFACE_ID = 0x3b3b57de; event AddrChanged(bytes32 indexed node, address a); mapping(bytes32=>address) addresses; /** * Sets the address associated with an ENS node. * May only be called by the owner of that node in the ENS registry. * @param node The node to update. * @param addr The address to set. */ function setAddr(bytes32 node, address addr) external authorised(node) { addresses[node] = addr; emit AddrChanged(node, addr); } /** * Returns the address associated with an ENS node. * @param node The ENS node to query. * @return The associated address. */ function addr(bytes32 node) public view returns (address) { return addresses[node]; } function supportsInterface(bytes4 interfaceID) public pure returns(bool) { return interfaceID == ADDR_INTERFACE_ID || super.supportsInterface(interfaceID); } } // File: ContentHashResolver.sol pragma solidity ^0.5.0; contract ContentHashResolver is ResolverBase { bytes4 constant private CONTENT_HASH_INTERFACE_ID = 0xbc1c58d1; event ContenthashChanged(bytes32 indexed node, bytes hash); mapping(bytes32=>bytes) hashes; /** * Sets the contenthash associated with an ENS node. * May only be called by the owner of that node in the ENS registry. * @param node The node to update. * @param hash The contenthash to set */ function setContenthash(bytes32 node, bytes calldata hash) external authorised(node) { hashes[node] = hash; emit ContenthashChanged(node, hash); } /** * Returns the contenthash associated with an ENS node. * @param node The ENS node to query. * @return The associated contenthash. */ function contenthash(bytes32 node) external view returns (bytes memory) { return hashes[node]; } function supportsInterface(bytes4 interfaceID) public pure returns(bool) { return interfaceID == CONTENT_HASH_INTERFACE_ID || super.supportsInterface(interfaceID); } } // File: InterfaceResolver.sol pragma solidity ^0.5.0; contract InterfaceResolver is ResolverBase, AddrResolver { bytes4 constant private INTERFACE_INTERFACE_ID = bytes4(keccak256("interfaceImplementer(bytes32,bytes4)")); bytes4 private constant INTERFACE_META_ID = 0x01ffc9a7; event InterfaceChanged(bytes32 indexed node, bytes4 indexed interfaceID, address implementer); mapping(bytes32=>mapping(bytes4=>address)) interfaces; /** * Sets an interface associated with a name. * Setting the address to 0 restores the default behaviour of querying the contract at `addr()` for interface support. * @param node The node to update. * @param interfaceID The EIP 168 interface ID. * @param implementer The address of a contract that implements this interface for this node. */ function setInterface(bytes32 node, bytes4 interfaceID, address implementer) external authorised(node) { interfaces[node][interfaceID] = implementer; emit InterfaceChanged(node, interfaceID, implementer); } /** * Returns the address of a contract that implements the specified interface for this name. * If an implementer has not been set for this interfaceID and name, the resolver will query * the contract at `addr()`. If `addr()` is set, a contract exists at that address, and that * contract implements EIP168 and returns `true` for the specified interfaceID, its address * will be returned. * @param node The ENS node to query. * @param interfaceID The EIP 168 interface ID to check for. * @return The address that implements this interface, or 0 if the interface is unsupported. */ function interfaceImplementer(bytes32 node, bytes4 interfaceID) external view returns (address) { address implementer = interfaces[node][interfaceID]; if(implementer != address(0)) { return implementer; } address a = addr(node); if(a == address(0)) { return address(0); } (bool success, bytes memory returnData) = a.staticcall(abi.encodeWithSignature("supportsInterface(bytes4)", INTERFACE_META_ID)); if(!success || returnData.length < 32 || returnData[31] == 0) { // EIP 168 not supported by target return address(0); } (success, returnData) = a.staticcall(abi.encodeWithSignature("supportsInterface(bytes4)", interfaceID)); if(!success || returnData.length < 32 || returnData[31] == 0) { // Specified interface not supported by target return address(0); } return a; } function supportsInterface(bytes4 interfaceID) public pure returns(bool) { return interfaceID == INTERFACE_INTERFACE_ID || super.supportsInterface(interfaceID); } } // File: NameResolver.sol pragma solidity ^0.5.0; contract NameResolver is ResolverBase { bytes4 constant private NAME_INTERFACE_ID = 0x691f3431; event NameChanged(bytes32 indexed node, string name); mapping(bytes32=>string) names; /** * Sets the name associated with an ENS node, for reverse records. * May only be called by the owner of that node in the ENS registry. * @param node The node to update. * @param name The name to set. */ function setName(bytes32 node, string calldata name) external authorised(node) { names[node] = name; emit NameChanged(node, name); } /** * Returns the name associated with an ENS node, for reverse records. * Defined in EIP181. * @param node The ENS node to query. * @return The associated name. */ function name(bytes32 node) external view returns (string memory) { return names[node]; } function supportsInterface(bytes4 interfaceID) public pure returns(bool) { return interfaceID == NAME_INTERFACE_ID || super.supportsInterface(interfaceID); } } // File: PubkeyResolver.sol pragma solidity ^0.5.0; contract PubkeyResolver is ResolverBase { bytes4 constant private PUBKEY_INTERFACE_ID = 0xc8690233; event PubkeyChanged(bytes32 indexed node, bytes32 x, bytes32 y); struct PublicKey { bytes32 x; bytes32 y; } mapping(bytes32=>PublicKey) pubkeys; /** * Sets the SECP256k1 public key associated with an ENS node. * @param node The ENS node to query * @param x the X coordinate of the curve point for the public key. * @param y the Y coordinate of the curve point for the public key. */ function setPubkey(bytes32 node, bytes32 x, bytes32 y) external authorised(node) { pubkeys[node] = PublicKey(x, y); emit PubkeyChanged(node, x, y); } /** * Returns the SECP256k1 public key associated with an ENS node. * Defined in EIP 619. * @param node The ENS node to query * @return x, y the X and Y coordinates of the curve point for the public key. */ function pubkey(bytes32 node) external view returns (bytes32 x, bytes32 y) { return (pubkeys[node].x, pubkeys[node].y); } function supportsInterface(bytes4 interfaceID) public pure returns(bool) { return interfaceID == PUBKEY_INTERFACE_ID || super.supportsInterface(interfaceID); } } // File: TextResolver.sol pragma solidity ^0.5.0; contract TextResolver is ResolverBase { bytes4 constant private TEXT_INTERFACE_ID = 0x59d1d43c; event TextChanged(bytes32 indexed node, string indexedKey, string key); mapping(bytes32=>mapping(string=>string)) texts; /** * Sets the text data associated with an ENS node and key. * May only be called by the owner of that node in the ENS registry. * @param node The node to update. * @param key The key to set. * @param value The text data value to set. */ function setText(bytes32 node, string calldata key, string calldata value) external authorised(node) { texts[node][key] = value; emit TextChanged(node, key, key); } /** * Returns the text data associated with an ENS node and key. * @param node The ENS node to query. * @param key The text data key to query. * @return The associated text data. */ function text(bytes32 node, string calldata key) external view returns (string memory) { return texts[node][key]; } function supportsInterface(bytes4 interfaceID) public pure returns(bool) { return interfaceID == TEXT_INTERFACE_ID || super.supportsInterface(interfaceID); } } // File: PublicResolver.sol /** * 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.5.0; /** * A simple resolver anyone can use; only allows the owner of a node to set its * address. */ contract PublicResolver is ABIResolver, AddrResolver, ContentHashResolver, InterfaceResolver, NameResolver, PubkeyResolver, TextResolver { ENS ens; /** * A mapping of authorisations. An address that is authorised for a name * may make any changes to the name that the owner could, but may not update * the set of authorisations. * (node, owner, caller) => isAuthorised */ mapping(bytes32=>mapping(address=>mapping(address=>bool))) public authorisations; event AuthorisationChanged(bytes32 indexed node, address indexed owner, address indexed target, bool isAuthorised); constructor(ENS _ens) public { ens = _ens; } /** * @dev Sets or clears an authorisation. * Authorisations are specific to the caller. Any account can set an authorisation * for any name, but the authorisation that is checked will be that of the * current owner of a name. Thus, transferring a name effectively clears any * existing authorisations, and new authorisations can be set in advance of * an ownership transfer if desired. * * @param node The name to change the authorisation on. * @param target The address that is to be authorised or deauthorised. * @param isAuthorised True if the address should be authorised, or false if it should be deauthorised. */ function setAuthorisation(bytes32 node, address target, bool isAuthorised) external { authorisations[node][msg.sender][target] = isAuthorised; emit AuthorisationChanged(node, msg.sender, target, isAuthorised); } function isAuthorised(bytes32 node) internal view returns(bool) { address owner = ens.owner(node); return owner == msg.sender || authorisations[node][owner][msg.sender]; } } // File: ensResolvable.sol /** * ENSResolvable - The Consumer Contract Wallet * Copyright (C) 2019 The Contract Wallet Company Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ pragma solidity ^0.5.10; ///@title ENSResolvable - Ethereum Name Service Resolver ///@notice contract should be used to get an address for an ENS node contract ENSResolvable { /// @notice _ens is an instance of ENS ENS private _ens; /// @notice _ensRegistry points to the ENS registry smart contract. address private _ensRegistry; /// @param _ensReg_ is the ENS registry used constructor(address _ensReg_) internal { _ensRegistry = _ensReg_; _ens = ENS(_ensRegistry); } /// @notice this is used to that one can observe which ENS registry is being used function ensRegistry() external view returns (address) { return _ensRegistry; } /// @notice helper function used to get the address of a node /// @param _node of the ENS entry that needs resolving /// @return the address of the said node function _ensResolve(bytes32 _node) internal view returns (address) { return PublicResolver(_ens.resolver(_node)).addr(_node); } } // File: controllable.sol /** * Controllable - The Consumer Contract Wallet * Copyright (C) 2019 The Contract Wallet Company Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ pragma solidity ^0.5.10; /// @title Controllable implements access control functionality of the Controller found via ENS. contract Controllable is ENSResolvable { /// @dev Is the registered ENS node identifying the controller contract. bytes32 private _controllerNode; /// @notice Constructor initializes the controller contract object. /// @param _controllerNode_ is the ENS node of the Controller. constructor(bytes32 _controllerNode_) internal { _controllerNode = _controllerNode_; } /// @notice Checks if message sender is a controller. modifier onlyController() { require(_isController(msg.sender), "sender is not a controller"); _; } /// @notice Checks if message sender is an admin. modifier onlyAdmin() { require(_isAdmin(msg.sender), "sender is not an admin"); _; } /// @return the controller node registered in ENS. function controllerNode() external view returns (bytes32) { return _controllerNode; } /// @return true if the provided account is a controller. function _isController(address _account) internal view returns (bool) { return IController(_ensResolve(_controllerNode)).isController(_account); } /// @return true if the provided account is an admin. function _isAdmin(address _account) internal view returns (bool) { return IController(_ensResolve(_controllerNode)).isAdmin(_account); } } // File: bytesUtils.sol /** * BytesUtils - The Consumer Contract Wallet * Copyright (C) 2019 The Contract Wallet Company Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ pragma solidity ^0.5.10; /// @title BytesUtils provides basic byte slicing and casting functionality. library BytesUtils { using SafeMath for uint256; /// @dev This function converts to an address /// @param _bts bytes /// @param _from start position function _bytesToAddress(bytes memory _bts, uint _from) internal pure returns (address) { require(_bts.length >= _from.add(20), "slicing out of range"); bytes20 convertedAddress; uint startByte = _from.add(32); //first 32 bytes denote the array length assembly { convertedAddress := mload(add(_bts, startByte)) } return address(convertedAddress); } /// @dev This function slices bytes into bytes4 /// @param _bts some bytes /// @param _from start position function _bytesToBytes4(bytes memory _bts, uint _from) internal pure returns (bytes4) { require(_bts.length >= _from.add(4), "slicing out of range"); bytes4 slicedBytes4; uint startByte = _from.add(32); //first 32 bytes denote the array length assembly { slicedBytes4 := mload(add(_bts, startByte)) } return slicedBytes4; } /// @dev This function slices a uint /// @param _bts some bytes /// @param _from start position // credit to https://ethereum.stackexchange.com/questions/51229/how-to-convert-bytes-to-uint-in-solidity // and Nick Johnson https://ethereum.stackexchange.com/questions/4170/how-to-convert-a-uint-to-bytes-in-solidity/4177#4177 function _bytesToUint256(bytes memory _bts, uint _from) internal pure returns (uint) { require(_bts.length >= _from.add(32), "slicing out of range"); uint convertedUint256; uint startByte = _from.add(32); //first 32 bytes denote the array length assembly { convertedUint256 := mload(add(_bts, startByte)) } return convertedUint256; } } // File: strings.sol /* * 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.5.0; library strings { struct slice { uint _len; uint _ptr; } function memcpy(uint dest, uint src, uint len) private pure { // Copy word-length chunks while possible for(; len >= 32; len -= 32) { assembly { mstore(dest, mload(src)) } dest += 32; src += 32; } // Copy remaining bytes uint mask = 256 ** (32 - len) - 1; assembly { let srcpart := and(mload(src), not(mask)) let destpart := and(mload(dest), mask) mstore(dest, or(destpart, srcpart)) } } /* * @dev Returns a slice containing the entire string. * @param self The string to make a slice from. * @return A newly allocated slice containing the entire string. */ function toSlice(string memory self) internal pure returns (slice memory) { uint ptr; assembly { ptr := add(self, 0x20) } return slice(bytes(self).length, ptr); } /* * @dev Returns the length of a null-terminated bytes32 string. * @param self The value to find the length of. * @return The length of the string, from 0 to 32. */ function len(bytes32 self) internal pure returns (uint) { uint ret; if (self == 0) return 0; if (uint(self) & 0xffffffffffffffffffffffffffffffff == 0) { ret += 16; self = bytes32(uint(self) / 0x100000000000000000000000000000000); } if (uint(self) & 0xffffffffffffffff == 0) { ret += 8; self = bytes32(uint(self) / 0x10000000000000000); } if (uint(self) & 0xffffffff == 0) { ret += 4; self = bytes32(uint(self) / 0x100000000); } if (uint(self) & 0xffff == 0) { ret += 2; self = bytes32(uint(self) / 0x10000); } if (uint(self) & 0xff == 0) { ret += 1; } return 32 - ret; } /* * @dev Returns a slice containing the entire bytes32, interpreted as a * null-terminated utf-8 string. * @param self The bytes32 value to convert to a slice. * @return A new slice containing the value of the input argument up to the * first null. */ function toSliceB32(bytes32 self) internal pure returns (slice memory ret) { // Allocate space for `self` in memory, copy it there, and point ret at it assembly { let ptr := mload(0x40) mstore(0x40, add(ptr, 0x20)) mstore(ptr, self) mstore(add(ret, 0x20), ptr) } ret._len = len(self); } /* * @dev Returns a new slice containing the same data as the current slice. * @param self The slice to copy. * @return A new slice containing the same data as `self`. */ function copy(slice memory self) internal pure returns (slice memory) { return slice(self._len, self._ptr); } /* * @dev Copies a slice to a new string. * @param self The slice to copy. * @return A newly allocated string containing the slice's text. */ function toString(slice memory self) internal pure returns (string memory) { string memory ret = new string(self._len); uint retptr; assembly { retptr := add(ret, 32) } memcpy(retptr, self._ptr, self._len); return ret; } /* * @dev Returns the length in runes of the slice. Note that this operation * takes time proportional to the length of the slice; avoid using it * in loops, and call `slice.empty()` if you only need to know whether * the slice is empty or not. * @param self The slice to operate on. * @return The length of the slice in runes. */ function len(slice memory self) internal pure returns (uint l) { // Starting at ptr-31 means the LSB will be the byte we care about uint ptr = self._ptr - 31; uint end = ptr + self._len; for (l = 0; ptr < end; l++) { uint8 b; assembly { b := and(mload(ptr), 0xFF) } if (b < 0x80) { ptr += 1; } else if (b < 0xE0) { ptr += 2; } else if (b < 0xF0) { ptr += 3; } else if (b < 0xF8) { ptr += 4; } else if (b < 0xFC) { ptr += 5; } else { ptr += 6; } } } /* * @dev Returns true if the slice is empty (has a length of 0). * @param self The slice to operate on. * @return True if the slice is empty, False otherwise. */ function empty(slice memory self) internal pure returns (bool) { return self._len == 0; } /* * @dev Returns a positive number if `other` comes lexicographically after * `self`, a negative number if it comes before, or zero if the * contents of the two slices are equal. Comparison is done per-rune, * on unicode codepoints. * @param self The first slice to compare. * @param other The second slice to compare. * @return The result of the comparison. */ function compare(slice memory self, slice memory other) internal pure returns (int) { uint shortest = self._len; if (other._len < self._len) shortest = other._len; uint selfptr = self._ptr; uint otherptr = other._ptr; for (uint idx = 0; idx < shortest; idx += 32) { uint a; uint b; assembly { a := mload(selfptr) b := mload(otherptr) } if (a != b) { // Mask out irrelevant bytes and check again uint256 mask = uint256(-1); // 0xffff... if (shortest < 32) { mask = ~(2 ** (8 * (32 - shortest + idx)) - 1); } uint256 diff = (a & mask) - (b & mask); if (diff != 0) return int(diff); } selfptr += 32; otherptr += 32; } return int(self._len) - int(other._len); } /* * @dev Returns true if the two slices contain the same text. * @param self The first slice to compare. * @param self The second slice to compare. * @return True if the slices are equal, false otherwise. */ function equals(slice memory self, slice memory other) internal pure returns (bool) { return compare(self, other) == 0; } /* * @dev Extracts the first rune in the slice into `rune`, advancing the * slice to point to the next rune and returning `self`. * @param self The slice to operate on. * @param rune The slice that will contain the first rune. * @return `rune`. */ function nextRune(slice memory self, slice memory rune) internal pure returns (slice memory) { rune._ptr = self._ptr; if (self._len == 0) { rune._len = 0; return rune; } uint l; uint b; // Load the first byte of the rune into the LSBs of b assembly { b := and(mload(sub(mload(add(self, 32)), 31)), 0xFF) } if (b < 0x80) { l = 1; } else if (b < 0xE0) { l = 2; } else if (b < 0xF0) { l = 3; } else { l = 4; } // Check for truncated codepoints if (l > self._len) { rune._len = self._len; self._ptr += self._len; self._len = 0; return rune; } self._ptr += l; self._len -= l; rune._len = l; return rune; } /* * @dev Returns the first rune in the slice, advancing the slice to point * to the next rune. * @param self The slice to operate on. * @return A slice containing only the first rune from `self`. */ function nextRune(slice memory self) internal pure returns (slice memory ret) { nextRune(self, ret); } /* * @dev Returns the number of the first codepoint in the slice. * @param self The slice to operate on. * @return The number of the first codepoint in the slice. */ function ord(slice memory self) internal pure returns (uint ret) { if (self._len == 0) { return 0; } uint word; uint length; uint divisor = 2 ** 248; // Load the rune into the MSBs of b assembly { word:= mload(mload(add(self, 32))) } uint b = word / divisor; if (b < 0x80) { ret = b; length = 1; } else if (b < 0xE0) { ret = b & 0x1F; length = 2; } else if (b < 0xF0) { ret = b & 0x0F; length = 3; } else { ret = b & 0x07; length = 4; } // Check for truncated codepoints if (length > self._len) { return 0; } for (uint i = 1; i < length; i++) { divisor = divisor / 256; b = (word / divisor) & 0xFF; if (b & 0xC0 != 0x80) { // Invalid UTF-8 sequence return 0; } ret = (ret * 64) | (b & 0x3F); } return ret; } /* * @dev Returns the keccak-256 hash of the slice. * @param self The slice to hash. * @return The hash of the slice. */ function keccak(slice memory self) internal pure returns (bytes32 ret) { assembly { ret := keccak256(mload(add(self, 32)), mload(self)) } } /* * @dev Returns true if `self` starts with `needle`. * @param self The slice to operate on. * @param needle The slice to search for. * @return True if the slice starts with the provided text, false otherwise. */ function startsWith(slice memory self, slice memory needle) internal pure returns (bool) { if (self._len < needle._len) { return false; } if (self._ptr == needle._ptr) { return true; } bool equal; assembly { let length := mload(needle) let selfptr := mload(add(self, 0x20)) let needleptr := mload(add(needle, 0x20)) equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } return equal; } /* * @dev If `self` starts with `needle`, `needle` is removed from the * beginning of `self`. Otherwise, `self` is unmodified. * @param self The slice to operate on. * @param needle The slice to search for. * @return `self` */ function beyond(slice memory self, slice memory needle) internal pure returns (slice memory) { if (self._len < needle._len) { return self; } bool equal = true; if (self._ptr != needle._ptr) { assembly { let length := mload(needle) let selfptr := mload(add(self, 0x20)) let needleptr := mload(add(needle, 0x20)) equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } } if (equal) { self._len -= needle._len; self._ptr += needle._len; } return self; } /* * @dev Returns true if the slice ends with `needle`. * @param self The slice to operate on. * @param needle The slice to search for. * @return True if the slice starts with the provided text, false otherwise. */ function endsWith(slice memory self, slice memory needle) internal pure returns (bool) { if (self._len < needle._len) { return false; } uint selfptr = self._ptr + self._len - needle._len; if (selfptr == needle._ptr) { return true; } bool equal; assembly { let length := mload(needle) let needleptr := mload(add(needle, 0x20)) equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } return equal; } /* * @dev If `self` ends with `needle`, `needle` is removed from the * end of `self`. Otherwise, `self` is unmodified. * @param self The slice to operate on. * @param needle The slice to search for. * @return `self` */ function until(slice memory self, slice memory needle) internal pure returns (slice memory) { if (self._len < needle._len) { return self; } uint selfptr = self._ptr + self._len - needle._len; bool equal = true; if (selfptr != needle._ptr) { assembly { let length := mload(needle) let needleptr := mload(add(needle, 0x20)) equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } } if (equal) { self._len -= needle._len; } return self; } // Returns the memory address of the first byte of the first occurrence of // `needle` in `self`, or the first byte after `self` if not found. function findPtr(uint selflen, uint selfptr, uint needlelen, uint needleptr) private pure returns (uint) { uint ptr = selfptr; uint idx; if (needlelen <= selflen) { if (needlelen <= 32) { bytes32 mask = bytes32(~(2 ** (8 * (32 - needlelen)) - 1)); bytes32 needledata; assembly { needledata := and(mload(needleptr), mask) } uint end = selfptr + selflen - needlelen; bytes32 ptrdata; assembly { ptrdata := and(mload(ptr), mask) } while (ptrdata != needledata) { if (ptr >= end) return selfptr + selflen; ptr++; assembly { ptrdata := and(mload(ptr), mask) } } return ptr; } else { // For long needles, use hashing bytes32 hash; assembly { hash := keccak256(needleptr, needlelen) } for (idx = 0; idx <= selflen - needlelen; idx++) { bytes32 testHash; assembly { testHash := keccak256(ptr, needlelen) } if (hash == testHash) return ptr; ptr += 1; } } } return selfptr + selflen; } // Returns the memory address of the first byte after the last occurrence of // `needle` in `self`, or the address of `self` if not found. function rfindPtr(uint selflen, uint selfptr, uint needlelen, uint needleptr) private pure returns (uint) { uint ptr; if (needlelen <= selflen) { if (needlelen <= 32) { bytes32 mask = bytes32(~(2 ** (8 * (32 - needlelen)) - 1)); bytes32 needledata; assembly { needledata := and(mload(needleptr), mask) } ptr = selfptr + selflen - needlelen; bytes32 ptrdata; assembly { ptrdata := and(mload(ptr), mask) } while (ptrdata != needledata) { if (ptr <= selfptr) return selfptr; ptr--; assembly { ptrdata := and(mload(ptr), mask) } } return ptr + needlelen; } else { // For long needles, use hashing bytes32 hash; assembly { hash := keccak256(needleptr, needlelen) } ptr = selfptr + (selflen - needlelen); while (ptr >= selfptr) { bytes32 testHash; assembly { testHash := keccak256(ptr, needlelen) } if (hash == testHash) return ptr + needlelen; ptr -= 1; } } } return selfptr; } /* * @dev Modifies `self` to contain everything from the first occurrence of * `needle` to the end of the slice. `self` is set to the empty slice * if `needle` is not found. * @param self The slice to search and modify. * @param needle The text to search for. * @return `self`. */ function find(slice memory self, slice memory needle) internal pure returns (slice memory) { uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr); self._len -= ptr - self._ptr; self._ptr = ptr; return self; } /* * @dev Modifies `self` to contain the part of the string from the start of * `self` to the end of the first occurrence of `needle`. If `needle` * is not found, `self` is set to the empty slice. * @param self The slice to search and modify. * @param needle The text to search for. * @return `self`. */ function rfind(slice memory self, slice memory needle) internal pure returns (slice memory) { uint ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr); self._len = ptr - self._ptr; return self; } /* * @dev Splits the slice, setting `self` to everything after the first * occurrence of `needle`, and `token` to everything before it. If * `needle` does not occur in `self`, `self` is set to the empty slice, * and `token` is set to the entirety of `self`. * @param self The slice to split. * @param needle The text to search for in `self`. * @param token An output parameter to which the first token is written. * @return `token`. */ function split(slice memory self, slice memory needle, slice memory token) internal pure returns (slice memory) { uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr); token._ptr = self._ptr; token._len = ptr - self._ptr; if (ptr == self._ptr + self._len) { // Not found self._len = 0; } else { self._len -= token._len + needle._len; self._ptr = ptr + needle._len; } return token; } /* * @dev Splits the slice, setting `self` to everything after the first * occurrence of `needle`, and returning everything before it. If * `needle` does not occur in `self`, `self` is set to the empty slice, * and the entirety of `self` is returned. * @param self The slice to split. * @param needle The text to search for in `self`. * @return The part of `self` up to the first occurrence of `delim`. */ function split(slice memory self, slice memory needle) internal pure returns (slice memory token) { split(self, needle, token); } /* * @dev Splits the slice, setting `self` to everything before the last * occurrence of `needle`, and `token` to everything after it. If * `needle` does not occur in `self`, `self` is set to the empty slice, * and `token` is set to the entirety of `self`. * @param self The slice to split. * @param needle The text to search for in `self`. * @param token An output parameter to which the first token is written. * @return `token`. */ function rsplit(slice memory self, slice memory needle, slice memory token) internal pure returns (slice memory) { uint ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr); token._ptr = ptr; token._len = self._len - (ptr - self._ptr); if (ptr == self._ptr) { // Not found self._len = 0; } else { self._len -= token._len + needle._len; } return token; } /* * @dev Splits the slice, setting `self` to everything before the last * occurrence of `needle`, and returning everything after it. If * `needle` does not occur in `self`, `self` is set to the empty slice, * and the entirety of `self` is returned. * @param self The slice to split. * @param needle The text to search for in `self`. * @return The part of `self` after the last occurrence of `delim`. */ function rsplit(slice memory self, slice memory needle) internal pure returns (slice memory token) { rsplit(self, needle, token); } /* * @dev Counts the number of nonoverlapping occurrences of `needle` in `self`. * @param self The slice to search. * @param needle The text to search for in `self`. * @return The number of occurrences of `needle` found in `self`. */ function count(slice memory self, slice memory needle) internal pure returns (uint cnt) { uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr) + needle._len; while (ptr <= self._ptr + self._len) { cnt++; ptr = findPtr(self._len - (ptr - self._ptr), ptr, needle._len, needle._ptr) + needle._len; } } /* * @dev Returns True if `self` contains `needle`. * @param self The slice to search. * @param needle The text to search for in `self`. * @return True if `needle` is found in `self`, false otherwise. */ function contains(slice memory self, slice memory needle) internal pure returns (bool) { return rfindPtr(self._len, self._ptr, needle._len, needle._ptr) != self._ptr; } /* * @dev Returns a newly allocated string containing the concatenation of * `self` and `other`. * @param self The first slice to concatenate. * @param other The second slice to concatenate. * @return The concatenation of the two strings. */ function concat(slice memory self, slice memory other) internal pure returns (string memory) { string memory ret = new string(self._len + other._len); uint retptr; assembly { retptr := add(ret, 32) } memcpy(retptr, self._ptr, self._len); memcpy(retptr + self._len, other._ptr, other._len); return ret; } /* * @dev Joins an array of slices, using `self` as a delimiter, returning a * newly allocated string. * @param self The delimiter to use. * @param parts A list of slices to join. * @return A newly allocated string containing all the slices in `parts`, * joined with `self`. */ function join(slice memory self, slice[] memory parts) internal pure returns (string memory) { if (parts.length == 0) return ""; uint length = self._len * (parts.length - 1); for (uint i = 0; i < parts.length; i++) { length += parts[i]._len; } string memory ret = new string(length); uint retptr; assembly { retptr := add(ret, 32) } for (uint i = 0; i < parts.length; i++) { memcpy(retptr, parts[i]._ptr, parts[i]._len); retptr += parts[i]._len; if (i < parts.length - 1) { memcpy(retptr, self._ptr, self._len); retptr += self._len; } } return ret; } } // File: tokenWhitelist.sol /** * TokenWhitelist - The Consumer Contract Wallet * Copyright (C) 2019 The Contract Wallet Company Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ pragma solidity ^0.5.10; /// @title The ITokenWhitelist interface provides access to a whitelist of tokens. interface ITokenWhitelist { function getTokenInfo(address) external view returns (string memory, uint256, uint256, bool, bool, bool, uint256); function getStablecoinInfo() external view returns (string memory, uint256, uint256, bool, bool, bool, uint256); function tokenAddressArray() external view returns (address[] memory); function redeemableTokens() external view returns (address[] memory); function methodIdWhitelist(bytes4) external view returns (bool); function getERC20RecipientAndAmount(address, bytes calldata) external view returns (address, uint); function stablecoin() external view returns (address); function updateTokenRate(address, uint, uint) external; } /// @title TokenWhitelist stores a list of tokens used by the Consumer Contract Wallet, the Oracle, the TKN Holder and the TKN Licence Contract contract TokenWhitelist is ENSResolvable, Controllable, Transferrable { using strings for *; using SafeMath for uint256; using BytesUtils for bytes; event UpdatedTokenRate(address _sender, address _token, uint _rate); event UpdatedTokenLoadable(address _sender, address _token, bool _loadable); event UpdatedTokenRedeemable(address _sender, address _token, bool _redeemable); event AddedToken(address _sender, address _token, string _symbol, uint _magnitude, bool _loadable, bool _redeemable); event RemovedToken(address _sender, address _token); event AddedMethodId(bytes4 _methodId); event RemovedMethodId(bytes4 _methodId); event AddedExclusiveMethod(address _token, bytes4 _methodId); event RemovedExclusiveMethod(address _token, bytes4 _methodId); event Claimed(address _to, address _asset, uint _amount); /// @dev these are the methods whitelisted by default in executeTransaction() for protected tokens bytes4 private constant _APPROVE = 0x095ea7b3; // keccak256(approve(address,uint256)) => 0x095ea7b3 bytes4 private constant _BURN = 0x42966c68; // keccak256(burn(uint256)) => 0x42966c68 bytes4 private constant _TRANSFER= 0xa9059cbb; // keccak256(transfer(address,uint256)) => 0xa9059cbb bytes4 private constant _TRANSFER_FROM = 0x23b872dd; // keccak256(transferFrom(address,address,uint256)) => 0x23b872dd struct Token { string symbol; // Token symbol uint magnitude; // 10^decimals uint rate; // Token exchange rate in wei bool available; // Flags if the token is available or not bool loadable; // Flags if token is loadable to the TokenCard bool redeemable; // Flags if token is redeemable in the TKN Holder contract uint lastUpdate; // Time of the last rate update } mapping(address => Token) private _tokenInfoMap; // @notice specifies whitelisted methodIds for protected tokens in wallet's excuteTranaction() e.g. keccak256(transfer(address,uint256)) => 0xa9059cbb mapping(bytes4 => bool) private _methodIdWhitelist; address[] private _tokenAddressArray; /// @notice keeping track of how many redeemable tokens are in the tokenWhitelist uint private _redeemableCounter; /// @notice Address of the stablecoin. address private _stablecoin; /// @notice is registered ENS node identifying the oracle contract. bytes32 private _oracleNode; /// @notice Constructor initializes ENSResolvable, and Controllable. /// @param _ens_ is the ENS registry address. /// @param _oracleNode_ is the ENS node of the Oracle. /// @param _controllerNode_ is our Controllers node. /// @param _stablecoinAddress_ is the address of the stablecoint used by the wallet for the card load limit. constructor(address _ens_, bytes32 _oracleNode_, bytes32 _controllerNode_, address _stablecoinAddress_) ENSResolvable(_ens_) Controllable(_controllerNode_) public { _oracleNode = _oracleNode_; _stablecoin = _stablecoinAddress_; //a priori ERC20 whitelisted methods _methodIdWhitelist[_APPROVE] = true; _methodIdWhitelist[_BURN] = true; _methodIdWhitelist[_TRANSFER] = true; _methodIdWhitelist[_TRANSFER_FROM] = true; } modifier onlyAdminOrOracle() { address oracleAddress = _ensResolve(_oracleNode); require (_isAdmin(msg.sender) || msg.sender == oracleAddress, "either oracle or admin"); _; } /// @notice Add ERC20 tokens to the list of whitelisted tokens. /// @param _tokens ERC20 token contract addresses. /// @param _symbols ERC20 token names. /// @param _magnitude 10 to the power of number of decimal places used by each ERC20 token. /// @param _loadable is a bool that states whether or not a token is loadable to the TokenCard. /// @param _redeemable is a bool that states whether or not a token is redeemable in the TKN Holder Contract. /// @param _lastUpdate is a unit representing an ISO datetime e.g. 20180913153211. function addTokens(address[] calldata _tokens, bytes32[] calldata _symbols, uint[] calldata _magnitude, bool[] calldata _loadable, bool[] calldata _redeemable, uint _lastUpdate) external onlyAdmin { // Require that all parameters have the same length. require(_tokens.length == _symbols.length && _tokens.length == _magnitude.length && _tokens.length == _loadable.length && _tokens.length == _loadable.length, "parameter lengths do not match"); // Add each token to the list of supported tokens. for (uint i = 0; i < _tokens.length; i++) { // Require that the token isn't already available. require(!_tokenInfoMap[_tokens[i]].available, "token already available"); // Store the intermediate values. string memory symbol = _symbols[i].toSliceB32().toString(); // Add the token to the token list. _tokenInfoMap[_tokens[i]] = Token({ symbol : symbol, magnitude : _magnitude[i], rate : 0, available : true, loadable : _loadable[i], redeemable: _redeemable[i], lastUpdate : _lastUpdate }); // Add the token address to the address list. _tokenAddressArray.push(_tokens[i]); //if the token is redeemable increase the redeemableCounter if (_redeemable[i]){ _redeemableCounter = _redeemableCounter.add(1); } // Emit token addition event. emit AddedToken(msg.sender, _tokens[i], symbol, _magnitude[i], _loadable[i], _redeemable[i]); } } /// @notice Remove ERC20 tokens from the whitelist of tokens. /// @param _tokens ERC20 token contract addresses. function removeTokens(address[] calldata _tokens) external onlyAdmin { // Delete each token object from the list of supported tokens based on the addresses provided. for (uint i = 0; i < _tokens.length; i++) { // Store the token address. address token = _tokens[i]; //token must be available, reverts on duplicates as well require(_tokenInfoMap[token].available, "token is not available"); //if the token is redeemable decrease the redeemableCounter if (_tokenInfoMap[token].redeemable){ _redeemableCounter = _redeemableCounter.sub(1); } // Delete the token object. delete _tokenInfoMap[token]; // Remove the token address from the address list. for (uint j = 0; j < _tokenAddressArray.length.sub(1); j++) { if (_tokenAddressArray[j] == token) { _tokenAddressArray[j] = _tokenAddressArray[_tokenAddressArray.length.sub(1)]; break; } } _tokenAddressArray.length--; // Emit token removal event. emit RemovedToken(msg.sender, token); } } /// @notice based on the method it returns the recipient address and amount/value, ERC20 specific. /// @param _data is the transaction payload. function getERC20RecipientAndAmount(address _token, bytes calldata _data) external view returns (address, uint) { // Require that there exist enough bytes for encoding at least a method signature + data in the transaction payload: // 4 (signature) + 32(address or uint256) require(_data.length >= 4 + 32, "not enough method-encoding bytes"); // Get the method signature bytes4 signature = _data._bytesToBytes4(0); // Check if method Id is supported require(isERC20MethodSupported(_token, signature), "unsupported method"); // returns the recipient's address and amount is the value to be transferred if (signature == _BURN) { // 4 (signature) + 32(uint256) return (_token, _data._bytesToUint256(4)); } else if (signature == _TRANSFER_FROM) { // 4 (signature) + 32(address) + 32(address) + 32(uint256) require(_data.length >= 4 + 32 + 32 + 32, "not enough data for transferFrom"); return ( _data._bytesToAddress(4 + 32 + 12), _data._bytesToUint256(4 + 32 + 32)); } else { //transfer or approve // 4 (signature) + 32(address) + 32(uint) require(_data.length >= 4 + 32 + 32, "not enough data for transfer/appprove"); return (_data._bytesToAddress(4 + 12), _data._bytesToUint256(4 + 32)); } } /// @notice Toggles whether or not a token is loadable or not. function setTokenLoadable(address _token, bool _loadable) external onlyAdmin { // Require that the token exists. require(_tokenInfoMap[_token].available, "token is not available"); // this sets the loadable flag to the value passed in _tokenInfoMap[_token].loadable = _loadable; emit UpdatedTokenLoadable(msg.sender, _token, _loadable); } /// @notice Toggles whether or not a token is redeemable or not. function setTokenRedeemable(address _token, bool _redeemable) external onlyAdmin { // Require that the token exists. require(_tokenInfoMap[_token].available, "token is not available"); // this sets the redeemable flag to the value passed in _tokenInfoMap[_token].redeemable = _redeemable; emit UpdatedTokenRedeemable(msg.sender, _token, _redeemable); } /// @notice Update ERC20 token exchange rate. /// @param _token ERC20 token contract address. /// @param _rate ERC20 token exchange rate in wei. /// @param _updateDate date for the token updates. This will be compared to when oracle updates are received. function updateTokenRate(address _token, uint _rate, uint _updateDate) external onlyAdminOrOracle { // Require that the token exists. require(_tokenInfoMap[_token].available, "token is not available"); // Update the token's rate. _tokenInfoMap[_token].rate = _rate; // Update the token's last update timestamp. _tokenInfoMap[_token].lastUpdate = _updateDate; // Emit the rate update event. emit UpdatedTokenRate(msg.sender, _token, _rate); } //// @notice Withdraw tokens from the smart contract to the specified account. function claim(address payable _to, address _asset, uint _amount) external onlyAdmin { _safeTransfer(_to, _asset, _amount); emit Claimed(_to, _asset, _amount); } /// @notice This returns all of the fields for a given token. /// @param _a is the address of a given token. /// @return string of the token's symbol. /// @return uint of the token's magnitude. /// @return uint of the token's exchange rate to ETH. /// @return bool whether the token is available. /// @return bool whether the token is loadable to the TokenCard. /// @return bool whether the token is redeemable to the TKN Holder Contract. /// @return uint of the lastUpdated time of the token's exchange rate. function getTokenInfo(address _a) external view returns (string memory, uint256, uint256, bool, bool, bool, uint256) { Token storage tokenInfo = _tokenInfoMap[_a]; return (tokenInfo.symbol, tokenInfo.magnitude, tokenInfo.rate, tokenInfo.available, tokenInfo.loadable, tokenInfo.redeemable, tokenInfo.lastUpdate); } /// @notice This returns all of the fields for our StableCoin. /// @return string of the token's symbol. /// @return uint of the token's magnitude. /// @return uint of the token's exchange rate to ETH. /// @return bool whether the token is available. /// @return bool whether the token is loadable to the TokenCard. /// @return bool whether the token is redeemable to the TKN Holder Contract. /// @return uint of the lastUpdated time of the token's exchange rate. function getStablecoinInfo() external view returns (string memory, uint256, uint256, bool, bool, bool, uint256) { Token storage stablecoinInfo = _tokenInfoMap[_stablecoin]; return (stablecoinInfo.symbol, stablecoinInfo.magnitude, stablecoinInfo.rate, stablecoinInfo.available, stablecoinInfo.loadable, stablecoinInfo.redeemable, stablecoinInfo.lastUpdate); } /// @notice This returns an array of all whitelisted token addresses. /// @return address[] of whitelisted tokens. function tokenAddressArray() external view returns (address[] memory) { return _tokenAddressArray; } /// @notice This returns an array of all redeemable token addresses. /// @return address[] of redeemable tokens. function redeemableTokens() external view returns (address[] memory) { address[] memory redeemableAddresses = new address[](_redeemableCounter); uint redeemableIndex = 0; for (uint i = 0; i < _tokenAddressArray.length; i++) { address token = _tokenAddressArray[i]; if (_tokenInfoMap[token].redeemable){ redeemableAddresses[redeemableIndex] = token; redeemableIndex += 1; } } return redeemableAddresses; } /// @notice This returns true if a method Id is supported for the specific token. /// @return true if _methodId is supported in general or just for the specific token. function isERC20MethodSupported(address _token, bytes4 _methodId) public view returns (bool) { require(_tokenInfoMap[_token].available, "non-existing token"); return (_methodIdWhitelist[_methodId]); } /// @notice This returns true if the method is supported for all protected tokens. /// @return true if _methodId is in the method whitelist. function isERC20MethodWhitelisted(bytes4 _methodId) external view returns (bool) { return (_methodIdWhitelist[_methodId]); } /// @notice This returns the number of redeemable tokens. /// @return current # of redeemables. function redeemableCounter() external view returns (uint) { return _redeemableCounter; } /// @notice This returns the address of our stablecoin of choice. /// @return the address of the stablecoin contract. function stablecoin() external view returns (address) { return _stablecoin; } /// @notice this returns the node hash of our Oracle. /// @return the oracle node registered in ENS. function oracleNode() external view returns (bytes32) { return _oracleNode; } } // File: tokenWhitelistable.sol /** * TokenWhitelistable - The Consumer Contract Wallet * Copyright (C) 2019 The Contract Wallet Company Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ pragma solidity ^0.5.10; /// @title TokenWhitelistable implements access to the TokenWhitelist located behind ENS. contract TokenWhitelistable is ENSResolvable { /// @notice Is the registered ENS node identifying the tokenWhitelist contract bytes32 private _tokenWhitelistNode; /// @notice Constructor initializes the TokenWhitelistable object. /// @param _tokenWhitelistNode_ is the ENS node of the TokenWhitelist. constructor(bytes32 _tokenWhitelistNode_) internal { _tokenWhitelistNode = _tokenWhitelistNode_; } /// @notice This shows what TokenWhitelist is being used /// @return TokenWhitelist's node registered in ENS. function tokenWhitelistNode() external view returns (bytes32) { return _tokenWhitelistNode; } /// @notice This returns all of the fields for a given token. /// @param _a is the address of a given token. /// @return string of the token's symbol. /// @return uint of the token's magnitude. /// @return uint of the token's exchange rate to ETH. /// @return bool whether the token is available. /// @return bool whether the token is loadable to the TokenCard. /// @return bool whether the token is redeemable to the TKN Holder Contract. /// @return uint of the lastUpdated time of the token's exchange rate. function _getTokenInfo(address _a) internal view returns (string memory, uint256, uint256, bool, bool, bool, uint256) { return ITokenWhitelist(_ensResolve(_tokenWhitelistNode)).getTokenInfo(_a); } /// @notice This returns all of the fields for our stablecoin token. /// @return string of the token's symbol. /// @return uint of the token's magnitude. /// @return uint of the token's exchange rate to ETH. /// @return bool whether the token is available. /// @return bool whether the token is loadable to the TokenCard. /// @return bool whether the token is redeemable to the TKN Holder Contract. /// @return uint of the lastUpdated time of the token's exchange rate. function _getStablecoinInfo() internal view returns (string memory, uint256, uint256, bool, bool, bool, uint256) { return ITokenWhitelist(_ensResolve(_tokenWhitelistNode)).getStablecoinInfo(); } /// @notice This returns an array of our whitelisted addresses. /// @return address[] of our whitelisted tokens. function _tokenAddressArray() internal view returns (address[] memory) { return ITokenWhitelist(_ensResolve(_tokenWhitelistNode)).tokenAddressArray(); } /// @notice This returns an array of all redeemable token addresses. /// @return address[] of redeemable tokens. function _redeemableTokens() internal view returns (address[] memory) { return ITokenWhitelist(_ensResolve(_tokenWhitelistNode)).redeemableTokens(); } /// @notice Update ERC20 token exchange rate. /// @param _token ERC20 token contract address. /// @param _rate ERC20 token exchange rate in wei. /// @param _updateDate date for the token updates. This will be compared to when oracle updates are received. function _updateTokenRate(address _token, uint _rate, uint _updateDate) internal { ITokenWhitelist(_ensResolve(_tokenWhitelistNode)).updateTokenRate(_token, _rate, _updateDate); } /// @notice based on the method it returns the recipient address and amount/value, ERC20 specific. /// @param _data is the transaction payload. function _getERC20RecipientAndAmount(address _destination, bytes memory _data) internal view returns (address, uint) { return ITokenWhitelist(_ensResolve(_tokenWhitelistNode)).getERC20RecipientAndAmount(_destination, _data); } /// @notice Checks whether a token is available. /// @return bool available or not. function _isTokenAvailable(address _a) internal view returns (bool) { ( , , , bool available, , , ) = _getTokenInfo(_a); return available; } /// @notice Checks whether a token is redeemable. /// @return bool redeemable or not. function _isTokenRedeemable(address _a) internal view returns (bool) { ( , , , , , bool redeemable, ) = _getTokenInfo(_a); return redeemable; } /// @notice Checks whether a token is loadable. /// @return bool loadable or not. function _isTokenLoadable(address _a) internal view returns (bool) { ( , , , , bool loadable, , ) = _getTokenInfo(_a); return loadable; } /// @notice This gets the address of the stablecoin. /// @return the address of the stablecoin contract. function _stablecoin() internal view returns (address) { return ITokenWhitelist(_ensResolve(_tokenWhitelistNode)).stablecoin(); } } // File: holder.sol /** * Holder (aka Asset Contract) - The Consumer Contract Wallet * Copyright (C) 2019 The Contract Wallet Company Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ pragma solidity ^0.5.10; /// @title Holder - The TKN Asset Contract /// @notice When the TKN contract calls the burn method, a share of the tokens held by this contract are disbursed to the burner. contract Holder is Balanceable, ENSResolvable, Controllable, Transferrable, TokenWhitelistable { using SafeMath for uint256; event Received(address _from, uint _amount); event CashAndBurned(address _to, address _asset, uint _amount); event Claimed(address _to, address _asset, uint _amount); /// @dev Check if the sender is the burner contract modifier onlyBurner() { require (msg.sender == _burner, "burner contract is not the sender"); _; } // Burner token which can be burned to redeem shares. address private _burner; /// @notice Constructor initializes the holder contract. /// @param _burnerContract_ is the address of the token contract TKN with burning functionality. /// @param _ens_ is the address of the ENS registry. /// @param _tokenWhitelistNode_ is the ENS node of the Token whitelist. /// @param _controllerNode_ is the ENS node of the Controller constructor (address _burnerContract_, address _ens_, bytes32 _tokenWhitelistNode_, bytes32 _controllerNode_) ENSResolvable(_ens_) Controllable(_controllerNode_) TokenWhitelistable(_tokenWhitelistNode_) public { _burner = _burnerContract_; } /// @notice Ether may be sent from anywhere. function() external payable { emit Received(msg.sender, msg.value); } /// @notice Burn handles disbursing a share of tokens in this contract to a given address. /// @param _to The address to disburse to /// @param _amount The amount of TKN that will be burned if this succeeds function burn(address payable _to, uint _amount) external onlyBurner returns (bool) { if (_amount == 0) { return true; } // The burner token deducts from the supply before calling. uint supply = IBurner(_burner).currentSupply().add(_amount); address[] memory redeemableAddresses = _redeemableTokens(); for (uint i = 0; i < redeemableAddresses.length; i++) { uint redeemableBalance = _balance(address(this), redeemableAddresses[i]); if (redeemableBalance > 0) { uint redeemableAmount = redeemableBalance.mul(_amount).div(supply); _safeTransfer(_to, redeemableAddresses[i], redeemableAmount); emit CashAndBurned(_to, redeemableAddresses[i], redeemableAmount); } } return true; } /// @notice This allows for the admin to reclaim the non-redeemableTokens /// @param _to this is the address which the reclaimed tokens will be sent to /// @param _nonRedeemableAddresses this is the array of tokens to be claimed function nonRedeemableTokenClaim(address payable _to, address[] calldata _nonRedeemableAddresses) external onlyAdmin returns (bool) { for (uint i = 0; i < _nonRedeemableAddresses.length; i++) { //revert if token is redeemable require(!_isTokenRedeemable(_nonRedeemableAddresses[i]), "redeemables cannot be claimed"); uint claimBalance = _balance(address(this), _nonRedeemableAddresses[i]); if (claimBalance > 0) { _safeTransfer(_to, _nonRedeemableAddresses[i], claimBalance); emit Claimed(_to, _nonRedeemableAddresses[i], claimBalance); } } return true; } /// @notice Returned the address of the burner contract /// @return the TKN address function burner() external view returns (address) { return _burner; } }
File 3 of 3: MultiSigWalletWithDailyLimit
pragma solidity ^0.4.15; /// @title Multisignature wallet - Allows multiple parties to agree on transactions before execution. /// @author Stefan George - <[email protected]> contract MultiSigWallet { /* * Events */ event Confirmation(address indexed sender, uint indexed transactionId); event Revocation(address indexed sender, uint indexed transactionId); event Submission(uint indexed transactionId); event Execution(uint indexed transactionId); event ExecutionFailure(uint indexed transactionId); event Deposit(address indexed sender, uint value); event OwnerAddition(address indexed owner); event OwnerRemoval(address indexed owner); event RequirementChange(uint required); /* * Constants */ uint constant public MAX_OWNER_COUNT = 50; /* * Storage */ mapping (uint => Transaction) public transactions; mapping (uint => mapping (address => bool)) public confirmations; mapping (address => bool) public isOwner; address[] public owners; uint public required; uint public transactionCount; struct Transaction { address destination; uint value; bytes data; bool executed; } /* * Modifiers */ modifier onlyWallet() { require(msg.sender == address(this)); _; } modifier ownerDoesNotExist(address owner) { require(!isOwner[owner]); _; } modifier ownerExists(address owner) { require(isOwner[owner]); _; } modifier transactionExists(uint transactionId) { require(transactions[transactionId].destination != 0); _; } modifier confirmed(uint transactionId, address owner) { require(confirmations[transactionId][owner]); _; } modifier notConfirmed(uint transactionId, address owner) { require(!confirmations[transactionId][owner]); _; } modifier notExecuted(uint transactionId) { require(!transactions[transactionId].executed); _; } modifier notNull(address _address) { require(_address != 0); _; } modifier validRequirement(uint ownerCount, uint _required) { require(ownerCount <= MAX_OWNER_COUNT && _required <= ownerCount && _required != 0 && ownerCount != 0); _; } /// @dev Fallback function allows to deposit ether. function() payable { if (msg.value > 0) Deposit(msg.sender, msg.value); } /* * Public functions */ /// @dev Contract constructor sets initial owners and required number of confirmations. /// @param _owners List of initial owners. /// @param _required Number of required confirmations. function MultiSigWallet(address[] _owners, uint _required) public validRequirement(_owners.length, _required) { for (uint i=0; i<_owners.length; i++) { require(!isOwner[_owners[i]] && _owners[i] != 0); isOwner[_owners[i]] = true; } owners = _owners; required = _required; } /// @dev Allows to add a new owner. Transaction has to be sent by wallet. /// @param owner Address of new owner. function addOwner(address owner) public onlyWallet ownerDoesNotExist(owner) notNull(owner) validRequirement(owners.length + 1, required) { isOwner[owner] = true; owners.push(owner); OwnerAddition(owner); } /// @dev Allows to remove an owner. Transaction has to be sent by wallet. /// @param owner Address of owner. function removeOwner(address owner) public onlyWallet ownerExists(owner) { isOwner[owner] = false; for (uint i=0; i<owners.length - 1; i++) if (owners[i] == owner) { owners[i] = owners[owners.length - 1]; break; } owners.length -= 1; if (required > owners.length) changeRequirement(owners.length); OwnerRemoval(owner); } /// @dev Allows to replace an owner with a new owner. Transaction has to be sent by wallet. /// @param owner Address of owner to be replaced. /// @param newOwner Address of new owner. function replaceOwner(address owner, address newOwner) public onlyWallet ownerExists(owner) ownerDoesNotExist(newOwner) { for (uint i=0; i<owners.length; i++) if (owners[i] == owner) { owners[i] = newOwner; break; } isOwner[owner] = false; isOwner[newOwner] = true; OwnerRemoval(owner); OwnerAddition(newOwner); } /// @dev Allows to change the number of required confirmations. Transaction has to be sent by wallet. /// @param _required Number of required confirmations. function changeRequirement(uint _required) public onlyWallet validRequirement(owners.length, _required) { required = _required; RequirementChange(_required); } /// @dev Allows an owner to submit and confirm a transaction. /// @param destination Transaction target address. /// @param value Transaction ether value. /// @param data Transaction data payload. /// @return Returns transaction ID. function submitTransaction(address destination, uint value, bytes data) public returns (uint transactionId) { transactionId = addTransaction(destination, value, data); confirmTransaction(transactionId); } /// @dev Allows an owner to confirm a transaction. /// @param transactionId Transaction ID. function confirmTransaction(uint transactionId) public ownerExists(msg.sender) transactionExists(transactionId) notConfirmed(transactionId, msg.sender) { confirmations[transactionId][msg.sender] = true; Confirmation(msg.sender, transactionId); executeTransaction(transactionId); } /// @dev Allows an owner to revoke a confirmation for a transaction. /// @param transactionId Transaction ID. function revokeConfirmation(uint transactionId) public ownerExists(msg.sender) confirmed(transactionId, msg.sender) notExecuted(transactionId) { confirmations[transactionId][msg.sender] = false; Revocation(msg.sender, transactionId); } /// @dev Allows anyone to execute a confirmed transaction. /// @param transactionId Transaction ID. function executeTransaction(uint transactionId) public ownerExists(msg.sender) confirmed(transactionId, msg.sender) notExecuted(transactionId) { if (isConfirmed(transactionId)) { Transaction storage txn = transactions[transactionId]; txn.executed = true; if (external_call(txn.destination, txn.value, txn.data.length, txn.data)) Execution(transactionId); else { ExecutionFailure(transactionId); txn.executed = false; } } } // 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 external_call(address destination, uint value, uint dataLength, bytes data) internal 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 Returns the confirmation status of a transaction. /// @param transactionId Transaction ID. /// @return Confirmation status. function isConfirmed(uint transactionId) public constant returns (bool) { uint count = 0; for (uint i=0; i<owners.length; i++) { if (confirmations[transactionId][owners[i]]) count += 1; if (count == required) return true; } } /* * Internal functions */ /// @dev Adds a new transaction to the transaction mapping, if transaction does not exist yet. /// @param destination Transaction target address. /// @param value Transaction ether value. /// @param data Transaction data payload. /// @return Returns transaction ID. function addTransaction(address destination, uint value, bytes data) internal notNull(destination) returns (uint transactionId) { transactionId = transactionCount; transactions[transactionId] = Transaction({ destination: destination, value: value, data: data, executed: false }); transactionCount += 1; Submission(transactionId); } /* * Web3 call functions */ /// @dev Returns number of confirmations of a transaction. /// @param transactionId Transaction ID. /// @return Number of confirmations. function getConfirmationCount(uint transactionId) public constant returns (uint count) { for (uint i=0; i<owners.length; i++) if (confirmations[transactionId][owners[i]]) count += 1; } /// @dev Returns total number of transactions after filers are applied. /// @param pending Include pending transactions. /// @param executed Include executed transactions. /// @return Total number of transactions after filters are applied. function getTransactionCount(bool pending, bool executed) public constant returns (uint count) { for (uint i=0; i<transactionCount; i++) if ( pending && !transactions[i].executed || executed && transactions[i].executed) count += 1; } /// @dev Returns list of owners. /// @return List of owner addresses. function getOwners() public constant returns (address[]) { return owners; } /// @dev Returns array with owner addresses, which confirmed transaction. /// @param transactionId Transaction ID. /// @return Returns array of owner addresses. function getConfirmations(uint transactionId) public constant returns (address[] _confirmations) { address[] memory confirmationsTemp = new address[](owners.length); uint count = 0; uint i; for (i=0; i<owners.length; i++) if (confirmations[transactionId][owners[i]]) { confirmationsTemp[count] = owners[i]; count += 1; } _confirmations = new address[](count); for (i=0; i<count; i++) _confirmations[i] = confirmationsTemp[i]; } /// @dev Returns list of transaction IDs in defined range. /// @param from Index start position of transaction array. /// @param to Index end position of transaction array. /// @param pending Include pending transactions. /// @param executed Include executed transactions. /// @return Returns array of transaction IDs. function getTransactionIds(uint from, uint to, bool pending, bool executed) public constant returns (uint[] _transactionIds) { uint[] memory transactionIdsTemp = new uint[](transactionCount); uint count = 0; uint i; for (i=0; i<transactionCount; i++) if ( pending && !transactions[i].executed || executed && transactions[i].executed) { transactionIdsTemp[count] = i; count += 1; } _transactionIds = new uint[](to - from); for (i=from; i<to; i++) _transactionIds[i - from] = transactionIdsTemp[i]; } } /// @title Multisignature wallet with daily limit - Allows an owner to withdraw a daily limit without multisig. /// @author Stefan George - <[email protected]> contract MultiSigWalletWithDailyLimit is MultiSigWallet { /* * Events */ event DailyLimitChange(uint dailyLimit); /* * Storage */ uint public dailyLimit; uint public lastDay; uint public spentToday; /* * Public functions */ /// @dev Contract constructor sets initial owners, required number of confirmations and daily withdraw limit. /// @param _owners List of initial owners. /// @param _required Number of required confirmations. /// @param _dailyLimit Amount in wei, which can be withdrawn without confirmations on a daily basis. function MultiSigWalletWithDailyLimit(address[] _owners, uint _required, uint _dailyLimit) public MultiSigWallet(_owners, _required) { dailyLimit = _dailyLimit; } /// @dev Allows to change the daily limit. Transaction has to be sent by wallet. /// @param _dailyLimit Amount in wei. function changeDailyLimit(uint _dailyLimit) public onlyWallet { dailyLimit = _dailyLimit; DailyLimitChange(_dailyLimit); } /// @dev Allows anyone to execute a confirmed transaction or ether withdraws until daily limit is reached. /// @param transactionId Transaction ID. function executeTransaction(uint transactionId) public ownerExists(msg.sender) confirmed(transactionId, msg.sender) notExecuted(transactionId) { Transaction storage txn = transactions[transactionId]; bool _confirmed = isConfirmed(transactionId); if (_confirmed || txn.data.length == 0 && isUnderLimit(txn.value)) { txn.executed = true; if (!_confirmed) spentToday += txn.value; if (external_call(txn.destination, txn.value, txn.data.length, txn.data)) Execution(transactionId); else { ExecutionFailure(transactionId); txn.executed = false; if (!_confirmed) spentToday -= txn.value; } } } /* * Internal functions */ /// @dev Returns if amount is within daily limit and resets spentToday after one day. /// @param amount Amount to withdraw. /// @return Returns if amount is under daily limit. function isUnderLimit(uint amount) internal returns (bool) { if (now > lastDay + 24 hours) { lastDay = now; spentToday = 0; } if (spentToday + amount > dailyLimit || spentToday + amount < spentToday) return false; return true; } /* * Web3 call functions */ /// @dev Returns maximum withdraw amount. /// @return Returns amount. function calcMaxWithdraw() public constant returns (uint) { if (now > lastDay + 24 hours) return dailyLimit; if (dailyLimit < spentToday) return 0; return dailyLimit - spentToday; } }