/** * Source Code first verified at https://etherscan.io on Friday, April 26, 2019 (UTC) */ pragma solidity 0.5.0; /** @title Solidified Vault @author JG Carvalho **/ contract SolidifiedVault { /* * 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); /* * views */ uint constant public MAX_OWNER_COUNT = 3; /* * 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; bool executed; } /* * Modifiers */ modifier onlyWallet() { require(msg.sender == address(this), "Vault: sender should be wallet"); _; } modifier ownerDoesNotExist(address owner) { require(!isOwner[owner], "Vault:sender shouldn't be owner"); _; } modifier ownerExists(address owner) { require(isOwner[owner], "Vault:sender should be owner"); _; } modifier transactionExists(uint transactionId) { require(transactions[transactionId].destination != address(0),"Vault:transaction should exist"); _; } modifier confirmed(uint transactionId, address owner) { require(confirmations[transactionId][owner], "Vault:transaction should be confirmed"); _; } modifier notConfirmed(uint transactionId, address owner) { require(!confirmations[transactionId][owner], "Vault:transaction is already confirmed"); _; } modifier notExecuted(uint transactionId) { require(!transactions[transactionId].executed, "Vault:transaction has already executed"); _; } modifier notNull(address _address) { require(_address != address(0), "Vault:address shouldn't be null"); _; } modifier validRequirement(uint ownerCount, uint _required) { require(ownerCount <= MAX_OWNER_COUNT && _required <= ownerCount && _required != 0 && ownerCount != 0, "Vault:invalid requirement"); _; } /** @dev Fallback function allows to deposit ether. **/ function() external payable { if (msg.value > 0) emit 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. **/ constructor(address[] memory _owners, uint _required) public validRequirement(_owners.length, _required) { for (uint i=0; i<_owners.length; i++) { require(!isOwner[_owners[i]] && _owners[i] != address(0), "Vault:Invalid owner"); isOwner[_owners[i]] = true; } owners = _owners; required = _required; } /// @dev Allows an owner to submit and confirm a transaction. /// @param destination Transaction target address. /// @param value Transaction ether value. /// @return Returns transaction ID. function submitTransaction(address destination, uint value) public returns (uint transactionId) { transactionId = addTransaction(destination, value); 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) public ownerExists(msg.sender) confirmed(transactionId, msg.sender) notExecuted(transactionId) { if (isConfirmed(transactionId)) { Transaction storage txn = transactions[transactionId]; txn.executed = true; (bool exec, bytes memory _) = txn.destination.call.value(txn.value)(""); if (exec) emit Execution(transactionId); else { emit ExecutionFailure(transactionId); txn.executed = false; } } } /// @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