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 <http://www.gnu.org/licenses/>.
*/
/// @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;
}
}
/// @dev `Escapable` is a base level contract built off of the `Owned`
/// contract that creates an escape hatch function to send its ether to
/// `escapeDestination` when called by the `escapeCaller` in the case that
/// something unexpected happens
contract Escapable is Owned {
address public escapeCaller;
address public escapeDestination;
/// @notice The Constructor assigns the `escapeDestination` and the
/// `escapeCaller`
/// @param _escapeDestination The address of a safe location (usu a
/// Multisig) to send the ether held in this contract
/// @param _escapeCaller The address of a trusted account or contract to
/// call `escapeHatch()` to send the ether in this contract to the
/// `escapeDestination` it would be ideal that `escapeCaller` cannot move
/// funds out of `escapeDestination`
function Escapable(address _escapeCaller, address _escapeDestination) {
escapeDestination = _escapeDestination;
escapeCaller = _escapeCaller;
}
/// @dev The addresses preassigned the `escapeCaller` role
/// is the only addresses that can call a function with this modifier
modifier onlyEscapeCallerOrOwner {
if ((msg.sender != escapeCaller)&&(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() onlyEscapeCallerOrOwner {
uint total = this.balance;
// Send the total balance of this contract to the `escapeDestination`
if (!escapeDestination.send(total)) {
throw;
}
EscapeCalled(total);
}
/// @notice Changes the address assigned to call `escapeHatch()`
/// @param _newEscapeCaller The address of a trusted account or contract to
/// call `escapeHatch()` to send the ether in this contract to the
/// `escapeDestination` it would be ideal that `escapeCaller` cannot
/// move funds out of `escapeDestination`
function changeEscapeCaller(address _newEscapeCaller) onlyEscapeCallerOrOwner {
escapeCaller = _newEscapeCaller;
}
event EscapeCalled(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 description; // What is the purpose of this 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 idPayment, address recipient, uint amount);
event PaymentExecuted(uint idPayment, address recipient, uint amount);
event PaymentCanceled(uint idPayment);
event EtherReceived(address from, uint amount);
event SpenderAuthorization(address spender, bool authorized);
/////////
// Constuctor
/////////
/// @notice The Constructor creates the Vault on the blockchain
/// @param _escapeCaller The address of a trusted account or contract to
/// call `escapeHatch()` to send the ether in this contract to the
/// `escapeDestination` it would be ideal if `escapeCaller` cannot move
/// funds out of `escapeDestination`
/// @param _escapeDestination 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 _escapeCaller,
address _escapeDestination,
uint _absoluteMinTimeLock,
uint _timeLock,
address _securityGuard,
uint _maxSecurityGuardDelay) Escapable(_escapeCaller, _escapeDestination)
{
securityGuard = _securityGuard;
timeLock = _timeLock;
absoluteMinTimeLock = _absoluteMinTimeLock;
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 _description Brief description of the payment that is authorized
/// @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 _description,
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;
// 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.description = _description;
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;
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;
}
}
The Vault Contract: Open Sourced by Giveth.
And the source code from https://github.com/Giveth/vaultcontract/blob/master/Vault.sol: