pragma solidity ^0.4.18; /* Copyright 2017, Jordi Baylina, RJ Ewing Contributors: AdriĆ  Massanet , Griff Green, Arthur Lunn 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 . */ import "./LiquidPledgingBase.sol"; /// @dev `LiquidPledging` allows for liquid pledging through the use of /// internal id structures and delegate chaining. All basic operations for /// handling liquid pledging are supplied as well as plugin features /// to allow for expanded functionality. contract LiquidPledging is LiquidPledgingBase { function addGiverAndDonate(uint64 idReceiver) public payable { addGiverAndDonate(idReceiver, msg.sender); } function addGiverAndDonate(uint64 idReceiver, address donorAddress) public payable { require(donorAddress != 0); // default to a 3 day (259200 seconds) commitTime uint64 idGiver = _addGiver(donorAddress, "", "", 259200, ILiquidPledgingPlugin(0)); donate(idGiver, idReceiver); } /// @notice This is how value enters the system and how pledges are created; /// the ether is sent to the vault, an pledge for the Giver is created (or /// found), the amount of ETH donated in wei is added to the `amount` in /// the Giver's Pledge, and an LP transfer is done to the idReceiver for /// the full amount /// @param idGiver The id of the Giver donating; if 0, a new id is created /// @param idReceiver The Admin receiving the donation; can be any Admin: /// the Giver themselves, another Giver, a Delegate or a Project function donate(uint64 idGiver, uint64 idReceiver) public payable { require(idGiver > 0); // prevent burning donations. idReceiver is checked in _transfer PledgeAdmins.PledgeAdmin storage sender = _findAdmin(idGiver); require(sender.adminType == PledgeAdminType.Giver); uint amount = msg.value; require(amount > 0); // Sends the `msg.value` (in wei) to the `vault` // b/c the vault is a proxy, send & transfer will fail since they only provide 2300 // gas, and the delegateProxy will sub(gas, 10000) before even making the call require(vault.call.value(amount).gas(16000)()); uint64 idPledge = _findOrCreatePledge( idGiver, new uint64[](0), // Creates empty array for delegationChain 0, 0, 0, Pledges.PledgeState.Pledged ); Pledges.Pledge storage pTo = _findPledge(idPledge); pTo.amount += amount; Transfer(0, idPledge, amount); _transfer(idGiver, idPledge, amount, idReceiver); } /// @notice Transfers amounts between pledges for internal accounting /// @param idSender Id of the Admin that is transferring the amount from /// Pledge to Pledge; this admin must have permissions to move the value /// @param idPledge Id of the pledge that's moving the value /// @param amount Quantity of ETH (in wei) that this pledge is transferring /// the authority to withdraw from the vault /// @param idReceiver Destination of the `amount`, can be a Giver/Project sending /// to a Giver, a Delegate or a Project; a Delegate sending to another /// Delegate, or a Delegate pre-commiting it to a Project function transfer( uint64 idSender, uint64 idPledge, uint amount, uint64 idReceiver ) authP(PLEDGE_ADMIN_ROLE, arr(uint(idSender), amount)) public { _transfer(idSender, idPledge, amount, idReceiver); } /// @notice Authorizes a payment be made from the `vault` can be used by the /// Giver to veto a pre-committed donation from a Delegate to an /// intendedProject /// @param idPledge Id of the pledge that is to be redeemed into ether /// @param amount Quantity of ether (in wei) to be authorized function withdraw(uint64 idPledge, uint amount) public { idPledge = normalizePledge(idPledge); // Updates pledge info Pledges.Pledge storage p = _findPledge(idPledge); require(p.pledgeState == PledgeState.Pledged); PledgeAdmins.PledgeAdmin storage owner = _findAdmin(p.owner); require(canPerform(msg.sender, PLEDGE_ADMIN_ROLE, arr(uint(p.owner)))); uint64 idNewPledge = _findOrCreatePledge( p.owner, p.delegationChain, 0, 0, p.oldPledge, Pledges.PledgeState.Paying ); _doTransfer(idPledge, idNewPledge, amount); vault.authorizePayment(bytes32(idNewPledge), owner.addr, amount); } /// @notice `onlyVault` Confirms a withdraw request changing the Pledges.PledgeState /// from Paying to Paid /// @param idPledge Id of the pledge that is to be withdrawn /// @param amount Quantity of ether (in wei) to be withdrawn function confirmPayment(uint64 idPledge, uint amount) public onlyVault { Pledges.Pledge storage p = _findPledge(idPledge); require(p.pledgeState == Pledges.PledgeState.Paying); uint64 idNewPledge = _findOrCreatePledge( p.owner, p.delegationChain, 0, 0, p.oldPledge, Pledges.PledgeState.Paid ); _doTransfer(idPledge, idNewPledge, amount); } /// @notice `onlyVault` Cancels a withdraw request, changing the Pledges.PledgeState /// from Paying back to Pledged /// @param idPledge Id of the pledge that's withdraw is to be canceled /// @param amount Quantity of ether (in wei) to be canceled function cancelPayment(uint64 idPledge, uint amount) public onlyVault { Pledges.Pledge storage p = _findPledge(idPledge); require(p.pledgeState == Pledges.PledgeState.Paying); // When a payment is canceled, never is assigned to a project. uint64 idOldPledge = _findOrCreatePledge( p.owner, p.delegationChain, 0, 0, p.oldPledge, Pledges.PledgeState.Pledged ); idOldPledge = normalizePledge(idOldPledge); _doTransfer(idPledge, idOldPledge, amount); } /// @notice Changes the `project.canceled` flag to `true`; cannot be undone /// @param idProject Id of the project that is to be canceled function cancelProject(uint64 idProject) authP(PLEDGE_ADMIN_ROLE, arr(uint(idProject))) public { PledgeAdmins.PledgeAdmin storage project = _findAdmin(idProject); // _checkAdminOwner(project); project.canceled = true; CancelProject(idProject); } /// @notice Transfers `amount` in `idPledge` back to the `oldPledge` that /// that sent it there in the first place, a Ctrl-z /// @param idPledge Id of the pledge that is to be canceled /// @param amount Quantity of ether (in wei) to be transfered to the /// `oldPledge` function cancelPledge(uint64 idPledge, uint amount) public { idPledge = normalizePledge(idPledge); Pledges.Pledge storage p = _findPledge(idPledge); require(p.oldPledge != 0); require(canPerform(msg.sender, PLEDGE_ADMIN_ROLE, arr(uint(p.owner)))); uint64 oldPledge = _getOldestPledgeNotCanceled(p.oldPledge); _doTransfer(idPledge, oldPledge, amount); } //////// // Multi pledge methods //////// // @dev This set of functions makes moving a lot of pledges around much more // efficient (saves gas) than calling these functions in series /// @dev Bitmask used for dividing pledge amounts in Multi pledge methods uint constant D64 = 0x10000000000000000; /// @notice Transfers multiple amounts within multiple Pledges in an /// efficient single call /// @param idSender Id of the Admin that is transferring the amounts from /// all the Pledges; this admin must have permissions to move the value /// @param pledgesAmounts An array of Pledge amounts and the idPledges with /// which the amounts are associated; these are extrapolated using the D64 /// bitmask /// @param idReceiver Destination of the `pledesAmounts`, can be a Giver or /// Project sending to a Giver, a Delegate or a Project; a Delegate sending /// to another Delegate, or a Delegate pre-commiting it to a Project function mTransfer( uint64 idSender, uint[] pledgesAmounts, uint64 idReceiver ) public { for (uint i = 0; i < pledgesAmounts.length; i++ ) { uint64 idPledge = uint64( pledgesAmounts[i] & (D64-1) ); uint amount = pledgesAmounts[i] / D64; transfer(idSender, idPledge, amount, idReceiver); } } /// @notice Authorizes multiple amounts within multiple Pledges to be /// withdrawn from the `vault` in an efficient single call /// @param pledgesAmounts An array of Pledge amounts and the idPledges with /// which the amounts are associated; these are extrapolated using the D64 /// bitmask function mWithdraw(uint[] pledgesAmounts) public { for (uint i = 0; i < pledgesAmounts.length; i++ ) { uint64 idPledge = uint64( pledgesAmounts[i] & (D64-1) ); uint amount = pledgesAmounts[i] / D64; withdraw(idPledge, amount); } } /// @notice `mConfirmPayment` allows for multiple pledges to be confirmed /// efficiently /// @param pledgesAmounts An array of pledge amounts and IDs which are extrapolated /// using the D64 bitmask function mConfirmPayment(uint[] pledgesAmounts) public { for (uint i = 0; i < pledgesAmounts.length; i++ ) { uint64 idPledge = uint64( pledgesAmounts[i] & (D64-1) ); uint amount = pledgesAmounts[i] / D64; confirmPayment(idPledge, amount); } } /// @notice `mCancelPayment` allows for multiple pledges to be canceled /// efficiently /// @param pledgesAmounts An array of pledge amounts and IDs which are extrapolated /// using the D64 bitmask function mCancelPayment(uint[] pledgesAmounts) public { for (uint i = 0; i < pledgesAmounts.length; i++ ) { uint64 idPledge = uint64( pledgesAmounts[i] & (D64-1) ); uint amount = pledgesAmounts[i] / D64; cancelPayment(idPledge, amount); } } /// @notice `mNormalizePledge` allows for multiple pledges to be /// normalized efficiently /// @param pledges An array of pledge IDs function mNormalizePledge(uint64[] pledges) public { for (uint i = 0; i < pledges.length; i++ ) { normalizePledge( pledges[i] ); } } }