Add enhanced Wallet solidity code
This commit is contained in:
		
							parent
							
								
									09334ca3bd
								
							
						
					
					
						commit
						63137b1548
					
				
							
								
								
									
										460
									
								
								js/src/contracts/snippets/enhanced-wallet.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										460
									
								
								js/src/contracts/snippets/enhanced-wallet.sol
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,460 @@ | ||||
| //sol Wallet | ||||
| // Multi-sig, daily-limited account proxy/wallet. | ||||
| // @authors: | ||||
| // Gav Wood <g@ethdev.com> | ||||
| // inheritable "property" contract that enables methods to be protected by requiring the acquiescence of either a | ||||
| // single, or, crucially, each of a number of, designated owners. | ||||
| // usage: | ||||
| // use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by | ||||
| // some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the | ||||
| // interior is executed. | ||||
| pragma solidity ^0.4.6; | ||||
| 
 | ||||
| contract multisig { | ||||
|     // EVENTS | ||||
| 
 | ||||
|     // this contract can accept a confirmation, in which case | ||||
|     // we record owner and operation (hash) alongside it. | ||||
|     event Confirmation(address owner, bytes32 operation); | ||||
|     event Revoke(address owner, bytes32 operation); | ||||
| 
 | ||||
|     // some others are in the case of an owner changing. | ||||
|     event OwnerChanged(address oldOwner, address newOwner); | ||||
|     event OwnerAdded(address newOwner); | ||||
|     event OwnerRemoved(address oldOwner); | ||||
| 
 | ||||
|     // the last one is emitted if the required signatures change | ||||
|     event RequirementChanged(uint newRequirement); | ||||
| 
 | ||||
|     // Funds has arrived into the wallet (record how much). | ||||
|     event Deposit(address _from, uint value); | ||||
|     // Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going). | ||||
|     event SingleTransact(address owner, uint value, address to, bytes data); | ||||
|     // Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going). | ||||
|     event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data); | ||||
|     // Confirmation still needed for a transaction. | ||||
|     event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data); | ||||
| } | ||||
| 
 | ||||
| contract multisigAbi is multisig { | ||||
|     function isOwner(address _addr) returns (bool); | ||||
| 
 | ||||
|     function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool); | ||||
| 
 | ||||
|     function confirm(bytes32 _h) returns(bool); | ||||
| 
 | ||||
|     // (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today. | ||||
|     function setDailyLimit(uint _newLimit); | ||||
| 
 | ||||
|     function addOwner(address _owner); | ||||
| 
 | ||||
|     function removeOwner(address _owner); | ||||
| 
 | ||||
|     function changeRequirement(uint _newRequired); | ||||
| 
 | ||||
|     // Revokes a prior confirmation of the given operation | ||||
|     function revoke(bytes32 _operation); | ||||
| 
 | ||||
|     function changeOwner(address _from, address _to); | ||||
| 
 | ||||
|     function execute(address _to, uint _value, bytes _data) returns(bool); | ||||
| } | ||||
| 
 | ||||
| contract WalletLibrary is multisig { | ||||
|     // TYPES | ||||
| 
 | ||||
|     // struct for the status of a pending operation. | ||||
|     struct PendingState { | ||||
|         uint yetNeeded; | ||||
|         uint ownersDone; | ||||
|         uint index; | ||||
|     } | ||||
| 
 | ||||
|     // Transaction structure to remember details of transaction lest it need be saved for a later call. | ||||
|     struct Transaction { | ||||
|         address to; | ||||
|         uint value; | ||||
|         bytes data; | ||||
|     } | ||||
| 
 | ||||
|     /****************************** | ||||
|      ***** MULTI OWNED SECTION **** | ||||
|      ******************************/ | ||||
| 
 | ||||
|     // MODIFIERS | ||||
| 
 | ||||
|     // simple single-sig function modifier. | ||||
|     modifier onlyowner { | ||||
|         if (isOwner(msg.sender)) | ||||
|             _; | ||||
|     } | ||||
|     // multi-sig function modifier: the operation must have an intrinsic hash in order | ||||
|     // that later attempts can be realised as the same underlying operation and | ||||
|     // thus count as confirmations. | ||||
|     modifier onlymanyowners(bytes32 _operation) { | ||||
|         if (confirmAndCheck(_operation)) | ||||
|             _; | ||||
|     } | ||||
| 
 | ||||
|     // METHODS | ||||
| 
 | ||||
|     // constructor is given number of sigs required to do protected "onlymanyowners" transactions | ||||
|     // as well as the selection of addresses capable of confirming them. | ||||
|     function initMultiowned(address[] _owners, uint _required) { | ||||
|         m_numOwners = _owners.length + 1; | ||||
|         m_owners[1] = uint(msg.sender); | ||||
|         m_ownerIndex[uint(msg.sender)] = 1; | ||||
|         m_required = _required; | ||||
| 
 | ||||
|         for (uint i = 0; i < _owners.length; ++i) | ||||
|         { | ||||
|             m_owners[2 + i] = uint(_owners[i]); | ||||
|             m_ownerIndex[uint(_owners[i])] = 2 + i; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Revokes a prior confirmation of the given operation | ||||
|     function revoke(bytes32 _operation) { | ||||
|         uint ownerIndex = m_ownerIndex[uint(msg.sender)]; | ||||
|         // make sure they're an owner | ||||
|         if (ownerIndex == 0) return; | ||||
|         uint ownerIndexBit = 2**ownerIndex; | ||||
|         var pending = m_pending[_operation]; | ||||
|         if (pending.ownersDone & ownerIndexBit > 0) { | ||||
|             pending.yetNeeded++; | ||||
|             pending.ownersDone -= ownerIndexBit; | ||||
|             Revoke(msg.sender, _operation); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Replaces an owner `_from` with another `_to`. | ||||
|     function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) { | ||||
|         if (isOwner(_to)) return; | ||||
|         uint ownerIndex = m_ownerIndex[uint(_from)]; | ||||
|         if (ownerIndex == 0) return; | ||||
| 
 | ||||
|         clearPending(); | ||||
|         m_owners[ownerIndex] = uint(_to); | ||||
|         m_ownerIndex[uint(_from)] = 0; | ||||
|         m_ownerIndex[uint(_to)] = ownerIndex; | ||||
|         OwnerChanged(_from, _to); | ||||
|     } | ||||
| 
 | ||||
|     function addOwner(address _owner) onlymanyowners(sha3(msg.data)) { | ||||
|         if (isOwner(_owner)) return; | ||||
| 
 | ||||
|         clearPending(); | ||||
|         if (m_numOwners >= c_maxOwners) | ||||
|             reorganizeOwners(); | ||||
|         if (m_numOwners >= c_maxOwners) | ||||
|             return; | ||||
|         m_numOwners++; | ||||
|         m_owners[m_numOwners] = uint(_owner); | ||||
|         m_ownerIndex[uint(_owner)] = m_numOwners; | ||||
|         OwnerAdded(_owner); | ||||
|     } | ||||
| 
 | ||||
|     function removeOwner(address _owner) onlymanyowners(sha3(msg.data)) { | ||||
|         uint ownerIndex = m_ownerIndex[uint(_owner)]; | ||||
|         if (ownerIndex == 0) return; | ||||
|         if (m_required > m_numOwners - 1) return; | ||||
| 
 | ||||
|         m_owners[ownerIndex] = 0; | ||||
|         m_ownerIndex[uint(_owner)] = 0; | ||||
|         clearPending(); | ||||
|         reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot | ||||
|         OwnerRemoved(_owner); | ||||
|     } | ||||
| 
 | ||||
|     function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) { | ||||
|         if (_newRequired > m_numOwners) return; | ||||
|         m_required = _newRequired; | ||||
|         clearPending(); | ||||
|         RequirementChanged(_newRequired); | ||||
|     } | ||||
| 
 | ||||
|     function isOwner(address _addr) returns (bool) { | ||||
|         return m_ownerIndex[uint(_addr)] > 0; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) { | ||||
|         var pending = m_pending[_operation]; | ||||
|         uint ownerIndex = m_ownerIndex[uint(_owner)]; | ||||
| 
 | ||||
|         // make sure they're an owner | ||||
|         if (ownerIndex == 0) return false; | ||||
| 
 | ||||
|         // determine the bit to set for this owner. | ||||
|         uint ownerIndexBit = 2**ownerIndex; | ||||
|         return !(pending.ownersDone & ownerIndexBit == 0); | ||||
|     } | ||||
| 
 | ||||
|     // INTERNAL METHODS | ||||
| 
 | ||||
|     function confirmAndCheck(bytes32 _operation) internal returns (bool) { | ||||
|         // determine what index the present sender is: | ||||
|         uint ownerIndex = m_ownerIndex[uint(msg.sender)]; | ||||
|         // make sure they're an owner | ||||
|         if (ownerIndex == 0) return; | ||||
| 
 | ||||
|         var pending = m_pending[_operation]; | ||||
|         // if we're not yet working on this operation, switch over and reset the confirmation status. | ||||
|         if (pending.yetNeeded == 0) { | ||||
|             // reset count of confirmations needed. | ||||
|             pending.yetNeeded = m_required; | ||||
|             // reset which owners have confirmed (none) - set our bitmap to 0. | ||||
|             pending.ownersDone = 0; | ||||
|             pending.index = m_pendingIndex.length++; | ||||
|             m_pendingIndex[pending.index] = _operation; | ||||
|         } | ||||
|         // determine the bit to set for this owner. | ||||
|         uint ownerIndexBit = 2**ownerIndex; | ||||
|         // make sure we (the message sender) haven't confirmed this operation previously. | ||||
|         if (pending.ownersDone & ownerIndexBit == 0) { | ||||
|             Confirmation(msg.sender, _operation); | ||||
|             // ok - check if count is enough to go ahead. | ||||
|             if (pending.yetNeeded <= 1) { | ||||
|                 // enough confirmations: reset and run interior. | ||||
|                 delete m_pendingIndex[m_pending[_operation].index]; | ||||
|                 delete m_pending[_operation]; | ||||
|                 return true; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 // not enough: record that this owner in particular confirmed. | ||||
|                 pending.yetNeeded--; | ||||
|                 pending.ownersDone |= ownerIndexBit; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     function reorganizeOwners() private { | ||||
|         uint free = 1; | ||||
|         while (free < m_numOwners) | ||||
|         { | ||||
|             while (free < m_numOwners && m_owners[free] != 0) free++; | ||||
|             while (m_numOwners > 1 && m_owners[m_numOwners] == 0) m_numOwners--; | ||||
|             if (free < m_numOwners && m_owners[m_numOwners] != 0 && m_owners[free] == 0) | ||||
|             { | ||||
|                 m_owners[free] = m_owners[m_numOwners]; | ||||
|                 m_ownerIndex[m_owners[free]] = free; | ||||
|                 m_owners[m_numOwners] = 0; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     function clearPending() internal { | ||||
|         uint length = m_pendingIndex.length; | ||||
|         for (uint i = 0; i < length; ++i) | ||||
|             if (m_pendingIndex[i] != 0) | ||||
|                 delete m_pending[m_pendingIndex[i]]; | ||||
|         delete m_pendingIndex; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /****************************** | ||||
|      ****** DAY LIMIT SECTION ***** | ||||
|      ******************************/ | ||||
| 
 | ||||
|     // MODIFIERS | ||||
| 
 | ||||
|     // simple modifier for daily limit. | ||||
|     modifier limitedDaily(uint _value) { | ||||
|         if (underLimit(_value)) | ||||
|             _; | ||||
|     } | ||||
| 
 | ||||
|     // METHODS | ||||
| 
 | ||||
|     // constructor - stores initial daily limit and records the present day's index. | ||||
|     function initDaylimit(uint _limit) { | ||||
|         m_dailyLimit = _limit; | ||||
|         m_lastDay = today(); | ||||
|     } | ||||
|     // (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today. | ||||
|     function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) { | ||||
|         m_dailyLimit = _newLimit; | ||||
|     } | ||||
|     // resets the amount already spent today. needs many of the owners to confirm. | ||||
|     function resetSpentToday() onlymanyowners(sha3(msg.data)) { | ||||
|         m_spentToday = 0; | ||||
|     } | ||||
| 
 | ||||
|     // INTERNAL METHODS | ||||
| 
 | ||||
|     // checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and | ||||
|     // returns true. otherwise just returns false. | ||||
|     function underLimit(uint _value) internal onlyowner returns (bool) { | ||||
|         // reset the spend limit if we're on a different day to last time. | ||||
|         if (today() > m_lastDay) { | ||||
|             m_spentToday = 0; | ||||
|             m_lastDay = today(); | ||||
|         } | ||||
|         // check to see if there's enough left - if so, subtract and return true. | ||||
|         // overflow protection                    // dailyLimit check | ||||
|         if (m_spentToday + _value >= m_spentToday && m_spentToday + _value <= m_dailyLimit) { | ||||
|             m_spentToday += _value; | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     // determines today's index. | ||||
|     function today() private constant returns (uint) { return now / 1 days; } | ||||
| 
 | ||||
| 
 | ||||
|     /****************************** | ||||
|      ********* WALLET SECTION ***** | ||||
|      ******************************/ | ||||
| 
 | ||||
|     // METHODS | ||||
| 
 | ||||
|     // constructor - just pass on the owner array to the multiowned and | ||||
|     // the limit to daylimit | ||||
|     function initWallet(address[] _owners, uint _required, uint _daylimit) { | ||||
|         initMultiowned(_owners, _required); | ||||
|         initDaylimit(_daylimit) ; | ||||
|     } | ||||
| 
 | ||||
|     // kills the contract sending everything to `_to`. | ||||
|     function kill(address _to) onlymanyowners(sha3(msg.data)) { | ||||
|         suicide(_to); | ||||
|     } | ||||
| 
 | ||||
|     // Outside-visible transact entry point. Executes transaction immediately if below daily spend limit. | ||||
|     // If not, goes into multisig process. We provide a hash on return to allow the sender to provide | ||||
|     // shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value | ||||
|     // and _data arguments). They still get the option of using them if they want, anyways. | ||||
|     function execute(address _to, uint _value, bytes _data) onlyowner returns(bool _callValue) { | ||||
|         // first, take the opportunity to check that we're under the daily limit. | ||||
|         if (underLimit(_value)) { | ||||
|             SingleTransact(msg.sender, _value, _to, _data); | ||||
|             // yes - just execute the call. | ||||
|             _callValue =_to.call.value(_value)(_data); | ||||
|         } else { | ||||
|             // determine our operation hash. | ||||
|             bytes32 _r = sha3(msg.data, block.number); | ||||
|             if (!confirm(_r) && m_txs[_r].to == 0) { | ||||
|                 m_txs[_r].to = _to; | ||||
|                 m_txs[_r].value = _value; | ||||
|                 m_txs[_r].data = _data; | ||||
|                 ConfirmationNeeded(_r, msg.sender, _value, _to, _data); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // confirm a transaction through just the hash. we use the previous transactions map, m_txs, in order | ||||
|     // to determine the body of the transaction from the hash provided. | ||||
|     function confirm(bytes32 _h) onlymanyowners(_h) returns (bool) { | ||||
|         if (m_txs[_h].to != 0) { | ||||
|             m_txs[_h].to.call.value(m_txs[_h].value)(m_txs[_h].data); | ||||
|             MultiTransact(msg.sender, _h, m_txs[_h].value, m_txs[_h].to, m_txs[_h].data); | ||||
|             delete m_txs[_h]; | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // INTERNAL METHODS | ||||
| 
 | ||||
|     function clearWalletPending() internal { | ||||
|         uint length = m_pendingIndex.length; | ||||
|         for (uint i = 0; i < length; ++i) | ||||
|             delete m_txs[m_pendingIndex[i]]; | ||||
|         clearPending(); | ||||
|     } | ||||
| 
 | ||||
|     // FIELDS | ||||
|     address constant _walletLibrary = 0xcafecafecafecafecafecafecafecafecafecafe; | ||||
| 
 | ||||
|     // the number of owners that must confirm the same operation before it is run. | ||||
|     uint m_required; | ||||
|     // pointer used to find a free slot in m_owners | ||||
|     uint m_numOwners; | ||||
| 
 | ||||
|     uint public m_dailyLimit; | ||||
|     uint public m_spentToday; | ||||
|     uint public m_lastDay; | ||||
| 
 | ||||
|     // list of owners | ||||
|     uint[256] m_owners; | ||||
|     uint constant c_maxOwners = 250; | ||||
| 
 | ||||
|     // index on the list of owners to allow reverse lookup | ||||
|     mapping(uint => uint) m_ownerIndex; | ||||
|     // the ongoing operations. | ||||
|     mapping(bytes32 => PendingState) m_pending; | ||||
|     bytes32[] m_pendingIndex; | ||||
| 
 | ||||
|     // pending transactions we have at present. | ||||
|     mapping (bytes32 => Transaction) m_txs; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| contract Wallet is multisig { | ||||
| 
 | ||||
|     // WALLET CONSTRUCTOR | ||||
|     //   calls the `initWallet` method of the Library in this context | ||||
|     function Wallet(address[] _owners, uint _required, uint _daylimit) { | ||||
|         // Signature of the Wallet Library's init function | ||||
|         bytes4 sig = bytes4(sha3("initWallet(address[],uint256,uint256)")); | ||||
|         address target = _walletLibrary; | ||||
| 
 | ||||
|         // Compute the size of the call data : arrays has 2 | ||||
|         // 32bytes for offset and length, plus 32bytes per element ; | ||||
|         // plus 2 32bytes for each uint | ||||
|         uint argarraysize = (2 + _owners.length); | ||||
|         uint argsize = (2 + argarraysize) * 32; | ||||
| 
 | ||||
|         assembly { | ||||
|             // Add the signature first to memory | ||||
|             mstore(0x0, sig) | ||||
|             // Add the call data, which is at the end of the | ||||
|             // code | ||||
|             codecopy(0x4,  sub(codesize, argsize), argsize) | ||||
|             // Delegate call to the library | ||||
|             delegatecall(sub(gas, 10000), target, 0x0, add(argsize, 0x4), 0x0, 0x0) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // METHODS | ||||
| 
 | ||||
|     // gets called when no other function matches | ||||
|     function() payable { | ||||
|         // just being sent some cash? | ||||
|         if (msg.value > 0) | ||||
|             Deposit(msg.sender, msg.value); | ||||
|         else if (msg.data.length > 0) | ||||
|             _walletLibrary.delegatecall(msg.data); | ||||
|     } | ||||
| 
 | ||||
|     // Gets an owner by 0-indexed position (using numOwners as the count) | ||||
|     function getOwner(uint ownerIndex) constant returns (address) { | ||||
|         return address(m_owners[ownerIndex + 1]); | ||||
|     } | ||||
| 
 | ||||
|     // As return statement unavailable in fallback, explicit the method here | ||||
| 
 | ||||
|     function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) { | ||||
|         return _walletLibrary.delegatecall(msg.data); | ||||
|     } | ||||
| 
 | ||||
|     function isOwner(address _addr) returns (bool) { | ||||
|         return _walletLibrary.delegatecall(msg.data); | ||||
|     } | ||||
| 
 | ||||
|     // FIELDS | ||||
|     address constant _walletLibrary = 0xcafecafecafecafecafecafecafecafecafecafe; | ||||
| 
 | ||||
|     // the number of owners that must confirm the same operation before it is run. | ||||
|     uint public m_required; | ||||
|     // pointer used to find a free slot in m_owners | ||||
|     uint public m_numOwners; | ||||
| 
 | ||||
|     uint public m_dailyLimit; | ||||
|     uint public m_spentToday; | ||||
|     uint public m_lastDay; | ||||
| 
 | ||||
|     // list of owners | ||||
|     uint[256] m_owners; | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user