1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
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: