pragma solidity ^0.4.6; /* Copyright 2016, Jordi Baylina This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /// @title Vault Contract /// @author Jordi Baylina /// @notice This contract holds funds for Campaigns and automates payments. For /// this iteration the funds will come straight from the Giveth Multisig as a /// safety precaution, but once fully tested and optimized this contract will /// be a safe place to store funds equipped with optional variable time delays /// to allow for an optional escape hatch /// @dev `Owned` is a base level contract that assigns an `owner` that can be /// later changed contract Owned { /// @dev `owner` is the only address that can call a function with this /// modifier modifier onlyOwner { if (msg.sender != owner) throw; _; } address public owner; /// @notice The Constructor assigns the message sender to be `owner` function Owned() { owner = msg.sender;} /// @notice `owner` can step down and assign some other address to this role /// @param _newOwner The address of the new owner. 0x0 can be used to create /// an unowned neutral vault, however that cannot be undone function changeOwner(address _newOwner) onlyOwner { owner = _newOwner; NewOwner(msg.sender, _newOwner); } event NewOwner(address indexed oldOwner, address indexed newOwner); } /// @dev `Escapable` is a base level contract built off of the `Owned` /// contract that creates an escape hatch function to send its ether to /// `escapeHatchDestination` when called by the `escapeHatchCaller` in the case that /// something unexpected happens contract Escapable is Owned { address public escapeHatchCaller; address public escapeHatchDestination; /// @notice The Constructor assigns the `escapeHatchDestination` and the /// `escapeHatchCaller` /// @param _escapeHatchDestination The address of a safe location (usu a /// Multisig) to send the ether held in this contract /// @param _escapeHatchCaller The address of a trusted account or contract to /// call `escapeHatch()` to send the ether in this contract to the /// `escapeHatchDestination` it would be ideal that `escapeHatchCaller` cannot move /// funds out of `escapeHatchDestination` function Escapable(address _escapeHatchCaller, address _escapeHatchDestination) { escapeHatchCaller = _escapeHatchCaller; escapeHatchDestination = _escapeHatchDestination; } /// @dev The addresses preassigned the `escapeHatchCaller` role /// is the only addresses that can call a function with this modifier modifier onlyEscapeHatchCallerOrOwner { if ((msg.sender != escapeHatchCaller)&&(msg.sender != owner)) throw; _; } /// @notice The `escapeHatch()` should only be called as a last resort if a /// security issue is uncovered or something unexpected happened function escapeHatch() onlyEscapeHatchCallerOrOwner { uint total = this.balance; // Send the total balance of this contract to the `escapeHatchDestination` if (!escapeHatchDestination.send(total)) { throw; } EscapeHatchCalled(total); } /// @notice Changes the address assigned to call `escapeHatch()` /// @param _newEscapeHatchCaller The address of a trusted account or contract to /// call `escapeHatch()` to send the ether in this contract to the /// `escapeHatchDestination` it would be ideal that `escapeHatchCaller` cannot /// move funds out of `escapeHatchDestination` function changeEscapeCaller(address _newEscapeHatchCaller) onlyEscapeHatchCallerOrOwner { escapeHatchCaller = _newEscapeHatchCaller; } event EscapeHatchCalled(uint amount); } /// @dev `Vault` is a higher level contract built off of the `Escapable` /// contract that holds funds for Campaigns and automates payments. contract Vault is Escapable { /// @dev `Payment` is a public structure that describes the details of /// each payment making it easy to track the movement of funds /// transparently struct Payment { string name; // What is the purpose of this payment bytes32 reference; // Reference of the payment. address spender; // Who is sending the funds uint earliestPayTime; // The earliest a payment can be made (Unix Time) bool canceled; // If True then the payment has been canceled bool paid; // If True then the payment has been paid address recipient; // Who is receiving the funds uint amount; // The amount of wei sent in the payment uint securityGuardDelay;// The seconds `securityGuard` can delay payment } Payment[] public authorizedPayments; address public securityGuard; uint public absoluteMinTimeLock; uint public timeLock; uint public maxSecurityGuardDelay; /// @dev The white list of approved addresses allowed to set up && receive /// payments from this vault mapping (address => bool) public allowedSpenders; /// @dev The address assigned the role of `securityGuard` is the only /// addresses that can call a function with this modifier modifier onlySecurityGuard { if (msg.sender != securityGuard) throw; _; } // @dev Events to make the payment movements easy to find on the blockchain event PaymentAuthorized(uint indexed idPayment, address indexed recipient, uint amount); event PaymentExecuted(uint indexed idPayment, address indexed recipient, uint amount); event PaymentCanceled(uint indexed idPayment); event EtherReceived(address indexed from, uint amount); event SpenderAuthorization(address indexed spender, bool authorized); ///////// // Constructor ///////// /// @notice The Constructor creates the Vault on the blockchain /// @param _escapeHatchCaller The address of a trusted account or contract to /// call `escapeHatch()` to send the ether in this contract to the /// `escapeHatchDestination` it would be ideal if `escapeHatchCaller` cannot move /// funds out of `escapeHatchDestination` /// @param _escapeHatchDestination The address of a safe location (usu a /// Multisig) to send the ether held in this contract in an emergency /// @param _absoluteMinTimeLock The minimum number of seconds `timelock` can /// be set to, if set to 0 the `owner` can remove the `timeLock` completely /// @param _timeLock Initial number of seconds that payments are delayed /// after they are authorized (a security precaution) /// @param _securityGuard Address that will be able to delay the payments /// beyond the initial timelock requirements; can be set to 0x0 to remove /// the `securityGuard` functionality /// @param _maxSecurityGuardDelay The maximum number of seconds in total /// that `securityGuard` can delay a payment so that the owner can cancel /// the payment if needed function Vault( address _escapeHatchCaller, address _escapeHatchDestination, uint _absoluteMinTimeLock, uint _timeLock, address _securityGuard, uint _maxSecurityGuardDelay) Escapable(_escapeHatchCaller, _escapeHatchDestination) { absoluteMinTimeLock = _absoluteMinTimeLock; timeLock = _timeLock; securityGuard = _securityGuard; maxSecurityGuardDelay = _maxSecurityGuardDelay; } ///////// // Helper functions ///////// /// @notice States the total number of authorized payments in this contract /// @return The number of payments ever authorized even if they were canceled function numberOfAuthorizedPayments() constant returns (uint) { return authorizedPayments.length; } ////// // Receive Ether ////// /// @notice Called anytime ether is sent to the contract && creates an event /// to more easily track the incoming transactions function receiveEther() payable { EtherReceived(msg.sender, msg.value); } /// @notice The fall back function is called whenever ether is sent to this /// contract function () payable { receiveEther(); } //////// // Spender Interface //////// /// @notice only `allowedSpenders[]` Creates a new `Payment` /// @param _name Brief description of the payment that is authorized /// @param _reference External reference of the payment /// @param _recipient Destination of the payment /// @param _amount Amount to be paid in wei /// @param _paymentDelay Number of seconds the payment is to be delayed, if /// this value is below `timeLock` then the `timeLock` determines the delay /// @return The Payment ID number for the new authorized payment function authorizePayment( string _name, bytes32 _reference, address _recipient, uint _amount, uint _paymentDelay ) returns(uint) { // Fail if you arent on the `allowedSpenders` white list if (!allowedSpenders[msg.sender] ) throw; uint idPayment = authorizedPayments.length; // Unique Payment ID authorizedPayments.length++; // The following lines fill out the payment struct Payment p = authorizedPayments[idPayment]; p.spender = msg.sender; // Overflow protection if (_paymentDelay > 10**18) throw; // Determines the earliest the recipient can receive payment (Unix time) p.earliestPayTime = _paymentDelay >= timeLock ? now + _paymentDelay : now + timeLock; p.recipient = _recipient; p.amount = _amount; p.name = _name; p.reference = _reference; PaymentAuthorized(idPayment, p.recipient, p.amount); return idPayment; } /// @notice only `allowedSpenders[]` The recipient of a payment calls this /// function to send themselves the ether after the `earliestPayTime` has /// expired /// @param _idPayment The payment ID to be executed function collectAuthorizedPayment(uint _idPayment) { // Check that the `_idPayment` has been added to the payments struct if (_idPayment >= authorizedPayments.length) throw; Payment p = authorizedPayments[_idPayment]; // Checking for reasons not to execute the payment if (msg.sender != p.recipient) throw; if (!allowedSpenders[p.spender]) throw; if (now < p.earliestPayTime) throw; if (p.canceled) throw; if (p.paid) throw; if (this.balance < p.amount) throw; p.paid = true; // Set the payment to being paid if (!p.recipient.send(p.amount)) { // Make the payment throw; } PaymentExecuted(_idPayment, p.recipient, p.amount); } ///////// // SecurityGuard Interface ///////// /// @notice `onlySecurityGuard` Delays a payment for a set number of seconds /// @param _idPayment ID of the payment to be delayed /// @param _delay The number of seconds to delay the payment function delayPayment(uint _idPayment, uint _delay) onlySecurityGuard { if (_idPayment >= authorizedPayments.length) throw; // Overflow test if (_delay > 10**18) throw; Payment p = authorizedPayments[_idPayment]; if ((p.securityGuardDelay + _delay > maxSecurityGuardDelay) || (p.paid) || (p.canceled)) throw; p.securityGuardDelay += _delay; p.earliestPayTime += _delay; } //////// // Owner Interface /////// /// @notice `onlyOwner` Cancel a payment all together /// @param _idPayment ID of the payment to be canceled. function cancelPayment(uint _idPayment) onlyOwner { if (_idPayment >= authorizedPayments.length) throw; Payment p = authorizedPayments[_idPayment]; if (p.canceled) throw; if (p.paid) throw; p.canceled = true; PaymentCanceled(_idPayment); } /// @notice `onlyOwner` Adds a spender to the `allowedSpenders[]` white list /// @param _spender The address of the contract being authorized/unauthorized /// @param _authorize `true` if authorizing and `false` if unauthorizing function authorizeSpender(address _spender, bool _authorize) onlyOwner { allowedSpenders[_spender] = _authorize; SpenderAuthorization(_spender, _authorize); } /// @notice `onlyOwner` Sets the address of `securityGuard` /// @param _newSecurityGuard Address of the new security guard function setSecurityGuard(address _newSecurityGuard) onlyOwner { securityGuard = _newSecurityGuard; } /// @notice `onlyOwner` Changes `timeLock`; the new `timeLock` cannot be /// lower than `absoluteMinTimeLock` /// @param _newTimeLock Sets the new minimum default `timeLock` in seconds; /// pending payments maintain their `earliestPayTime` function setTimelock(uint _newTimeLock) onlyOwner { if (_newTimeLock < absoluteMinTimeLock) throw; timeLock = _newTimeLock; } /// @notice `onlyOwner` Changes the maximum number of seconds /// `securityGuard` can delay a payment /// @param _maxSecurityGuardDelay The new maximum delay in seconds that /// `securityGuard` can delay the payment's execution in total function setMaxSecurityGuardDelay(uint _maxSecurityGuardDelay) onlyOwner { maxSecurityGuardDelay = _maxSecurityGuardDelay; } }