diff --git a/contracts/DelegatedCall.sol b/contracts/DelegatedCall.sol deleted file mode 100644 index 6ae754c..0000000 --- a/contracts/DelegatedCall.sol +++ /dev/null @@ -1,72 +0,0 @@ -pragma solidity ^0.4.18; - -/** - * @title DelegatedCall - * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH) - * Abstract contract that delegates all calls to contract returned by abstract function `_getDelegatedContract` - */ -contract DelegatedCall { - - /** - * @dev delegates the call of this function - */ - modifier delegated - { - uint inSize = msg.data.length; - bytes32 inDataPtr = _malloc(inSize); - - assembly { - calldatacopy(inDataPtr, 0x0, inSize) - } - - bytes32 outDataPtr; - uint256 outSize; - - (outDataPtr, outSize) = _delegatecall(inDataPtr, inSize); - _; - assembly { - return(outDataPtr, outSize) - } - } - - /** - * @dev defines the address for delegation of calls - */ - function _getDelegatedContract() - internal - returns(address); - - /** - * @dev allocates memory to a pointer - */ - function _malloc(uint size) - internal - returns(bytes32 ptr) - { - assembly { - ptr := mload(0x40) - mstore(0x40, add(ptr, size)) - } - } - - /** - * @dev delegates the data in pointer - */ - function _delegatecall(bytes32 inDataPtr, uint inSize) - internal - returns(bytes32 outDataPtr, uint256 outSize) - { - address target = _getDelegatedContract(); - bool failed; - assembly { - failed := iszero(delegatecall(sub(gas, 10000), target, inDataPtr, inSize, 0, 0)) - outSize := returndatasize - } - require(!failed); - outDataPtr = _malloc(outSize); - assembly { - returndatacopy(outDataPtr, 0, outSize) - } - } - -} diff --git a/contracts/ERC20.sol b/contracts/ERC20.sol index fb47128..e82eabb 100644 --- a/contracts/ERC20.sol +++ b/contracts/ERC20.sol @@ -1,12 +1,12 @@ -pragma solidity ^0.4.18; +pragma solidity ^0.4.15; contract ERC20 { uint256 public totalSupply; - function balanceOf(address who) public constant returns (uint256); - function allowance(address owner, address spender) public constant returns (uint256); - function transfer(address to, uint256 value) public returns (bool ok); - function transferFrom(address from, address to, uint256 value) public returns (bool ok); - function approve(address spender, uint256 value) public returns (bool ok); + function balanceOf(address who) constant returns (uint256); + function allowance(address owner, address spender) constant returns (uint256); + function transfer(address to, uint256 value) returns (bool ok); + function transferFrom(address from, address to, uint256 value) returns (bool ok); + function approve(address spender, uint256 value) returns (bool ok); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); } \ No newline at end of file diff --git a/contracts/MultiSigFactory.sol b/contracts/MultiSigFactory.sol index 30a5ac3..62bc00a 100644 --- a/contracts/MultiSigFactory.sol +++ b/contracts/MultiSigFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.18; +pragma solidity ^0.4.15; import "./MultiSigStub.sol"; @@ -6,10 +6,7 @@ contract MultiSigFactory { event Create(address indexed caller, address createdContract); - function create(address[] owners, uint256 required) - public - returns (address wallet) - { + function create(address[] owners, uint256 required) returns (address wallet) { wallet = new MultiSigStub(owners, required); Create(msg.sender, wallet); } diff --git a/contracts/MultiSigStub.sol b/contracts/MultiSigStub.sol index 709bda0..4857129 100644 --- a/contracts/MultiSigStub.sol +++ b/contracts/MultiSigStub.sol @@ -1,14 +1,24 @@ -pragma solidity ^0.4.18; - -import "./DelegatedCall.sol"; +pragma solidity ^0.4.15; /** * @title MultiSigStub - * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH) * Contract that delegates calls to a library to build a full MultiSigWallet that is cheap to create. */ -contract MultiSigStub is DelegatedCall { +contract MultiSigStub { + address[] public owners; + address[] public tokens; + mapping (uint => Transaction) public transactions; + mapping (uint => mapping (address => bool)) public confirmations; + uint public transactionCount; + + struct Transaction { + address destination; + uint value; + bytes data; + bool executed; + } + function MultiSigStub(address[] _owners, uint256 _required) { //bytes4 sig = bytes4(sha3("constructor(address[],uint256)")); bytes4 sig = 0x36756a23; @@ -24,6 +34,21 @@ contract MultiSigStub is DelegatedCall { _delegatecall(mData, size); } + modifier delegated { + uint size = msg.data.length; + bytes32 mData = _malloc(size); + + assembly { + calldatacopy(mData, 0x0, size) + } + + bytes32 mResult = _delegatecall(mData, size); + _; + assembly { + return(mResult, 0x20) + } + } + function() payable delegated @@ -31,6 +56,34 @@ contract MultiSigStub is DelegatedCall { } + function submitTransaction(address destination, uint value, bytes data) + public + delegated + returns (uint) + { + + } + + function confirmTransaction(uint transactionId) + public + delegated + { + + } + + function watch(address _tokenAddr) + public + delegated + { + + } + + function setMyTokenList(address[] _tokenList) + public + delegated + { + + } /// @dev Returns the confirmation status of a transaction. /// @param transactionId Transaction ID. /// @return Confirmation status. @@ -46,6 +99,15 @@ contract MultiSigStub is DelegatedCall { /* * Web3 call functions */ + function tokenBalances(address tokenAddress) + public + constant + delegated + returns (uint) + { + + } + /// @dev Returns number of confirmations of a transaction. /// @param transactionId Transaction ID. @@ -77,10 +139,19 @@ contract MultiSigStub is DelegatedCall { function getOwners() public constant - delegated returns (address[]) { + return owners; + } + /// @dev Returns list of tokens. + /// @return List of token addresses. + function getTokenList() + public + constant + returns (address[]) + { + return tokens; } /// @dev Returns array with owner addresses, which confirmed transaction. @@ -89,10 +160,21 @@ contract MultiSigStub is DelegatedCall { function getConfirmations(uint transactionId) public constant - delegated 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. @@ -101,20 +183,50 @@ contract MultiSigStub is DelegatedCall { /// @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) + function getTransactionIds(uint from, uint to, bool pending, bool executed) public constant - delegated 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]; + } } - function _getDelegatedContract() - internal - returns(address) + + function _malloc(uint size) + private + returns(bytes32 mData) { - return 0xCaFFE810d0dF52E27DC580AD4a3C6283B0094291; //hardcoded multinetwork address + assembly { + mData := mload(0x40) + mstore(0x40, add(mData, size)) + } + } + + function _delegatecall(bytes32 mData, uint size) + private + returns(bytes32 mResult) + { + address target = 0xc0FFeEE61948d8993864a73a099c0E38D887d3F4; //Multinetwork + mResult = _malloc(32); + bool failed; + + assembly { + failed := iszero(delegatecall(sub(gas, 10000), target, mData, size, mResult, 0x20)) + } + + assert(!failed); } } \ No newline at end of file diff --git a/contracts/MultiSigTokenWallet.sol b/contracts/MultiSigTokenWallet.sol index 0a3a963..d1f12f9 100644 --- a/contracts/MultiSigTokenWallet.sol +++ b/contracts/MultiSigTokenWallet.sol @@ -1,30 +1,114 @@ -pragma solidity ^0.4.18; +pragma solidity ^0.4.15; -import "./MultiSigWallet.sol"; import "./ERC20.sol"; -/** - * @title MultiSigTokenWallet - * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH) - * MultiSigWallet that supports withdrawing all ERC20 tokens at once. - */ -contract MultiSigTokenWallet is MultiSigWallet { +contract MultiSigTokenWallet { - event TokenDeposit(address indexed token, address indexed sender, uint value); + 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 only call parent constructor - */ - function MultiSigTokenWallet(address[] _owners, uint _required) - MultiSigWallet(_owners,_required) - public + /// @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) { - //does nothing + 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 @@ -48,26 +132,49 @@ contract MultiSigTokenWallet is MultiSigWallet { { if (_from == address(this)) return; + uint _nonce = nonce; bool result = ERC20(_token).transferFrom(_from, this, _amount); - require(result); - TokenDeposit(_token, _from, _amount); + assert(result); + //ERC23 not executed _deposited tokenFallback by + if (nonce == _nonce) { + _deposited(_from, _amount, _token, _data); + } + } + /** + * @notice watches for balance in a token contract + * @param _tokenAddr the token contract address + **/ + function watch(address _tokenAddr) + ownerExists(msg.sender) + { + uint oldBal = tokenBalances[_tokenAddr]; + uint newBal = ERC20(_tokenAddr).balanceOf(this); + if (newBal > oldBal) { + _deposited(0x0, newBal-oldBal, _tokenAddr, new bytes(0)); + } } + function setMyTokenList(address[] _tokenList) + public + { + userList[msg.sender] = _tokenList; + } + + function setTokenList(address[] _tokenList) + onlyWallet + { + tokens = _tokenList; + } + /** * @notice ERC23 Token fallback * @param _from address incoming token * @param _amount incoming amount **/ - function tokenFallback( - address _from, - uint _amount, - bytes _data - ) + function tokenFallback(address _from, uint _amount, bytes _data) public - returns (bool) { - TokenDeposit(msg.sender, _from, _amount); - return true; + _deposited(_from, _amount, msg.sender, _data); } /** @@ -77,50 +184,366 @@ contract MultiSigTokenWallet is MultiSigWallet { * @param _token the token contract address * @param _data (might be used by child classes) */ - function receiveApproval( - address _from, - uint256 _amount, - address _token, - bytes _data - ) - public - { + function receiveApproval(address _from, uint256 _amount, address _token, bytes _data) { 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 withdraw all tokens in list and ether to `_dest` - * @param _dest the address of receiver - * @param _tokenList the list of tokens to withdraw all balance + * @dev gives full ownership of this wallet to `_dest` removing older owners from wallet + * @param _dest the address of new controller **/ - function withdrawEverything(address _dest, address[] _tokenList) + function releaseWallet(address _dest) + public + notNull(_dest) + ownerDoesNotExist(_dest) + onlyWallet + { + address[] memory _owners = owners; + uint numOwners = _owners.length; + addOwner(_dest); + for (uint i = 0; i < numOwners; i++) { + removeOwner(_owners[i]); + } + } + + /// @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 + **/ + function withdrawEverything(address _dest) public notNull(_dest) onlyWallet { - withdrawAllTokens(_dest, _tokenList); + withdrawAllTokens(_dest); _dest.transfer(this.balance); } /** - * @dev withdraw all listed tokens balances to `_dest` + * @dev withdraw all recognized tokens balances to `_dest` * @param _dest the address of receiver - * @param _tokenList the list of tokens to withdraw all balance **/ - function withdrawAllTokens(address _dest, address[] _tokenList) + function withdrawAllTokens(address _dest) public notNull(_dest) onlyWallet { + address[] memory _tokenList; + if (userList[_dest].length > 0) { + _tokenList = userList[_dest]; + } else { + _tokenList = tokens; + } uint len = _tokenList.length; for (uint i = 0;i < len; i++) { address _tokenAddr = _tokenList[i]; - uint _amount = ERC20(_tokenAddr).balanceOf(address(this)); + uint _amount = tokenBalances[_tokenAddr]; if (_amount > 0) { + delete tokenBalances[_tokenAddr]; ERC20(_tokenAddr).transfer(_dest, _amount); } } } -} \ No newline at end of file + /** + * @dev withdraw `_tokenAddr` `_amount` to `_dest` + * @param _tokenAddr the address of the token + * @param _dest the address of receiver + * @param _amount the number of tokens to send + **/ + function withdrawToken(address _tokenAddr, address _dest, uint _amount) + public + notNull(_dest) + onlyWallet + { + require(_amount > 0); + uint _balance = tokenBalances[_tokenAddr]; + require(_amount <= _balance); + tokenBalances[_tokenAddr] = _balance - _amount; + 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 + **/ + function _deposited(address _from, uint _amount, address _tokenAddr, bytes) + internal + { + TokenDeposit(_tokenAddr,_from,_amount); + nonce++; + if (tokenBalances[_tokenAddr] == 0) { + tokens.push(_tokenAddr); + tokenBalances[_tokenAddr] = ERC20(_tokenAddr).balanceOf(this); + } else { + tokenBalances[_tokenAddr] += _amount; + } + } + + /* + * 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() + public + constant + returns (address[]) + { + 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/MultiSigTokenWalletV1.sol b/contracts/MultiSigTokenWalletV1.sol deleted file mode 100644 index 4375daf..0000000 --- a/contracts/MultiSigTokenWalletV1.sol +++ /dev/null @@ -1,203 +0,0 @@ -pragma solidity ^0.4.15; - -import "./MultiSigWallet.sol"; -import "./ERC20.sol"; -// @dev This contract is deprecated, use the new version. -contract MultiSigTokenWalletV1 is MultiSigWallet { - - address[] public tokens; - mapping (address => uint) public tokenBalances; - mapping (address => address[]) public userList; - uint public nonce; - - event TokenDeposit(address _token, address indexed _sender, uint _value); - - /** - * Public functions - * - **/ - /** - * @notice deposit a ERC20 token. The amount of deposit is the allowance set to this contract. - * @param _token the token contract address - * @param _data might be used by child implementations - **/ - function depositToken(address _token, bytes _data) - public - { - address sender = msg.sender; - uint amount = ERC20(_token).allowance(sender, this); - deposit(sender, amount, _token, _data); - } - - /** - * @notice deposit a ERC20 token. The amount of deposit is the allowance set to this contract. - * @param _token the token contract address - * @param _data might be used by child implementations - **/ - function deposit(address _from, uint256 _amount, address _token, bytes _data) - public - { - if (_from == address(this)) - return; - uint _nonce = nonce; - bool result = ERC20(_token).transferFrom(_from, this, _amount); - assert(result); - //ERC23 not executed _deposited tokenFallback by - if (nonce == _nonce) { - _deposited(_from, _amount, _token, _data); - } - } - /** - * @notice watches for balance in a token contract - * @param _tokenAddr the token contract address - **/ - function watch(address _tokenAddr) - ownerExists(msg.sender) - { - uint oldBal = tokenBalances[_tokenAddr]; - uint newBal = ERC20(_tokenAddr).balanceOf(this); - if (newBal > oldBal) { - _deposited(0x0, newBal-oldBal, _tokenAddr, new bytes(0)); - } - } - - function setMyTokenList(address[] _tokenList) - public - { - userList[msg.sender] = _tokenList; - } - - function setTokenList(address[] _tokenList) - onlyWallet - { - tokens = _tokenList; - } - - /** - * @notice ERC23 Token fallback - * @param _from address incoming token - * @param _amount incoming amount - **/ - function tokenFallback(address _from, uint _amount, bytes _data) - public - { - _deposited(_from, _amount, msg.sender, _data); - } - - /** - * @notice Called MiniMeToken approvesAndCall to this contract, calls deposit. - * @param _from address incoming token - * @param _amount incoming amount - * @param _token the token contract address - * @param _data (might be used by child classes) - */ - function receiveApproval(address _from, uint256 _amount, address _token, bytes _data) { - deposit(_from, _amount, _token, _data); - } - - /** - * @dev gives full ownership of this wallet to `_dest` removing older owners from wallet - * @param _dest the address of new controller - **/ - function releaseWallet(address _dest) - public - notNull(_dest) - ownerDoesNotExist(_dest) - onlyWallet - { - address[] memory _owners = owners; - uint numOwners = _owners.length; - addOwner(_dest); - for (uint i = 0; i < numOwners; i++) { - removeOwner(_owners[i]); - } - } - - /** - * @dev withdraw all recognized tokens balances and ether to `_dest` - * @param _dest the address of receiver - **/ - function withdrawEverything(address _dest) - public - notNull(_dest) - onlyWallet - { - withdrawAllTokens(_dest); - _dest.transfer(this.balance); - } - - /** - * @dev withdraw all recognized tokens balances to `_dest` - * @param _dest the address of receiver - **/ - function withdrawAllTokens(address _dest) - public - notNull(_dest) - onlyWallet - { - address[] memory _tokenList; - if (userList[_dest].length > 0) { - _tokenList = userList[_dest]; - } else { - _tokenList = tokens; - } - uint len = _tokenList.length; - for (uint i = 0;i < len; i++) { - address _tokenAddr = _tokenList[i]; - uint _amount = tokenBalances[_tokenAddr]; - if (_amount > 0) { - delete tokenBalances[_tokenAddr]; - ERC20(_tokenAddr).transfer(_dest, _amount); - } - } - } - - /** - * @dev withdraw `_tokenAddr` `_amount` to `_dest` - * @param _tokenAddr the address of the token - * @param _dest the address of receiver - * @param _amount the number of tokens to send - **/ - function withdrawToken(address _tokenAddr, address _dest, uint _amount) - public - notNull(_dest) - onlyWallet - { - require(_amount > 0); - uint _balance = tokenBalances[_tokenAddr]; - require(_amount <= _balance); - tokenBalances[_tokenAddr] = _balance - _amount; - bool result = ERC20(_tokenAddr).transfer(_dest, _amount); - assert(result); - } - - /** - * @dev register the deposit - **/ - function _deposited(address _from, uint _amount, address _tokenAddr, bytes) - internal - { - TokenDeposit(_tokenAddr,_from,_amount); - nonce++; - if (tokenBalances[_tokenAddr] == 0) { - tokens.push(_tokenAddr); - tokenBalances[_tokenAddr] = ERC20(_tokenAddr).balanceOf(this); - } else { - tokenBalances[_tokenAddr] += _amount; - } - } - - /* - * Web3 call functions - */ - /// @dev Returns list of tokens. - /// @return List of token addresses. - function getTokenList() - public - constant - returns (address[]) - { - return tokens; - } - -} \ No newline at end of file diff --git a/contracts/MultiSigWallet.sol b/contracts/MultiSigWallet.sol deleted file mode 100644 index a6a70b4..0000000 --- a/contracts/MultiSigWallet.sol +++ /dev/null @@ -1,378 +0,0 @@ -pragma solidity ^0.4.18; - - -/// @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