first pass at refactoring to Pledges lib

This commit is contained in:
perissology 2018-01-19 10:33:58 -08:00
parent 674397cc80
commit 4f2bf61dd2
5 changed files with 286 additions and 175 deletions

View File

@ -70,18 +70,19 @@ contract LiquidPledging is LiquidPledgingBase {
require(amount > 0);
vault.transfer(amount); // Sends the `msg.value` (in wei) to the `vault`
uint64 idPledge = findOrCreatePledge(
uint64 idPledge = _storage.findOrCreatePledge(
idGiver,
new uint64[](0), // Creates empty array for delegationChain
0,
0,
0,
PledgeState.Pledged
Pledges.PledgeState.Pledged
);
Pledge storage nTo = findPledge(idPledge);
nTo.amount += amount;
uint pAmount = _storage.getPledgeAmount(idPledge);
pAmount += amount;
_storage.setPledgeAmount(idPledge, pAmount);
Transfer(0, idPledge, amount); // An event
@ -107,10 +108,10 @@ contract LiquidPledging is LiquidPledgingBase {
idPledge = normalizePledge(idPledge);
Pledge storage p = findPledge(idPledge);
Pledges.Pledge memory p = _storage.findPledge(idPledge);
PledgeAdmins.PledgeAdminType receiverAdminType = _storage.getAdminType(idReceiver);
require(p.pledgeState == PledgeState.Pledged);
require(p.pledgeState == Pledges.PledgeState.Pledged);
// If the sender is the owner of the Pledge
if (p.owner == idSender) {
@ -128,13 +129,13 @@ contract LiquidPledging is LiquidPledgingBase {
// intendedProject by the owner
if (recieverDIdx == p.delegationChain.length - 1) {
uint64 toPledge = findOrCreatePledge(
uint64 toPledge = _storage.findOrCreatePledge(
p.owner,
p.delegationChain,
0,
0,
p.oldPledge,
PledgeState.Pledged);
Pledges.PledgeState.Pledged);
doTransfer(idPledge, toPledge, amount);
} else {
undelegate(idPledge, amount, p.delegationChain.length - receiverDIdx - 1);
@ -229,17 +230,17 @@ contract LiquidPledging is LiquidPledgingBase {
/// @param amount Quantity of ether (in wei) to be authorized
function withdraw(uint64 idPledge, uint amount) public {
idPledge = normalizePledge(idPledge); // Updates pledge info
Pledge storage p = findPledge(idPledge);
require(p.pledgeState == PledgeState.Pledged);
Pledges.Pledge memory p = _storage.findPledge(idPledge);
require(p.pledgeState == Pledges.PledgeState.Pledged);
checkAdminOwner(p.owner);
uint64 idNewPledge = findOrCreatePledge(
uint64 idNewPledge = _storage.findOrCreatePledge(
p.owner,
p.delegationChain,
0,
0,
p.oldPledge,
PledgeState.Paying
Pledges.PledgeState.Paying
);
doTransfer(idPledge, idNewPledge, amount);
@ -247,44 +248,44 @@ contract LiquidPledging is LiquidPledgingBase {
vault.authorizePayment(bytes32(idNewPledge), _storage.getAdminAddr(p.owner), amount);
}
/// @notice `onlyVault` Confirms a withdraw request changing the PledgeState
/// @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 {
Pledge storage p = findPledge(idPledge);
Pledges.Pledge memory p = _storage.findPledge(idPledge);
require(p.pledgeState == PledgeState.Paying);
require(p.pledgeState == Pledges.PledgeState.Paying);
uint64 idNewPledge = findOrCreatePledge(
uint64 idNewPledge = _storage.findOrCreatePledge(
p.owner,
p.delegationChain,
0,
0,
p.oldPledge,
PledgeState.Paid
Pledges.PledgeState.Paid
);
doTransfer(idPledge, idNewPledge, amount);
}
/// @notice `onlyVault` Cancels a withdraw request, changing the PledgeState
/// @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 {
Pledge storage p = findPledge(idPledge);
Pledges.Pledge memory p = _storage.findPledge(idPledge);
require(p.pledgeState == PledgeState.Paying); //TODO change to revert????????????????????????????
require(p.pledgeState == Pledges.PledgeState.Paying); //TODO change to revert????????????????????????????
// When a payment is canceled, never is assigned to a project.
uint64 oldPledge = findOrCreatePledge(
uint64 oldPledge = _storage.findOrCreatePledge(
p.owner,
p.delegationChain,
0,
0,
p.oldPledge,
PledgeState.Pledged
Pledges.PledgeState.Pledged
);
oldPledge = normalizePledge(oldPledge);
@ -307,7 +308,7 @@ contract LiquidPledging is LiquidPledgingBase {
function cancelPledge(uint64 idPledge, uint amount) public {
idPledge = normalizePledge(idPledge);
Pledge storage p = findPledge(idPledge);
Pledges.Pledge memory p = _storage.findPledge(idPledge);
require(p.oldPledge != 0);
checkAdminOwner(p.owner);
@ -415,28 +416,28 @@ contract LiquidPledging is LiquidPledgingBase {
uint amount,
uint64 idReceiver
) internal {
Pledge storage p = findPledge(idPledge);
Pledges.Pledge memory p = _storage.findPledge(idPledge);
// Ensure that the pledge is not already at max pledge depth
// and the project has not been canceled
require(getPledgeLevel(p) < MAX_INTERPROJECT_LEVEL);
require(_storage.getPledgeLevel(p.oldPledge) < MAX_INTERPROJECT_LEVEL);
require(!_storage.isProjectCanceled(idReceiver));
uint64 oldPledge = findOrCreatePledge(
uint64 oldPledge = _storage.findOrCreatePledge(
p.owner,
p.delegationChain,
0,
0,
p.oldPledge,
PledgeState.Pledged
Pledges.PledgeState.Pledged
);
uint64 toPledge = findOrCreatePledge(
uint64 toPledge = _storage.findOrCreatePledge(
idReceiver, // Set the new owner
new uint64[](0), // clear the delegation chain
0,
0,
oldPledge,
PledgeState.Pledged
Pledges.PledgeState.Pledged
);
doTransfer(idPledge, toPledge, amount);
}
@ -453,13 +454,13 @@ contract LiquidPledging is LiquidPledgingBase {
uint amount,
uint64 idReceiver
) internal {
uint64 toPledge = findOrCreatePledge(
uint64 toPledge = _storage.findOrCreatePledge(
idReceiver,
new uint64[](0),
0,
0,
0,
PledgeState.Pledged
Pledges.PledgeState.Pledged
);
doTransfer(idPledge, toPledge, amount);
}
@ -474,7 +475,7 @@ contract LiquidPledging is LiquidPledgingBase {
uint amount,
uint64 idReceiver
) internal {
Pledge storage p = findPledge(idPledge);
Pledges.Pledge memory p = _storage.findPledge(idPledge);
require(p.delegationChain.length < MAX_DELEGATES);
uint64[] memory newDelegationChain = new uint64[](
@ -487,13 +488,13 @@ contract LiquidPledging is LiquidPledgingBase {
// Make the last item in the array the idReceiver
newDelegationChain[p.delegationChain.length] = idReceiver;
uint64 toPledge = findOrCreatePledge(
uint64 toPledge = _storage.findOrCreatePledge(
p.owner,
newDelegationChain,
0,
0,
p.oldPledge,
PledgeState.Pledged
Pledges.PledgeState.Pledged
);
doTransfer(idPledge, toPledge, amount);
}
@ -510,7 +511,7 @@ contract LiquidPledging is LiquidPledgingBase {
uint q
) internal returns (uint64)
{
Pledge storage p = findPledge(idPledge);
Pledges.Pledge memory p = _storage.findPledge(idPledge);
uint64[] memory newDelegationChain = new uint64[](
p.delegationChain.length - q
);
@ -518,13 +519,13 @@ contract LiquidPledging is LiquidPledgingBase {
for (uint i=0; i<p.delegationChain.length - q; i++) {
newDelegationChain[i] = p.delegationChain[i];
}
uint64 toPledge = findOrCreatePledge(
uint64 toPledge = _storage.findOrCreatePledge(
p.owner,
newDelegationChain,
0,
0,
p.oldPledge,
PledgeState.Pledged
Pledges.PledgeState.Pledged
);
doTransfer(idPledge, toPledge, amount);
@ -543,18 +544,18 @@ contract LiquidPledging is LiquidPledgingBase {
uint amount,
uint64 idReceiver
) internal {
Pledge storage p = findPledge(idPledge);
Pledges.Pledge memory p = _storage.findPledge(idPledge);
require(getPledgeLevel(p) < MAX_INTERPROJECT_LEVEL);
require(_storage.getPledgeLevel(p.oldPledge) < MAX_INTERPROJECT_LEVEL);
require(!_storage.isProjectCanceled(idReceiver));
uint64 toPledge = findOrCreatePledge(
uint64 toPledge = _storage.findOrCreatePledge(
p.owner,
p.delegationChain,
idReceiver,
uint64(getTime() + maxCommitTime(p)),
p.oldPledge,
PledgeState.Pledged
Pledges.PledgeState.Pledged
);
doTransfer(idPledge, toPledge, amount);
}
@ -573,8 +574,8 @@ contract LiquidPledging is LiquidPledgingBase {
if (amount == 0) {
return;
}
Pledge storage nFrom = findPledge(from);
Pledge storage nTo = findPledge(to);
Pledges.Pledge memory nFrom = _storage.findPledge(from);
Pledges.Pledge memory nTo = _storage.findPledge(to);
require(nFrom.amount >= amount);
nFrom.amount -= amount;
nTo.amount += amount;
@ -583,7 +584,7 @@ contract LiquidPledging is LiquidPledgingBase {
// callPlugins(false, from, to, amount);
}
/// @notice Only affects pledges with the Pledged PledgeState for 2 things:
/// @notice Only affects pledges with the Pledged Pledges.PledgeState for 2 things:
/// #1: Checks if the pledge should be committed. This means that
/// if the pledge has an intendedProject and it is past the
/// commitTime, it changes the owner to be the proposed project
@ -602,35 +603,35 @@ contract LiquidPledging is LiquidPledgingBase {
/// @return The normalized Pledge!
function normalizePledge(uint64 idPledge) public returns(uint64) {
Pledge storage p = findPledge(idPledge);
Pledges.Pledge memory p = _storage.findPledge(idPledge);
// Check to make sure this pledge hasn't already been used
// or is in the process of being used
if (p.pledgeState != PledgeState.Pledged) {
if (p.pledgeState != Pledges.PledgeState.Pledged) {
return idPledge;
}
// First send to a project if it's proposed and committed
if ((p.intendedProject > 0) && ( getTime() > p.commitTime)) {
uint64 oldPledge = findOrCreatePledge(
uint64 oldPledge = _storage.findOrCreatePledge(
p.owner,
p.delegationChain,
0,
0,
p.oldPledge,
PledgeState.Pledged
Pledges.PledgeState.Pledged
);
uint64 toPledge = findOrCreatePledge(
uint64 toPledge = _storage.findOrCreatePledge(
p.intendedProject,
new uint64[](0),
0,
0,
oldPledge,
PledgeState.Pledged
Pledges.PledgeState.Pledged
);
doTransfer(idPledge, toPledge, p.amount);
idPledge = toPledge;
p = findPledge(idPledge);
p = _storage.findPledge(idPledge);
}
toPledge = getOldestPledgeNotCanceled(idPledge);
@ -720,7 +721,7 @@ contract LiquidPledging is LiquidPledgingBase {
// or transferring context
uint64 offset = idPledge == fromPledge ? 0 : 256;
allowedAmount = amount;
Pledge storage p = findPledge(idPledge);
Pledges.Pledge memory p = _storage.findPledge(idPledge);
// TODO I think we can remove these check b/c the admins array only grows, thus if adminId is out of index, it will just return 0x0 & skip the plugin call
// uint adminsSize = _storage.pledgeAdminsCount();

View File

@ -21,6 +21,7 @@ pragma solidity ^0.4.11;
import "./ILiquidPledgingPlugin.sol";
import "giveth-common-contracts/contracts/Escapable.sol";
import "./PledgeAdmins.sol";
import "./Pledges.sol";
import "./EternalStorage.sol";
/// @dev This is an interface for `LPVault` which serves as a secure storage for
@ -36,32 +37,14 @@ interface LPVault {
/// data structures
contract LiquidPledgingBase is Escapable {
using PledgeAdmins for EternalStorage;
using Pledges for EternalStorage;
// Limits inserted to prevent large loops that could prevent canceling
uint constant MAX_DELEGATES = 10;
uint constant MAX_SUBPROJECT_LEVEL = 20;
uint constant MAX_INTERPROJECT_LEVEL = 20;
enum PledgeState { Pledged, Paying, Paid }
struct Pledge {
uint amount;
uint64 owner; // PledgeAdmin
uint64[] delegationChain; // List of delegates in order of authority
uint64 intendedProject; // Used when delegates are sending to projects
uint64 commitTime; // When the intendedProject will become the owner
uint64 oldPledge; // Points to the id that this Pledge was derived from
PledgeState pledgeState; // Pledged, Paying, Paid
}
EternalStorage public _storage;
Pledge[] pledges;
/// @dev this mapping allows you to search for a specific pledge's
/// index number by the hash of that pledge
mapping (bytes32 => uint64) hPledge2idx;
LPVault public vault;
mapping (bytes32 => bool) pluginWhitelist;
@ -76,9 +59,6 @@ contract LiquidPledgingBase is Escapable {
event ProjectAdded(uint indexed idProject);
event ProjectUpdated(uint indexed idProject);
// for testing
event Gas(uint remainingGas);
/////////////
// Modifiers
/////////////
@ -91,6 +71,12 @@ contract LiquidPledgingBase is Escapable {
_;
}
/// @notice A check to see if the msg.sender is the owner or the
/// plugin contract for a specific Admin
/// @param idAdmin The id of the admin being checked
function checkAdminOwner(uint idAdmin) internal constant {
require(msg.sender == _storage.getAdminPlugin(idAdmin) || msg.sender == _storage.getAdminAddr(idAdmin));
}
///////////////
// Constructor
@ -230,8 +216,8 @@ contract LiquidPledgingBase is Escapable {
/// @notice A constant getter that returns the total number of pledges
/// @return The total number of Pledges in the system
function numberOfPledges() public constant returns (uint) {
return pledges.length - 1;
function numberOfPledges() public view returns (uint) {
return _storage.pledgesCount();
}
/// @notice A getter that returns the details of the specified pledge
@ -246,12 +232,12 @@ contract LiquidPledgingBase is Escapable {
uint64 intendedProject,
uint64 commitTime,
uint64 oldPledge,
PledgeState pledgeState
Pledges.PledgeState pledgeState
) {
Pledge storage p = findPledge(idPledge);
Pledges.Pledge memory p = _storage.findPledge(idPledge);
amount = p.amount;
owner = p.owner;
nDelegates = uint64(p.delegationChain.length);
nDelegates = uint64(_storage.getPledgeDelegateCount(idPledge));
intendedProject = p.intendedProject;
commitTime = p.commitTime;
oldPledge = p.oldPledge;
@ -266,81 +252,37 @@ contract LiquidPledgingBase is Escapable {
address addr,
string name
) {
Pledge storage p = findPledge(idPledge);
idDelegate = p.delegationChain[idxDelegate - 1];
require(_storage.pledgeAdminsCount() >= idxDelegate);
idDelegate = uint64(_storage.getPledgeDelegate(idPledge, idxDelegate));
addr = _storage.getAdminAddr(idDelegate);
name = _storage.getAdminName(idDelegate);
}
/// @notice A constant getter used to check how many total Admins exist
/// @return The total number of admins (Givers, Delegates and Projects) .
// function numberOfPledgeAdmins() public constant returns(uint) {
// return _storage.pledgeAdminsCount();
// }
function numberOfPledgeAdmins() public constant returns(uint) {
return _storage.pledgeAdminsCount();
}
// can use _storage.getAdmin(idAdmin);
// function getPledgeAdmin(uint64 idAdmin) public constant returns (
// PledgeAdmins.PledgeAdminType adminType,
// address addr,
// string name,
// string url,
// uint64 commitTime,
// uint64 parentProject,
// bool canceled,
// address plugin)
// {
// (adminType, addr, name, url, commitTime, parentProject, canceled, plugin) = _storage.getAdmin(idAdmin);
// }
function getPledgeAdmin(uint64 idAdmin) public constant returns (
PledgeAdmins.PledgeAdminType adminType,
address addr,
string name,
string url,
uint64 commitTime,
uint64 parentProject,
bool canceled,
address plugin)
{
// uint p;
// (adminType, addr, name, url, commitTime, p, canceled, plugin) = _storage.getAdmin(idAdmin);
return _storage.getAdmin(idAdmin);
// parentProject = uint64(p);
}
////////
// Private methods
///////
/// @notice This creates a Pledge with an initial amount of 0 if one is not
/// created already; otherwise it finds the pledge with the specified
/// attributes; all pledges technically exist, if the pledge hasn't been
/// created in this system yet it simply isn't in the hash array
/// hPledge2idx[] yet
/// @param owner The owner of the pledge being looked up
/// @param delegationChain The list of delegates in order of authority
/// @param intendedProject The project this pledge will Fund after the
/// commitTime has passed
/// @param commitTime The length of time in seconds the Giver has to
/// veto when the Giver's delegates Pledge funds to a project
/// @param oldPledge This value is used to store the pledge the current
/// pledge was came from, and in the case a Project is canceled, the Pledge
/// will revert back to it's previous state
/// @param state The pledge state: Pledged, Paying, or state
/// @return The hPledge2idx index number
function findOrCreatePledge(
uint64 owner,
uint64[] delegationChain,
uint64 intendedProject,
uint64 commitTime,
uint64 oldPledge,
PledgeState state
) internal returns (uint64)
{
bytes32 hPledge = keccak256(
owner, delegationChain, intendedProject, commitTime, oldPledge, state);
uint64 idx = hPledge2idx[hPledge];
if (idx > 0) return idx;
idx = uint64(pledges.length);
hPledge2idx[hPledge] = idx;
pledges.push(Pledge(
0, owner, delegationChain, intendedProject, commitTime, oldPledge, state));
return idx;
}
/// @notice A getter to look up a Pledge's details
/// @param idPledge The id for the Pledge to lookup
/// @return The PledgeA struct for the specified Pledge
function findPledge(uint64 idPledge) internal view returns (Pledge storage) {
require(idPledge < pledges.length);
return pledges[idPledge];
}
// a constant for when a delegate is requested that is not in the system
uint64 constant NOTFOUND = 0xFFFFFFFFFFFFFFFF;
@ -352,28 +294,18 @@ contract LiquidPledgingBase is Escapable {
/// `admins` array index `idDelegate` this returns that delegates
/// corresponding index in the delegationChain. Otherwise it returns
/// the NOTFOUND constant
function getDelegateIdx(Pledge p, uint64 idDelegate) internal pure returns(uint64) {
function getDelegateIdx(Pledges.Pledge p, uint64 idDelegate) internal pure returns(uint64) {
for (uint i=0; i < p.delegationChain.length; i++) {
if (p.delegationChain[i] == idDelegate) return uint64(i);
}
return NOTFOUND;
}
/// @notice A getter to find how many old "parent" pledges a specific Pledge
/// had using a self-referential loop
/// @param p The Pledge being queried
/// @return The number of old "parent" pledges a specific Pledge had
function getPledgeLevel(Pledge p) internal returns(uint) {
if (p.oldPledge == 0) return 0;
Pledge storage oldN = findPledge(p.oldPledge);
return getPledgeLevel(oldN) + 1; // a loop lookup
}
/// @notice A getter to find the longest commitTime out of the owner and all
/// the delegates for a specified pledge
/// @param p The Pledge being queried
/// @return The maximum commitTime out of the owner and all the delegates
function maxCommitTime(Pledge p) internal view returns(uint commitTime) {
function maxCommitTime(Pledges.Pledge p) internal view returns(uint commitTime) {
uint adminsSize = _storage.pledgeAdminsCount();
require(adminsSize >= p.owner);
@ -393,25 +325,19 @@ contract LiquidPledgingBase is Escapable {
/// @return The oldest idPledge that hasn't been canceled (DUH!)
function getOldestPledgeNotCanceled(
uint64 idPledge
) internal constant returns(uint64)
) internal view returns(uint64)
{
if (idPledge == 0) return 0;
Pledge storage p = findPledge(idPledge);
uint owner = _storage.getPledgeOwner(idPledge);
PledgeAdmins.PledgeAdminType adminType = _storage.getAdminType(p.owner);
PledgeAdmins.PledgeAdminType adminType = _storage.getAdminType(owner);
if (adminType == PledgeAdmins.PledgeAdminType.Giver) return idPledge;
assert(adminType == PledgeAdmins.PledgeAdminType.Project);
if (!_storage.isProjectCanceled(p.owner)) return idPledge;
if (!_storage.isProjectCanceled(owner)) return idPledge;
return getOldestPledgeNotCanceled(p.oldPledge);
}
/// @notice A check to see if the msg.sender is the owner or the
/// plugin contract for a specific Admin
/// @param idAdmin The id of the admin being checked
function checkAdminOwner(uint idAdmin) internal constant {
require((msg.sender == _storage.getAdminPlugin(idAdmin)) || (msg.sender == _storage.getAdminAddr(idAdmin)));
uint64 oldPledge = uint64(_storage.getPledgeOldPledge(idPledge));
return getOldestPledgeNotCanceled(oldPledge);
}
///////////////////////////

View File

@ -34,7 +34,7 @@ contract LiquidPledgingMock is LiquidPledging {
address _vault,
address _escapeHatchCaller,
address _escapeHatchDestination
) LiquidPledging(_storage, _vault, _escapeHatchCaller, _escapeHatchDestination) {
) LiquidPledging(_storage, _vault, _escapeHatchCaller, _escapeHatchDestination) public {
mock_time = now;
}
@ -48,7 +48,7 @@ contract LiquidPledgingMock is LiquidPledging {
/// the mock_time parameter
/// @param _t This is the value to which the mocked time
/// will be set.
function setMockedTime(uint _t) {
function setMockedTime(uint _t) public {
mock_time = _t;
}
}

View File

@ -3,6 +3,9 @@ pragma solidity ^0.4.18;
import "./ILiquidPledgingPlugin.sol";
import "./EternallyPersistentLib.sol";
//18,446,744,070,000,000,000 uint64
//1,516,144,546,228,000 uint56
library PledgeAdmins {
using EternallyPersistentLib for EternalStorage;
@ -30,7 +33,7 @@ library PledgeAdmins {
uint commitTime,
ILiquidPledgingPlugin plugin
) internal returns (uint idGiver) {
idGiver = _storage.stgCollectionAddItem(admins);//, idGiver);
idGiver = _storage.stgCollectionAddItem(admins);
// Save the fields
_storage.stgObjectSetUInt(class, idGiver, "adminType", uint(PledgeAdminType.Giver));
@ -248,7 +251,7 @@ library PledgeAdmins {
/// @notice A constant getter used to check how many total Admins exist
/// @return The total number of admins (Givers, Delegates and Projects) .
function pledgeAdminsCount(EternalStorage _storage) internal constant returns(uint) {
return _storage.stgCollectionLength(admins);// - 1;
return _storage.stgCollectionLength(admins);
}
/// @notice A constant getter to check the details of a specified Admin
@ -270,7 +273,7 @@ library PledgeAdmins {
string name,
string url,
uint64 commitTime,
uint parentProject,
uint64 parentProject,
bool canceled,
address plugin
)
@ -284,7 +287,7 @@ library PledgeAdmins {
// parentProject & canceled only belong to Project admins,
// so don't waste the gas to fetch the data
if (adminType == PledgeAdminType.Project) {
parentProject = getAdminParentProject(_storage, idAdmin);
parentProject = uint64(getAdminParentProject(_storage, idAdmin));
canceled = getAdminCanceled(_storage, idAdmin);
}
@ -302,9 +305,6 @@ library PledgeAdmins {
return getProjectLevel(_storage, parentProject) + 1;
}
//18,446,744,070,000,000,000 uint64
//1,516,144,546,228,000 uint56
////////
// Methods to fetch individual attributes of a PledgeAdmin
///////

184
contracts/Pledges.sol Normal file
View File

@ -0,0 +1,184 @@
pragma solidity ^0.4.18;
import "./EternallyPersistentLib.sol";
library Pledges {
using EternallyPersistentLib for EternalStorage;
string constant class = "Pledge";
bytes32 constant pledges = keccak256("pledges");
enum PledgeState { Pledged, Paying, Paid }
struct Pledge {
uint id;
uint amount;
uint64 owner; // PledgeAdmin
uint64[] delegationChain; // List of delegates in order of authority
uint64 intendedProject; // Used when delegates are sending to projects
uint64 commitTime; // When the intendedProject will become the owner
uint64 oldPledge; // Points to the id that this Pledge was derived from
PledgeState pledgeState; // Pledged, Paying, Paid
}
function pledgesCount(EternalStorage _storage) internal view returns(uint) {
return _storage.stgCollectionLength(pledges);
}
/// @notice This creates a Pledge with an initial amount of 0 if one is not
/// created already; otherwise it finds the pledge with the specified
/// attributes; all pledges technically exist, if the pledge hasn't been
/// created in this system yet it simply isn't in the hash array
/// hPledge2idx[] yet
/// @param owner The owner of the pledge being looked up
/// @param delegationChain The list of delegates in order of authority
/// @param intendedProject The project this pledge will Fund after the
/// commitTime has passed
/// @param commitTime The length of time in seconds the Giver has to
/// veto when the Giver's delegates Pledge funds to a project
/// @param oldPledge This value is used to store the pledge the current
/// pledge was came from, and in the case a Project is canceled, the Pledge
/// will revert back to it's previous state
/// @param state The pledge state: Pledged, Paying, or state
/// @return The hPledge2idx index number
function findOrCreatePledge(
EternalStorage _storage,
uint64 owner,
uint64[] delegationChain,
uint64 intendedProject,
uint64 commitTime,
uint64 oldPledge,
PledgeState state
) internal returns (uint64)
{
bytes32 hPledge = keccak256(owner, delegationChain, intendedProject, commitTime, oldPledge, state);
uint id = _storage.getUIntValue(hPledge);
if (id > 0) return uint64(id);
id = _storage.stgCollectionAddItem(pledges);
_storage.setUIntValue(hPledge, id);
_storage.stgObjectSetUInt(class, id, "owner", owner);
if (intendedProject > 0) {
_storage.stgObjectSetUInt(class, id, "intendedProject", intendedProject);
}
if (commitTime > 0) {
_storage.stgObjectSetUInt(class, id, "commitTime", commitTime);
}
if (oldPledge > 0) {
_storage.stgObjectSetUInt(class, id, "oldPledge", oldPledge);
}
_storage.stgObjectSetUInt(class, id, "state", uint(state));
if (delegationChain.length > 0) {
_storage.setUIntValue(keccak256("delegationChain", id, "length"), delegationChain.length);
// TODO pack these? possibly add array method to EternalStorage in anticipation of the new solidity abi encoder
for (uint i=0; i < delegationChain.length; i++) {
_storage.setUIntValue(keccak256("delegationChain", id, i), delegationChain[i]);
}
}
return uint64(id);
}
function findPledge(EternalStorage _storage, uint idPledge) internal view returns(Pledge) {
require(idPledge <= pledgesCount(_storage));
uint amount = getPledgeAmount(_storage, idPledge);
uint owner = getPledgeOwner(_storage, idPledge);
uint intendedProject = getPledgeIntendedProject(_storage, idPledge);
uint commitTime = getPledgeCommitTime(_storage, idPledge);
uint oldPledge = getPledgeOldPledge(_storage, idPledge);
PledgeState state = getPledgeState(_storage, idPledge);
uint64[] memory delegates = getPledgeDelegates(_storage, idPledge);
return Pledge(
idPledge,
amount,
uint64(owner),
delegates,
uint64(intendedProject),
uint64(commitTime),
uint64(oldPledge),
state
);
}
// a constant for when a delegate is requested that is not in the system
uint64 constant NOTFOUND = 0xFFFFFFFFFFFFFFFF;
/// @notice A getter that searches the delegationChain for the level of
/// authority a specific delegate has within a Pledge
/// @param idPledge The Pledge that will be searched
/// @param idDelegate The specified delegate that's searched for
/// @return If the delegate chain contains the delegate with the
/// `admins` array index `idDelegate` this returns that delegates
/// corresponding index in the delegationChain. Otherwise it returns
/// the NOTFOUND constant
function getDelegateIdx(EternalStorage _storage, uint idPledge, uint64 idDelegate) internal view returns(uint64) {
//TODO pack/unpack chain
uint length = getPledgeDelegateCount(_storage, idPledge);
for (uint i=0; i < length; i++) {
if (getPledgeDelegate(_storage, idPledge, i) == idDelegate) return uint64(i);
}
return NOTFOUND;
}
/// @notice A getter to find how many old "parent" pledges a specific Pledge
/// had using a self-referential loop
/// @param idOldPledge The Pledge being queried
/// @return The number of old "parent" pledges a specific Pledge had
function getPledgeLevel(EternalStorage _storage, uint idOldPledge) internal view returns(uint) {
if (idOldPledge == 0) return 0;
idOldPledge = _storage.stgObjectGetUInt(class, idOldPledge, "oldPledge");
return getPledgeLevel(_storage, idOldPledge) + 1; // a loop lookup
}
function getPledgeOwner(EternalStorage _storage, uint idPledge) internal view returns(uint) {
return _storage.stgObjectGetUInt(class, idPledge, "owner");
}
function getPledgeDelegate(EternalStorage _storage, uint idPledge, uint index) internal view returns(uint) {
return _storage.getUIntValue(keccak256("delegationChain", idPledge, index));
}
function getPledgeOldPledge(EternalStorage _storage, uint idPledge) internal view returns(uint) {
return _storage.stgObjectGetUInt(class, idPledge, "oldPledge");
}
function getPledgeAmount(EternalStorage _storage, uint idPledge) internal view returns(uint) {
return _storage.stgObjectGetUInt(class, idPledge, "amount");
}
function getPledgeIntendedProject(EternalStorage _storage, uint idPledge) internal view returns(uint) {
return _storage.stgObjectGetUInt(class, idPledge, "intendedProject");
}
function getPledgeCommitTime(EternalStorage _storage, uint idPledge) internal view returns(uint) {
return _storage.stgObjectGetUInt(class, idPledge, "commitTime");
}
function getPledgeState(EternalStorage _storage, uint idPledge) internal view returns(PledgeState) {
return PledgeState(_storage.stgObjectGetUInt(class, idPledge, "state"));
}
function getPledgeDelegates(EternalStorage _storage, uint idPledge) internal view returns(uint64[]) {
//TODO pack/unpack chain
uint length = getPledgeDelegateCount(_storage, idPledge);
uint64[] memory delegates = new uint64[](length);
for (uint i=0; i < length; i++) {
delegates[i] = uint64(getPledgeDelegate(_storage, idPledge, i));
}
return delegates;
}
function getPledgeDelegateCount(EternalStorage _storage, uint idPledge) internal view returns(uint) {
return _storage.getUIntValue(keccak256("delegationChain", idPledge, "length"));
}
function setPledgeAmount(EternalStorage _storage, uint idPledge, uint amount) internal {
_storage.stgObjectSetUInt(class, idPledge, "amount", amount);
}
}