mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			366 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Solidity
		
	
	
	
	
	
			
		
		
	
	
			366 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Solidity
		
	
	
	
	
	
| pragma solidity >=0.0;
 | |
| 
 | |
| 
 | |
| /// @title Multisignature wallet - Allows multiple parties to agree on transactions before execution.
 | |
| /// @author Stefan George - <stefan.george@consensys.net>
 | |
| contract MultiSigWallet {
 | |
| 
 | |
|     uint constant public MAX_OWNER_COUNT = 50;
 | |
| 
 | |
|     event Confirmation(address indexed sender, uint indexed transactionId);
 | |
|     event Revocation(address indexed sender, uint indexed transactionId);
 | |
|     event Submission(uint indexed transactionId);
 | |
|     event Execution(uint indexed transactionId);
 | |
|     event ExecutionFailure(uint indexed transactionId);
 | |
|     event Deposit(address indexed sender, uint value);
 | |
|     event OwnerAddition(address indexed owner);
 | |
|     event OwnerRemoval(address indexed owner);
 | |
|     event RequirementChange(uint required);
 | |
| 
 | |
|     mapping (uint => Transaction) public transactions;
 | |
|     mapping (uint => mapping (address => bool)) public confirmations;
 | |
|     mapping (address => bool) public isOwner;
 | |
|     address[] public owners;
 | |
|     uint public required;
 | |
|     uint public transactionCount;
 | |
| 
 | |
|     struct Transaction {
 | |
|         address destination;
 | |
|         uint value;
 | |
|         bytes data;
 | |
|         bool executed;
 | |
|     }
 | |
| 
 | |
|     modifier onlyWallet() {
 | |
|         if (msg.sender != address(this))
 | |
|             revert();
 | |
|         _;
 | |
|     }
 | |
| 
 | |
|     modifier ownerDoesNotExist(address owner) {
 | |
|         if (isOwner[owner])
 | |
|             revert();
 | |
|         _;
 | |
|     }
 | |
| 
 | |
|     modifier ownerExists(address owner) {
 | |
|         if (!isOwner[owner])
 | |
|             revert();
 | |
|         _;
 | |
|     }
 | |
| 
 | |
|     modifier transactionExists(uint transactionId) {
 | |
|         if (transactions[transactionId].destination == address(0))
 | |
|             revert();
 | |
|         _;
 | |
|     }
 | |
| 
 | |
|     modifier confirmed(uint transactionId, address owner) {
 | |
|         if (!confirmations[transactionId][owner])
 | |
|             revert();
 | |
|         _;
 | |
|     }
 | |
| 
 | |
|     modifier notConfirmed(uint transactionId, address owner) {
 | |
|         if (confirmations[transactionId][owner])
 | |
|             revert();
 | |
|         _;
 | |
|     }
 | |
| 
 | |
|     modifier notExecuted(uint transactionId) {
 | |
|         if (transactions[transactionId].executed)
 | |
|             revert();
 | |
|         _;
 | |
|     }
 | |
| 
 | |
|     modifier notNull(address _address) {
 | |
|         if (_address == address(0))
 | |
|             revert();
 | |
|         _;
 | |
|     }
 | |
| 
 | |
|     modifier validRequirement(uint ownerCount, uint _required) {
 | |
|         if (   ownerCount > MAX_OWNER_COUNT
 | |
|             || _required > ownerCount
 | |
|             || _required == 0
 | |
|             || ownerCount == 0)
 | |
|             revert();
 | |
|         _;
 | |
|     }
 | |
| 
 | |
|     /// @dev Contract constructor sets initial owners and required number of confirmations.
 | |
|     /// @param _owners List of initial owners.
 | |
|     /// @param _required Number of required confirmations.
 | |
|     constructor(address[] memory _owners, uint _required)
 | |
|         validRequirement(_owners.length, _required)
 | |
|     {
 | |
|         for (uint i=0; i<_owners.length; i++) {
 | |
|             if (isOwner[_owners[i]] || _owners[i] == address(0))
 | |
|                 revert();
 | |
|             isOwner[_owners[i]] = true;
 | |
|         }
 | |
|         owners = _owners;
 | |
|         required = _required;
 | |
|     }
 | |
| 
 | |
|     /// @dev Receive function allows to deposit ether.
 | |
|     receive()
 | |
|         external
 | |
|         payable
 | |
|     {
 | |
|         if (msg.value > 0)
 | |
|             emit Deposit(msg.sender, msg.value);
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|      * Public functions
 | |
|      */
 | |
| 
 | |
|     /// @dev Allows to add a new owner. Transaction has to be sent by wallet.
 | |
|     /// @param owner Address of new owner.
 | |
|     function addOwner(address owner)
 | |
|         public
 | |
|         onlyWallet
 | |
|         ownerDoesNotExist(owner)
 | |
|         notNull(owner)
 | |
|         validRequirement(owners.length + 1, required)
 | |
|     {
 | |
|         isOwner[owner] = true;
 | |
|         owners.push(owner);
 | |
|         emit OwnerAddition(owner);
 | |
|     }
 | |
| 
 | |
|     /// @dev Allows to remove an owner. Transaction has to be sent by wallet.
 | |
|     /// @param owner Address of owner.
 | |
|     function removeOwner(address owner)
 | |
|         public
 | |
|         onlyWallet
 | |
|         ownerExists(owner)
 | |
|     {
 | |
|         isOwner[owner] = false;
 | |
|         for (uint i=0; i<owners.length - 1; i++)
 | |
|             if (owners[i] == owner) {
 | |
|                 owners[i] = owners[owners.length - 1];
 | |
|                 break;
 | |
|             }
 | |
|         owners.pop();
 | |
|         if (required > owners.length)
 | |
|             changeRequirement(owners.length);
 | |
|         emit OwnerRemoval(owner);
 | |
|     }
 | |
| 
 | |
|     /// @dev Allows to replace an owner with a new owner. Transaction has to be sent by wallet.
 | |
|     /// @param owner Address of owner to be replaced.
 | |
|     /// @param owner Address of new owner.
 | |
|     function replaceOwner(address owner, address newOwner)
 | |
|         public
 | |
|         onlyWallet
 | |
|         ownerExists(owner)
 | |
|         ownerDoesNotExist(newOwner)
 | |
|     {
 | |
|         for (uint i=0; i<owners.length; i++)
 | |
|             if (owners[i] == owner) {
 | |
|                 owners[i] = newOwner;
 | |
|                 break;
 | |
|             }
 | |
|         isOwner[owner] = false;
 | |
|         isOwner[newOwner] = true;
 | |
|         emit OwnerRemoval(owner);
 | |
|         emit OwnerAddition(newOwner);
 | |
|     }
 | |
| 
 | |
|     /// @dev Allows to change the number of required confirmations. Transaction has to be sent by wallet.
 | |
|     /// @param _required Number of required confirmations.
 | |
|     function changeRequirement(uint _required)
 | |
|         public
 | |
|         onlyWallet
 | |
|         validRequirement(owners.length, _required)
 | |
|     {
 | |
|         required = _required;
 | |
|         emit RequirementChange(_required);
 | |
|     }
 | |
| 
 | |
|     /// @dev Allows an owner to submit and confirm a transaction.
 | |
|     /// @param destination Transaction target address.
 | |
|     /// @param value Transaction ether value.
 | |
|     /// @param data Transaction data payload.
 | |
|     /// @return transactionId Returns transaction ID.
 | |
|     function submitTransaction(address destination, uint value, bytes memory data)
 | |
|         public
 | |
|         returns (uint transactionId)
 | |
|     {
 | |
|         transactionId = addTransaction(destination, value, data);
 | |
|         confirmTransaction(transactionId);
 | |
|     }
 | |
| 
 | |
|     /// @dev Allows an owner to confirm a transaction.
 | |
|     /// @param transactionId Transaction ID.
 | |
|     function confirmTransaction(uint transactionId)
 | |
|         public
 | |
|         ownerExists(msg.sender)
 | |
|         transactionExists(transactionId)
 | |
|         notConfirmed(transactionId, msg.sender)
 | |
|     {
 | |
|         confirmations[transactionId][msg.sender] = true;
 | |
|         emit Confirmation(msg.sender, transactionId);
 | |
|         executeTransaction(transactionId);
 | |
|     }
 | |
| 
 | |
|     /// @dev Allows an owner to revoke a confirmation for a transaction.
 | |
|     /// @param transactionId Transaction ID.
 | |
|     function revokeConfirmation(uint transactionId)
 | |
|         public
 | |
|         ownerExists(msg.sender)
 | |
|         confirmed(transactionId, msg.sender)
 | |
|         notExecuted(transactionId)
 | |
|     {
 | |
|         confirmations[transactionId][msg.sender] = false;
 | |
|         emit Revocation(msg.sender, transactionId);
 | |
|     }
 | |
| 
 | |
|     /// @dev Allows anyone to execute a confirmed transaction.
 | |
|     /// @param transactionId Transaction ID.
 | |
|     function executeTransaction(uint transactionId)
 | |
|         virtual
 | |
|         public
 | |
|         notExecuted(transactionId)
 | |
|     {
 | |
|         if (isConfirmed(transactionId)) {
 | |
|             Transaction storage transaction = transactions[transactionId];
 | |
|             (transaction.executed,) = transaction.destination.call{value: transaction.value}(transaction.data);
 | |
|             if (transaction.executed)
 | |
|                 emit Execution(transactionId);
 | |
|             else
 | |
|                 emit ExecutionFailure(transactionId);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /// @dev Returns the confirmation status of a transaction.
 | |
|     /// @param transactionId Transaction ID.
 | |
|     /// @return Confirmation status.
 | |
|     function isConfirmed(uint transactionId)
 | |
|         public
 | |
|         view
 | |
|         returns (bool)
 | |
|     {
 | |
|         uint count = 0;
 | |
|         for (uint i=0; i<owners.length; i++) {
 | |
|             if (confirmations[transactionId][owners[i]])
 | |
|                 count += 1;
 | |
|             if (count == required)
 | |
|                 return true;
 | |
|         }
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|      * Web3 call functions
 | |
|      */
 | |
|     /// @dev Returns number of confirmations of a transaction.
 | |
|     /// @param transactionId Transaction ID.
 | |
|     /// @return count Number of confirmations.
 | |
|     function getConfirmationCount(uint transactionId)
 | |
|         public
 | |
|         view
 | |
|         returns (uint count)
 | |
|     {
 | |
|         for (uint i=0; i<owners.length; i++)
 | |
|             if (confirmations[transactionId][owners[i]])
 | |
|                 count += 1;
 | |
|     }
 | |
| 
 | |
|     /// @dev Returns total number of transactions after filers are applied.
 | |
|     /// @param pending Include pending transactions.
 | |
|     /// @param executed Include executed transactions.
 | |
|     /// @return count Total number of transactions after filters are applied.
 | |
|     function getTransactionCount(bool pending, bool executed)
 | |
|         public
 | |
|         view
 | |
|         returns (uint count)
 | |
|     {
 | |
|         for (uint i=0; i<transactionCount; i++)
 | |
|             if (   pending && !transactions[i].executed
 | |
|                 || executed && transactions[i].executed)
 | |
|                 count += 1;
 | |
|     }
 | |
| 
 | |
|     /// @dev Returns list of owners.
 | |
|     /// @return List of owner addresses.
 | |
|     function getOwners()
 | |
|         public
 | |
|         view
 | |
|         returns (address[] memory)
 | |
|     {
 | |
|         return owners;
 | |
|     }
 | |
| 
 | |
|     /// @dev Returns array with owner addresses, which confirmed transaction.
 | |
|     /// @param transactionId Transaction ID.
 | |
|     /// @return _confirmations Returns array of owner addresses.
 | |
|     function getConfirmations(uint transactionId)
 | |
|         public
 | |
|         view
 | |
|         returns (address[] memory _confirmations)
 | |
|     {
 | |
|         address[] memory confirmationsTemp = new address[](owners.length);
 | |
|         uint count = 0;
 | |
|         uint i;
 | |
|         for (i=0; i<owners.length; i++)
 | |
|             if (confirmations[transactionId][owners[i]]) {
 | |
|                 confirmationsTemp[count] = owners[i];
 | |
|                 count += 1;
 | |
|             }
 | |
|         _confirmations = new address[](count);
 | |
|         for (i=0; i<count; i++)
 | |
|             _confirmations[i] = confirmationsTemp[i];
 | |
|     }
 | |
| 
 | |
|     /// @dev Returns list of transaction IDs in defined range.
 | |
|     /// @param from Index start position of transaction array.
 | |
|     /// @param to Index end position of transaction array.
 | |
|     /// @param pending Include pending transactions.
 | |
|     /// @param executed Include executed transactions.
 | |
|     /// @return _transactionIds Returns array of transaction IDs.
 | |
|     function getTransactionIds(uint from, uint to, bool pending, bool executed)
 | |
|         public
 | |
|         view
 | |
|         returns (uint[] memory _transactionIds)
 | |
|     {
 | |
|         uint[] memory transactionIdsTemp = new uint[](transactionCount);
 | |
|         uint count = 0;
 | |
|         uint i;
 | |
|         for (i=0; i<transactionCount; i++)
 | |
|             if (   pending && !transactions[i].executed
 | |
|                 || executed && transactions[i].executed)
 | |
|             {
 | |
|                 transactionIdsTemp[count] = i;
 | |
|                 count += 1;
 | |
|             }
 | |
|         _transactionIds = new uint[](to - from);
 | |
|         for (i=from; i<to; i++)
 | |
|             _transactionIds[i - from] = transactionIdsTemp[i];
 | |
|     }
 | |
|     /*
 | |
|      * Internal functions
 | |
|      */
 | |
|     /// @dev Adds a new transaction to the transaction mapping, if transaction does not exist yet.
 | |
|     /// @param destination Transaction target address.
 | |
|     /// @param value Transaction ether value.
 | |
|     /// @param data Transaction data payload.
 | |
|     /// @return transactionId Returns transaction ID.
 | |
|     function addTransaction(address destination, uint value, bytes memory data)
 | |
|         internal
 | |
|         notNull(destination)
 | |
|         returns (uint transactionId)
 | |
|     {
 | |
|         transactionId = transactionCount;
 | |
|         transactions[transactionId] = Transaction({
 | |
|             destination: destination,
 | |
|             value: value,
 | |
|             data: data,
 | |
|             executed: false
 | |
|         });
 | |
|         transactionCount += 1;
 | |
|         emit Submission(transactionId);
 | |
|     }
 | |
| } |