Permalink
Fetching contributors…
Cannot retrieve contributors at this time
348 lines (291 sloc) 13.8 KB
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;
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;
}
}