diff --git a/contracts/MultiSigTokenWallet.sol b/contracts/MultiSigTokenWallet.sol index d1f12f9..bd1fedc 100644 --- a/contracts/MultiSigTokenWallet.sol +++ b/contracts/MultiSigTokenWallet.sol @@ -1,114 +1,21 @@ pragma solidity ^0.4.15; +import "./MultiSigWallet.sol"; import "./ERC20.sol"; -contract MultiSigTokenWallet { +contract MultiSigTokenWallet is MultiSigWallet { - address[] public owners; address[] public tokens; - mapping (uint => Transaction) public transactions; - mapping (uint => mapping (address => bool)) public confirmations; - uint public transactionCount; - mapping (address => uint) public tokenBalances; - mapping (address => bool) public isOwner; mapping (address => address[]) public userList; - uint public required; uint public nonce; - struct Transaction { - address destination; - uint value; - bytes data; - bool executed; - } - - 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 TokenDeposit(address _token, address indexed _sender, uint _value); - event OwnerAddition(address indexed _owner); - event OwnerRemoval(address indexed _owner); - event RequirementChange(uint _required); - - modifier onlyWallet() { - require (msg.sender == address(this)); - _; - } - - modifier ownerDoesNotExist(address owner) { - require (!isOwner[owner]); - _; - } - - modifier ownerExists(address owner) { - require (isOwner[owner]); - _; - } - - modifier transactionExists(uint transactionId) { - require (transactions[transactionId].destination != 0); - _; - } - - modifier confirmed(uint transactionId, address owner) { - require (confirmations[transactionId][owner]); - _; - } - - modifier notConfirmed(uint transactionId, address owner) { - require(!confirmations[transactionId][owner]); - _; - } - - modifier notExecuted(uint transactionId) { - require (!transactions[transactionId].executed); - _; - } - - modifier notNull(address _address) { - require (_address != 0); - _; - } - - modifier validRequirement(uint ownerCount, uint _required) { - require (ownerCount <= MAX_OWNER_COUNT && _required <= ownerCount && _required != 0 && ownerCount != 0); - _; - } - - /// @dev Fallback function allows to deposit ether. - function() - payable - { - if (msg.value > 0) - Deposit(msg.sender, msg.value); - } /** * Public functions * **/ - /// @dev Contract constructor sets initial owners and required number of confirmations. - /// @param _owners List of initial owners. - /// @param _required Number of required confirmations. - function constructor(address[] _owners, uint _required) - public - validRequirement(_owners.length, _required) - { - require(owners.length == 0 && required == 0); - for (uint i = 0; i < _owners.length; i++) { - require(!isOwner[_owners[i]] && _owners[i] != 0); - isOwner[_owners[i]] = true; - } - owners = _owners; - required = _required; - } - /** * @notice deposit a ERC20 token. The amount of deposit is the allowance set to this contract. * @param _token the token contract address @@ -188,63 +95,6 @@ contract MultiSigTokenWallet { deposit(_from, _amount, _token, _data); } - - /// @dev Allows to add a new owner. Transaction has to be sent by wallet. - /// @param owner Address of new owner. - function addOwner(address owner) - public - onlyWallet - ownerDoesNotExist(owner) - notNull(owner) - validRequirement(owners.length + 1, required) - { - isOwner[owner] = true; - owners.push(owner); - OwnerAddition(owner); - } - - /// @dev Allows to remove an owner. Transaction has to be sent by wallet. - /// @param owner Address of owner. - function removeOwner(address owner) - public - onlyWallet - ownerExists(owner) - { - isOwner[owner] = false; - uint _len = owners.length - 1; - for (uint i = 0; i < _len; i++) { - if (owners[i] == owner) { - owners[i] = owners[owners.length - 1]; - break; - } - } - owners.length -= 1; - if (required > owners.length) - changeRequirement(owners.length); - OwnerRemoval(owner); - } - - /// @dev Allows to replace an owner with a new owner. Transaction has to be sent by wallet. - /// @param owner Address of owner to be replaced. - /// @param 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; - OwnerRemoval(owner); - OwnerAddition(newOwner); - } - /** * @dev gives full ownership of this wallet to `_dest` removing older owners from wallet * @param _dest the address of new controller @@ -263,73 +113,6 @@ contract MultiSigTokenWallet { } } - /// @dev Allows to change the number of required confirmations. Transaction has to be sent by wallet. - /// @param _required Number of required confirmations. - function changeRequirement(uint _required) - public - onlyWallet - validRequirement(owners.length, _required) - { - required = _required; - RequirementChange(_required); - } - - /// @dev Allows an owner to submit and confirm a transaction. - /// @param destination Transaction target address. - /// @param value Transaction ether value. - /// @param data Transaction data payload. - /// @return Returns transaction ID. - function submitTransaction(address destination, uint value, bytes data) - public - returns (uint transactionId) - { - transactionId = addTransaction(destination, value, data); - confirmTransaction(transactionId); - } - - /// @dev Allows an owner to confirm a transaction. - /// @param transactionId Transaction ID. - function confirmTransaction(uint transactionId) - public - ownerExists(msg.sender) - transactionExists(transactionId) - notConfirmed(transactionId, msg.sender) - { - confirmations[transactionId][msg.sender] = true; - Confirmation(msg.sender, transactionId); - executeTransaction(transactionId); - } - - /// @dev Allows an owner to revoke a confirmation for a transaction. - /// @param transactionId Transaction ID. - function revokeConfirmation(uint transactionId) - public - ownerExists(msg.sender) - confirmed(transactionId, msg.sender) - notExecuted(transactionId) - { - confirmations[transactionId][msg.sender] = false; - Revocation(msg.sender, transactionId); - } - - /// @dev Allows anyone to execute a confirmed transaction. - /// @param transactionId Transaction ID. - function executeTransaction(uint transactionId) - public - notExecuted(transactionId) - { - if (isConfirmed(transactionId)) { - Transaction storage txx = transactions[transactionId]; - txx.executed = true; - if (txx.destination.call.value(txx.value)(txx.data)) { - Execution(transactionId); - } else { - ExecutionFailure(transactionId); - txx.executed = false; - } - } - } - /** * @dev withdraw all recognized tokens balances and ether to `_dest` * @param _dest the address of receiver @@ -387,47 +170,6 @@ contract MultiSigTokenWallet { bool result = ERC20(_tokenAddr).transfer(_dest, _amount); assert(result); } - - /// @dev Returns the confirmation status of a transaction. - /// @param transactionId Transaction ID. - /// @return Confirmation status. - function isConfirmed(uint transactionId) - public - constant - returns (bool) - { - uint count = 0; - for (uint i = 0; i < owners.length; i++) { - if (confirmations[transactionId][owners[i]]) - count += 1; - if (count == required) - return true; - } - } - - /* - * Internal functions - */ - /// @dev Adds a new transaction to the transaction mapping, if transaction does not exist yet. - /// @param destination Transaction target address. - /// @param value Transaction ether value. - /// @param data Transaction data payload. - /// @return Returns transaction ID. - function addTransaction(address destination, uint value, bytes data) - internal - notNull(destination) - returns (uint transactionId) - { - transactionId = transactionCount; - transactions[transactionId] = Transaction({ - destination: destination, - value: value, - data: data, - executed: false - }); - transactionCount += 1; - Submission(transactionId); - } /** * @dev register the deposit @@ -448,45 +190,6 @@ contract MultiSigTokenWallet { /* * Web3 call functions */ - /// @dev Returns number of confirmations of a transaction. - /// @param transactionId Transaction ID. - /// @return Number of confirmations. - function getConfirmationCount(uint transactionId) - public - constant - returns (uint count) - { - for (uint i = 0; i < owners.length; i++) { - if (confirmations[transactionId][owners[i]]) - count += 1; - } - } - - /// @dev Returns total number of transactions after filters are applied. - /// @param pending Include pending transactions. - /// @param executed Include executed transactions. - /// @return Total number of transactions after filters are applied. - function getTransactionCount(bool pending, bool executed) - public - constant - returns (uint count) - { - for (uint i = 0; i < transactionCount; i++) { - if (pending && !transactions[i].executed || executed && transactions[i].executed) - count += 1; - } - } - - /// @dev Returns list of owners. - /// @return List of owner addresses. - function getOwners() - public - constant - returns (address[]) - { - return owners; - } - /// @dev Returns list of tokens. /// @return List of token addresses. function getTokenList() @@ -497,53 +200,4 @@ contract MultiSigTokenWallet { return tokens; } - /// @dev Returns array with owner addresses, which confirmed transaction. - /// @param transactionId Transaction ID. - /// @return Returns array of owner addresses. - function getConfirmations(uint transactionId) - public - constant - returns (address[] _confirmations) - { - address[] memory confirmationsTemp = new address[](owners.length); - uint count = 0; - uint i; - for (i = 0; i < owners.length; i++) { - if (confirmations[transactionId][owners[i]]) { - confirmationsTemp[count] = owners[i]; - count += 1; - } - } - _confirmations = new address[](count); - for (i = 0; i < count; i++) { - _confirmations[i] = confirmationsTemp[i]; - } - } - - /// @dev Returns list of transaction IDs in defined range. - /// @param from Index start position of transaction array. - /// @param to Index end position of transaction array. - /// @param pending Include pending transactions. - /// @param executed Include executed transactions. - /// @return Returns array of transaction IDs. - function getTransactionIds(uint from, uint to, bool pending, bool executed) - public - constant - returns (uint[] _transactionIds) - { - uint[] memory transactionIdsTemp = new uint[](transactionCount); - uint count = 0; - uint i; - for (i = 0; i < transactionCount; i++) { - if (pending && !transactions[i].executed || executed && transactions[i].executed) { - transactionIdsTemp[count] = i; - count += 1; - } - } - _transactionIds = new uint[](to - from); - for (i = from; i < to; i++) { - _transactionIds[i - from] = transactionIdsTemp[i]; - } - } - } diff --git a/contracts/MultiSigWallet.sol b/contracts/MultiSigWallet.sol new file mode 100644 index 0000000..642aef3 --- /dev/null +++ b/contracts/MultiSigWallet.sol @@ -0,0 +1,378 @@ +pragma solidity ^0.4.17; + + +/// @title Multisignature wallet - Allows multiple parties to agree on transactions before execution. +/// @author Stefan George - +contract MultiSigWallet { + + /* + * Events + */ + event Confirmation(address indexed sender, uint indexed transactionId); + event Revocation(address indexed sender, uint indexed transactionId); + event Submission(uint indexed transactionId); + event Execution(uint indexed transactionId); + event ExecutionFailure(uint indexed transactionId); + event Deposit(address indexed sender, uint value); + event OwnerAddition(address indexed owner); + event OwnerRemoval(address indexed owner); + event RequirementChange(uint required); + + /* + * Constants + */ + uint constant public MAX_OWNER_COUNT = 50; + + /* + * Storage + */ + mapping (uint => Transaction) public transactions; + mapping (uint => mapping (address => bool)) public confirmations; + mapping (address => bool) public isOwner; + address[] public owners; + uint public required; + uint public transactionCount; + + struct Transaction { + address destination; + uint value; + bytes data; + bool executed; + } + + /* + * Modifiers + */ + modifier onlyWallet() { + require (msg.sender == address(this)); + _; + } + + modifier ownerDoesNotExist(address owner) { + require (!isOwner[owner]); + _; + } + + modifier ownerExists(address owner) { + require (isOwner[owner]); + _; + } + + modifier transactionExists(uint transactionId) { + require (transactions[transactionId].destination != 0); + _; + } + + modifier confirmed(uint transactionId, address owner) { + require (confirmations[transactionId][owner]); + _; + } + + modifier notConfirmed(uint transactionId, address owner) { + require(!confirmations[transactionId][owner]); + _; + } + + modifier notExecuted(uint transactionId) { + require (!transactions[transactionId].executed); + _; + } + + modifier notNull(address _address) { + require (_address != 0); + _; + } + + modifier validRequirement(uint ownerCount, uint _required) { + require (ownerCount <= MAX_OWNER_COUNT && _required <= ownerCount && _required != 0 && ownerCount != 0); + _; + } + + /// @dev Fallback function allows to deposit ether. + function () + public + payable + { + if (msg.value > 0) + Deposit(msg.sender, msg.value); + } + + /* + * Public functions + */ + /// @dev Constructor calls function `constructor(address[],uint256)` + /// @param _owners List of initial owners. + /// @param _required Number of required confirmations. + function MultiSigWallet(address[] _owners, uint _required) + public + { + constructor(_owners, _required); + } + + /// @dev Contract constructor sets initial owners and required number of confirmations, can only be called once. + /// @param _owners List of initial owners. + /// @param _required Number of required confirmations. + function constructor(address[] _owners, uint _required) + public + validRequirement(_owners.length, _required) + { + require(owners.length == 0 && required == 0); + for (uint i = 0; i < _owners.length; i++) { + require(!isOwner[_owners[i]] && _owners[i] != 0); + isOwner[_owners[i]] = true; + } + owners = _owners; + required = _required; + } + + /// @dev Allows to add a new owner. Transaction has to be sent by wallet. + /// @param owner Address of new owner. + function addOwner(address owner) + public + onlyWallet + ownerDoesNotExist(owner) + notNull(owner) + validRequirement(owners.length + 1, required) + { + isOwner[owner] = true; + owners.push(owner); + OwnerAddition(owner); + } + + /// @dev Allows to remove an owner. Transaction has to be sent by wallet. + /// @param owner Address of owner. + function removeOwner(address owner) + public + onlyWallet + ownerExists(owner) + { + isOwner[owner] = false; + for (uint i=0; i owners.length) + changeRequirement(owners.length); + OwnerRemoval(owner); + } + + /// @dev Allows to replace an owner with a new owner. Transaction has to be sent by wallet. + /// @param owner Address of owner to be replaced. + /// @param newOwner Address of new owner. + function replaceOwner(address owner, address newOwner) + public + onlyWallet + ownerExists(owner) + ownerDoesNotExist(newOwner) + { + for (uint i=0; i