liquid-funding/contracts/LiquidPledging.sol

296 lines
11 KiB
Solidity
Raw Normal View History

pragma solidity ^0.4.18;
2017-10-19 21:49:53 +00:00
/*
Copyright 2017, Jordi Baylina, RJ Ewing
Contributors: Adrià Massanet <adria@codecontext.io>, Griff Green,
Arthur Lunn
2017-06-06 17:40:14 +00:00
2017-10-19 21:49:53 +00:00
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.
2017-06-06 17:40:14 +00:00
2017-10-19 21:49:53 +00:00
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
2017-06-06 17:40:14 +00:00
2017-10-19 21:49:53 +00:00
import "./LiquidPledgingBase.sol";
2018-02-07 11:52:08 +00:00
/// @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.
2017-06-06 17:40:14 +00:00
contract LiquidPledging is LiquidPledgingBase {
2018-03-27 17:55:37 +00:00
function LiquidPledging(address _escapeHatchDestination) EscapableApp(_escapeHatchDestination) public {
}
2018-02-20 16:12:01 +00:00
function addGiverAndDonate(uint64 idReceiver, address token, uint amount)
public
{
addGiverAndDonate(idReceiver, msg.sender, token, amount);
}
function addGiverAndDonate(uint64 idReceiver, address donorAddress, address token, uint amount)
public
{
require(donorAddress != 0);
// default to a 3 day (259200 seconds) commitTime
2018-02-20 16:12:01 +00:00
uint64 idGiver = addGiver(donorAddress, "", "", 259200, ILiquidPledgingPlugin(0));
donate(idGiver, idReceiver, token, amount);
}
2017-12-04 01:12:46 +00:00
/// @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
2018-03-27 17:55:37 +00:00
/// @param idGiver The id of the Giver donating
2017-12-04 01:12:46 +00:00
/// @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, address token, uint amount)
public
2018-02-10 14:14:52 +00:00
{
require(idGiver > 0); // prevent burning donations. idReceiver is checked in _transfer
require(amount > 0);
require(token != 0x0);
PledgeAdmin storage sender = _findAdmin(idGiver);
2018-02-10 14:14:52 +00:00
require(sender.adminType == PledgeAdminType.Giver);
2018-03-27 17:55:37 +00:00
// TODO should this be done at the end of this function?
// what re-entrancy issues are there if this is done here?
// if done at the end of the function, will that affect plugins?
require(ERC20(token).transferFrom(msg.sender, address(vault), amount)); // transfer the token to the `vault`
2018-02-10 14:14:52 +00:00
uint64 idPledge = _findOrCreatePledge(
2017-10-03 10:20:23 +00:00
idGiver,
2017-12-04 01:12:46 +00:00
new uint64[](0), // Creates empty array for delegationChain
2017-06-06 17:40:14 +00:00
0,
0,
0,
token,
PledgeState.Pledged
2017-10-23 00:04:58 +00:00
);
2017-06-06 17:40:14 +00:00
Pledge storage pTo = _findPledge(idPledge);
2018-02-10 14:14:52 +00:00
pTo.amount += amount;
2017-06-06 17:40:14 +00:00
Transfer(0, idPledge, amount);
2017-06-06 17:40:14 +00:00
_transfer(idGiver, idPledge, amount, idReceiver);
2017-06-06 17:40:14 +00:00
}
/// @notice Transfers amounts between pledges for internal accounting
2017-12-04 01:12:46 +00:00
/// @param idSender Id of the Admin that is transferring the amount from
/// Pledge to Pledge; this admin must have permissions to move the value
2017-10-03 12:42:21 +00:00
/// @param idPledge Id of the pledge that's moving the value
2017-12-04 01:12:46 +00:00
/// @param amount Quantity of ETH (in wei) that this pledge is transferring
/// the authority to withdraw from the vault
2017-12-04 20:33:15 +00:00
/// @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
2017-12-04 01:12:46 +00:00
/// Delegate, or a Delegate pre-commiting it to a Project
function transfer(
2017-10-23 00:04:58 +00:00
uint64 idSender,
uint64 idPledge,
uint amount,
uint64 idReceiver
) public
{
2018-03-27 17:55:37 +00:00
_checkAdminOwner(idSender);
_transfer(idSender, idPledge, amount, idReceiver);
2017-06-06 17:40:14 +00:00
}
2017-12-04 01:12:46 +00:00
/// @notice Authorizes a payment be made from the `vault` can be used by the
2017-12-04 20:33:15 +00:00
/// Giver to veto a pre-committed donation from a Delegate to an
/// intendedProject
2017-12-09 23:24:47 +00:00
/// @param idPledge Id of the pledge that is to be redeemed into ether
2017-12-04 01:12:46 +00:00
/// @param amount Quantity of ether (in wei) to be authorized
2018-02-12 22:55:11 +00:00
function withdraw(uint64 idPledge, uint amount) public {
2018-02-10 14:14:52 +00:00
idPledge = normalizePledge(idPledge); // Updates pledge info
Pledge storage p = _findPledge(idPledge);
2018-02-10 14:14:52 +00:00
require(p.pledgeState == PledgeState.Pledged);
2018-03-27 17:55:37 +00:00
_checkAdminOwner(p.owner);
2018-02-10 14:14:52 +00:00
uint64 idNewPledge = _findOrCreatePledge(
2017-12-05 20:47:38 +00:00
p.owner,
p.delegationChain,
2017-06-06 17:40:14 +00:00
0,
0,
2017-12-05 20:47:38 +00:00
p.oldPledge,
p.token,
PledgeState.Paying
2017-06-06 17:40:14 +00:00
);
2018-02-10 14:14:52 +00:00
_doTransfer(idPledge, idNewPledge, amount);
2017-06-06 17:40:14 +00:00
PledgeAdmin storage owner = _findAdmin(p.owner);
vault.authorizePayment(bytes32(idNewPledge), owner.addr, p.token, amount);
2017-06-06 17:40:14 +00:00
}
/// @notice `onlyVault` Confirms a withdraw request changing the PledgeState
2017-12-09 23:24:47 +00:00
/// 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
2018-01-15 22:36:17 +00:00
function confirmPayment(uint64 idPledge, uint amount) public onlyVault {
Pledge storage p = _findPledge(idPledge);
2017-06-06 17:40:14 +00:00
require(p.pledgeState == PledgeState.Paying);
2017-06-06 17:40:14 +00:00
2018-02-10 14:14:52 +00:00
uint64 idNewPledge = _findOrCreatePledge(
2017-12-05 20:47:38 +00:00
p.owner,
p.delegationChain,
2017-06-06 17:40:14 +00:00
0,
0,
2017-12-05 20:47:38 +00:00
p.oldPledge,
p.token,
PledgeState.Paid
2017-06-06 17:40:14 +00:00
);
2018-02-10 14:14:52 +00:00
_doTransfer(idPledge, idNewPledge, amount);
2017-06-06 17:40:14 +00:00
}
/// @notice `onlyVault` Cancels a withdraw request, changing the PledgeState
2017-12-09 23:24:47 +00:00
/// 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
2018-01-15 22:36:17 +00:00
function cancelPayment(uint64 idPledge, uint amount) public onlyVault {
Pledge storage p = _findPledge(idPledge);
2017-06-06 17:40:14 +00:00
require(p.pledgeState == PledgeState.Paying);
2017-06-06 17:40:14 +00:00
2017-10-04 23:27:23 +00:00
// When a payment is canceled, never is assigned to a project.
2018-02-10 14:14:52 +00:00
uint64 idOldPledge = _findOrCreatePledge(
2017-12-05 20:47:38 +00:00
p.owner,
p.delegationChain,
2017-06-06 17:40:14 +00:00
0,
0,
2017-12-05 20:47:38 +00:00
p.oldPledge,
p.token,
PledgeState.Pledged
2017-06-06 17:40:14 +00:00
);
2018-02-10 14:14:52 +00:00
idOldPledge = normalizePledge(idOldPledge);
2017-06-06 17:40:14 +00:00
2018-02-10 14:14:52 +00:00
_doTransfer(idPledge, idOldPledge, amount);
2017-06-06 17:40:14 +00:00
}
2017-12-09 23:24:47 +00:00
/// @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) public {
PledgeAdmin storage project = _findAdmin(idProject);
2018-03-27 17:55:37 +00:00
_checkAdminOwner(idProject);
2018-02-10 14:14:52 +00:00
project.canceled = true;
CancelProject(idProject);
2017-06-06 17:40:14 +00:00
}
2017-12-09 23:24:47 +00:00
/// @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`
2018-02-12 23:24:26 +00:00
function cancelPledge(uint64 idPledge, uint amount) public {
2018-02-10 14:14:52 +00:00
idPledge = normalizePledge(idPledge);
Pledge storage p = _findPledge(idPledge);
2017-12-05 20:47:38 +00:00
require(p.oldPledge != 0);
2018-03-27 17:55:37 +00:00
require(p.pledgeState == PledgeState.Pledged);
_checkAdminOwner(p.owner);
2018-02-10 14:14:52 +00:00
uint64 oldPledge = _getOldestPledgeNotCanceled(p.oldPledge);
_doTransfer(idPledge, oldPledge, amount);
}
2017-06-06 17:40:14 +00:00
////////
2017-10-03 12:42:21 +00:00
// Multi pledge methods
2017-06-06 17:40:14 +00:00
////////
// @dev This set of functions makes moving a lot of pledges around much more
// efficient (saves gas) than calling these functions in series
2017-10-19 21:49:53 +00:00
2017-12-10 01:24:39 +00:00
/// @dev Bitmask used for dividing pledge amounts in Multi pledge methods
2017-06-06 17:40:14 +00:00
uint constant D64 = 0x10000000000000000;
2017-10-19 21:49:53 +00:00
2017-12-10 01:24:39 +00:00
/// @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
2017-10-23 00:04:58 +00:00
function mTransfer(
uint64 idSender,
uint[] pledgesAmounts,
uint64 idReceiver
) public
{
2017-10-03 12:42:21 +00:00
for (uint i = 0; i < pledgesAmounts.length; i++ ) {
2018-03-27 17:55:37 +00:00
uint64 idPledge = uint64(pledgesAmounts[i] & (D64-1));
2017-10-03 12:42:21 +00:00
uint amount = pledgesAmounts[i] / D64;
2017-06-06 17:40:14 +00:00
2017-10-03 12:42:21 +00:00
transfer(idSender, idPledge, amount, idReceiver);
2017-06-06 17:40:14 +00:00
}
}
2017-12-10 01:24:39 +00:00
/// @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
2018-01-15 22:36:17 +00:00
function mWithdraw(uint[] pledgesAmounts) public {
2017-10-03 12:42:21 +00:00
for (uint i = 0; i < pledgesAmounts.length; i++ ) {
2018-03-27 17:55:37 +00:00
uint64 idPledge = uint64(pledgesAmounts[i] & (D64-1));
2017-10-03 12:42:21 +00:00
uint amount = pledgesAmounts[i] / D64;
2017-06-06 17:40:14 +00:00
2017-10-03 12:42:21 +00:00
withdraw(idPledge, amount);
2017-06-06 17:40:14 +00:00
}
}
2017-10-23 00:04:58 +00:00
/// @notice `mConfirmPayment` allows for multiple pledges to be confirmed
/// efficiently
2017-11-01 21:03:03 +00:00
/// @param pledgesAmounts An array of pledge amounts and IDs which are extrapolated
2017-10-23 00:04:58 +00:00
/// using the D64 bitmask
2018-01-15 22:36:17 +00:00
function mConfirmPayment(uint[] pledgesAmounts) public {
2017-10-03 12:42:21 +00:00
for (uint i = 0; i < pledgesAmounts.length; i++ ) {
2018-03-27 17:55:37 +00:00
uint64 idPledge = uint64(pledgesAmounts[i] & (D64-1));
2017-10-03 12:42:21 +00:00
uint amount = pledgesAmounts[i] / D64;
2017-06-06 17:40:14 +00:00
2017-10-03 12:42:21 +00:00
confirmPayment(idPledge, amount);
2017-06-06 17:40:14 +00:00
}
}
2017-10-28 08:27:47 +00:00
/// @notice `mCancelPayment` allows for multiple pledges to be canceled
2017-10-23 00:04:58 +00:00
/// efficiently
2017-11-01 21:03:03 +00:00
/// @param pledgesAmounts An array of pledge amounts and IDs which are extrapolated
2017-10-23 00:04:58 +00:00
/// using the D64 bitmask
2018-01-15 22:36:17 +00:00
function mCancelPayment(uint[] pledgesAmounts) public {
2017-10-03 12:42:21 +00:00
for (uint i = 0; i < pledgesAmounts.length; i++ ) {
2018-03-27 17:55:37 +00:00
uint64 idPledge = uint64(pledgesAmounts[i] & (D64-1));
2017-10-03 12:42:21 +00:00
uint amount = pledgesAmounts[i] / D64;
2017-06-06 17:40:14 +00:00
2017-10-03 12:42:21 +00:00
cancelPayment(idPledge, amount);
2017-06-06 17:40:14 +00:00
}
}
2017-10-23 00:04:58 +00:00
/// @notice `mNormalizePledge` allows for multiple pledges to be
/// normalized efficiently
2017-12-03 00:59:30 +00:00
/// @param pledges An array of pledge IDs
2018-01-15 22:36:17 +00:00
function mNormalizePledge(uint64[] pledges) public {
2017-10-03 12:42:21 +00:00
for (uint i = 0; i < pledges.length; i++ ) {
2018-03-27 17:55:37 +00:00
normalizePledge(pledges[i]);
}
}
2017-06-06 17:40:14 +00:00
}