Transaction Hash:
Block:
12694823 at Jun-24-2021 04:45:19 AM +UTC
Transaction Fee:
0.000338892 ETH
$0.85
Gas Used:
56,482 Gas / 6 Gwei
Emitted Events:
109 |
ERC20Proxy.Transfer( _from=[Sender] 0x4629c3acc568be227d19dc767f48bd05882d56ed, _to=0x419CeDd4c1790B90887d135eA505a2104Ca76D3E, _value=507500 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x04668Ec2...D451c8F7F
Miner
| (zhizhu.top) | 722.069927773344514756 Eth | 722.070266665344514756 Eth | 0.000338892 | |
0x4629C3ac...5882d56Ed |
0.112259343 Eth
Nonce: 9
|
0.111920451 Eth
Nonce: 10
| 0.000338892 | ||
0xc42B14e4...af35e68a0 |
Execution Trace
ERC20Proxy.transfer( _to=0x419CeDd4c1790B90887d135eA505a2104Ca76D3E, _value=507500 ) => ( success=True )

ERC20Impl.transferWithSender( _sender=0x4629C3acC568Be227D19dC767F48Bd05882d56Ed, _to=0x419CeDd4c1790B90887d135eA505a2104Ca76D3E, _value=507500 ) => ( success=True )
-
ERC20Store.balances( 0x4629C3acC568Be227D19dC767F48Bd05882d56Ed ) => ( 507500 )
-
ERC20Store.setBalance( _owner=0x4629C3acC568Be227D19dC767F48Bd05882d56Ed, _newBalance=0 )
-
ERC20Store.addBalance( _owner=0x419CeDd4c1790B90887d135eA505a2104Ca76D3E, _balanceIncrease=507500 )
-
ERC20Proxy.emitTransfer( _from=0x4629C3acC568Be227D19dC767F48Bd05882d56Ed, _to=0x419CeDd4c1790B90887d135eA505a2104Ca76D3E, _value=507500 )
-
transfer[ERC20Proxy (ln:370)]
transferWithSender[ERC20Proxy (ln:371)]
File 1 of 3: ERC20Proxy
File 2 of 3: ERC20Impl
File 3 of 3: ERC20Store
pragma solidity ^0.4.21; /** @title A contract for generating unique identifiers * * @notice A contract that provides a identifier generation scheme, * guaranteeing uniqueness across all contracts that inherit from it, * as well as unpredictability of future identifiers. * * @dev This contract is intended to be inherited by any contract that * implements the callback software pattern for cooperative custodianship. * * @author Gemini Trust Company, LLC */ contract LockRequestable { // MEMBERS /// @notice the count of all invocations of `generateLockId`. uint256 public lockRequestCount; // CONSTRUCTOR function LockRequestable() public { lockRequestCount = 0; } // FUNCTIONS /** @notice Returns a fresh unique identifier. * * @dev the generation scheme uses three components. * First, the blockhash of the previous block. * Second, the deployed address. * Third, the next value of the counter. * This ensure that identifiers are unique across all contracts * following this scheme, and that future identifiers are * unpredictable. * * @return a 32-byte unique identifier. */ function generateLockId() internal returns (bytes32 lockId) { return keccak256(block.blockhash(block.number - 1), address(this), ++lockRequestCount); } } /** @title A contract to inherit upgradeable custodianship. * * @notice A contract that provides re-usable code for upgradeable * custodianship. That custodian may be an account or another contract. * * @dev This contract is intended to be inherited by any contract * requiring a custodian to control some aspect of its functionality. * This contract provides the mechanism for that custodianship to be * passed from one custodian to the next. * * @author Gemini Trust Company, LLC */ contract CustodianUpgradeable is LockRequestable { // TYPES /// @dev The struct type for pending custodian changes. struct CustodianChangeRequest { address proposedNew; } // MEMBERS /// @dev The address of the account or contract that acts as the custodian. address public custodian; /// @dev The map of lock ids to pending custodian changes. mapping (bytes32 => CustodianChangeRequest) public custodianChangeReqs; // CONSTRUCTOR function CustodianUpgradeable( address _custodian ) LockRequestable() public { custodian = _custodian; } // MODIFIERS modifier onlyCustodian { require(msg.sender == custodian); _; } // PUBLIC FUNCTIONS // (UPGRADE) /** @notice Requests a change of the custodian associated with this contract. * * @dev Returns a unique lock id associated with the request. * Anyone can call this function, but confirming the request is authorized * by the custodian. * * @param _proposedCustodian The address of the new custodian. * @return lockId A unique identifier for this request. */ function requestCustodianChange(address _proposedCustodian) public returns (bytes32 lockId) { require(_proposedCustodian != address(0)); lockId = generateLockId(); custodianChangeReqs[lockId] = CustodianChangeRequest({ proposedNew: _proposedCustodian }); emit CustodianChangeRequested(lockId, msg.sender, _proposedCustodian); } /** @notice Confirms a pending change of the custodian associated with this contract. * * @dev When called by the current custodian with a lock id associated with a * pending custodian change, the `address custodian` member will be updated with the * requested address. * * @param _lockId The identifier of a pending change request. */ function confirmCustodianChange(bytes32 _lockId) public onlyCustodian { custodian = getCustodianChangeReq(_lockId); delete custodianChangeReqs[_lockId]; emit CustodianChangeConfirmed(_lockId, custodian); } // PRIVATE FUNCTIONS function getCustodianChangeReq(bytes32 _lockId) private view returns (address _proposedNew) { CustodianChangeRequest storage changeRequest = custodianChangeReqs[_lockId]; // reject ‘null’ results from the map lookup // this can only be the case if an unknown `_lockId` is received require(changeRequest.proposedNew != 0); return changeRequest.proposedNew; } /// @dev Emitted by successful `requestCustodianChange` calls. event CustodianChangeRequested( bytes32 _lockId, address _msgSender, address _proposedCustodian ); /// @dev Emitted by successful `confirmCustodianChange` calls. event CustodianChangeConfirmed(bytes32 _lockId, address _newCustodian); } /** @title A contract to inherit upgradeable token implementations. * * @notice A contract that provides re-usable code for upgradeable * token implementations. It itself inherits from `CustodianUpgradable` * as the upgrade process is controlled by the custodian. * * @dev This contract is intended to be inherited by any contract * requiring a reference to the active token implementation, either * to delegate calls to it, or authorize calls from it. This contract * provides the mechanism for that implementation to be be replaced, * which constitutes an implementation upgrade. * * @author Gemini Trust Company, LLC */ contract ERC20ImplUpgradeable is CustodianUpgradeable { // TYPES /// @dev The struct type for pending implementation changes. struct ImplChangeRequest { address proposedNew; } // MEMBERS // @dev The reference to the active token implementation. ERC20Impl public erc20Impl; /// @dev The map of lock ids to pending implementation changes. mapping (bytes32 => ImplChangeRequest) public implChangeReqs; // CONSTRUCTOR function ERC20ImplUpgradeable(address _custodian) CustodianUpgradeable(_custodian) public { erc20Impl = ERC20Impl(0x0); } // MODIFIERS modifier onlyImpl { require(msg.sender == address(erc20Impl)); _; } // PUBLIC FUNCTIONS // (UPGRADE) /** @notice Requests a change of the active implementation associated * with this contract. * * @dev Returns a unique lock id associated with the request. * Anyone can call this function, but confirming the request is authorized * by the custodian. * * @param _proposedImpl The address of the new active implementation. * @return lockId A unique identifier for this request. */ function requestImplChange(address _proposedImpl) public returns (bytes32 lockId) { require(_proposedImpl != address(0)); lockId = generateLockId(); implChangeReqs[lockId] = ImplChangeRequest({ proposedNew: _proposedImpl }); emit ImplChangeRequested(lockId, msg.sender, _proposedImpl); } /** @notice Confirms a pending change of the active implementation * associated with this contract. * * @dev When called by the custodian with a lock id associated with a * pending change, the `ERC20Impl erc20Impl` member will be updated * with the requested address. * * @param _lockId The identifier of a pending change request. */ function confirmImplChange(bytes32 _lockId) public onlyCustodian { erc20Impl = getImplChangeReq(_lockId); delete implChangeReqs[_lockId]; emit ImplChangeConfirmed(_lockId, address(erc20Impl)); } // PRIVATE FUNCTIONS function getImplChangeReq(bytes32 _lockId) private view returns (ERC20Impl _proposedNew) { ImplChangeRequest storage changeRequest = implChangeReqs[_lockId]; // reject ‘null’ results from the map lookup // this can only be the case if an unknown `_lockId` is received require(changeRequest.proposedNew != address(0)); return ERC20Impl(changeRequest.proposedNew); } /// @dev Emitted by successful `requestImplChange` calls. event ImplChangeRequested( bytes32 _lockId, address _msgSender, address _proposedImpl ); /// @dev Emitted by successful `confirmImplChange` calls. event ImplChangeConfirmed(bytes32 _lockId, address _newImpl); } contract ERC20Interface { // METHODS // NOTE: // public getter functions are not currently recognised as an // implementation of the matching abstract function by the compiler. // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#name // function name() public view returns (string); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#symbol // function symbol() public view returns (string); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#totalsupply // function decimals() public view returns (uint8); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#totalsupply function totalSupply() public view returns (uint256); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#balanceof function balanceOf(address _owner) public view returns (uint256 balance); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#transfer function transfer(address _to, uint256 _value) public returns (bool success); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#transferfrom function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#approve function approve(address _spender, uint256 _value) public returns (bool success); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#allowance function allowance(address _owner, address _spender) public view returns (uint256 remaining); // EVENTS // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#transfer-1 event Transfer(address indexed _from, address indexed _to, uint256 _value); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#approval event Approval(address indexed _owner, address indexed _spender, uint256 _value); } /** @title Public interface to ERC20 compliant token. * * @notice This contract is a permanent entry point to an ERC20 compliant * system of contracts. * * @dev This contract contains no business logic and instead * delegates to an instance of ERC20Impl. This contract also has no storage * that constitutes the operational state of the token. This contract is * upgradeable in the sense that the `custodian` can update the * `erc20Impl` address, thus redirecting the delegation of business logic. * The `custodian` is also authorized to pass custodianship. * * @author Gemini Trust Company, LLC */ contract ERC20Proxy is ERC20Interface, ERC20ImplUpgradeable { // MEMBERS /// @notice Returns the name of the token. string public name; /// @notice Returns the symbol of the token. string public symbol; /// @notice Returns the number of decimals the token uses. uint8 public decimals; // CONSTRUCTOR function ERC20Proxy( string _name, string _symbol, uint8 _decimals, address _custodian ) ERC20ImplUpgradeable(_custodian) public { name = _name; symbol = _symbol; decimals = _decimals; } // PUBLIC FUNCTIONS // (ERC20Interface) /** @notice Returns the total token supply. * * @return the total token supply. */ function totalSupply() public view returns (uint256) { return erc20Impl.totalSupply(); } /** @notice Returns the account balance of another account with address * `_owner`. * * @return balance the balance of account with address `_owner`. */ function balanceOf(address _owner) public view returns (uint256 balance) { return erc20Impl.balanceOf(_owner); } /** @dev Internal use only. */ function emitTransfer(address _from, address _to, uint256 _value) public onlyImpl { emit Transfer(_from, _to, _value); } /** @notice Transfers `_value` amount of tokens to address `_to`. * * @dev Will fire the `Transfer` event. Will revert if the `_from` * account balance does not have enough tokens to spend. * * @return success true if transfer completes. */ function transfer(address _to, uint256 _value) public returns (bool success) { return erc20Impl.transferWithSender(msg.sender, _to, _value); } /** @notice Transfers `_value` amount of tokens from address `_from` * to address `_to`. * * @dev Will fire the `Transfer` event. Will revert unless the `_from` * account has deliberately authorized the sender of the message * via some mechanism. * * @return success true if transfer completes. */ function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) { return erc20Impl.transferFromWithSender(msg.sender, _from, _to, _value); } /** @dev Internal use only. */ function emitApproval(address _owner, address _spender, uint256 _value) public onlyImpl { emit Approval(_owner, _spender, _value); } /** @notice Allows `_spender` to withdraw from your account multiple times, * up to the `_value` amount. If this function is called again it * overwrites the current allowance with _value. * * @dev Will fire the `Approval` event. * * @return success true if approval completes. */ function approve(address _spender, uint256 _value) public returns (bool success) { return erc20Impl.approveWithSender(msg.sender, _spender, _value); } /** @notice Increases the amount `_spender` is allowed to withdraw from * your account. * This function is implemented to avoid the race condition in standard * ERC20 contracts surrounding the `approve` method. * * @dev Will fire the `Approval` event. This function should be used instead of * `approve`. * * @return success true if approval completes. */ function increaseApproval(address _spender, uint256 _addedValue) public returns (bool success) { return erc20Impl.increaseApprovalWithSender(msg.sender, _spender, _addedValue); } /** @notice Decreases the amount `_spender` is allowed to withdraw from * your account. This function is implemented to avoid the race * condition in standard ERC20 contracts surrounding the `approve` method. * * @dev Will fire the `Approval` event. This function should be used * instead of `approve`. * * @return success true if approval completes. */ function decreaseApproval(address _spender, uint256 _subtractedValue) public returns (bool success) { return erc20Impl.decreaseApprovalWithSender(msg.sender, _spender, _subtractedValue); } /** @notice Returns how much `_spender` is currently allowed to spend from * `_owner`'s balance. * * @return remaining the remaining allowance. */ function allowance(address _owner, address _spender) public view returns (uint256 remaining) { return erc20Impl.allowance(_owner, _spender); } } /** @title ERC20 compliant token intermediary contract holding core logic. * * @notice This contract serves as an intermediary between the exposed ERC20 * interface in ERC20Proxy and the store of balances in ERC20Store. This * contract contains core logic that the proxy can delegate to * and that the store is called by. * * @dev This contract contains the core logic to implement the * ERC20 specification as well as several extensions. * 1. Changes to the token supply. * 2. Batched transfers. * 3. Relative changes to spending approvals. * 4. Delegated transfer control ('sweeping'). * * @author Gemini Trust Company, LLC */ contract ERC20Impl is CustodianUpgradeable { // TYPES /// @dev The struct type for pending increases to the token supply (print). struct PendingPrint { address receiver; uint256 value; } // MEMBERS /// @dev The reference to the proxy. ERC20Proxy public erc20Proxy; /// @dev The reference to the store. ERC20Store public erc20Store; /// @dev The sole authorized caller of delegated transfer control ('sweeping'). address public sweeper; /** @dev The static message to be signed by an external account that * signifies their permission to forward their balance to any arbitrary * address. This is used to consolidate the control of all accounts * backed by a shared keychain into the control of a single key. * Initialized as the concatenation of the address of this contract * and the word "sweep". This concatenation is done to prevent a replay * attack in a subsequent contract, where the sweep message could * potentially be replayed to re-enable sweeping ability. */ bytes32 public sweepMsg; /** @dev The mapping that stores whether the address in question has * enabled sweeping its contents to another account or not. * If an address maps to "true", it has already enabled sweeping, * and thus does not need to re-sign the `sweepMsg` to enact the sweep. */ mapping (address => bool) public sweptSet; /// @dev The map of lock ids to pending token increases. mapping (bytes32 => PendingPrint) public pendingPrintMap; // CONSTRUCTOR function ERC20Impl( address _erc20Proxy, address _erc20Store, address _custodian, address _sweeper ) CustodianUpgradeable(_custodian) public { require(_sweeper != 0); erc20Proxy = ERC20Proxy(_erc20Proxy); erc20Store = ERC20Store(_erc20Store); sweeper = _sweeper; sweepMsg = keccak256(address(this), "sweep"); } // MODIFIERS modifier onlyProxy { require(msg.sender == address(erc20Proxy)); _; } modifier onlySweeper { require(msg.sender == sweeper); _; } /** @notice Core logic of the ERC20 `approve` function. * * @dev This function can only be called by the referenced proxy, * which has an `approve` function. * Every argument passed to that function as well as the original * `msg.sender` gets passed to this function. * NOTE: approvals for the zero address (unspendable) are disallowed. * * @param _sender The address initiating the approval in proxy. */ function approveWithSender( address _sender, address _spender, uint256 _value ) public onlyProxy returns (bool success) { require(_spender != address(0)); // disallow unspendable approvals erc20Store.setAllowance(_sender, _spender, _value); erc20Proxy.emitApproval(_sender, _spender, _value); return true; } /** @notice Core logic of the `increaseApproval` function. * * @dev This function can only be called by the referenced proxy, * which has an `increaseApproval` function. * Every argument passed to that function as well as the original * `msg.sender` gets passed to this function. * NOTE: approvals for the zero address (unspendable) are disallowed. * * @param _sender The address initiating the approval. */ function increaseApprovalWithSender( address _sender, address _spender, uint256 _addedValue ) public onlyProxy returns (bool success) { require(_spender != address(0)); // disallow unspendable approvals uint256 currentAllowance = erc20Store.allowed(_sender, _spender); uint256 newAllowance = currentAllowance + _addedValue; require(newAllowance >= currentAllowance); erc20Store.setAllowance(_sender, _spender, newAllowance); erc20Proxy.emitApproval(_sender, _spender, newAllowance); return true; } /** @notice Core logic of the `decreaseApproval` function. * * @dev This function can only be called by the referenced proxy, * which has a `decreaseApproval` function. * Every argument passed to that function as well as the original * `msg.sender` gets passed to this function. * NOTE: approvals for the zero address (unspendable) are disallowed. * * @param _sender The address initiating the approval. */ function decreaseApprovalWithSender( address _sender, address _spender, uint256 _subtractedValue ) public onlyProxy returns (bool success) { require(_spender != address(0)); // disallow unspendable approvals uint256 currentAllowance = erc20Store.allowed(_sender, _spender); uint256 newAllowance = currentAllowance - _subtractedValue; require(newAllowance <= currentAllowance); erc20Store.setAllowance(_sender, _spender, newAllowance); erc20Proxy.emitApproval(_sender, _spender, newAllowance); return true; } /** @notice Requests an increase in the token supply, with the newly created * tokens to be added to the balance of the specified account. * * @dev Returns a unique lock id associated with the request. * Anyone can call this function, but confirming the request is authorized * by the custodian. * NOTE: printing to the zero address is disallowed. * * @param _receiver The receiving address of the print, if confirmed. * @param _value The number of tokens to add to the total supply and the * balance of the receiving address, if confirmed. * * @return lockId A unique identifier for this request. */ function requestPrint(address _receiver, uint256 _value) public returns (bytes32 lockId) { require(_receiver != address(0)); lockId = generateLockId(); pendingPrintMap[lockId] = PendingPrint({ receiver: _receiver, value: _value }); emit PrintingLocked(lockId, _receiver, _value); } /** @notice Confirms a pending increase in the token supply. * * @dev When called by the custodian with a lock id associated with a * pending increase, the amount requested to be printed in the print request * is printed to the receiving address specified in that same request. * NOTE: this function will not execute any print that would overflow the * total supply, but it will not revert either. * * @param _lockId The identifier of a pending print request. */ function confirmPrint(bytes32 _lockId) public onlyCustodian { PendingPrint storage print = pendingPrintMap[_lockId]; // reject ‘null’ results from the map lookup // this can only be the case if an unknown `_lockId` is received address receiver = print.receiver; require (receiver != address(0)); uint256 value = print.value; delete pendingPrintMap[_lockId]; uint256 supply = erc20Store.totalSupply(); uint256 newSupply = supply + value; if (newSupply >= supply) { erc20Store.setTotalSupply(newSupply); erc20Store.addBalance(receiver, value); emit PrintingConfirmed(_lockId, receiver, value); erc20Proxy.emitTransfer(address(0), receiver, value); } } /** @notice Burns the specified value from the sender's balance. * * @dev Sender's balanced is subtracted by the amount they wish to burn. * * @param _value The amount to burn. * * @return success true if the burn succeeded. */ function burn(uint256 _value) public returns (bool success) { uint256 balanceOfSender = erc20Store.balances(msg.sender); require(_value <= balanceOfSender); erc20Store.setBalance(msg.sender, balanceOfSender - _value); erc20Store.setTotalSupply(erc20Store.totalSupply() - _value); erc20Proxy.emitTransfer(msg.sender, address(0), _value); return true; } /** @notice A function for a sender to issue multiple transfers to multiple * different addresses at once. This function is implemented for gas * considerations when someone wishes to transfer, as one transaction is * cheaper than issuing several distinct individual `transfer` transactions. * * @dev By specifying a set of destination addresses and values, the * sender can issue one transaction to transfer multiple amounts to * distinct addresses, rather than issuing each as a separate * transaction. The `_tos` and `_values` arrays must be equal length, and * an index in one array corresponds to the same index in the other array * (e.g. `_tos[0]` will receive `_values[0]`, `_tos[1]` will receive * `_values[1]`, and so on.) * NOTE: transfers to the zero address are disallowed. * * @param _tos The destination addresses to receive the transfers. * @param _values The values for each destination address. * @return success If transfers succeeded. */ function batchTransfer(address[] _tos, uint256[] _values) public returns (bool success) { require(_tos.length == _values.length); uint256 numTransfers = _tos.length; uint256 senderBalance = erc20Store.balances(msg.sender); for (uint256 i = 0; i < numTransfers; i++) { address to = _tos[i]; require(to != address(0)); uint256 v = _values[i]; require(senderBalance >= v); if (msg.sender != to) { senderBalance -= v; erc20Store.addBalance(to, v); } erc20Proxy.emitTransfer(msg.sender, to, v); } erc20Store.setBalance(msg.sender, senderBalance); return true; } /** @notice Enables the delegation of transfer control for many * accounts to the sweeper account, transferring any balances * as well to the given destination. * * @dev An account delegates transfer control by signing the * value of `sweepMsg`. The sweeper account is the only authorized * caller of this function, so it must relay signatures on behalf * of accounts that delegate transfer control to it. Enabling * delegation is idempotent and permanent. If the account has a * balance at the time of enabling delegation, its balance is * also transfered to the given destination account `_to`. * NOTE: transfers to the zero address are disallowed. * * @param _vs The array of recovery byte components of the ECDSA signatures. * @param _rs The array of 'R' components of the ECDSA signatures. * @param _ss The array of 'S' components of the ECDSA signatures. * @param _to The destination for swept balances. */ function enableSweep(uint8[] _vs, bytes32[] _rs, bytes32[] _ss, address _to) public onlySweeper { require(_to != address(0)); require((_vs.length == _rs.length) && (_vs.length == _ss.length)); uint256 numSignatures = _vs.length; uint256 sweptBalance = 0; for (uint256 i=0; i<numSignatures; ++i) { address from = ecrecover(sweepMsg, _vs[i], _rs[i], _ss[i]); // ecrecover returns 0 on malformed input if (from != address(0)) { sweptSet[from] = true; uint256 fromBalance = erc20Store.balances(from); if (fromBalance > 0) { sweptBalance += fromBalance; erc20Store.setBalance(from, 0); erc20Proxy.emitTransfer(from, _to, fromBalance); } } } if (sweptBalance > 0) { erc20Store.addBalance(_to, sweptBalance); } } /** @notice For accounts that have delegated, transfer control * to the sweeper, this function transfers their balances to the given * destination. * * @dev The sweeper account is the only authorized caller of * this function. This function accepts an array of addresses to have their * balances transferred for gas efficiency purposes. * NOTE: any address for an account that has not been previously enabled * will be ignored. * NOTE: transfers to the zero address are disallowed. * * @param _froms The addresses to have their balances swept. * @param _to The destination address of all these transfers. */ function replaySweep(address[] _froms, address _to) public onlySweeper { require(_to != address(0)); uint256 lenFroms = _froms.length; uint256 sweptBalance = 0; for (uint256 i=0; i<lenFroms; ++i) { address from = _froms[i]; if (sweptSet[from]) { uint256 fromBalance = erc20Store.balances(from); if (fromBalance > 0) { sweptBalance += fromBalance; erc20Store.setBalance(from, 0); erc20Proxy.emitTransfer(from, _to, fromBalance); } } } if (sweptBalance > 0) { erc20Store.addBalance(_to, sweptBalance); } } /** @notice Core logic of the ERC20 `transferFrom` function. * * @dev This function can only be called by the referenced proxy, * which has a `transferFrom` function. * Every argument passed to that function as well as the original * `msg.sender` gets passed to this function. * NOTE: transfers to the zero address are disallowed. * * @param _sender The address initiating the transfer in proxy. */ function transferFromWithSender( address _sender, address _from, address _to, uint256 _value ) public onlyProxy returns (bool success) { require(_to != address(0)); // ensure burn is the cannonical transfer to 0x0 uint256 balanceOfFrom = erc20Store.balances(_from); require(_value <= balanceOfFrom); uint256 senderAllowance = erc20Store.allowed(_from, _sender); require(_value <= senderAllowance); erc20Store.setBalance(_from, balanceOfFrom - _value); erc20Store.addBalance(_to, _value); erc20Store.setAllowance(_from, _sender, senderAllowance - _value); erc20Proxy.emitTransfer(_from, _to, _value); return true; } /** @notice Core logic of the ERC20 `transfer` function. * * @dev This function can only be called by the referenced proxy, * which has a `transfer` function. * Every argument passed to that function as well as the original * `msg.sender` gets passed to this function. * NOTE: transfers to the zero address are disallowed. * * @param _sender The address initiating the transfer in proxy. */ function transferWithSender( address _sender, address _to, uint256 _value ) public onlyProxy returns (bool success) { require(_to != address(0)); // ensure burn is the cannonical transfer to 0x0 uint256 balanceOfSender = erc20Store.balances(_sender); require(_value <= balanceOfSender); erc20Store.setBalance(_sender, balanceOfSender - _value); erc20Store.addBalance(_to, _value); erc20Proxy.emitTransfer(_sender, _to, _value); return true; } // METHODS (ERC20 sub interface impl.) /// @notice Core logic of the ERC20 `totalSupply` function. function totalSupply() public view returns (uint256) { return erc20Store.totalSupply(); } /// @notice Core logic of the ERC20 `balanceOf` function. function balanceOf(address _owner) public view returns (uint256 balance) { return erc20Store.balances(_owner); } /// @notice Core logic of the ERC20 `allowance` function. function allowance(address _owner, address _spender) public view returns (uint256 remaining) { return erc20Store.allowed(_owner, _spender); } // EVENTS /// @dev Emitted by successful `requestPrint` calls. event PrintingLocked(bytes32 _lockId, address _receiver, uint256 _value); /// @dev Emitted by successful `confirmPrint` calls. event PrintingConfirmed(bytes32 _lockId, address _receiver, uint256 _value); } /** @title ERC20 compliant token balance store. * * @notice This contract serves as the store of balances, allowances, and * supply for the ERC20 compliant token. No business logic exists here. * * @dev This contract contains no business logic and instead * is the final destination for any change in balances, allowances, or token * supply. This contract is upgradeable in the sense that its custodian can * update the `erc20Impl` address, thus redirecting the source of logic that * determines how the balances will be updated. * * @author Gemini Trust Company, LLC */ contract ERC20Store is ERC20ImplUpgradeable { // MEMBERS /// @dev The total token supply. uint256 public totalSupply; /// @dev The mapping of balances. mapping (address => uint256) public balances; /// @dev The mapping of allowances. mapping (address => mapping (address => uint256)) public allowed; // CONSTRUCTOR function ERC20Store(address _custodian) ERC20ImplUpgradeable(_custodian) public { totalSupply = 0; } // PUBLIC FUNCTIONS // (ERC20 Ledger) /** @notice The function to set the total supply of tokens. * * @dev Intended for use by token implementation functions * that update the total supply. The only authorized caller * is the active implementation. * * @param _newTotalSupply the value to set as the new total supply */ function setTotalSupply( uint256 _newTotalSupply ) public onlyImpl { totalSupply = _newTotalSupply; } /** @notice Sets how much `_owner` allows `_spender` to transfer on behalf * of `_owner`. * * @dev Intended for use by token implementation functions * that update spending allowances. The only authorized caller * is the active implementation. * * @param _owner The account that will allow an on-behalf-of spend. * @param _spender The account that will spend on behalf of the owner. * @param _value The limit of what can be spent. */ function setAllowance( address _owner, address _spender, uint256 _value ) public onlyImpl { allowed[_owner][_spender] = _value; } /** @notice Sets the balance of `_owner` to `_newBalance`. * * @dev Intended for use by token implementation functions * that update balances. The only authorized caller * is the active implementation. * * @param _owner The account that will hold a new balance. * @param _newBalance The balance to set. */ function setBalance( address _owner, uint256 _newBalance ) public onlyImpl { balances[_owner] = _newBalance; } /** @notice Adds `_balanceIncrease` to `_owner`'s balance. * * @dev Intended for use by token implementation functions * that update balances. The only authorized caller * is the active implementation. * WARNING: the caller is responsible for preventing overflow. * * @param _owner The account that will hold a new balance. * @param _balanceIncrease The balance to add. */ function addBalance( address _owner, uint256 _balanceIncrease ) public onlyImpl { balances[_owner] = balances[_owner] + _balanceIncrease; } }
File 2 of 3: ERC20Impl
pragma solidity ^0.4.21; /** @title A contract for generating unique identifiers * * @notice A contract that provides a identifier generation scheme, * guaranteeing uniqueness across all contracts that inherit from it, * as well as unpredictability of future identifiers. * * @dev This contract is intended to be inherited by any contract that * implements the callback software pattern for cooperative custodianship. * * @author Gemini Trust Company, LLC */ contract LockRequestable { // MEMBERS /// @notice the count of all invocations of `generateLockId`. uint256 public lockRequestCount; // CONSTRUCTOR function LockRequestable() public { lockRequestCount = 0; } // FUNCTIONS /** @notice Returns a fresh unique identifier. * * @dev the generation scheme uses three components. * First, the blockhash of the previous block. * Second, the deployed address. * Third, the next value of the counter. * This ensure that identifiers are unique across all contracts * following this scheme, and that future identifiers are * unpredictable. * * @return a 32-byte unique identifier. */ function generateLockId() internal returns (bytes32 lockId) { return keccak256(block.blockhash(block.number - 1), address(this), ++lockRequestCount); } } /** @title A contract to inherit upgradeable custodianship. * * @notice A contract that provides re-usable code for upgradeable * custodianship. That custodian may be an account or another contract. * * @dev This contract is intended to be inherited by any contract * requiring a custodian to control some aspect of its functionality. * This contract provides the mechanism for that custodianship to be * passed from one custodian to the next. * * @author Gemini Trust Company, LLC */ contract CustodianUpgradeable is LockRequestable { // TYPES /// @dev The struct type for pending custodian changes. struct CustodianChangeRequest { address proposedNew; } // MEMBERS /// @dev The address of the account or contract that acts as the custodian. address public custodian; /// @dev The map of lock ids to pending custodian changes. mapping (bytes32 => CustodianChangeRequest) public custodianChangeReqs; // CONSTRUCTOR function CustodianUpgradeable( address _custodian ) LockRequestable() public { custodian = _custodian; } // MODIFIERS modifier onlyCustodian { require(msg.sender == custodian); _; } // PUBLIC FUNCTIONS // (UPGRADE) /** @notice Requests a change of the custodian associated with this contract. * * @dev Returns a unique lock id associated with the request. * Anyone can call this function, but confirming the request is authorized * by the custodian. * * @param _proposedCustodian The address of the new custodian. * @return lockId A unique identifier for this request. */ function requestCustodianChange(address _proposedCustodian) public returns (bytes32 lockId) { require(_proposedCustodian != address(0)); lockId = generateLockId(); custodianChangeReqs[lockId] = CustodianChangeRequest({ proposedNew: _proposedCustodian }); emit CustodianChangeRequested(lockId, msg.sender, _proposedCustodian); } /** @notice Confirms a pending change of the custodian associated with this contract. * * @dev When called by the current custodian with a lock id associated with a * pending custodian change, the `address custodian` member will be updated with the * requested address. * * @param _lockId The identifier of a pending change request. */ function confirmCustodianChange(bytes32 _lockId) public onlyCustodian { custodian = getCustodianChangeReq(_lockId); delete custodianChangeReqs[_lockId]; emit CustodianChangeConfirmed(_lockId, custodian); } // PRIVATE FUNCTIONS function getCustodianChangeReq(bytes32 _lockId) private view returns (address _proposedNew) { CustodianChangeRequest storage changeRequest = custodianChangeReqs[_lockId]; // reject ‘null’ results from the map lookup // this can only be the case if an unknown `_lockId` is received require(changeRequest.proposedNew != 0); return changeRequest.proposedNew; } /// @dev Emitted by successful `requestCustodianChange` calls. event CustodianChangeRequested( bytes32 _lockId, address _msgSender, address _proposedCustodian ); /// @dev Emitted by successful `confirmCustodianChange` calls. event CustodianChangeConfirmed(bytes32 _lockId, address _newCustodian); } /** @title A contract to inherit upgradeable token implementations. * * @notice A contract that provides re-usable code for upgradeable * token implementations. It itself inherits from `CustodianUpgradable` * as the upgrade process is controlled by the custodian. * * @dev This contract is intended to be inherited by any contract * requiring a reference to the active token implementation, either * to delegate calls to it, or authorize calls from it. This contract * provides the mechanism for that implementation to be be replaced, * which constitutes an implementation upgrade. * * @author Gemini Trust Company, LLC */ contract ERC20ImplUpgradeable is CustodianUpgradeable { // TYPES /// @dev The struct type for pending implementation changes. struct ImplChangeRequest { address proposedNew; } // MEMBERS // @dev The reference to the active token implementation. ERC20Impl public erc20Impl; /// @dev The map of lock ids to pending implementation changes. mapping (bytes32 => ImplChangeRequest) public implChangeReqs; // CONSTRUCTOR function ERC20ImplUpgradeable(address _custodian) CustodianUpgradeable(_custodian) public { erc20Impl = ERC20Impl(0x0); } // MODIFIERS modifier onlyImpl { require(msg.sender == address(erc20Impl)); _; } // PUBLIC FUNCTIONS // (UPGRADE) /** @notice Requests a change of the active implementation associated * with this contract. * * @dev Returns a unique lock id associated with the request. * Anyone can call this function, but confirming the request is authorized * by the custodian. * * @param _proposedImpl The address of the new active implementation. * @return lockId A unique identifier for this request. */ function requestImplChange(address _proposedImpl) public returns (bytes32 lockId) { require(_proposedImpl != address(0)); lockId = generateLockId(); implChangeReqs[lockId] = ImplChangeRequest({ proposedNew: _proposedImpl }); emit ImplChangeRequested(lockId, msg.sender, _proposedImpl); } /** @notice Confirms a pending change of the active implementation * associated with this contract. * * @dev When called by the custodian with a lock id associated with a * pending change, the `ERC20Impl erc20Impl` member will be updated * with the requested address. * * @param _lockId The identifier of a pending change request. */ function confirmImplChange(bytes32 _lockId) public onlyCustodian { erc20Impl = getImplChangeReq(_lockId); delete implChangeReqs[_lockId]; emit ImplChangeConfirmed(_lockId, address(erc20Impl)); } // PRIVATE FUNCTIONS function getImplChangeReq(bytes32 _lockId) private view returns (ERC20Impl _proposedNew) { ImplChangeRequest storage changeRequest = implChangeReqs[_lockId]; // reject ‘null’ results from the map lookup // this can only be the case if an unknown `_lockId` is received require(changeRequest.proposedNew != address(0)); return ERC20Impl(changeRequest.proposedNew); } /// @dev Emitted by successful `requestImplChange` calls. event ImplChangeRequested( bytes32 _lockId, address _msgSender, address _proposedImpl ); /// @dev Emitted by successful `confirmImplChange` calls. event ImplChangeConfirmed(bytes32 _lockId, address _newImpl); } contract ERC20Interface { // METHODS // NOTE: // public getter functions are not currently recognised as an // implementation of the matching abstract function by the compiler. // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#name // function name() public view returns (string); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#symbol // function symbol() public view returns (string); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#totalsupply // function decimals() public view returns (uint8); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#totalsupply function totalSupply() public view returns (uint256); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#balanceof function balanceOf(address _owner) public view returns (uint256 balance); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#transfer function transfer(address _to, uint256 _value) public returns (bool success); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#transferfrom function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#approve function approve(address _spender, uint256 _value) public returns (bool success); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#allowance function allowance(address _owner, address _spender) public view returns (uint256 remaining); // EVENTS // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#transfer-1 event Transfer(address indexed _from, address indexed _to, uint256 _value); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#approval event Approval(address indexed _owner, address indexed _spender, uint256 _value); } /** @title Public interface to ERC20 compliant token. * * @notice This contract is a permanent entry point to an ERC20 compliant * system of contracts. * * @dev This contract contains no business logic and instead * delegates to an instance of ERC20Impl. This contract also has no storage * that constitutes the operational state of the token. This contract is * upgradeable in the sense that the `custodian` can update the * `erc20Impl` address, thus redirecting the delegation of business logic. * The `custodian` is also authorized to pass custodianship. * * @author Gemini Trust Company, LLC */ contract ERC20Proxy is ERC20Interface, ERC20ImplUpgradeable { // MEMBERS /// @notice Returns the name of the token. string public name; /// @notice Returns the symbol of the token. string public symbol; /// @notice Returns the number of decimals the token uses. uint8 public decimals; // CONSTRUCTOR function ERC20Proxy( string _name, string _symbol, uint8 _decimals, address _custodian ) ERC20ImplUpgradeable(_custodian) public { name = _name; symbol = _symbol; decimals = _decimals; } // PUBLIC FUNCTIONS // (ERC20Interface) /** @notice Returns the total token supply. * * @return the total token supply. */ function totalSupply() public view returns (uint256) { return erc20Impl.totalSupply(); } /** @notice Returns the account balance of another account with address * `_owner`. * * @return balance the balance of account with address `_owner`. */ function balanceOf(address _owner) public view returns (uint256 balance) { return erc20Impl.balanceOf(_owner); } /** @dev Internal use only. */ function emitTransfer(address _from, address _to, uint256 _value) public onlyImpl { emit Transfer(_from, _to, _value); } /** @notice Transfers `_value` amount of tokens to address `_to`. * * @dev Will fire the `Transfer` event. Will revert if the `_from` * account balance does not have enough tokens to spend. * * @return success true if transfer completes. */ function transfer(address _to, uint256 _value) public returns (bool success) { return erc20Impl.transferWithSender(msg.sender, _to, _value); } /** @notice Transfers `_value` amount of tokens from address `_from` * to address `_to`. * * @dev Will fire the `Transfer` event. Will revert unless the `_from` * account has deliberately authorized the sender of the message * via some mechanism. * * @return success true if transfer completes. */ function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) { return erc20Impl.transferFromWithSender(msg.sender, _from, _to, _value); } /** @dev Internal use only. */ function emitApproval(address _owner, address _spender, uint256 _value) public onlyImpl { emit Approval(_owner, _spender, _value); } /** @notice Allows `_spender` to withdraw from your account multiple times, * up to the `_value` amount. If this function is called again it * overwrites the current allowance with _value. * * @dev Will fire the `Approval` event. * * @return success true if approval completes. */ function approve(address _spender, uint256 _value) public returns (bool success) { return erc20Impl.approveWithSender(msg.sender, _spender, _value); } /** @notice Increases the amount `_spender` is allowed to withdraw from * your account. * This function is implemented to avoid the race condition in standard * ERC20 contracts surrounding the `approve` method. * * @dev Will fire the `Approval` event. This function should be used instead of * `approve`. * * @return success true if approval completes. */ function increaseApproval(address _spender, uint256 _addedValue) public returns (bool success) { return erc20Impl.increaseApprovalWithSender(msg.sender, _spender, _addedValue); } /** @notice Decreases the amount `_spender` is allowed to withdraw from * your account. This function is implemented to avoid the race * condition in standard ERC20 contracts surrounding the `approve` method. * * @dev Will fire the `Approval` event. This function should be used * instead of `approve`. * * @return success true if approval completes. */ function decreaseApproval(address _spender, uint256 _subtractedValue) public returns (bool success) { return erc20Impl.decreaseApprovalWithSender(msg.sender, _spender, _subtractedValue); } /** @notice Returns how much `_spender` is currently allowed to spend from * `_owner`'s balance. * * @return remaining the remaining allowance. */ function allowance(address _owner, address _spender) public view returns (uint256 remaining) { return erc20Impl.allowance(_owner, _spender); } } /** @title ERC20 compliant token intermediary contract holding core logic. * * @notice This contract serves as an intermediary between the exposed ERC20 * interface in ERC20Proxy and the store of balances in ERC20Store. This * contract contains core logic that the proxy can delegate to * and that the store is called by. * * @dev This contract contains the core logic to implement the * ERC20 specification as well as several extensions. * 1. Changes to the token supply. * 2. Batched transfers. * 3. Relative changes to spending approvals. * 4. Delegated transfer control ('sweeping'). * * @author Gemini Trust Company, LLC */ contract ERC20Impl is CustodianUpgradeable { // TYPES /// @dev The struct type for pending increases to the token supply (print). struct PendingPrint { address receiver; uint256 value; } // MEMBERS /// @dev The reference to the proxy. ERC20Proxy public erc20Proxy; /// @dev The reference to the store. ERC20Store public erc20Store; /// @dev The sole authorized caller of delegated transfer control ('sweeping'). address public sweeper; /** @dev The static message to be signed by an external account that * signifies their permission to forward their balance to any arbitrary * address. This is used to consolidate the control of all accounts * backed by a shared keychain into the control of a single key. * Initialized as the concatenation of the address of this contract * and the word "sweep". This concatenation is done to prevent a replay * attack in a subsequent contract, where the sweep message could * potentially be replayed to re-enable sweeping ability. */ bytes32 public sweepMsg; /** @dev The mapping that stores whether the address in question has * enabled sweeping its contents to another account or not. * If an address maps to "true", it has already enabled sweeping, * and thus does not need to re-sign the `sweepMsg` to enact the sweep. */ mapping (address => bool) public sweptSet; /// @dev The map of lock ids to pending token increases. mapping (bytes32 => PendingPrint) public pendingPrintMap; // CONSTRUCTOR function ERC20Impl( address _erc20Proxy, address _erc20Store, address _custodian, address _sweeper ) CustodianUpgradeable(_custodian) public { require(_sweeper != 0); erc20Proxy = ERC20Proxy(_erc20Proxy); erc20Store = ERC20Store(_erc20Store); sweeper = _sweeper; sweepMsg = keccak256(address(this), "sweep"); } // MODIFIERS modifier onlyProxy { require(msg.sender == address(erc20Proxy)); _; } modifier onlySweeper { require(msg.sender == sweeper); _; } /** @notice Core logic of the ERC20 `approve` function. * * @dev This function can only be called by the referenced proxy, * which has an `approve` function. * Every argument passed to that function as well as the original * `msg.sender` gets passed to this function. * NOTE: approvals for the zero address (unspendable) are disallowed. * * @param _sender The address initiating the approval in proxy. */ function approveWithSender( address _sender, address _spender, uint256 _value ) public onlyProxy returns (bool success) { require(_spender != address(0)); // disallow unspendable approvals erc20Store.setAllowance(_sender, _spender, _value); erc20Proxy.emitApproval(_sender, _spender, _value); return true; } /** @notice Core logic of the `increaseApproval` function. * * @dev This function can only be called by the referenced proxy, * which has an `increaseApproval` function. * Every argument passed to that function as well as the original * `msg.sender` gets passed to this function. * NOTE: approvals for the zero address (unspendable) are disallowed. * * @param _sender The address initiating the approval. */ function increaseApprovalWithSender( address _sender, address _spender, uint256 _addedValue ) public onlyProxy returns (bool success) { require(_spender != address(0)); // disallow unspendable approvals uint256 currentAllowance = erc20Store.allowed(_sender, _spender); uint256 newAllowance = currentAllowance + _addedValue; require(newAllowance >= currentAllowance); erc20Store.setAllowance(_sender, _spender, newAllowance); erc20Proxy.emitApproval(_sender, _spender, newAllowance); return true; } /** @notice Core logic of the `decreaseApproval` function. * * @dev This function can only be called by the referenced proxy, * which has a `decreaseApproval` function. * Every argument passed to that function as well as the original * `msg.sender` gets passed to this function. * NOTE: approvals for the zero address (unspendable) are disallowed. * * @param _sender The address initiating the approval. */ function decreaseApprovalWithSender( address _sender, address _spender, uint256 _subtractedValue ) public onlyProxy returns (bool success) { require(_spender != address(0)); // disallow unspendable approvals uint256 currentAllowance = erc20Store.allowed(_sender, _spender); uint256 newAllowance = currentAllowance - _subtractedValue; require(newAllowance <= currentAllowance); erc20Store.setAllowance(_sender, _spender, newAllowance); erc20Proxy.emitApproval(_sender, _spender, newAllowance); return true; } /** @notice Requests an increase in the token supply, with the newly created * tokens to be added to the balance of the specified account. * * @dev Returns a unique lock id associated with the request. * Anyone can call this function, but confirming the request is authorized * by the custodian. * NOTE: printing to the zero address is disallowed. * * @param _receiver The receiving address of the print, if confirmed. * @param _value The number of tokens to add to the total supply and the * balance of the receiving address, if confirmed. * * @return lockId A unique identifier for this request. */ function requestPrint(address _receiver, uint256 _value) public returns (bytes32 lockId) { require(_receiver != address(0)); lockId = generateLockId(); pendingPrintMap[lockId] = PendingPrint({ receiver: _receiver, value: _value }); emit PrintingLocked(lockId, _receiver, _value); } /** @notice Confirms a pending increase in the token supply. * * @dev When called by the custodian with a lock id associated with a * pending increase, the amount requested to be printed in the print request * is printed to the receiving address specified in that same request. * NOTE: this function will not execute any print that would overflow the * total supply, but it will not revert either. * * @param _lockId The identifier of a pending print request. */ function confirmPrint(bytes32 _lockId) public onlyCustodian { PendingPrint storage print = pendingPrintMap[_lockId]; // reject ‘null’ results from the map lookup // this can only be the case if an unknown `_lockId` is received address receiver = print.receiver; require (receiver != address(0)); uint256 value = print.value; delete pendingPrintMap[_lockId]; uint256 supply = erc20Store.totalSupply(); uint256 newSupply = supply + value; if (newSupply >= supply) { erc20Store.setTotalSupply(newSupply); erc20Store.addBalance(receiver, value); emit PrintingConfirmed(_lockId, receiver, value); erc20Proxy.emitTransfer(address(0), receiver, value); } } /** @notice Burns the specified value from the sender's balance. * * @dev Sender's balanced is subtracted by the amount they wish to burn. * * @param _value The amount to burn. * * @return success true if the burn succeeded. */ function burn(uint256 _value) public returns (bool success) { uint256 balanceOfSender = erc20Store.balances(msg.sender); require(_value <= balanceOfSender); erc20Store.setBalance(msg.sender, balanceOfSender - _value); erc20Store.setTotalSupply(erc20Store.totalSupply() - _value); erc20Proxy.emitTransfer(msg.sender, address(0), _value); return true; } /** @notice A function for a sender to issue multiple transfers to multiple * different addresses at once. This function is implemented for gas * considerations when someone wishes to transfer, as one transaction is * cheaper than issuing several distinct individual `transfer` transactions. * * @dev By specifying a set of destination addresses and values, the * sender can issue one transaction to transfer multiple amounts to * distinct addresses, rather than issuing each as a separate * transaction. The `_tos` and `_values` arrays must be equal length, and * an index in one array corresponds to the same index in the other array * (e.g. `_tos[0]` will receive `_values[0]`, `_tos[1]` will receive * `_values[1]`, and so on.) * NOTE: transfers to the zero address are disallowed. * * @param _tos The destination addresses to receive the transfers. * @param _values The values for each destination address. * @return success If transfers succeeded. */ function batchTransfer(address[] _tos, uint256[] _values) public returns (bool success) { require(_tos.length == _values.length); uint256 numTransfers = _tos.length; uint256 senderBalance = erc20Store.balances(msg.sender); for (uint256 i = 0; i < numTransfers; i++) { address to = _tos[i]; require(to != address(0)); uint256 v = _values[i]; require(senderBalance >= v); if (msg.sender != to) { senderBalance -= v; erc20Store.addBalance(to, v); } erc20Proxy.emitTransfer(msg.sender, to, v); } erc20Store.setBalance(msg.sender, senderBalance); return true; } /** @notice Enables the delegation of transfer control for many * accounts to the sweeper account, transferring any balances * as well to the given destination. * * @dev An account delegates transfer control by signing the * value of `sweepMsg`. The sweeper account is the only authorized * caller of this function, so it must relay signatures on behalf * of accounts that delegate transfer control to it. Enabling * delegation is idempotent and permanent. If the account has a * balance at the time of enabling delegation, its balance is * also transfered to the given destination account `_to`. * NOTE: transfers to the zero address are disallowed. * * @param _vs The array of recovery byte components of the ECDSA signatures. * @param _rs The array of 'R' components of the ECDSA signatures. * @param _ss The array of 'S' components of the ECDSA signatures. * @param _to The destination for swept balances. */ function enableSweep(uint8[] _vs, bytes32[] _rs, bytes32[] _ss, address _to) public onlySweeper { require(_to != address(0)); require((_vs.length == _rs.length) && (_vs.length == _ss.length)); uint256 numSignatures = _vs.length; uint256 sweptBalance = 0; for (uint256 i=0; i<numSignatures; ++i) { address from = ecrecover(sweepMsg, _vs[i], _rs[i], _ss[i]); // ecrecover returns 0 on malformed input if (from != address(0)) { sweptSet[from] = true; uint256 fromBalance = erc20Store.balances(from); if (fromBalance > 0) { sweptBalance += fromBalance; erc20Store.setBalance(from, 0); erc20Proxy.emitTransfer(from, _to, fromBalance); } } } if (sweptBalance > 0) { erc20Store.addBalance(_to, sweptBalance); } } /** @notice For accounts that have delegated, transfer control * to the sweeper, this function transfers their balances to the given * destination. * * @dev The sweeper account is the only authorized caller of * this function. This function accepts an array of addresses to have their * balances transferred for gas efficiency purposes. * NOTE: any address for an account that has not been previously enabled * will be ignored. * NOTE: transfers to the zero address are disallowed. * * @param _froms The addresses to have their balances swept. * @param _to The destination address of all these transfers. */ function replaySweep(address[] _froms, address _to) public onlySweeper { require(_to != address(0)); uint256 lenFroms = _froms.length; uint256 sweptBalance = 0; for (uint256 i=0; i<lenFroms; ++i) { address from = _froms[i]; if (sweptSet[from]) { uint256 fromBalance = erc20Store.balances(from); if (fromBalance > 0) { sweptBalance += fromBalance; erc20Store.setBalance(from, 0); erc20Proxy.emitTransfer(from, _to, fromBalance); } } } if (sweptBalance > 0) { erc20Store.addBalance(_to, sweptBalance); } } /** @notice Core logic of the ERC20 `transferFrom` function. * * @dev This function can only be called by the referenced proxy, * which has a `transferFrom` function. * Every argument passed to that function as well as the original * `msg.sender` gets passed to this function. * NOTE: transfers to the zero address are disallowed. * * @param _sender The address initiating the transfer in proxy. */ function transferFromWithSender( address _sender, address _from, address _to, uint256 _value ) public onlyProxy returns (bool success) { require(_to != address(0)); // ensure burn is the cannonical transfer to 0x0 uint256 balanceOfFrom = erc20Store.balances(_from); require(_value <= balanceOfFrom); uint256 senderAllowance = erc20Store.allowed(_from, _sender); require(_value <= senderAllowance); erc20Store.setBalance(_from, balanceOfFrom - _value); erc20Store.addBalance(_to, _value); erc20Store.setAllowance(_from, _sender, senderAllowance - _value); erc20Proxy.emitTransfer(_from, _to, _value); return true; } /** @notice Core logic of the ERC20 `transfer` function. * * @dev This function can only be called by the referenced proxy, * which has a `transfer` function. * Every argument passed to that function as well as the original * `msg.sender` gets passed to this function. * NOTE: transfers to the zero address are disallowed. * * @param _sender The address initiating the transfer in proxy. */ function transferWithSender( address _sender, address _to, uint256 _value ) public onlyProxy returns (bool success) { require(_to != address(0)); // ensure burn is the cannonical transfer to 0x0 uint256 balanceOfSender = erc20Store.balances(_sender); require(_value <= balanceOfSender); erc20Store.setBalance(_sender, balanceOfSender - _value); erc20Store.addBalance(_to, _value); erc20Proxy.emitTransfer(_sender, _to, _value); return true; } // METHODS (ERC20 sub interface impl.) /// @notice Core logic of the ERC20 `totalSupply` function. function totalSupply() public view returns (uint256) { return erc20Store.totalSupply(); } /// @notice Core logic of the ERC20 `balanceOf` function. function balanceOf(address _owner) public view returns (uint256 balance) { return erc20Store.balances(_owner); } /// @notice Core logic of the ERC20 `allowance` function. function allowance(address _owner, address _spender) public view returns (uint256 remaining) { return erc20Store.allowed(_owner, _spender); } // EVENTS /// @dev Emitted by successful `requestPrint` calls. event PrintingLocked(bytes32 _lockId, address _receiver, uint256 _value); /// @dev Emitted by successful `confirmPrint` calls. event PrintingConfirmed(bytes32 _lockId, address _receiver, uint256 _value); } /** @title ERC20 compliant token balance store. * * @notice This contract serves as the store of balances, allowances, and * supply for the ERC20 compliant token. No business logic exists here. * * @dev This contract contains no business logic and instead * is the final destination for any change in balances, allowances, or token * supply. This contract is upgradeable in the sense that its custodian can * update the `erc20Impl` address, thus redirecting the source of logic that * determines how the balances will be updated. * * @author Gemini Trust Company, LLC */ contract ERC20Store is ERC20ImplUpgradeable { // MEMBERS /// @dev The total token supply. uint256 public totalSupply; /// @dev The mapping of balances. mapping (address => uint256) public balances; /// @dev The mapping of allowances. mapping (address => mapping (address => uint256)) public allowed; // CONSTRUCTOR function ERC20Store(address _custodian) ERC20ImplUpgradeable(_custodian) public { totalSupply = 0; } // PUBLIC FUNCTIONS // (ERC20 Ledger) /** @notice The function to set the total supply of tokens. * * @dev Intended for use by token implementation functions * that update the total supply. The only authorized caller * is the active implementation. * * @param _newTotalSupply the value to set as the new total supply */ function setTotalSupply( uint256 _newTotalSupply ) public onlyImpl { totalSupply = _newTotalSupply; } /** @notice Sets how much `_owner` allows `_spender` to transfer on behalf * of `_owner`. * * @dev Intended for use by token implementation functions * that update spending allowances. The only authorized caller * is the active implementation. * * @param _owner The account that will allow an on-behalf-of spend. * @param _spender The account that will spend on behalf of the owner. * @param _value The limit of what can be spent. */ function setAllowance( address _owner, address _spender, uint256 _value ) public onlyImpl { allowed[_owner][_spender] = _value; } /** @notice Sets the balance of `_owner` to `_newBalance`. * * @dev Intended for use by token implementation functions * that update balances. The only authorized caller * is the active implementation. * * @param _owner The account that will hold a new balance. * @param _newBalance The balance to set. */ function setBalance( address _owner, uint256 _newBalance ) public onlyImpl { balances[_owner] = _newBalance; } /** @notice Adds `_balanceIncrease` to `_owner`'s balance. * * @dev Intended for use by token implementation functions * that update balances. The only authorized caller * is the active implementation. * WARNING: the caller is responsible for preventing overflow. * * @param _owner The account that will hold a new balance. * @param _balanceIncrease The balance to add. */ function addBalance( address _owner, uint256 _balanceIncrease ) public onlyImpl { balances[_owner] = balances[_owner] + _balanceIncrease; } }
File 3 of 3: ERC20Store
pragma solidity ^0.4.21; /** @title A contract for generating unique identifiers * * @notice A contract that provides a identifier generation scheme, * guaranteeing uniqueness across all contracts that inherit from it, * as well as unpredictability of future identifiers. * * @dev This contract is intended to be inherited by any contract that * implements the callback software pattern for cooperative custodianship. * * @author Gemini Trust Company, LLC */ contract LockRequestable { // MEMBERS /// @notice the count of all invocations of `generateLockId`. uint256 public lockRequestCount; // CONSTRUCTOR function LockRequestable() public { lockRequestCount = 0; } // FUNCTIONS /** @notice Returns a fresh unique identifier. * * @dev the generation scheme uses three components. * First, the blockhash of the previous block. * Second, the deployed address. * Third, the next value of the counter. * This ensure that identifiers are unique across all contracts * following this scheme, and that future identifiers are * unpredictable. * * @return a 32-byte unique identifier. */ function generateLockId() internal returns (bytes32 lockId) { return keccak256(block.blockhash(block.number - 1), address(this), ++lockRequestCount); } } /** @title A contract to inherit upgradeable custodianship. * * @notice A contract that provides re-usable code for upgradeable * custodianship. That custodian may be an account or another contract. * * @dev This contract is intended to be inherited by any contract * requiring a custodian to control some aspect of its functionality. * This contract provides the mechanism for that custodianship to be * passed from one custodian to the next. * * @author Gemini Trust Company, LLC */ contract CustodianUpgradeable is LockRequestable { // TYPES /// @dev The struct type for pending custodian changes. struct CustodianChangeRequest { address proposedNew; } // MEMBERS /// @dev The address of the account or contract that acts as the custodian. address public custodian; /// @dev The map of lock ids to pending custodian changes. mapping (bytes32 => CustodianChangeRequest) public custodianChangeReqs; // CONSTRUCTOR function CustodianUpgradeable( address _custodian ) LockRequestable() public { custodian = _custodian; } // MODIFIERS modifier onlyCustodian { require(msg.sender == custodian); _; } // PUBLIC FUNCTIONS // (UPGRADE) /** @notice Requests a change of the custodian associated with this contract. * * @dev Returns a unique lock id associated with the request. * Anyone can call this function, but confirming the request is authorized * by the custodian. * * @param _proposedCustodian The address of the new custodian. * @return lockId A unique identifier for this request. */ function requestCustodianChange(address _proposedCustodian) public returns (bytes32 lockId) { require(_proposedCustodian != address(0)); lockId = generateLockId(); custodianChangeReqs[lockId] = CustodianChangeRequest({ proposedNew: _proposedCustodian }); emit CustodianChangeRequested(lockId, msg.sender, _proposedCustodian); } /** @notice Confirms a pending change of the custodian associated with this contract. * * @dev When called by the current custodian with a lock id associated with a * pending custodian change, the `address custodian` member will be updated with the * requested address. * * @param _lockId The identifier of a pending change request. */ function confirmCustodianChange(bytes32 _lockId) public onlyCustodian { custodian = getCustodianChangeReq(_lockId); delete custodianChangeReqs[_lockId]; emit CustodianChangeConfirmed(_lockId, custodian); } // PRIVATE FUNCTIONS function getCustodianChangeReq(bytes32 _lockId) private view returns (address _proposedNew) { CustodianChangeRequest storage changeRequest = custodianChangeReqs[_lockId]; // reject ‘null’ results from the map lookup // this can only be the case if an unknown `_lockId` is received require(changeRequest.proposedNew != 0); return changeRequest.proposedNew; } /// @dev Emitted by successful `requestCustodianChange` calls. event CustodianChangeRequested( bytes32 _lockId, address _msgSender, address _proposedCustodian ); /// @dev Emitted by successful `confirmCustodianChange` calls. event CustodianChangeConfirmed(bytes32 _lockId, address _newCustodian); } /** @title A contract to inherit upgradeable token implementations. * * @notice A contract that provides re-usable code for upgradeable * token implementations. It itself inherits from `CustodianUpgradable` * as the upgrade process is controlled by the custodian. * * @dev This contract is intended to be inherited by any contract * requiring a reference to the active token implementation, either * to delegate calls to it, or authorize calls from it. This contract * provides the mechanism for that implementation to be be replaced, * which constitutes an implementation upgrade. * * @author Gemini Trust Company, LLC */ contract ERC20ImplUpgradeable is CustodianUpgradeable { // TYPES /// @dev The struct type for pending implementation changes. struct ImplChangeRequest { address proposedNew; } // MEMBERS // @dev The reference to the active token implementation. ERC20Impl public erc20Impl; /// @dev The map of lock ids to pending implementation changes. mapping (bytes32 => ImplChangeRequest) public implChangeReqs; // CONSTRUCTOR function ERC20ImplUpgradeable(address _custodian) CustodianUpgradeable(_custodian) public { erc20Impl = ERC20Impl(0x0); } // MODIFIERS modifier onlyImpl { require(msg.sender == address(erc20Impl)); _; } // PUBLIC FUNCTIONS // (UPGRADE) /** @notice Requests a change of the active implementation associated * with this contract. * * @dev Returns a unique lock id associated with the request. * Anyone can call this function, but confirming the request is authorized * by the custodian. * * @param _proposedImpl The address of the new active implementation. * @return lockId A unique identifier for this request. */ function requestImplChange(address _proposedImpl) public returns (bytes32 lockId) { require(_proposedImpl != address(0)); lockId = generateLockId(); implChangeReqs[lockId] = ImplChangeRequest({ proposedNew: _proposedImpl }); emit ImplChangeRequested(lockId, msg.sender, _proposedImpl); } /** @notice Confirms a pending change of the active implementation * associated with this contract. * * @dev When called by the custodian with a lock id associated with a * pending change, the `ERC20Impl erc20Impl` member will be updated * with the requested address. * * @param _lockId The identifier of a pending change request. */ function confirmImplChange(bytes32 _lockId) public onlyCustodian { erc20Impl = getImplChangeReq(_lockId); delete implChangeReqs[_lockId]; emit ImplChangeConfirmed(_lockId, address(erc20Impl)); } // PRIVATE FUNCTIONS function getImplChangeReq(bytes32 _lockId) private view returns (ERC20Impl _proposedNew) { ImplChangeRequest storage changeRequest = implChangeReqs[_lockId]; // reject ‘null’ results from the map lookup // this can only be the case if an unknown `_lockId` is received require(changeRequest.proposedNew != address(0)); return ERC20Impl(changeRequest.proposedNew); } /// @dev Emitted by successful `requestImplChange` calls. event ImplChangeRequested( bytes32 _lockId, address _msgSender, address _proposedImpl ); /// @dev Emitted by successful `confirmImplChange` calls. event ImplChangeConfirmed(bytes32 _lockId, address _newImpl); } contract ERC20Interface { // METHODS // NOTE: // public getter functions are not currently recognised as an // implementation of the matching abstract function by the compiler. // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#name // function name() public view returns (string); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#symbol // function symbol() public view returns (string); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#totalsupply // function decimals() public view returns (uint8); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#totalsupply function totalSupply() public view returns (uint256); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#balanceof function balanceOf(address _owner) public view returns (uint256 balance); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#transfer function transfer(address _to, uint256 _value) public returns (bool success); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#transferfrom function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#approve function approve(address _spender, uint256 _value) public returns (bool success); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#allowance function allowance(address _owner, address _spender) public view returns (uint256 remaining); // EVENTS // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#transfer-1 event Transfer(address indexed _from, address indexed _to, uint256 _value); // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#approval event Approval(address indexed _owner, address indexed _spender, uint256 _value); } /** @title Public interface to ERC20 compliant token. * * @notice This contract is a permanent entry point to an ERC20 compliant * system of contracts. * * @dev This contract contains no business logic and instead * delegates to an instance of ERC20Impl. This contract also has no storage * that constitutes the operational state of the token. This contract is * upgradeable in the sense that the `custodian` can update the * `erc20Impl` address, thus redirecting the delegation of business logic. * The `custodian` is also authorized to pass custodianship. * * @author Gemini Trust Company, LLC */ contract ERC20Proxy is ERC20Interface, ERC20ImplUpgradeable { // MEMBERS /// @notice Returns the name of the token. string public name; /// @notice Returns the symbol of the token. string public symbol; /// @notice Returns the number of decimals the token uses. uint8 public decimals; // CONSTRUCTOR function ERC20Proxy( string _name, string _symbol, uint8 _decimals, address _custodian ) ERC20ImplUpgradeable(_custodian) public { name = _name; symbol = _symbol; decimals = _decimals; } // PUBLIC FUNCTIONS // (ERC20Interface) /** @notice Returns the total token supply. * * @return the total token supply. */ function totalSupply() public view returns (uint256) { return erc20Impl.totalSupply(); } /** @notice Returns the account balance of another account with address * `_owner`. * * @return balance the balance of account with address `_owner`. */ function balanceOf(address _owner) public view returns (uint256 balance) { return erc20Impl.balanceOf(_owner); } /** @dev Internal use only. */ function emitTransfer(address _from, address _to, uint256 _value) public onlyImpl { emit Transfer(_from, _to, _value); } /** @notice Transfers `_value` amount of tokens to address `_to`. * * @dev Will fire the `Transfer` event. Will revert if the `_from` * account balance does not have enough tokens to spend. * * @return success true if transfer completes. */ function transfer(address _to, uint256 _value) public returns (bool success) { return erc20Impl.transferWithSender(msg.sender, _to, _value); } /** @notice Transfers `_value` amount of tokens from address `_from` * to address `_to`. * * @dev Will fire the `Transfer` event. Will revert unless the `_from` * account has deliberately authorized the sender of the message * via some mechanism. * * @return success true if transfer completes. */ function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) { return erc20Impl.transferFromWithSender(msg.sender, _from, _to, _value); } /** @dev Internal use only. */ function emitApproval(address _owner, address _spender, uint256 _value) public onlyImpl { emit Approval(_owner, _spender, _value); } /** @notice Allows `_spender` to withdraw from your account multiple times, * up to the `_value` amount. If this function is called again it * overwrites the current allowance with _value. * * @dev Will fire the `Approval` event. * * @return success true if approval completes. */ function approve(address _spender, uint256 _value) public returns (bool success) { return erc20Impl.approveWithSender(msg.sender, _spender, _value); } /** @notice Increases the amount `_spender` is allowed to withdraw from * your account. * This function is implemented to avoid the race condition in standard * ERC20 contracts surrounding the `approve` method. * * @dev Will fire the `Approval` event. This function should be used instead of * `approve`. * * @return success true if approval completes. */ function increaseApproval(address _spender, uint256 _addedValue) public returns (bool success) { return erc20Impl.increaseApprovalWithSender(msg.sender, _spender, _addedValue); } /** @notice Decreases the amount `_spender` is allowed to withdraw from * your account. This function is implemented to avoid the race * condition in standard ERC20 contracts surrounding the `approve` method. * * @dev Will fire the `Approval` event. This function should be used * instead of `approve`. * * @return success true if approval completes. */ function decreaseApproval(address _spender, uint256 _subtractedValue) public returns (bool success) { return erc20Impl.decreaseApprovalWithSender(msg.sender, _spender, _subtractedValue); } /** @notice Returns how much `_spender` is currently allowed to spend from * `_owner`'s balance. * * @return remaining the remaining allowance. */ function allowance(address _owner, address _spender) public view returns (uint256 remaining) { return erc20Impl.allowance(_owner, _spender); } } /** @title ERC20 compliant token intermediary contract holding core logic. * * @notice This contract serves as an intermediary between the exposed ERC20 * interface in ERC20Proxy and the store of balances in ERC20Store. This * contract contains core logic that the proxy can delegate to * and that the store is called by. * * @dev This contract contains the core logic to implement the * ERC20 specification as well as several extensions. * 1. Changes to the token supply. * 2. Batched transfers. * 3. Relative changes to spending approvals. * 4. Delegated transfer control ('sweeping'). * * @author Gemini Trust Company, LLC */ contract ERC20Impl is CustodianUpgradeable { // TYPES /// @dev The struct type for pending increases to the token supply (print). struct PendingPrint { address receiver; uint256 value; } // MEMBERS /// @dev The reference to the proxy. ERC20Proxy public erc20Proxy; /// @dev The reference to the store. ERC20Store public erc20Store; /// @dev The sole authorized caller of delegated transfer control ('sweeping'). address public sweeper; /** @dev The static message to be signed by an external account that * signifies their permission to forward their balance to any arbitrary * address. This is used to consolidate the control of all accounts * backed by a shared keychain into the control of a single key. * Initialized as the concatenation of the address of this contract * and the word "sweep". This concatenation is done to prevent a replay * attack in a subsequent contract, where the sweep message could * potentially be replayed to re-enable sweeping ability. */ bytes32 public sweepMsg; /** @dev The mapping that stores whether the address in question has * enabled sweeping its contents to another account or not. * If an address maps to "true", it has already enabled sweeping, * and thus does not need to re-sign the `sweepMsg` to enact the sweep. */ mapping (address => bool) public sweptSet; /// @dev The map of lock ids to pending token increases. mapping (bytes32 => PendingPrint) public pendingPrintMap; // CONSTRUCTOR function ERC20Impl( address _erc20Proxy, address _erc20Store, address _custodian, address _sweeper ) CustodianUpgradeable(_custodian) public { require(_sweeper != 0); erc20Proxy = ERC20Proxy(_erc20Proxy); erc20Store = ERC20Store(_erc20Store); sweeper = _sweeper; sweepMsg = keccak256(address(this), "sweep"); } // MODIFIERS modifier onlyProxy { require(msg.sender == address(erc20Proxy)); _; } modifier onlySweeper { require(msg.sender == sweeper); _; } /** @notice Core logic of the ERC20 `approve` function. * * @dev This function can only be called by the referenced proxy, * which has an `approve` function. * Every argument passed to that function as well as the original * `msg.sender` gets passed to this function. * NOTE: approvals for the zero address (unspendable) are disallowed. * * @param _sender The address initiating the approval in proxy. */ function approveWithSender( address _sender, address _spender, uint256 _value ) public onlyProxy returns (bool success) { require(_spender != address(0)); // disallow unspendable approvals erc20Store.setAllowance(_sender, _spender, _value); erc20Proxy.emitApproval(_sender, _spender, _value); return true; } /** @notice Core logic of the `increaseApproval` function. * * @dev This function can only be called by the referenced proxy, * which has an `increaseApproval` function. * Every argument passed to that function as well as the original * `msg.sender` gets passed to this function. * NOTE: approvals for the zero address (unspendable) are disallowed. * * @param _sender The address initiating the approval. */ function increaseApprovalWithSender( address _sender, address _spender, uint256 _addedValue ) public onlyProxy returns (bool success) { require(_spender != address(0)); // disallow unspendable approvals uint256 currentAllowance = erc20Store.allowed(_sender, _spender); uint256 newAllowance = currentAllowance + _addedValue; require(newAllowance >= currentAllowance); erc20Store.setAllowance(_sender, _spender, newAllowance); erc20Proxy.emitApproval(_sender, _spender, newAllowance); return true; } /** @notice Core logic of the `decreaseApproval` function. * * @dev This function can only be called by the referenced proxy, * which has a `decreaseApproval` function. * Every argument passed to that function as well as the original * `msg.sender` gets passed to this function. * NOTE: approvals for the zero address (unspendable) are disallowed. * * @param _sender The address initiating the approval. */ function decreaseApprovalWithSender( address _sender, address _spender, uint256 _subtractedValue ) public onlyProxy returns (bool success) { require(_spender != address(0)); // disallow unspendable approvals uint256 currentAllowance = erc20Store.allowed(_sender, _spender); uint256 newAllowance = currentAllowance - _subtractedValue; require(newAllowance <= currentAllowance); erc20Store.setAllowance(_sender, _spender, newAllowance); erc20Proxy.emitApproval(_sender, _spender, newAllowance); return true; } /** @notice Requests an increase in the token supply, with the newly created * tokens to be added to the balance of the specified account. * * @dev Returns a unique lock id associated with the request. * Anyone can call this function, but confirming the request is authorized * by the custodian. * NOTE: printing to the zero address is disallowed. * * @param _receiver The receiving address of the print, if confirmed. * @param _value The number of tokens to add to the total supply and the * balance of the receiving address, if confirmed. * * @return lockId A unique identifier for this request. */ function requestPrint(address _receiver, uint256 _value) public returns (bytes32 lockId) { require(_receiver != address(0)); lockId = generateLockId(); pendingPrintMap[lockId] = PendingPrint({ receiver: _receiver, value: _value }); emit PrintingLocked(lockId, _receiver, _value); } /** @notice Confirms a pending increase in the token supply. * * @dev When called by the custodian with a lock id associated with a * pending increase, the amount requested to be printed in the print request * is printed to the receiving address specified in that same request. * NOTE: this function will not execute any print that would overflow the * total supply, but it will not revert either. * * @param _lockId The identifier of a pending print request. */ function confirmPrint(bytes32 _lockId) public onlyCustodian { PendingPrint storage print = pendingPrintMap[_lockId]; // reject ‘null’ results from the map lookup // this can only be the case if an unknown `_lockId` is received address receiver = print.receiver; require (receiver != address(0)); uint256 value = print.value; delete pendingPrintMap[_lockId]; uint256 supply = erc20Store.totalSupply(); uint256 newSupply = supply + value; if (newSupply >= supply) { erc20Store.setTotalSupply(newSupply); erc20Store.addBalance(receiver, value); emit PrintingConfirmed(_lockId, receiver, value); erc20Proxy.emitTransfer(address(0), receiver, value); } } /** @notice Burns the specified value from the sender's balance. * * @dev Sender's balanced is subtracted by the amount they wish to burn. * * @param _value The amount to burn. * * @return success true if the burn succeeded. */ function burn(uint256 _value) public returns (bool success) { uint256 balanceOfSender = erc20Store.balances(msg.sender); require(_value <= balanceOfSender); erc20Store.setBalance(msg.sender, balanceOfSender - _value); erc20Store.setTotalSupply(erc20Store.totalSupply() - _value); erc20Proxy.emitTransfer(msg.sender, address(0), _value); return true; } /** @notice A function for a sender to issue multiple transfers to multiple * different addresses at once. This function is implemented for gas * considerations when someone wishes to transfer, as one transaction is * cheaper than issuing several distinct individual `transfer` transactions. * * @dev By specifying a set of destination addresses and values, the * sender can issue one transaction to transfer multiple amounts to * distinct addresses, rather than issuing each as a separate * transaction. The `_tos` and `_values` arrays must be equal length, and * an index in one array corresponds to the same index in the other array * (e.g. `_tos[0]` will receive `_values[0]`, `_tos[1]` will receive * `_values[1]`, and so on.) * NOTE: transfers to the zero address are disallowed. * * @param _tos The destination addresses to receive the transfers. * @param _values The values for each destination address. * @return success If transfers succeeded. */ function batchTransfer(address[] _tos, uint256[] _values) public returns (bool success) { require(_tos.length == _values.length); uint256 numTransfers = _tos.length; uint256 senderBalance = erc20Store.balances(msg.sender); for (uint256 i = 0; i < numTransfers; i++) { address to = _tos[i]; require(to != address(0)); uint256 v = _values[i]; require(senderBalance >= v); if (msg.sender != to) { senderBalance -= v; erc20Store.addBalance(to, v); } erc20Proxy.emitTransfer(msg.sender, to, v); } erc20Store.setBalance(msg.sender, senderBalance); return true; } /** @notice Enables the delegation of transfer control for many * accounts to the sweeper account, transferring any balances * as well to the given destination. * * @dev An account delegates transfer control by signing the * value of `sweepMsg`. The sweeper account is the only authorized * caller of this function, so it must relay signatures on behalf * of accounts that delegate transfer control to it. Enabling * delegation is idempotent and permanent. If the account has a * balance at the time of enabling delegation, its balance is * also transfered to the given destination account `_to`. * NOTE: transfers to the zero address are disallowed. * * @param _vs The array of recovery byte components of the ECDSA signatures. * @param _rs The array of 'R' components of the ECDSA signatures. * @param _ss The array of 'S' components of the ECDSA signatures. * @param _to The destination for swept balances. */ function enableSweep(uint8[] _vs, bytes32[] _rs, bytes32[] _ss, address _to) public onlySweeper { require(_to != address(0)); require((_vs.length == _rs.length) && (_vs.length == _ss.length)); uint256 numSignatures = _vs.length; uint256 sweptBalance = 0; for (uint256 i=0; i<numSignatures; ++i) { address from = ecrecover(sweepMsg, _vs[i], _rs[i], _ss[i]); // ecrecover returns 0 on malformed input if (from != address(0)) { sweptSet[from] = true; uint256 fromBalance = erc20Store.balances(from); if (fromBalance > 0) { sweptBalance += fromBalance; erc20Store.setBalance(from, 0); erc20Proxy.emitTransfer(from, _to, fromBalance); } } } if (sweptBalance > 0) { erc20Store.addBalance(_to, sweptBalance); } } /** @notice For accounts that have delegated, transfer control * to the sweeper, this function transfers their balances to the given * destination. * * @dev The sweeper account is the only authorized caller of * this function. This function accepts an array of addresses to have their * balances transferred for gas efficiency purposes. * NOTE: any address for an account that has not been previously enabled * will be ignored. * NOTE: transfers to the zero address are disallowed. * * @param _froms The addresses to have their balances swept. * @param _to The destination address of all these transfers. */ function replaySweep(address[] _froms, address _to) public onlySweeper { require(_to != address(0)); uint256 lenFroms = _froms.length; uint256 sweptBalance = 0; for (uint256 i=0; i<lenFroms; ++i) { address from = _froms[i]; if (sweptSet[from]) { uint256 fromBalance = erc20Store.balances(from); if (fromBalance > 0) { sweptBalance += fromBalance; erc20Store.setBalance(from, 0); erc20Proxy.emitTransfer(from, _to, fromBalance); } } } if (sweptBalance > 0) { erc20Store.addBalance(_to, sweptBalance); } } /** @notice Core logic of the ERC20 `transferFrom` function. * * @dev This function can only be called by the referenced proxy, * which has a `transferFrom` function. * Every argument passed to that function as well as the original * `msg.sender` gets passed to this function. * NOTE: transfers to the zero address are disallowed. * * @param _sender The address initiating the transfer in proxy. */ function transferFromWithSender( address _sender, address _from, address _to, uint256 _value ) public onlyProxy returns (bool success) { require(_to != address(0)); // ensure burn is the cannonical transfer to 0x0 uint256 balanceOfFrom = erc20Store.balances(_from); require(_value <= balanceOfFrom); uint256 senderAllowance = erc20Store.allowed(_from, _sender); require(_value <= senderAllowance); erc20Store.setBalance(_from, balanceOfFrom - _value); erc20Store.addBalance(_to, _value); erc20Store.setAllowance(_from, _sender, senderAllowance - _value); erc20Proxy.emitTransfer(_from, _to, _value); return true; } /** @notice Core logic of the ERC20 `transfer` function. * * @dev This function can only be called by the referenced proxy, * which has a `transfer` function. * Every argument passed to that function as well as the original * `msg.sender` gets passed to this function. * NOTE: transfers to the zero address are disallowed. * * @param _sender The address initiating the transfer in proxy. */ function transferWithSender( address _sender, address _to, uint256 _value ) public onlyProxy returns (bool success) { require(_to != address(0)); // ensure burn is the cannonical transfer to 0x0 uint256 balanceOfSender = erc20Store.balances(_sender); require(_value <= balanceOfSender); erc20Store.setBalance(_sender, balanceOfSender - _value); erc20Store.addBalance(_to, _value); erc20Proxy.emitTransfer(_sender, _to, _value); return true; } // METHODS (ERC20 sub interface impl.) /// @notice Core logic of the ERC20 `totalSupply` function. function totalSupply() public view returns (uint256) { return erc20Store.totalSupply(); } /// @notice Core logic of the ERC20 `balanceOf` function. function balanceOf(address _owner) public view returns (uint256 balance) { return erc20Store.balances(_owner); } /// @notice Core logic of the ERC20 `allowance` function. function allowance(address _owner, address _spender) public view returns (uint256 remaining) { return erc20Store.allowed(_owner, _spender); } // EVENTS /// @dev Emitted by successful `requestPrint` calls. event PrintingLocked(bytes32 _lockId, address _receiver, uint256 _value); /// @dev Emitted by successful `confirmPrint` calls. event PrintingConfirmed(bytes32 _lockId, address _receiver, uint256 _value); } /** @title ERC20 compliant token balance store. * * @notice This contract serves as the store of balances, allowances, and * supply for the ERC20 compliant token. No business logic exists here. * * @dev This contract contains no business logic and instead * is the final destination for any change in balances, allowances, or token * supply. This contract is upgradeable in the sense that its custodian can * update the `erc20Impl` address, thus redirecting the source of logic that * determines how the balances will be updated. * * @author Gemini Trust Company, LLC */ contract ERC20Store is ERC20ImplUpgradeable { // MEMBERS /// @dev The total token supply. uint256 public totalSupply; /// @dev The mapping of balances. mapping (address => uint256) public balances; /// @dev The mapping of allowances. mapping (address => mapping (address => uint256)) public allowed; // CONSTRUCTOR function ERC20Store(address _custodian) ERC20ImplUpgradeable(_custodian) public { totalSupply = 0; } // PUBLIC FUNCTIONS // (ERC20 Ledger) /** @notice The function to set the total supply of tokens. * * @dev Intended for use by token implementation functions * that update the total supply. The only authorized caller * is the active implementation. * * @param _newTotalSupply the value to set as the new total supply */ function setTotalSupply( uint256 _newTotalSupply ) public onlyImpl { totalSupply = _newTotalSupply; } /** @notice Sets how much `_owner` allows `_spender` to transfer on behalf * of `_owner`. * * @dev Intended for use by token implementation functions * that update spending allowances. The only authorized caller * is the active implementation. * * @param _owner The account that will allow an on-behalf-of spend. * @param _spender The account that will spend on behalf of the owner. * @param _value The limit of what can be spent. */ function setAllowance( address _owner, address _spender, uint256 _value ) public onlyImpl { allowed[_owner][_spender] = _value; } /** @notice Sets the balance of `_owner` to `_newBalance`. * * @dev Intended for use by token implementation functions * that update balances. The only authorized caller * is the active implementation. * * @param _owner The account that will hold a new balance. * @param _newBalance The balance to set. */ function setBalance( address _owner, uint256 _newBalance ) public onlyImpl { balances[_owner] = _newBalance; } /** @notice Adds `_balanceIncrease` to `_owner`'s balance. * * @dev Intended for use by token implementation functions * that update balances. The only authorized caller * is the active implementation. * WARNING: the caller is responsible for preventing overflow. * * @param _owner The account that will hold a new balance. * @param _balanceIncrease The balance to add. */ function addBalance( address _owner, uint256 _balanceIncrease ) public onlyImpl { balances[_owner] = balances[_owner] + _balanceIncrease; } }