solidity/test/compilationTests/MultiSigWallet/MultiSigWallet.sol

366 lines
11 KiB
Solidity
Raw Normal View History

2018-10-24 12:52:11 +00:00
pragma solidity >=0.0;
2017-07-05 10:28:15 +00:00
/// @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();
2017-07-05 10:28:15 +00:00
_;
}
modifier ownerDoesNotExist(address owner) {
if (isOwner[owner])
revert();
2017-07-05 10:28:15 +00:00
_;
}
modifier ownerExists(address owner) {
if (!isOwner[owner])
revert();
2017-07-05 10:28:15 +00:00
_;
}
modifier transactionExists(uint transactionId) {
if (transactions[transactionId].destination == address(0))
revert();
2017-07-05 10:28:15 +00:00
_;
}
modifier confirmed(uint transactionId, address owner) {
if (!confirmations[transactionId][owner])
revert();
2017-07-05 10:28:15 +00:00
_;
}
modifier notConfirmed(uint transactionId, address owner) {
if (confirmations[transactionId][owner])
revert();
2017-07-05 10:28:15 +00:00
_;
}
modifier notExecuted(uint transactionId) {
if (transactions[transactionId].executed)
revert();
2017-07-05 10:28:15 +00:00
_;
}
modifier notNull(address _address) {
if (_address == address(0))
revert();
2017-07-05 10:28:15 +00:00
_;
}
modifier validRequirement(uint ownerCount, uint _required) {
if ( ownerCount > MAX_OWNER_COUNT
|| _required > ownerCount
|| _required == 0
|| ownerCount == 0)
revert();
2017-07-05 10:28:15 +00:00
_;
}
/// @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)
2017-07-05 10:28:15 +00:00
validRequirement(_owners.length, _required)
{
for (uint i=0; i<_owners.length; i++) {
if (isOwner[_owners[i]] || _owners[i] == address(0))
revert();
2017-07-05 10:28:15 +00:00
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
*/
2017-07-05 10:28:15 +00:00
/// @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);
2018-06-27 08:35:38 +00:00
emit OwnerAddition(owner);
2017-07-05 10:28:15 +00:00
}
/// @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();
2017-07-05 10:28:15 +00:00
if (required > owners.length)
changeRequirement(owners.length);
2018-06-27 08:35:38 +00:00
emit OwnerRemoval(owner);
2017-07-05 10:28:15 +00:00
}
/// @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;
2018-06-27 08:35:38 +00:00
emit OwnerRemoval(owner);
emit OwnerAddition(newOwner);
2017-07-05 10:28:15 +00:00
}
/// @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;
2018-06-27 08:35:38 +00:00
emit RequirementChange(_required);
2017-07-05 10:28:15 +00:00
}
/// @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)
2017-07-05 10:28:15 +00:00
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;
2018-06-27 08:35:38 +00:00
emit Confirmation(msg.sender, transactionId);
2017-07-05 10:28:15 +00:00
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;
2018-06-27 08:35:38 +00:00
emit Revocation(msg.sender, transactionId);
2017-07-05 10:28:15 +00:00
}
/// @dev Allows anyone to execute a confirmed transaction.
/// @param transactionId Transaction ID.
function executeTransaction(uint transactionId)
2019-11-05 17:25:34 +00:00
virtual
2017-07-05 10:28:15 +00:00
public
notExecuted(transactionId)
{
if (isConfirmed(transactionId)) {
Transaction storage transaction = transactions[transactionId];
(transaction.executed,) = transaction.destination.call{value: transaction.value}(transaction.data);
if (transaction.executed)
2018-06-27 08:35:38 +00:00
emit Execution(transactionId);
2018-08-15 22:39:19 +00:00
else
2018-06-27 08:35:38 +00:00
emit ExecutionFailure(transactionId);
2017-07-05 10:28:15 +00:00
}
}
/// @dev Returns the confirmation status of a transaction.
/// @param transactionId Transaction ID.
/// @return Confirmation status.
function isConfirmed(uint transactionId)
public
2018-07-02 09:14:28 +00:00
view
2017-07-05 10:28:15 +00:00
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;
2017-07-05 10:28:15 +00:00
}
/*
* Web3 call functions
*/
/// @dev Returns number of confirmations of a transaction.
/// @param transactionId Transaction ID.
/// @return count Number of confirmations.
2017-07-05 10:28:15 +00:00
function getConfirmationCount(uint transactionId)
public
2018-07-02 09:14:28 +00:00
view
2017-07-05 10:28:15 +00:00
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.
2017-07-05 10:28:15 +00:00
function getTransactionCount(bool pending, bool executed)
public
2018-07-02 09:14:28 +00:00
view
2017-07-05 10:28:15 +00:00
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
2018-07-02 09:14:28 +00:00
view
returns (address[] memory)
2017-07-05 10:28:15 +00:00
{
return owners;
}
/// @dev Returns array with owner addresses, which confirmed transaction.
/// @param transactionId Transaction ID.
/// @return _confirmations Returns array of owner addresses.
2017-07-05 10:28:15 +00:00
function getConfirmations(uint transactionId)
public
2018-07-02 09:14:28 +00:00
view
returns (address[] memory _confirmations)
2017-07-05 10:28:15 +00:00
{
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.
2017-07-05 10:28:15 +00:00
function getTransactionIds(uint from, uint to, bool pending, bool executed)
public
2018-07-02 09:14:28 +00:00
view
returns (uint[] memory _transactionIds)
2017-07-05 10:28:15 +00:00
{
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);
}
}