refactor PledgeAdmins to lib w/ EternalStorage
This commit is contained in:
parent
80a8e5a9b4
commit
80b8ffc2ef
|
@ -0,0 +1,92 @@
|
|||
pragma solidity ^0.4.0;
|
||||
|
||||
// change to Escapable
|
||||
import "node_modules/giveth-common-contracts/contracts/Owned.sol";
|
||||
|
||||
contract EternalStorage is Owned {
|
||||
|
||||
mapping(bytes32 => uint) UIntStorage;
|
||||
mapping(bytes32 => int) IntStorage;
|
||||
mapping(bytes32 => bool) BooleanStorage;
|
||||
mapping(bytes32 => address) AddressStorage;
|
||||
mapping(bytes32 => string) StringStorage;
|
||||
mapping(bytes32 => bytes) BytesStorage;
|
||||
mapping(bytes32 => bytes32) Bytes32Storage;
|
||||
|
||||
/// UInt Storage
|
||||
|
||||
function getUIntValue(bytes32 record) public view returns (uint) {
|
||||
return UIntStorage[record];
|
||||
}
|
||||
|
||||
function setUIntValue(bytes32 record, uint value) public onlyOwner
|
||||
{
|
||||
UIntStorage[record] = value;
|
||||
}
|
||||
|
||||
/// Int Storage
|
||||
|
||||
function getIntValue(bytes32 record) public view returns (int){
|
||||
return IntStorage[record];
|
||||
}
|
||||
|
||||
function setIntValue(bytes32 record, int value) public onlyOwner
|
||||
{
|
||||
IntStorage[record] = value;
|
||||
}
|
||||
|
||||
/// Address Storage
|
||||
|
||||
function getAddressValue(bytes32 record) public view returns (address){
|
||||
return AddressStorage[record];
|
||||
}
|
||||
|
||||
function setAddressValue(bytes32 record, address value) public onlyOwner
|
||||
{
|
||||
AddressStorage[record] = value;
|
||||
}
|
||||
|
||||
/// String Storage
|
||||
|
||||
function getStringValue(bytes32 record) public view returns (string){
|
||||
return StringStorage[record];
|
||||
}
|
||||
|
||||
function setStringValue(bytes32 record, string value) public onlyOwner
|
||||
{
|
||||
StringStorage[record] = value;
|
||||
}
|
||||
|
||||
/// Bytes Storage
|
||||
|
||||
function getBytesValue(bytes32 record) public view returns (bytes){
|
||||
return BytesStorage[record];
|
||||
}
|
||||
|
||||
function setBytesValue(bytes32 record, bytes value) public onlyOwner
|
||||
{
|
||||
BytesStorage[record] = value;
|
||||
}
|
||||
|
||||
/// Bytes Storage
|
||||
|
||||
function getBytes32Value(bytes32 record) public view returns (bytes32){
|
||||
return Bytes32Storage[record];
|
||||
}
|
||||
|
||||
function setBytes32Value(bytes32 record, bytes32 value) public onlyOwner
|
||||
{
|
||||
Bytes32Storage[record] = value;
|
||||
}
|
||||
|
||||
/// Boolean Storage
|
||||
|
||||
function getBooleanValue(bytes32 record) public view returns (bool){
|
||||
return BooleanStorage[record];
|
||||
}
|
||||
|
||||
function setBooleanValue(bytes32 record, bool value) public onlyOwner
|
||||
{
|
||||
BooleanStorage[record] = value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
pragma solidity ^0.4.0;
|
||||
|
||||
import "./EternalStorage.sol";
|
||||
|
||||
library EternallyPersistentLib {
|
||||
|
||||
// UInt
|
||||
|
||||
function stgObjectGetUInt(EternalStorage _storage, string class, uint id, string fieldName) public view returns (uint) {
|
||||
bytes32 record = keccak256(class, id, fieldName);
|
||||
return _storage.getUIntValue(record);
|
||||
}
|
||||
|
||||
function stgObjectSetUInt(EternalStorage _storage, string class, uint id, string fieldName, uint value) internal {
|
||||
bytes32 record = keccak256(class, id, fieldName);
|
||||
return _storage.setUIntValue(record, value);
|
||||
}
|
||||
|
||||
// Boolean
|
||||
|
||||
function stgObjectGetBoolean(EternalStorage _storage, string class, uint id, string fieldName) public view returns (bool) {
|
||||
bytes32 record = keccak256(class, id, fieldName);
|
||||
return _storage.getBooleanValue(record);
|
||||
}
|
||||
|
||||
function stgObjectSetBoolean(EternalStorage _storage, string class, uint id, string fieldName, bool value) internal {
|
||||
bytes32 record = keccak256(class, id, fieldName);
|
||||
return _storage.setBooleanValue(record, value);
|
||||
}
|
||||
|
||||
// string
|
||||
|
||||
function stgObjectGetString(EternalStorage _storage, string class, uint id, string fieldName) internal view returns (string) {
|
||||
bytes32 record = keccak256(class, id, fieldName);
|
||||
bytes4 sig = bytes4(keccak256("getStringValue(bytes32)"));
|
||||
//Function signature
|
||||
address a = address(_storage);
|
||||
string memory s;
|
||||
|
||||
assembly {
|
||||
let x := mload(0x40) //Find empty storage location using "free memory pointer"
|
||||
mstore(x, sig) //Place signature at begining of empty storage
|
||||
mstore(add(x, 0x04), record) //Place first argument directly next to signature
|
||||
|
||||
let success := call(//This is the critical change (Pop the top stack value)
|
||||
5000, //5k gas
|
||||
a, //To addr
|
||||
0, //No value
|
||||
x, //Inputs are stored at location x
|
||||
0x24, //Inputs are 36 byes long
|
||||
x, //Store output over input (saves space)
|
||||
0x80) //Outputs are 32 bytes long
|
||||
|
||||
let strL := mload(add(x, 0x20)) // Load the length of the sring
|
||||
|
||||
jumpi(ask_more, gt(strL, 64))
|
||||
|
||||
mstore(0x40, add(x, add(strL, 0x40)))
|
||||
|
||||
s := add(x, 0x20)
|
||||
// return(x, add(strL, 0x40))
|
||||
ask_more :
|
||||
mstore(x, sig) //Place signature at begining of empty storage
|
||||
mstore(add(x, 0x04), record) //Place first argument directly next to signature
|
||||
|
||||
success := call(//This is the critical change (Pop the top stack value)
|
||||
5000, //5k gas
|
||||
a, //To addr
|
||||
0, //No value
|
||||
x, //Inputs are stored at location x
|
||||
0x24, //Inputs are 36 byes long
|
||||
x, //Store output over input (saves space)
|
||||
add(0x40, strL)) //Outputs are 32 bytes long
|
||||
|
||||
mstore(0x40, add(x, add(strL, 0x40)))
|
||||
s := add(x, 0x20)
|
||||
|
||||
// return(x, add(strL, 0x40))
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
function stgObjectSetString(EternalStorage _storage, string class, uint id, string fieldName, string value) internal {
|
||||
bytes32 record = keccak256(class, id, fieldName);
|
||||
return _storage.setStringValue(record, value);
|
||||
}
|
||||
|
||||
// address
|
||||
|
||||
function stgObjectGetAddress(EternalStorage _storage, string class, uint id, string fieldName) public view returns (address) {
|
||||
bytes32 record = keccak256(class, id, fieldName);
|
||||
return _storage.getAddressValue(record);
|
||||
}
|
||||
|
||||
function stgObjectSetAddress(EternalStorage _storage, string class, uint id, string fieldName, address value) internal {
|
||||
bytes32 record = keccak256(class, id, fieldName);
|
||||
return _storage.setAddressValue(record, value);
|
||||
}
|
||||
|
||||
// bytes32
|
||||
|
||||
function stgObjectGetBytes32(EternalStorage _storage, string class, uint id, string fieldName) public view returns (bytes32) {
|
||||
bytes32 record = keccak256(class, id, fieldName);
|
||||
return _storage.getBytes32Value(record);
|
||||
}
|
||||
|
||||
function stgObjectSetBytes32(EternalStorage _storage, string class, uint id, string fieldName, bytes32 value) internal {
|
||||
bytes32 record = keccak256(class, id, fieldName);
|
||||
return _storage.setBytes32Value(record, value);
|
||||
}
|
||||
|
||||
// Array
|
||||
|
||||
// function stgCollectionAddItem(bytes32 idArray, bytes32 idItem) internal returns (uint64) {
|
||||
function stgCollectionAddItem(EternalStorage _storage, bytes32 idArray) internal returns (uint) {
|
||||
|
||||
uint length = _storage.getUIntValue(keccak256(idArray, "length"));
|
||||
|
||||
|
||||
// Set the position in the array as a field so it can be deleted
|
||||
// _storage.setUIntValue(keccak256(idArray, idItem, "_idx"), length);
|
||||
|
||||
// Add the object to the array
|
||||
// _storage.setBytes32Value(keccak256(idArray, length), idItem);
|
||||
|
||||
|
||||
// Increment the size of the array
|
||||
length++;
|
||||
_storage.setUIntValue(keccak256(idArray, "length"), length);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// function stgCollectionRemoveItem(EternalStorage _storage, bytes32 idArray, bytes32 idItem) internal {
|
||||
// uint idx = _storage.getUIntValue(keccak256(idArray, idItem, "_idx"));
|
||||
//
|
||||
// uint length = _storage.getUIntValue(keccak256(idArray, "length"));
|
||||
// length --;
|
||||
//
|
||||
// // Move the last element ot the array to this place
|
||||
// bytes32 lastId = _storage.getBytes32Value(keccak256(idArray, length));
|
||||
// _storage.setBytes32Value(keccak256(idArray, idx), lastId);
|
||||
// _storage.setUIntValue(keccak256(idArray, lastId, "_idx"), idx);
|
||||
//
|
||||
//
|
||||
// // Decrement the length
|
||||
// _storage.setUIntValue(keccak256(idArray, "length"), length);
|
||||
//
|
||||
// // Cleanup the last element of the array
|
||||
// _storage.setBytes32Value(keccak256(idArray, length), 0);
|
||||
//
|
||||
// _storage.setUIntValue(keccak256(idArray, idItem, "_idx"), 0);
|
||||
// }
|
||||
|
||||
function stgCollectionLength(EternalStorage _storage, bytes32 idArray) public view returns (uint) {
|
||||
return _storage.getUIntValue(keccak256(idArray, "length"));
|
||||
}
|
||||
|
||||
function stgCollectionIdFromIdx(EternalStorage _storage, bytes32 idArray, uint idx) public view returns (bytes32) {
|
||||
return _storage.getBytes32Value(keccak256(idArray, idx));
|
||||
}
|
||||
|
||||
|
||||
// bytes32 lastId;
|
||||
|
||||
// function stgGetNewId() internal returns (bytes32) {
|
||||
// lastId = keccak256(lastId, now);
|
||||
// return lastId;
|
||||
// }
|
||||
|
||||
function stgUpgrade(EternalStorage _storage, address newContract) internal {
|
||||
_storage.changeOwnership(newContract);
|
||||
}
|
||||
|
||||
}
|
|
@ -38,13 +38,15 @@ contract LiquidPledging is LiquidPledgingBase {
|
|||
/// for `LiquidPledgingBase`
|
||||
/// @param _vault The vault where ETH backing this pledge is stored
|
||||
function LiquidPledging(
|
||||
address _storage,
|
||||
address _vault,
|
||||
address _escapeHatchCaller,
|
||||
address _escapeHatchDestination
|
||||
) LiquidPledgingBase(_vault, _escapeHatchCaller, _escapeHatchDestination) public {
|
||||
|
||||
) LiquidPledgingBase(_storage, _vault, _escapeHatchCaller, _escapeHatchDestination) public
|
||||
{
|
||||
}
|
||||
|
||||
event Name(string name);
|
||||
/// @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
|
||||
|
@ -57,15 +59,23 @@ contract LiquidPledging is LiquidPledgingBase {
|
|||
if (idGiver == 0) {
|
||||
|
||||
// default to a 3 day (259200 seconds) commitTime
|
||||
idGiver = addGiver("", "", 259200, ILiquidPledgingPlugin(0x0));
|
||||
idGiver = uint64(addGiver("", "", 259200, ILiquidPledgingPlugin(0x0)));
|
||||
}
|
||||
|
||||
PledgeAdmin storage sender = findAdmin(idGiver);
|
||||
checkAdminOwner(sender);
|
||||
require(sender.adminType == PledgeAdminType.Giver);
|
||||
checkAdminOwner(idGiver);
|
||||
|
||||
PledgeAdmins.PledgeAdminType adminType = _storage.getAdminType(idGiver);
|
||||
require(adminType == PledgeAdmins.PledgeAdminType.Giver);
|
||||
|
||||
Gas(msg.gas);
|
||||
_storage.getAdmin(idGiver);
|
||||
Gas(msg.gas);
|
||||
return;
|
||||
|
||||
uint amount = msg.value;
|
||||
require(amount > 0);
|
||||
vault.transfer(amount); // Sends the `msg.value` (in wei) to the `vault`
|
||||
|
||||
uint64 idPledge = findOrCreatePledge(
|
||||
idGiver,
|
||||
new uint64[](0), // Creates empty array for delegationChain
|
||||
|
@ -84,7 +94,7 @@ contract LiquidPledging is LiquidPledgingBase {
|
|||
transfer(idGiver, idPledge, amount, idReceiver); // LP accounting
|
||||
}
|
||||
|
||||
/// @notice Transfers amounts between pledges for internal accounting
|
||||
/// @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
|
||||
|
@ -99,24 +109,23 @@ contract LiquidPledging is LiquidPledgingBase {
|
|||
uint amount,
|
||||
uint64 idReceiver
|
||||
) public {
|
||||
checkAdminOwner(idSender);
|
||||
|
||||
idPledge = normalizePledge(idPledge);
|
||||
|
||||
Pledge storage p = findPledge(idPledge);
|
||||
PledgeAdmin storage receiver = findAdmin(idReceiver);
|
||||
PledgeAdmin storage sender = findAdmin(idSender);
|
||||
PledgeAdmins.PledgeAdminType receiverAdminType = _storage.getAdminType(idReceiver);
|
||||
|
||||
checkAdminOwner(sender);
|
||||
require(p.pledgeState == PledgeState.Pledged);
|
||||
|
||||
// If the sender is the owner of the Pledge
|
||||
if (p.owner == idSender) {
|
||||
|
||||
if (receiver.adminType == PledgeAdminType.Giver) {
|
||||
if (receiverAdminType == PledgeAdmins.PledgeAdminType.Giver) {
|
||||
transferOwnershipToGiver(idPledge, amount, idReceiver);
|
||||
} else if (receiver.adminType == PledgeAdminType.Project) {
|
||||
} else if (receiverAdminType == PledgeAdmins.PledgeAdminType.Project) {
|
||||
transferOwnershipToProject(idPledge, amount, idReceiver);
|
||||
} else if (receiver.adminType == PledgeAdminType.Delegate) {
|
||||
} else if (receiverAdminType == PledgeAdmins.PledgeAdminType.Delegate) {
|
||||
|
||||
uint recieverDIdx = getDelegateIdx(p, idReceiver);
|
||||
if (p.intendedProject > 0 && recieverDIdx != NOTFOUND) {
|
||||
|
@ -146,7 +155,7 @@ contract LiquidPledging is LiquidPledgingBase {
|
|||
);
|
||||
appendDelegate(idPledge, amount, idReceiver);
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
// This should never be reached as the reciever.adminType
|
||||
// should always be either a Giver, Project, or Delegate
|
||||
|
@ -160,7 +169,7 @@ contract LiquidPledging is LiquidPledgingBase {
|
|||
if (senderDIdx != NOTFOUND) {
|
||||
|
||||
// And the receiver is another Giver
|
||||
if (receiver.adminType == PledgeAdminType.Giver) {
|
||||
if (receiverAdminType == PledgeAdmins.PledgeAdminType.Giver) {
|
||||
// Only transfer to the Giver who owns the pldege
|
||||
assert(p.owner == idReceiver);
|
||||
undelegate(idPledge, amount, p.delegationChain.length);
|
||||
|
@ -168,7 +177,7 @@ contract LiquidPledging is LiquidPledgingBase {
|
|||
}
|
||||
|
||||
// And the receiver is another Delegate
|
||||
if (receiver.adminType == PledgeAdminType.Delegate) {
|
||||
if (receiverAdminType == PledgeAdmins.PledgeAdminType.Delegate) {
|
||||
uint receiverDIdx = getDelegateIdx(p, idReceiver);
|
||||
|
||||
// And not in the delegationChain
|
||||
|
@ -193,8 +202,8 @@ contract LiquidPledging is LiquidPledgingBase {
|
|||
|
||||
// And is already part of the delegate chain but is before the
|
||||
// sender, then the sender and all of the other delegates after
|
||||
// the RECEIVER are removed from the delegationChain
|
||||
} else if (receiverDIdx <= senderDIdx) {//TODO Check for Game Theory issues (from Arthur) this allows the sender to sort of go komakosi and remove himself and the delegates between himself and the receiver... should this authority be allowed?
|
||||
// the RECEIVER are removed from the delegationChain
|
||||
} else if (receiverDIdx <= senderDIdx) {//TODO Check for Game Theory issues (from Arthur) this allows the sender to sort of go komakosi and remove himself and the delegates between himself and the receiver... should this authority be allowed?
|
||||
undelegate(
|
||||
idPledge,
|
||||
amount,
|
||||
|
@ -206,7 +215,7 @@ contract LiquidPledging is LiquidPledgingBase {
|
|||
|
||||
// And the receiver is a Project, all the delegates after the sender
|
||||
// are removed and the amount is pre-committed to the project
|
||||
if (receiver.adminType == PledgeAdminType.Project) {
|
||||
if (receiverAdminType == PledgeAdmins.PledgeAdminType.Project) {
|
||||
idPledge = undelegate(
|
||||
idPledge,
|
||||
amount,
|
||||
|
@ -216,7 +225,7 @@ contract LiquidPledging is LiquidPledgingBase {
|
|||
return;
|
||||
}
|
||||
}
|
||||
assert(false); // When the sender is not an owner or a delegate
|
||||
assert(false); // When the sender is not an owner or a delegate
|
||||
}
|
||||
|
||||
/// @notice Authorizes a payment be made from the `vault` can be used by the
|
||||
|
@ -228,8 +237,7 @@ contract LiquidPledging is LiquidPledgingBase {
|
|||
idPledge = normalizePledge(idPledge); // Updates pledge info
|
||||
Pledge storage p = findPledge(idPledge);
|
||||
require(p.pledgeState == PledgeState.Pledged);
|
||||
PledgeAdmin storage owner = findAdmin(p.owner);
|
||||
checkAdminOwner(owner);
|
||||
checkAdminOwner(p.owner);
|
||||
|
||||
uint64 idNewPledge = findOrCreatePledge(
|
||||
p.owner,
|
||||
|
@ -242,7 +250,7 @@ contract LiquidPledging is LiquidPledgingBase {
|
|||
|
||||
doTransfer(idPledge, idNewPledge, amount);
|
||||
|
||||
vault.authorizePayment(bytes32(idNewPledge), owner.addr, amount);
|
||||
vault.authorizePayment(bytes32(idNewPledge), _storage.getAdminAddr(p.owner), amount);
|
||||
}
|
||||
|
||||
/// @notice `onlyVault` Confirms a withdraw request changing the PledgeState
|
||||
|
@ -293,11 +301,8 @@ contract LiquidPledging is LiquidPledgingBase {
|
|||
/// @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);
|
||||
checkAdminOwner(project);
|
||||
project.canceled = true;
|
||||
|
||||
CancelProject(idProject);
|
||||
checkAdminOwner(idProject);
|
||||
_storage.cancelProject(idProject);
|
||||
}
|
||||
|
||||
/// @notice Transfers `amount` in `idPledge` back to the `oldPledge` that
|
||||
|
@ -311,8 +316,7 @@ contract LiquidPledging is LiquidPledgingBase {
|
|||
Pledge storage p = findPledge(idPledge);
|
||||
require(p.oldPledge != 0);
|
||||
|
||||
PledgeAdmin storage m = findAdmin(p.owner);
|
||||
checkAdminOwner(m);
|
||||
checkAdminOwner(p.owner);
|
||||
|
||||
uint64 oldPledge = getOldestPledgeNotCanceled(p.oldPledge);
|
||||
doTransfer(idPledge, oldPledge, amount);
|
||||
|
@ -422,7 +426,7 @@ contract LiquidPledging is LiquidPledgingBase {
|
|||
// 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(!isProjectCanceled(idReceiver));
|
||||
require(!_storage.isProjectCanceled(idReceiver));
|
||||
|
||||
uint64 oldPledge = findOrCreatePledge(
|
||||
p.owner,
|
||||
|
@ -548,7 +552,7 @@ contract LiquidPledging is LiquidPledgingBase {
|
|||
Pledge storage p = findPledge(idPledge);
|
||||
|
||||
require(getPledgeLevel(p) < MAX_INTERPROJECT_LEVEL);
|
||||
require(!isProjectCanceled(idReceiver));
|
||||
require(!_storage.isProjectCanceled(idReceiver));
|
||||
|
||||
uint64 toPledge = findOrCreatePledge(
|
||||
p.owner,
|
||||
|
@ -568,7 +572,8 @@ contract LiquidPledging is LiquidPledgingBase {
|
|||
/// @param _amount The amount of value that will be transfered.
|
||||
function doTransfer(uint64 from, uint64 to, uint _amount) internal {
|
||||
uint amount = callPlugins(true, from, to, _amount);
|
||||
if (from == to) {
|
||||
// uint amount = _amount;
|
||||
if (from == to) {
|
||||
return;
|
||||
}
|
||||
if (amount == 0) {
|
||||
|
@ -581,7 +586,7 @@ contract LiquidPledging is LiquidPledgingBase {
|
|||
nTo.amount += amount;
|
||||
|
||||
Transfer(from, to, amount);
|
||||
callPlugins(false, from, to, amount);
|
||||
// callPlugins(false, from, to, amount);
|
||||
}
|
||||
|
||||
/// @notice Only affects pledges with the Pledged PledgeState for 2 things:
|
||||
|
@ -671,13 +676,14 @@ contract LiquidPledging is LiquidPledgingBase {
|
|||
|
||||
uint newAmount;
|
||||
allowedAmount = amount;
|
||||
PledgeAdmin storage admin = findAdmin(adminId);
|
||||
address plugin = _storage.getAdminPlugin(adminId); // this takes ~10000 gas
|
||||
|
||||
// Checks admin has a plugin assigned and a non-zero amount is requested
|
||||
if ((address(admin.plugin) != 0) && (allowedAmount > 0)) {
|
||||
if (plugin != 0 && allowedAmount > 0) {
|
||||
// There are two seperate functions called in the plugin.
|
||||
// One is called before the transfer and one after
|
||||
if (before) {
|
||||
newAmount = admin.plugin.beforeTransfer(
|
||||
newAmount = ILiquidPledgingPlugin(plugin).beforeTransfer(
|
||||
adminId,
|
||||
fromPledge,
|
||||
toPledge,
|
||||
|
@ -687,7 +693,7 @@ contract LiquidPledging is LiquidPledgingBase {
|
|||
require(newAmount <= allowedAmount);
|
||||
allowedAmount = newAmount;
|
||||
} else {
|
||||
admin.plugin.afterTransfer(
|
||||
ILiquidPledgingPlugin(plugin).afterTransfer(
|
||||
adminId,
|
||||
fromPledge,
|
||||
toPledge,
|
||||
|
@ -722,6 +728,10 @@ contract LiquidPledging is LiquidPledgingBase {
|
|||
allowedAmount = amount;
|
||||
Pledge storage p = 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();
|
||||
// require(adminsSize >= p.owner);
|
||||
|
||||
// Always call the plugin on the owner
|
||||
allowedAmount = callPlugin(
|
||||
before,
|
||||
|
@ -734,6 +744,7 @@ contract LiquidPledging is LiquidPledgingBase {
|
|||
|
||||
// Apply call plugin to all delegates
|
||||
for (uint64 i=0; i<p.delegationChain.length; i++) {
|
||||
// require(adminsSize >= p.delegationChain[i]);
|
||||
allowedAmount = callPlugin(
|
||||
before,
|
||||
p.delegationChain[i],
|
||||
|
@ -748,6 +759,7 @@ contract LiquidPledging is LiquidPledgingBase {
|
|||
// either a transferring or receiving context based on offset
|
||||
// on the intended project
|
||||
if (p.intendedProject > 0) {
|
||||
// require(adminsSize >= p.intendedProject);
|
||||
allowedAmount = callPlugin(
|
||||
before,
|
||||
p.intendedProject,
|
||||
|
@ -806,6 +818,6 @@ contract LiquidPledging is LiquidPledgingBase {
|
|||
|
||||
// Event Delcerations
|
||||
event Transfer(uint64 indexed from, uint64 indexed to, uint amount);
|
||||
event CancelProject(uint64 indexed idProject);
|
||||
event CancelProject(uint indexed idProject);
|
||||
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ pragma solidity ^0.4.11;
|
|||
|
||||
import "./ILiquidPledgingPlugin.sol";
|
||||
import "giveth-common-contracts/contracts/Escapable.sol";
|
||||
import "./PledgeAdmins.sol";
|
||||
import "./EternalStorage.sol";
|
||||
|
||||
/// @dev This is an interface for `LPVault` which serves as a secure storage for
|
||||
/// the ETH that backs the Pledges, only after `LiquidPledging` authorizes
|
||||
|
@ -33,53 +35,50 @@ interface LPVault {
|
|||
/// liquidPledging's most basic functions, mostly handling and searching the
|
||||
/// data structures
|
||||
contract LiquidPledgingBase is Escapable {
|
||||
using PledgeAdmins 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 PledgeAdminType { Giver, Delegate, Project }
|
||||
enum PledgeState { Pledged, Paying, Paid }
|
||||
|
||||
/// @dev This struct defines the details of a `PledgeAdmin` which are
|
||||
/// commonly referenced by their index in the `admins` array
|
||||
/// and can own pledges and act as delegates
|
||||
struct PledgeAdmin {
|
||||
PledgeAdminType adminType; // Giver, Delegate or Project
|
||||
address addr; // Account or contract address for admin
|
||||
string name;
|
||||
string url; // Can be IPFS hash
|
||||
uint64 commitTime; // In seconds, used for Givers' & Delegates' vetos
|
||||
uint64 parentProject; // Only for projects
|
||||
bool canceled; //Always false except for canceled projects
|
||||
|
||||
/// @dev if the plugin is 0x0 then nothing happens, if its an address
|
||||
// than that smart contract is called when appropriate
|
||||
ILiquidPledgingPlugin plugin;
|
||||
}
|
||||
|
||||
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 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;
|
||||
PledgeAdmin[] admins; //The list of pledgeAdmins 0 means there is no admin
|
||||
LPVault public vault;
|
||||
|
||||
/// @dev this mapping allows you to search for a specific pledge's
|
||||
/// @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;
|
||||
|
||||
|
||||
bool public usePluginWhitelist = true;
|
||||
|
||||
// Duplicate Events from libs so they are added to the abi
|
||||
event GiverAdded(uint indexed idGiver);
|
||||
event GiverUpdated(uint indexed idGiver);
|
||||
event DelegateAdded(uint indexed idDelegate);
|
||||
event DelegateUpdated(uint indexed idDelegate);
|
||||
event ProjectAdded(uint indexed idProject);
|
||||
event ProjectUpdated(uint indexed idProject);
|
||||
|
||||
// for testing
|
||||
event Gas(uint remainingGas);
|
||||
|
||||
/////////////
|
||||
// Modifiers
|
||||
/////////////
|
||||
|
@ -100,12 +99,12 @@ contract LiquidPledgingBase is Escapable {
|
|||
/// @notice The Constructor creates `LiquidPledgingBase` on the blockchain
|
||||
/// @param _vault The vault where the ETH backing the pledges is stored
|
||||
function LiquidPledgingBase(
|
||||
address _storageAddr,
|
||||
address _vault,
|
||||
address _escapeHatchCaller,
|
||||
address _escapeHatchDestination
|
||||
) Escapable(_escapeHatchCaller, _escapeHatchDestination) public {
|
||||
admins.length = 1; // we reserve the 0 admin
|
||||
pledges.length = 1; // we reserve the 0 pledge
|
||||
_storage = EternalStorage(_storageAddr);
|
||||
vault = LPVault(_vault); // Assigns the specified vault
|
||||
}
|
||||
|
||||
|
@ -114,146 +113,73 @@ contract LiquidPledgingBase is Escapable {
|
|||
// PledgeAdmin functions
|
||||
/////////////////////////
|
||||
|
||||
/// @notice Creates a Giver Admin with the `msg.sender` as the Admin address
|
||||
/// @param name The name used to identify the Giver
|
||||
/// @param url The link to the Giver's profile often an IPFS hash
|
||||
/// @param commitTime The length of time in seconds the Giver has to
|
||||
/// veto when the Giver's delegates Pledge funds to a project
|
||||
/// @param plugin This is Giver's liquid pledge plugin allowing for
|
||||
/// extended functionality
|
||||
/// @return idGiver The id number used to reference this Admin
|
||||
function addGiver(
|
||||
string name,
|
||||
string url,
|
||||
uint64 commitTime,
|
||||
ILiquidPledgingPlugin plugin
|
||||
) public returns (uint64 idGiver) {
|
||||
|
||||
) public returns (uint idGiver) {
|
||||
require(isValidPlugin(plugin)); // Plugin check
|
||||
|
||||
idGiver = uint64(admins.length);
|
||||
|
||||
admins.push(PledgeAdmin(
|
||||
PledgeAdminType.Giver,
|
||||
msg.sender,
|
||||
return _storage.addGiver(
|
||||
name,
|
||||
url,
|
||||
commitTime,
|
||||
0,
|
||||
false,
|
||||
plugin));
|
||||
|
||||
GiverAdded(idGiver);
|
||||
plugin
|
||||
);
|
||||
}
|
||||
|
||||
event GiverAdded(uint64 indexed idGiver);
|
||||
|
||||
/// @notice Updates a Giver's info to change the address, name, url, or
|
||||
/// commitTime, it cannot be used to change a plugin, and it must be called
|
||||
/// by the current address of the Giver
|
||||
/// @param idGiver This is the Admin id number used to specify the Giver
|
||||
/// @param newAddr The new address that represents this Giver
|
||||
/// @param newName The new name used to identify the Giver
|
||||
/// @param newUrl The new link to the Giver's profile often an IPFS hash
|
||||
/// @param newCommitTime Sets the length of time in seconds the Giver has to
|
||||
/// veto when the Giver's delegates Pledge funds to a project
|
||||
function updateGiver(
|
||||
uint64 idGiver,
|
||||
address newAddr,
|
||||
string newName,
|
||||
string newUrl,
|
||||
uint64 newCommitTime) public
|
||||
uint64 newCommitTime
|
||||
) public
|
||||
{
|
||||
PledgeAdmin storage giver = findAdmin(idGiver);
|
||||
require(giver.adminType == PledgeAdminType.Giver); // Must be a Giver
|
||||
require(giver.addr == msg.sender); // Current addr had to send this tx
|
||||
giver.addr = newAddr;
|
||||
giver.name = newName;
|
||||
giver.url = newUrl;
|
||||
giver.commitTime = newCommitTime;
|
||||
GiverUpdated(idGiver);
|
||||
_storage.updateGiver(
|
||||
idGiver,
|
||||
newAddr,
|
||||
newName,
|
||||
newUrl,
|
||||
newCommitTime
|
||||
);
|
||||
}
|
||||
|
||||
event GiverUpdated(uint64 indexed idGiver);
|
||||
|
||||
/// @notice Creates a Delegate Admin with the `msg.sender` as the Admin addr
|
||||
/// @param name The name used to identify the Delegate
|
||||
/// @param url The link to the Delegate's profile often an IPFS hash
|
||||
/// @param commitTime Sets the length of time in seconds that this delegate
|
||||
/// can be vetoed. Whenever this delegate is in a delegate chain the time
|
||||
/// allowed to veto any event must be greater than or equal to this time.
|
||||
/// @param plugin This is Delegate's liquid pledge plugin allowing for
|
||||
/// extended functionality
|
||||
/// @return idxDelegate The id number used to reference this Delegate within
|
||||
/// the admins array
|
||||
function addDelegate(
|
||||
string name,
|
||||
string url,
|
||||
uint64 commitTime,
|
||||
ILiquidPledgingPlugin plugin
|
||||
) public returns (uint64 idDelegate) {
|
||||
|
||||
) public returns (uint64 idDelegate)
|
||||
{
|
||||
require(isValidPlugin(plugin)); // Plugin check
|
||||
|
||||
idDelegate = uint64(admins.length);
|
||||
|
||||
admins.push(PledgeAdmin(
|
||||
PledgeAdminType.Delegate,
|
||||
msg.sender,
|
||||
return uint64(_storage.addDelegate(
|
||||
name,
|
||||
url,
|
||||
commitTime,
|
||||
0,
|
||||
false,
|
||||
plugin));
|
||||
|
||||
DelegateAdded(idDelegate);
|
||||
plugin
|
||||
));
|
||||
}
|
||||
|
||||
event DelegateAdded(uint64 indexed idDelegate);
|
||||
|
||||
/// @notice Updates a Delegate's info to change the address, name, url, or
|
||||
/// commitTime, it cannot be used to change a plugin, and it must be called
|
||||
/// by the current address of the Delegate
|
||||
/// @param idDelegate The Admin id number used to specify the Delegate
|
||||
/// @param newAddr The new address that represents this Delegate
|
||||
/// @param newName The new name used to identify the Delegate
|
||||
/// @param newUrl The new link to the Delegate's profile often an IPFS hash
|
||||
/// @param newCommitTime Sets the length of time in seconds that this
|
||||
/// delegate can be vetoed. Whenever this delegate is in a delegate chain
|
||||
/// the time allowed to veto any event must be greater than or equal to
|
||||
/// this time.
|
||||
function updateDelegate(
|
||||
uint64 idDelegate,
|
||||
address newAddr,
|
||||
string newName,
|
||||
string newUrl,
|
||||
uint64 newCommitTime) public
|
||||
uint64 newCommitTime
|
||||
) public
|
||||
{
|
||||
PledgeAdmin storage delegate = findAdmin(idDelegate);
|
||||
require(delegate.adminType == PledgeAdminType.Delegate);
|
||||
require(delegate.addr == msg.sender);// Current addr had to send this tx
|
||||
delegate.addr = newAddr;
|
||||
delegate.name = newName;
|
||||
delegate.url = newUrl;
|
||||
delegate.commitTime = newCommitTime;
|
||||
DelegateUpdated(idDelegate);
|
||||
_storage.updateDelegate(
|
||||
idDelegate,
|
||||
newAddr,
|
||||
newName,
|
||||
newUrl,
|
||||
newCommitTime
|
||||
);
|
||||
}
|
||||
|
||||
event DelegateUpdated(uint64 indexed idDelegate);
|
||||
|
||||
/// @notice Creates a Project Admin with the `msg.sender` as the Admin addr
|
||||
/// @param name The name used to identify the Project
|
||||
/// @param url The link to the Project's profile often an IPFS hash
|
||||
/// @param projectAdmin The address for the trusted project manager
|
||||
/// @param parentProject The Admin id number for the parent project or 0 if
|
||||
/// there is no parentProject
|
||||
/// @param commitTime Sets the length of time in seconds the Project has to
|
||||
/// veto when the Project delegates to another Delegate and they pledge
|
||||
/// those funds to a project
|
||||
/// @param plugin This is Project's liquid pledge plugin allowing for
|
||||
/// extended functionality
|
||||
/// @return idProject The id number used to reference this Admin
|
||||
function addProject(
|
||||
string name,
|
||||
string url,
|
||||
|
@ -266,59 +192,37 @@ contract LiquidPledgingBase is Escapable {
|
|||
require(isValidPlugin(plugin));
|
||||
|
||||
if (parentProject != 0) {
|
||||
PledgeAdmin storage pa = findAdmin(parentProject);
|
||||
require(pa.adminType == PledgeAdminType.Project);
|
||||
require(getProjectLevel(pa) < MAX_SUBPROJECT_LEVEL);
|
||||
// getProjectLevel will check that parentProject has a `Project` adminType
|
||||
require(_storage.getProjectLevel(parentProject) < MAX_SUBPROJECT_LEVEL);
|
||||
}
|
||||
|
||||
idProject = uint64(admins.length);
|
||||
|
||||
admins.push(PledgeAdmin(
|
||||
PledgeAdminType.Project,
|
||||
projectAdmin,
|
||||
name,
|
||||
url,
|
||||
commitTime,
|
||||
parentProject,
|
||||
false,
|
||||
plugin));
|
||||
|
||||
|
||||
ProjectAdded(idProject);
|
||||
return uint64(_storage.addProject(
|
||||
name,
|
||||
url,
|
||||
projectAdmin,
|
||||
parentProject,
|
||||
commitTime,
|
||||
plugin
|
||||
));
|
||||
}
|
||||
|
||||
event ProjectAdded(uint64 indexed idProject);
|
||||
|
||||
|
||||
/// @notice Updates a Project's info to change the address, name, url, or
|
||||
/// commitTime, it cannot be used to change a plugin or a parentProject,
|
||||
/// and it must be called by the current address of the Project
|
||||
/// @param idProject The Admin id number used to specify the Project
|
||||
/// @param newAddr The new address that represents this Project
|
||||
/// @param newName The new name used to identify the Project
|
||||
/// @param newUrl The new link to the Project's profile often an IPFS hash
|
||||
/// @param newCommitTime Sets the length of time in seconds the Project has
|
||||
/// to veto when the Project delegates to a Delegate and they pledge those
|
||||
/// funds to a project
|
||||
function updateProject(
|
||||
uint64 idProject,
|
||||
address newAddr,
|
||||
string newName,
|
||||
string newUrl,
|
||||
uint64 newCommitTime) public
|
||||
uint64 newCommitTime
|
||||
) public
|
||||
{
|
||||
PledgeAdmin storage project = findAdmin(idProject);
|
||||
require(project.adminType == PledgeAdminType.Project);
|
||||
require(project.addr == msg.sender);
|
||||
project.addr = newAddr;
|
||||
project.name = newName;
|
||||
project.url = newUrl;
|
||||
project.commitTime = newCommitTime;
|
||||
ProjectUpdated(idProject);
|
||||
_storage.updateProject(
|
||||
idProject,
|
||||
newAddr,
|
||||
newName,
|
||||
newUrl,
|
||||
newCommitTime
|
||||
);
|
||||
}
|
||||
|
||||
event ProjectUpdated(uint64 indexed idAdmin);
|
||||
|
||||
|
||||
//////////
|
||||
// Public constant functions
|
||||
|
@ -357,57 +261,37 @@ contract LiquidPledgingBase is Escapable {
|
|||
/// @notice Getter to find Delegate w/ the Pledge ID & the Delegate index
|
||||
/// @param idPledge The id number representing the pledge being queried
|
||||
/// @param idxDelegate The index number for the delegate in this Pledge
|
||||
function getPledgeDelegate(uint64 idPledge, uint idxDelegate) public constant returns(
|
||||
function getPledgeDelegate(uint64 idPledge, uint idxDelegate) public view returns(
|
||||
uint64 idDelegate,
|
||||
address addr,
|
||||
string name
|
||||
) {
|
||||
Pledge storage p = findPledge(idPledge);
|
||||
idDelegate = p.delegationChain[idxDelegate - 1];
|
||||
PledgeAdmin storage delegate = findAdmin(idDelegate);
|
||||
addr = delegate.addr;
|
||||
name = delegate.name;
|
||||
require(_storage.pledgeAdminsCount() >= 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 admins.length - 1;
|
||||
}
|
||||
// function numberOfPledgeAdmins() public constant returns(uint) {
|
||||
// return _storage.pledgeAdminsCount();
|
||||
// }
|
||||
|
||||
/// @notice A constant getter to check the details of a specified Admin
|
||||
/// @return addr Account or contract address for admin
|
||||
/// @return name Name of the pledgeAdmin
|
||||
/// @return url The link to the Project's profile often an IPFS hash
|
||||
/// @return commitTime The length of time in seconds the Admin has to veto
|
||||
/// when the Admin delegates to a Delegate and that Delegate pledges those
|
||||
/// funds to a project
|
||||
/// @return parentProject The Admin id number for the parent project or 0
|
||||
/// if there is no parentProject
|
||||
/// @return canceled 0 for Delegates & Givers, true if a Project has been
|
||||
/// canceled
|
||||
/// @return plugin This is Project's liquidPledging plugin allowing for
|
||||
/// extended functionality
|
||||
function getPledgeAdmin(uint64 idAdmin) public constant returns (
|
||||
PledgeAdminType adminType,
|
||||
address addr,
|
||||
string name,
|
||||
string url,
|
||||
uint64 commitTime,
|
||||
uint64 parentProject,
|
||||
bool canceled,
|
||||
address plugin)
|
||||
{
|
||||
PledgeAdmin storage m = findAdmin(idAdmin);
|
||||
adminType = m.adminType;
|
||||
addr = m.addr;
|
||||
name = m.name;
|
||||
url = m.url;
|
||||
commitTime = m.commitTime;
|
||||
parentProject = m.parentProject;
|
||||
canceled = m.canceled;
|
||||
plugin = address(m.plugin);
|
||||
}
|
||||
// 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);
|
||||
// }
|
||||
|
||||
////////
|
||||
// Private methods
|
||||
|
@ -449,14 +333,6 @@ contract LiquidPledgingBase is Escapable {
|
|||
return idx;
|
||||
}
|
||||
|
||||
/// @notice A getter to look up a Admin's details
|
||||
/// @param idAdmin The id for the Admin to lookup
|
||||
/// @return The PledgeAdmin struct for the specified Admin
|
||||
function findAdmin(uint64 idAdmin) internal view returns (PledgeAdmin storage) {
|
||||
require(idAdmin < admins.length);
|
||||
return admins[idAdmin];
|
||||
}
|
||||
|
||||
/// @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
|
||||
|
@ -498,53 +374,35 @@ contract LiquidPledgingBase is Escapable {
|
|||
/// @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) {
|
||||
PledgeAdmin storage m = findAdmin(p.owner);
|
||||
commitTime = m.commitTime; // start with the owner's commitTime
|
||||
uint adminsSize = _storage.pledgeAdminsCount();
|
||||
require(adminsSize >= p.owner);
|
||||
|
||||
commitTime = _storage.getAdminCommitTime(p.owner); // start with the owner's commitTime
|
||||
|
||||
for (uint i=0; i<p.delegationChain.length; i++) {
|
||||
m = findAdmin(p.delegationChain[i]);
|
||||
require(adminsSize >= p.delegationChain[i]);
|
||||
uint delegateCommitTime = _storage.getAdminCommitTime(p.delegationChain[i]);
|
||||
|
||||
// If a delegate's commitTime is longer, make it the new commitTime
|
||||
if (m.commitTime > commitTime) commitTime = m.commitTime;
|
||||
if (delegateCommitTime > commitTime) commitTime = delegateCommitTime;
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice A getter to find the level of authority a specific Project has
|
||||
/// using a self-referential loop
|
||||
/// @param m The Project being queried
|
||||
/// @return The level of authority a specific Project has
|
||||
function getProjectLevel(PledgeAdmin m) internal returns(uint) {
|
||||
assert(m.adminType == PledgeAdminType.Project);
|
||||
if (m.parentProject == 0) return(1);
|
||||
PledgeAdmin storage parentNM = findAdmin(m.parentProject);
|
||||
return getProjectLevel(parentNM) + 1;
|
||||
}
|
||||
|
||||
/// @notice A getter to find if a specified Project has been canceled
|
||||
/// @param projectId The Admin id number used to specify the Project
|
||||
/// @return True if the Project has been canceled
|
||||
function isProjectCanceled(uint64 projectId) public constant returns (bool) {
|
||||
PledgeAdmin storage m = findAdmin(projectId);
|
||||
if (m.adminType == PledgeAdminType.Giver) return false;
|
||||
assert(m.adminType == PledgeAdminType.Project);
|
||||
if (m.canceled) return true;
|
||||
if (m.parentProject == 0) return false;
|
||||
return isProjectCanceled(m.parentProject);
|
||||
}
|
||||
|
||||
/// @notice A getter to find the oldest pledge that hasn't been canceled
|
||||
/// @param idPledge The starting place to lookup the pledges
|
||||
/// @return The oldest idPledge that hasn't been canceled (DUH!)
|
||||
function getOldestPledgeNotCanceled(uint64 idPledge
|
||||
) internal constant returns(uint64) {
|
||||
function getOldestPledgeNotCanceled(
|
||||
uint64 idPledge
|
||||
) internal constant returns(uint64)
|
||||
{
|
||||
if (idPledge == 0) return 0;
|
||||
Pledge storage p = findPledge(idPledge);
|
||||
PledgeAdmin storage admin = findAdmin(p.owner);
|
||||
if (admin.adminType == PledgeAdminType.Giver) return idPledge;
|
||||
|
||||
assert(admin.adminType == PledgeAdminType.Project);
|
||||
PledgeAdmins.PledgeAdminType adminType = _storage.getAdminType(p.owner);
|
||||
if (adminType == PledgeAdmins.PledgeAdminType.Giver) return idPledge;
|
||||
assert(adminType == PledgeAdmins.PledgeAdminType.Project);
|
||||
|
||||
if (!isProjectCanceled(p.owner)) return idPledge;
|
||||
if (!_storage.isProjectCanceled(p.owner)) return idPledge;
|
||||
|
||||
return getOldestPledgeNotCanceled(p.oldPledge);
|
||||
}
|
||||
|
@ -552,9 +410,14 @@ 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 m The Admin being checked
|
||||
function checkAdminOwner(PledgeAdmin m) internal constant {
|
||||
function checkAdminOwner(PledgeAdmins.PledgeAdmin m) internal constant {
|
||||
require((msg.sender == m.addr) || (msg.sender == address(m.plugin)));
|
||||
}
|
||||
|
||||
function checkAdminOwner(uint idAdmin) internal constant {
|
||||
require((msg.sender == _storage.getAdminPlugin(idAdmin)) || (msg.sender == _storage.getAdminAddr(idAdmin)));
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
// Plugin Whitelist Methods
|
||||
///////////////////////////
|
||||
|
|
|
@ -0,0 +1,411 @@
|
|||
pragma solidity ^0.4.17;
|
||||
|
||||
import "./ILiquidPledgingPlugin.sol";
|
||||
import "./EternallyPersistentLib.sol";
|
||||
|
||||
library PledgeAdmins {
|
||||
using EternallyPersistentLib for EternalStorage;
|
||||
|
||||
//TODO we can pack some of these struct values, which should save space. TEST THIS
|
||||
//TODO making functions public may lower deployment cost, but increase gas / tx costs. TEST THIS
|
||||
//TODO is it cheaper to issue a storage check before updating? where should this be done? EternalStorage?
|
||||
|
||||
string constant class = "PledgeAdmins";
|
||||
bytes32 constant admins = keccak256("pledgeAdmins");
|
||||
|
||||
enum PledgeAdminType { Giver, Delegate, Project }
|
||||
|
||||
/// @dev This struct defines the details of a `PledgeAdmin` which are
|
||||
/// commonly referenced by their index in the `admins` array
|
||||
/// and can own pledges and act as delegates
|
||||
struct PledgeAdmin {
|
||||
PledgeAdminType adminType; // Giver, Delegate or Project
|
||||
address addr; // Account or contract address for admin
|
||||
string name;
|
||||
string url; // Can be IPFS hash
|
||||
uint64 commitTime; // In seconds, used for Givers' & Delegates' vetos
|
||||
uint64 parentProject; // Only for projects
|
||||
bool canceled; //Always false except for canceled projects
|
||||
|
||||
/// @dev if the plugin is 0x0 then nothing happens, if its an address
|
||||
// than that smart contract is called when appropriate
|
||||
ILiquidPledgingPlugin plugin;
|
||||
}
|
||||
|
||||
// PledgeAdmins[] admins;
|
||||
|
||||
// function PledgeAdmins(address _storage) EternallyPersistent(_storage) public {
|
||||
// function setStorage(address _storage) internal {
|
||||
// require(address(adminStorage == 0x0));
|
||||
// adminStorage = EternallyPersistent(_storage);
|
||||
// TODO maybe make an init method?
|
||||
// admins.length = 1; // we reserve the 0 admin
|
||||
// }
|
||||
|
||||
/// @notice Creates a Giver Admin with the `msg.sender` as the Admin address
|
||||
/// @param name The name used to identify the Giver
|
||||
/// @param url The link to the Giver's profile often an IPFS hash
|
||||
/// @param commitTime The length of time in seconds the Giver has to
|
||||
/// veto when the Giver's delegates Pledge funds to a project
|
||||
/// @param plugin This is Giver's liquid pledge plugin allowing for
|
||||
/// extended functionality
|
||||
/// @return idGiver The id number used to reference this Admin
|
||||
function addGiver(
|
||||
EternalStorage _storage,
|
||||
string name,
|
||||
string url,
|
||||
uint commitTime,
|
||||
ILiquidPledgingPlugin plugin
|
||||
) internal returns (uint idGiver) {
|
||||
// bytes32 idGuardian = bytes32(addrGuardian);
|
||||
//
|
||||
// if (guardian_exists(addrGuardian)) {
|
||||
// _storage.stgObjectSetString( "Guardian", idGuardian, "name", name);
|
||||
// return;
|
||||
// }
|
||||
|
||||
idGiver = _storage.stgCollectionAddItem(admins);//, idGiver);
|
||||
|
||||
// Save the fields
|
||||
_storage.stgObjectSetUInt(class, idGiver, "adminType", uint(PledgeAdminType.Giver));
|
||||
_storage.stgObjectSetAddress(class, idGiver, "addr", msg.sender);
|
||||
_storage.stgObjectSetString(class, idGiver, "name", name);
|
||||
_storage.stgObjectSetString(class, idGiver, "url", url);
|
||||
_storage.stgObjectSetUInt(class, idGiver, "commitTime", commitTime);
|
||||
_storage.stgObjectSetAddress(class, idGiver, "plugin", address(plugin));
|
||||
|
||||
GiverAdded(idGiver);
|
||||
}
|
||||
|
||||
event GiverAdded(uint indexed idGiver);
|
||||
|
||||
/// @notice Updates a Giver's info to change the address, name, url, or
|
||||
/// commitTime, it cannot be used to change a plugin, and it must be called
|
||||
/// by the current address of the Giver
|
||||
/// @param idGiver This is the Admin id number used to specify the Giver
|
||||
/// @param newAddr The new address that represents this Giver
|
||||
/// @param newName The new name used to identify the Giver
|
||||
/// @param newUrl The new link to the Giver's profile often an IPFS hash
|
||||
/// @param newCommitTime Sets the length of time in seconds the Giver has to
|
||||
/// veto when the Giver's delegates Pledge funds to a project
|
||||
function updateGiver(
|
||||
EternalStorage _storage,
|
||||
uint idGiver,
|
||||
address newAddr,
|
||||
string newName,
|
||||
string newUrl,
|
||||
uint64 newCommitTime
|
||||
) public
|
||||
{
|
||||
require(getAdminType(_storage, idGiver) == PledgeAdminType.Giver); // Must be a Giver
|
||||
require(getAdminAddr(_storage, idGiver) == msg.sender); // Current addr had to send this tx
|
||||
|
||||
// Save the fields
|
||||
_storage.stgObjectSetAddress(class, idGiver, "addr", newAddr);
|
||||
_storage.stgObjectSetString(class, idGiver, "name", newName);
|
||||
_storage.stgObjectSetString(class, idGiver, "url", newUrl);
|
||||
_storage.stgObjectSetUInt(class, idGiver, "commitTime", newCommitTime);
|
||||
|
||||
GiverUpdated(idGiver);
|
||||
}
|
||||
|
||||
event GiverUpdated(uint indexed idGiver);
|
||||
|
||||
/// @notice Creates a Delegate Admin with the `msg.sender` as the Admin addr
|
||||
/// @param name The name used to identify the Delegate
|
||||
/// @param url The link to the Delegate's profile often an IPFS hash
|
||||
/// @param commitTime Sets the length of time in seconds that this delegate
|
||||
/// can be vetoed. Whenever this delegate is in a delegate chain the time
|
||||
/// allowed to veto any event must be greater than or equal to this time.
|
||||
/// @param plugin This is Delegate's liquid pledge plugin allowing for
|
||||
/// extended functionality
|
||||
/// @return idxDelegate The id number used to reference this Delegate within
|
||||
/// the admins array
|
||||
function addDelegate(
|
||||
EternalStorage _storage,
|
||||
string name,
|
||||
string url,
|
||||
uint64 commitTime,
|
||||
ILiquidPledgingPlugin plugin
|
||||
) internal returns (uint idDelegate) {
|
||||
idDelegate = _storage.stgCollectionAddItem(admins);//, idDelegate);
|
||||
|
||||
// Save the fields
|
||||
_storage.stgObjectSetUInt(class, idDelegate, "adminType", uint(PledgeAdminType.Delegate));
|
||||
_storage.stgObjectSetAddress(class, idDelegate, "addr", msg.sender);
|
||||
_storage.stgObjectSetString(class, idDelegate, "name", name);
|
||||
_storage.stgObjectSetString(class, idDelegate, "url", url);
|
||||
_storage.stgObjectSetUInt(class, idDelegate, "commitTime", commitTime);
|
||||
_storage.stgObjectSetAddress(class, idDelegate, "plugin", address(plugin));
|
||||
|
||||
DelegateAdded(idDelegate);
|
||||
}
|
||||
|
||||
event DelegateAdded(uint indexed idDelegate);
|
||||
|
||||
/// @notice Updates a Delegate's info to change the address, name, url, or
|
||||
/// commitTime, it cannot be used to change a plugin, and it must be called
|
||||
/// by the current address of the Delegate
|
||||
/// @param idDelegate The Admin id number used to specify the Delegate
|
||||
/// @param newAddr The new address that represents this Delegate
|
||||
/// @param newName The new name used to identify the Delegate
|
||||
/// @param newUrl The new link to the Delegate's profile often an IPFS hash
|
||||
/// @param newCommitTime Sets the length of time in seconds that this
|
||||
/// delegate can be vetoed. Whenever this delegate is in a delegate chain
|
||||
/// the time allowed to veto any event must be greater than or equal to
|
||||
/// this time.
|
||||
function updateDelegate(
|
||||
EternalStorage _storage,
|
||||
uint idDelegate,
|
||||
address newAddr,
|
||||
string newName,
|
||||
string newUrl,
|
||||
uint64 newCommitTime
|
||||
) public
|
||||
{
|
||||
require(getAdminType(_storage, idDelegate) == PledgeAdminType.Delegate);
|
||||
require(getAdminAddr(_storage, idDelegate) == msg.sender); // Current addr had to send this tx
|
||||
|
||||
// Save the fields
|
||||
_storage.stgObjectSetAddress(class, idDelegate, "addr", newAddr);
|
||||
_storage.stgObjectSetString(class, idDelegate, "name", newName);
|
||||
_storage.stgObjectSetString(class, idDelegate, "url", newUrl);
|
||||
_storage.stgObjectSetUInt(class, idDelegate, "commitTime", newCommitTime);
|
||||
|
||||
DelegateUpdated(idDelegate);
|
||||
}
|
||||
|
||||
event DelegateUpdated(uint indexed idDelegate);
|
||||
|
||||
/// @notice Creates a Project Admin with the `msg.sender` as the Admin addr
|
||||
/// @param name The name used to identify the Project
|
||||
/// @param url The link to the Project's profile often an IPFS hash
|
||||
/// @param projectAdmin The address for the trusted project manager
|
||||
/// @param parentProject The Admin id number for the parent project or 0 if
|
||||
/// there is no parentProject
|
||||
/// @param commitTime Sets the length of time in seconds the Project has to
|
||||
/// veto when the Project delegates to another Delegate and they pledge
|
||||
/// those funds to a project
|
||||
/// @param plugin This is Project's liquid pledge plugin allowing for
|
||||
/// extended functionality
|
||||
/// @return idProject The id number used to reference this Admin
|
||||
function addProject(
|
||||
EternalStorage _storage,
|
||||
string name,
|
||||
string url,
|
||||
address projectAdmin,
|
||||
uint64 parentProject,
|
||||
uint64 commitTime,
|
||||
ILiquidPledgingPlugin plugin
|
||||
) internal returns (uint idProject) {
|
||||
idProject = _storage.stgCollectionAddItem(admins);//, idProject);
|
||||
|
||||
// Save the fields
|
||||
_storage.stgObjectSetUInt(class, idProject, "adminType", uint(PledgeAdminType.Project));
|
||||
_storage.stgObjectSetAddress(class, idProject, "addr", projectAdmin);
|
||||
_storage.stgObjectSetString(class, idProject, "name", name);
|
||||
_storage.stgObjectSetString(class, idProject, "url", url);
|
||||
|
||||
// NOTICE: we do not verify that the parentProject has a `Project` adminType
|
||||
// this is expected to be done by the calling method
|
||||
_storage.stgObjectSetUInt(class, idProject, "parentProject", parentProject);
|
||||
|
||||
_storage.stgObjectSetUInt(class, idProject, "commitTime", commitTime);
|
||||
_storage.stgObjectSetAddress(class, idProject, "plugin", address(plugin));
|
||||
|
||||
ProjectAdded(idProject);
|
||||
}
|
||||
|
||||
event ProjectAdded(uint indexed idProject);
|
||||
|
||||
/// @notice Updates a Project's info to change the address, name, url, or
|
||||
/// commitTime, it cannot be used to change a plugin or a parentProject,
|
||||
/// and it must be called by the current address of the Project
|
||||
/// @param idProject The Admin id number used to specify the Project
|
||||
/// @param newAddr The new address that represents this Project
|
||||
/// @param newName The new name used to identify the Project
|
||||
/// @param newUrl The new link to the Project's profile often an IPFS hash
|
||||
/// @param newCommitTime Sets the length of time in seconds the Project has
|
||||
/// to veto when the Project delegates to a Delegate and they pledge those
|
||||
/// funds to a project
|
||||
function updateProject(
|
||||
EternalStorage _storage,
|
||||
uint idProject,
|
||||
address newAddr,
|
||||
string newName,
|
||||
string newUrl,
|
||||
uint64 newCommitTime
|
||||
) public
|
||||
{
|
||||
require(getAdminType(_storage, idProject) == PledgeAdminType.Project);
|
||||
require(getAdminAddr(_storage, idProject) == msg.sender); // Current addr had to send this tx
|
||||
|
||||
// Save the fields
|
||||
_storage.stgObjectSetAddress(class, idProject, "addr", newAddr);
|
||||
_storage.stgObjectSetString(class, idProject, "name", newName);
|
||||
_storage.stgObjectSetString(class, idProject, "url", newUrl);
|
||||
_storage.stgObjectSetUInt(class, idProject, "commitTime", newCommitTime);
|
||||
|
||||
ProjectUpdated(idProject);
|
||||
}
|
||||
|
||||
event ProjectUpdated(uint indexed idAdmin);
|
||||
|
||||
function cancelProject(EternalStorage _storage, uint idProject) internal {
|
||||
_storage.stgObjectSetBoolean(class, idProject, "canceled", true);
|
||||
CancelProject(idProject);
|
||||
}
|
||||
|
||||
/// @notice A getter to find if a specified Project has been canceled
|
||||
/// @param projectId The Admin id number used to specify the Project
|
||||
/// @return True if the Project has been canceled
|
||||
function isProjectCanceled(EternalStorage _storage, uint projectId)
|
||||
public constant returns (bool)
|
||||
{
|
||||
require(pledgeAdminsCount(_storage) >= projectId);
|
||||
|
||||
PledgeAdminType adminType = getAdminType(_storage, projectId);
|
||||
|
||||
if (adminType == PledgeAdminType.Giver) return false;
|
||||
assert(adminType == PledgeAdminType.Project);
|
||||
|
||||
if (getAdminCanceled(_storage, projectId)) return true;
|
||||
|
||||
uint parentProject = getAdminParentProject(_storage, projectId);
|
||||
if (parentProject == 0) return false;
|
||||
|
||||
return isProjectCanceled(_storage, parentProject);
|
||||
}
|
||||
|
||||
event CancelProject(uint indexed idProject);
|
||||
|
||||
/// @notice A constant getter used to check how many total Admins exist
|
||||
/// @return The total number of admins (Givers, Delegates and Projects) .
|
||||
//TODO I think using 'size' in both Pledges lib & PledgeAdmins lib will cause a conflict since they use the same storage contract
|
||||
// function size(EternalStorage _storage) constant returns(uint) {
|
||||
function pledgeAdminsCount(EternalStorage _storage) public constant returns(uint) {
|
||||
return _storage.stgCollectionLength(admins);// - 1;
|
||||
}
|
||||
|
||||
/// @notice A constant getter to check the details of a specified Admin
|
||||
/// @return addr Account or contract address for admin
|
||||
/// @return name Name of the pledgeAdmin
|
||||
/// @return url The link to the Project's profile often an IPFS hash
|
||||
/// @return commitTime The length of time in seconds the Admin has to veto
|
||||
/// when the Admin delegates to a Delegate and that Delegate pledges those
|
||||
/// funds to a project
|
||||
/// @return parentProject The Admin id number for the parent project or 0
|
||||
/// if there is no parentProject
|
||||
/// @return canceled 0 for Delegates & Givers, true if a Project has been
|
||||
/// canceled
|
||||
/// @return plugin This is Project's liquidPledging plugin allowing for
|
||||
/// extended functionality
|
||||
function getAdmin(EternalStorage _storage, uint idAdmin) internal view returns (
|
||||
PledgeAdminType adminType,
|
||||
address addr,
|
||||
string name,
|
||||
string url,
|
||||
uint64 commitTime,
|
||||
uint parentProject,
|
||||
bool canceled,
|
||||
address plugin
|
||||
)
|
||||
{
|
||||
adminType = getAdminType(_storage, idAdmin);
|
||||
addr = getAdminAddr(_storage, idAdmin);
|
||||
name = getAdminName(_storage, idAdmin);
|
||||
url = _storage.stgObjectGetString(class, idAdmin, "url");
|
||||
commitTime = uint64(getAdminCommitTime(_storage, idAdmin));
|
||||
|
||||
// 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);
|
||||
canceled = getAdminCanceled(_storage, idAdmin);
|
||||
}
|
||||
|
||||
plugin = getAdminPlugin(_storage, idAdmin);
|
||||
}
|
||||
|
||||
/// @notice Find the level of authority a specific Project has
|
||||
/// using a recursive loop
|
||||
/// @param idProject The id of the Project being queried
|
||||
/// @return The level of authority a specific Project has
|
||||
function getProjectLevel(EternalStorage _storage, uint idProject) public returns(uint) {
|
||||
assert(getAdminType(_storage, idProject) == PledgeAdminType.Project);
|
||||
uint parentProject = getAdminParentProject(_storage, idProject);
|
||||
if (parentProject == 0) return(1);
|
||||
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
|
||||
///////
|
||||
|
||||
// costs ~10k gas
|
||||
function getAdminType(
|
||||
EternalStorage _storage,
|
||||
uint idAdmin
|
||||
) public view returns (PledgeAdminType)
|
||||
{
|
||||
return PledgeAdminType(_storage.stgObjectGetUInt(class, idAdmin, "adminType"));
|
||||
}
|
||||
|
||||
// costs ~10k gas
|
||||
function getAdminAddr(
|
||||
EternalStorage _storage,
|
||||
uint idAdmin
|
||||
) public view returns (address)
|
||||
{
|
||||
return _storage.stgObjectGetAddress(class, idAdmin, "addr");
|
||||
}
|
||||
|
||||
// costs ~8k gas
|
||||
function getAdminName(
|
||||
EternalStorage _storage,
|
||||
uint idAdmin
|
||||
) internal view returns (string)
|
||||
{
|
||||
return _storage.stgObjectGetString(class, idAdmin, "name");
|
||||
}
|
||||
|
||||
// costs ~10k gas
|
||||
function getAdminParentProject(
|
||||
EternalStorage _storage,
|
||||
uint idAdmin
|
||||
) public view returns (uint)
|
||||
{
|
||||
return _storage.stgObjectGetUInt(class, idAdmin, "parentProject");
|
||||
}
|
||||
|
||||
// costs ~10k gas
|
||||
function getAdminCanceled(
|
||||
EternalStorage _storage,
|
||||
uint idAdmin
|
||||
) public view returns (bool)
|
||||
{
|
||||
return _storage.stgObjectGetBoolean(class, idAdmin, "canceled");
|
||||
}
|
||||
|
||||
// costs ~10k gas
|
||||
function getAdminPlugin(
|
||||
EternalStorage _storage,
|
||||
uint idAdmin
|
||||
) public view returns (address)
|
||||
{
|
||||
return _storage.stgObjectGetAddress(class, idAdmin, "plugin");
|
||||
}
|
||||
|
||||
// costs ~10k gas
|
||||
function getAdminCommitTime(
|
||||
EternalStorage _storage,
|
||||
uint idAdmin
|
||||
) public view returns (uint)
|
||||
{
|
||||
return _storage.stgObjectGetUInt(class, idAdmin, "commitTime");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
const EternalStorageAbi = require('../build/EternalStorage.sol').EternalStorageAbi;
|
||||
const EternalStorageCode = require('../build/EternalStorage.sol').EternalStorageByteCode;
|
||||
const generateClass = require('eth-contract-class').default;
|
||||
|
||||
module.exports = generateClass(EternalStorageAbi, EternalStorageCode);
|
||||
|
Loading…
Reference in New Issue