refactor PledgeAdmins to lib w/ EternalStorage

This commit is contained in:
perissology 2018-01-16 17:20:07 -08:00
parent 80a8e5a9b4
commit 80b8ffc2ef
6 changed files with 847 additions and 287 deletions

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -38,13 +38,15 @@ contract LiquidPledging is LiquidPledgingBase {
/// for `LiquidPledgingBase` /// for `LiquidPledgingBase`
/// @param _vault The vault where ETH backing this pledge is stored /// @param _vault The vault where ETH backing this pledge is stored
function LiquidPledging( function LiquidPledging(
address _storage,
address _vault, address _vault,
address _escapeHatchCaller, address _escapeHatchCaller,
address _escapeHatchDestination 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; /// @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 /// 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 /// 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) { if (idGiver == 0) {
// default to a 3 day (259200 seconds) commitTime // 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(idGiver);
checkAdminOwner(sender);
require(sender.adminType == PledgeAdminType.Giver); 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; uint amount = msg.value;
require(amount > 0); require(amount > 0);
vault.transfer(amount); // Sends the `msg.value` (in wei) to the `vault` vault.transfer(amount); // Sends the `msg.value` (in wei) to the `vault`
uint64 idPledge = findOrCreatePledge( uint64 idPledge = findOrCreatePledge(
idGiver, idGiver,
new uint64[](0), // Creates empty array for delegationChain new uint64[](0), // Creates empty array for delegationChain
@ -99,24 +109,23 @@ contract LiquidPledging is LiquidPledgingBase {
uint amount, uint amount,
uint64 idReceiver uint64 idReceiver
) public { ) public {
checkAdminOwner(idSender);
idPledge = normalizePledge(idPledge); idPledge = normalizePledge(idPledge);
Pledge storage p = findPledge(idPledge); Pledge storage p = findPledge(idPledge);
PledgeAdmin storage receiver = findAdmin(idReceiver); PledgeAdmins.PledgeAdminType receiverAdminType = _storage.getAdminType(idReceiver);
PledgeAdmin storage sender = findAdmin(idSender);
checkAdminOwner(sender);
require(p.pledgeState == PledgeState.Pledged); require(p.pledgeState == PledgeState.Pledged);
// If the sender is the owner of the Pledge // If the sender is the owner of the Pledge
if (p.owner == idSender) { if (p.owner == idSender) {
if (receiver.adminType == PledgeAdminType.Giver) { if (receiverAdminType == PledgeAdmins.PledgeAdminType.Giver) {
transferOwnershipToGiver(idPledge, amount, idReceiver); transferOwnershipToGiver(idPledge, amount, idReceiver);
} else if (receiver.adminType == PledgeAdminType.Project) { } else if (receiverAdminType == PledgeAdmins.PledgeAdminType.Project) {
transferOwnershipToProject(idPledge, amount, idReceiver); transferOwnershipToProject(idPledge, amount, idReceiver);
} else if (receiver.adminType == PledgeAdminType.Delegate) { } else if (receiverAdminType == PledgeAdmins.PledgeAdminType.Delegate) {
uint recieverDIdx = getDelegateIdx(p, idReceiver); uint recieverDIdx = getDelegateIdx(p, idReceiver);
if (p.intendedProject > 0 && recieverDIdx != NOTFOUND) { if (p.intendedProject > 0 && recieverDIdx != NOTFOUND) {
@ -160,7 +169,7 @@ contract LiquidPledging is LiquidPledgingBase {
if (senderDIdx != NOTFOUND) { if (senderDIdx != NOTFOUND) {
// And the receiver is another Giver // 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 // Only transfer to the Giver who owns the pldege
assert(p.owner == idReceiver); assert(p.owner == idReceiver);
undelegate(idPledge, amount, p.delegationChain.length); undelegate(idPledge, amount, p.delegationChain.length);
@ -168,7 +177,7 @@ contract LiquidPledging is LiquidPledgingBase {
} }
// And the receiver is another Delegate // And the receiver is another Delegate
if (receiver.adminType == PledgeAdminType.Delegate) { if (receiverAdminType == PledgeAdmins.PledgeAdminType.Delegate) {
uint receiverDIdx = getDelegateIdx(p, idReceiver); uint receiverDIdx = getDelegateIdx(p, idReceiver);
// And not in the delegationChain // And not in the delegationChain
@ -206,7 +215,7 @@ contract LiquidPledging is LiquidPledgingBase {
// And the receiver is a Project, all the delegates after the sender // And the receiver is a Project, all the delegates after the sender
// are removed and the amount is pre-committed to the project // are removed and the amount is pre-committed to the project
if (receiver.adminType == PledgeAdminType.Project) { if (receiverAdminType == PledgeAdmins.PledgeAdminType.Project) {
idPledge = undelegate( idPledge = undelegate(
idPledge, idPledge,
amount, amount,
@ -228,8 +237,7 @@ contract LiquidPledging is LiquidPledgingBase {
idPledge = normalizePledge(idPledge); // Updates pledge info idPledge = normalizePledge(idPledge); // Updates pledge info
Pledge storage p = findPledge(idPledge); Pledge storage p = findPledge(idPledge);
require(p.pledgeState == PledgeState.Pledged); require(p.pledgeState == PledgeState.Pledged);
PledgeAdmin storage owner = findAdmin(p.owner); checkAdminOwner(p.owner);
checkAdminOwner(owner);
uint64 idNewPledge = findOrCreatePledge( uint64 idNewPledge = findOrCreatePledge(
p.owner, p.owner,
@ -242,7 +250,7 @@ contract LiquidPledging is LiquidPledgingBase {
doTransfer(idPledge, idNewPledge, amount); 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 /// @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 /// @notice Changes the `project.canceled` flag to `true`; cannot be undone
/// @param idProject Id of the project that is to be canceled /// @param idProject Id of the project that is to be canceled
function cancelProject(uint64 idProject) public { function cancelProject(uint64 idProject) public {
PledgeAdmin storage project = findAdmin(idProject); checkAdminOwner(idProject);
checkAdminOwner(project); _storage.cancelProject(idProject);
project.canceled = true;
CancelProject(idProject);
} }
/// @notice Transfers `amount` in `idPledge` back to the `oldPledge` that /// @notice Transfers `amount` in `idPledge` back to the `oldPledge` that
@ -311,8 +316,7 @@ contract LiquidPledging is LiquidPledgingBase {
Pledge storage p = findPledge(idPledge); Pledge storage p = findPledge(idPledge);
require(p.oldPledge != 0); require(p.oldPledge != 0);
PledgeAdmin storage m = findAdmin(p.owner); checkAdminOwner(p.owner);
checkAdminOwner(m);
uint64 oldPledge = getOldestPledgeNotCanceled(p.oldPledge); uint64 oldPledge = getOldestPledgeNotCanceled(p.oldPledge);
doTransfer(idPledge, oldPledge, amount); doTransfer(idPledge, oldPledge, amount);
@ -422,7 +426,7 @@ contract LiquidPledging is LiquidPledgingBase {
// Ensure that the pledge is not already at max pledge depth // Ensure that the pledge is not already at max pledge depth
// and the project has not been canceled // and the project has not been canceled
require(getPledgeLevel(p) < MAX_INTERPROJECT_LEVEL); require(getPledgeLevel(p) < MAX_INTERPROJECT_LEVEL);
require(!isProjectCanceled(idReceiver)); require(!_storage.isProjectCanceled(idReceiver));
uint64 oldPledge = findOrCreatePledge( uint64 oldPledge = findOrCreatePledge(
p.owner, p.owner,
@ -548,7 +552,7 @@ contract LiquidPledging is LiquidPledgingBase {
Pledge storage p = findPledge(idPledge); Pledge storage p = findPledge(idPledge);
require(getPledgeLevel(p) < MAX_INTERPROJECT_LEVEL); require(getPledgeLevel(p) < MAX_INTERPROJECT_LEVEL);
require(!isProjectCanceled(idReceiver)); require(!_storage.isProjectCanceled(idReceiver));
uint64 toPledge = findOrCreatePledge( uint64 toPledge = findOrCreatePledge(
p.owner, p.owner,
@ -568,6 +572,7 @@ contract LiquidPledging is LiquidPledgingBase {
/// @param _amount The amount of value that will be transfered. /// @param _amount The amount of value that will be transfered.
function doTransfer(uint64 from, uint64 to, uint _amount) internal { function doTransfer(uint64 from, uint64 to, uint _amount) internal {
uint amount = callPlugins(true, from, to, _amount); uint amount = callPlugins(true, from, to, _amount);
// uint amount = _amount;
if (from == to) { if (from == to) {
return; return;
} }
@ -581,7 +586,7 @@ contract LiquidPledging is LiquidPledgingBase {
nTo.amount += amount; nTo.amount += amount;
Transfer(from, to, 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: /// @notice Only affects pledges with the Pledged PledgeState for 2 things:
@ -671,13 +676,14 @@ contract LiquidPledging is LiquidPledgingBase {
uint newAmount; uint newAmount;
allowedAmount = amount; 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 // 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. // There are two seperate functions called in the plugin.
// One is called before the transfer and one after // One is called before the transfer and one after
if (before) { if (before) {
newAmount = admin.plugin.beforeTransfer( newAmount = ILiquidPledgingPlugin(plugin).beforeTransfer(
adminId, adminId,
fromPledge, fromPledge,
toPledge, toPledge,
@ -687,7 +693,7 @@ contract LiquidPledging is LiquidPledgingBase {
require(newAmount <= allowedAmount); require(newAmount <= allowedAmount);
allowedAmount = newAmount; allowedAmount = newAmount;
} else { } else {
admin.plugin.afterTransfer( ILiquidPledgingPlugin(plugin).afterTransfer(
adminId, adminId,
fromPledge, fromPledge,
toPledge, toPledge,
@ -722,6 +728,10 @@ contract LiquidPledging is LiquidPledgingBase {
allowedAmount = amount; allowedAmount = amount;
Pledge storage p = findPledge(idPledge); 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 // Always call the plugin on the owner
allowedAmount = callPlugin( allowedAmount = callPlugin(
before, before,
@ -734,6 +744,7 @@ contract LiquidPledging is LiquidPledgingBase {
// Apply call plugin to all delegates // Apply call plugin to all delegates
for (uint64 i=0; i<p.delegationChain.length; i++) { for (uint64 i=0; i<p.delegationChain.length; i++) {
// require(adminsSize >= p.delegationChain[i]);
allowedAmount = callPlugin( allowedAmount = callPlugin(
before, before,
p.delegationChain[i], p.delegationChain[i],
@ -748,6 +759,7 @@ contract LiquidPledging is LiquidPledgingBase {
// either a transferring or receiving context based on offset // either a transferring or receiving context based on offset
// on the intended project // on the intended project
if (p.intendedProject > 0) { if (p.intendedProject > 0) {
// require(adminsSize >= p.intendedProject);
allowedAmount = callPlugin( allowedAmount = callPlugin(
before, before,
p.intendedProject, p.intendedProject,
@ -806,6 +818,6 @@ contract LiquidPledging is LiquidPledgingBase {
// Event Delcerations // Event Delcerations
event Transfer(uint64 indexed from, uint64 indexed to, uint amount); event Transfer(uint64 indexed from, uint64 indexed to, uint amount);
event CancelProject(uint64 indexed idProject); event CancelProject(uint indexed idProject);
} }

View File

@ -20,6 +20,8 @@ pragma solidity ^0.4.11;
import "./ILiquidPledgingPlugin.sol"; import "./ILiquidPledgingPlugin.sol";
import "giveth-common-contracts/contracts/Escapable.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 /// @dev This is an interface for `LPVault` which serves as a secure storage for
/// the ETH that backs the Pledges, only after `LiquidPledging` authorizes /// the ETH that backs the Pledges, only after `LiquidPledging` authorizes
@ -33,32 +35,15 @@ interface LPVault {
/// liquidPledging's most basic functions, mostly handling and searching the /// liquidPledging's most basic functions, mostly handling and searching the
/// data structures /// data structures
contract LiquidPledgingBase is Escapable { contract LiquidPledgingBase is Escapable {
using PledgeAdmins for EternalStorage;
// Limits inserted to prevent large loops that could prevent canceling // Limits inserted to prevent large loops that could prevent canceling
uint constant MAX_DELEGATES = 10; uint constant MAX_DELEGATES = 10;
uint constant MAX_SUBPROJECT_LEVEL = 20; uint constant MAX_SUBPROJECT_LEVEL = 20;
uint constant MAX_INTERPROJECT_LEVEL = 20; uint constant MAX_INTERPROJECT_LEVEL = 20;
enum PledgeAdminType { Giver, Delegate, Project }
enum PledgeState { Pledged, Paying, Paid } 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 { struct Pledge {
uint amount; uint amount;
uint64 owner; // PledgeAdmin uint64 owner; // PledgeAdmin
@ -69,17 +54,31 @@ contract LiquidPledgingBase is Escapable {
PledgeState pledgeState; // Pledged, Paying, Paid PledgeState pledgeState; // Pledged, Paying, Paid
} }
EternalStorage public _storage;
Pledge[] pledges; 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 /// index number by the hash of that pledge
mapping (bytes32 => uint64) hPledge2idx; mapping (bytes32 => uint64) hPledge2idx;
LPVault public vault;
mapping (bytes32 => bool) pluginWhitelist; mapping (bytes32 => bool) pluginWhitelist;
bool public usePluginWhitelist = true; 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 // Modifiers
///////////// /////////////
@ -100,12 +99,12 @@ contract LiquidPledgingBase is Escapable {
/// @notice The Constructor creates `LiquidPledgingBase` on the blockchain /// @notice The Constructor creates `LiquidPledgingBase` on the blockchain
/// @param _vault The vault where the ETH backing the pledges is stored /// @param _vault The vault where the ETH backing the pledges is stored
function LiquidPledgingBase( function LiquidPledgingBase(
address _storageAddr,
address _vault, address _vault,
address _escapeHatchCaller, address _escapeHatchCaller,
address _escapeHatchDestination address _escapeHatchDestination
) Escapable(_escapeHatchCaller, _escapeHatchDestination) public { ) Escapable(_escapeHatchCaller, _escapeHatchDestination) public {
admins.length = 1; // we reserve the 0 admin _storage = EternalStorage(_storageAddr);
pledges.length = 1; // we reserve the 0 pledge
vault = LPVault(_vault); // Assigns the specified vault vault = LPVault(_vault); // Assigns the specified vault
} }
@ -114,146 +113,73 @@ contract LiquidPledgingBase is Escapable {
// PledgeAdmin functions // 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( function addGiver(
string name, string name,
string url, string url,
uint64 commitTime, uint64 commitTime,
ILiquidPledgingPlugin plugin ILiquidPledgingPlugin plugin
) public returns (uint64 idGiver) { ) public returns (uint idGiver) {
require(isValidPlugin(plugin)); // Plugin check require(isValidPlugin(plugin)); // Plugin check
idGiver = uint64(admins.length); return _storage.addGiver(
admins.push(PledgeAdmin(
PledgeAdminType.Giver,
msg.sender,
name, name,
url, url,
commitTime, commitTime,
0, plugin
false, );
plugin));
GiverAdded(idGiver);
} }
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( function updateGiver(
uint64 idGiver, uint64 idGiver,
address newAddr, address newAddr,
string newName, string newName,
string newUrl, string newUrl,
uint64 newCommitTime) public uint64 newCommitTime
) public
{ {
PledgeAdmin storage giver = findAdmin(idGiver); _storage.updateGiver(
require(giver.adminType == PledgeAdminType.Giver); // Must be a Giver idGiver,
require(giver.addr == msg.sender); // Current addr had to send this tx newAddr,
giver.addr = newAddr; newName,
giver.name = newName; newUrl,
giver.url = newUrl; newCommitTime
giver.commitTime = newCommitTime; );
GiverUpdated(idGiver);
} }
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( function addDelegate(
string name, string name,
string url, string url,
uint64 commitTime, uint64 commitTime,
ILiquidPledgingPlugin plugin ILiquidPledgingPlugin plugin
) public returns (uint64 idDelegate) { ) public returns (uint64 idDelegate)
{
require(isValidPlugin(plugin)); // Plugin check require(isValidPlugin(plugin)); // Plugin check
idDelegate = uint64(admins.length); return uint64(_storage.addDelegate(
admins.push(PledgeAdmin(
PledgeAdminType.Delegate,
msg.sender,
name, name,
url, url,
commitTime, commitTime,
0, plugin
false, ));
plugin));
DelegateAdded(idDelegate);
} }
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( function updateDelegate(
uint64 idDelegate, uint64 idDelegate,
address newAddr, address newAddr,
string newName, string newName,
string newUrl, string newUrl,
uint64 newCommitTime) public uint64 newCommitTime
) public
{ {
PledgeAdmin storage delegate = findAdmin(idDelegate); _storage.updateDelegate(
require(delegate.adminType == PledgeAdminType.Delegate); idDelegate,
require(delegate.addr == msg.sender);// Current addr had to send this tx newAddr,
delegate.addr = newAddr; newName,
delegate.name = newName; newUrl,
delegate.url = newUrl; newCommitTime
delegate.commitTime = newCommitTime; );
DelegateUpdated(idDelegate);
} }
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( function addProject(
string name, string name,
string url, string url,
@ -266,59 +192,37 @@ contract LiquidPledgingBase is Escapable {
require(isValidPlugin(plugin)); require(isValidPlugin(plugin));
if (parentProject != 0) { if (parentProject != 0) {
PledgeAdmin storage pa = findAdmin(parentProject); // getProjectLevel will check that parentProject has a `Project` adminType
require(pa.adminType == PledgeAdminType.Project); require(_storage.getProjectLevel(parentProject) < MAX_SUBPROJECT_LEVEL);
require(getProjectLevel(pa) < MAX_SUBPROJECT_LEVEL);
} }
idProject = uint64(admins.length); return uint64(_storage.addProject(
admins.push(PledgeAdmin(
PledgeAdminType.Project,
projectAdmin,
name, name,
url, url,
commitTime, projectAdmin,
parentProject, parentProject,
false, commitTime,
plugin)); plugin
));
ProjectAdded(idProject);
} }
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( function updateProject(
uint64 idProject, uint64 idProject,
address newAddr, address newAddr,
string newName, string newName,
string newUrl, string newUrl,
uint64 newCommitTime) public uint64 newCommitTime
) public
{ {
PledgeAdmin storage project = findAdmin(idProject); _storage.updateProject(
require(project.adminType == PledgeAdminType.Project); idProject,
require(project.addr == msg.sender); newAddr,
project.addr = newAddr; newName,
project.name = newName; newUrl,
project.url = newUrl; newCommitTime
project.commitTime = newCommitTime; );
ProjectUpdated(idProject);
} }
event ProjectUpdated(uint64 indexed idAdmin);
////////// //////////
// Public constant functions // Public constant functions
@ -357,57 +261,37 @@ contract LiquidPledgingBase is Escapable {
/// @notice Getter to find Delegate w/ the Pledge ID & the Delegate index /// @notice Getter to find Delegate w/ the Pledge ID & the Delegate index
/// @param idPledge The id number representing the pledge being queried /// @param idPledge The id number representing the pledge being queried
/// @param idxDelegate The index number for the delegate in this Pledge /// @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, uint64 idDelegate,
address addr, address addr,
string name string name
) { ) {
Pledge storage p = findPledge(idPledge); Pledge storage p = findPledge(idPledge);
idDelegate = p.delegationChain[idxDelegate - 1]; idDelegate = p.delegationChain[idxDelegate - 1];
PledgeAdmin storage delegate = findAdmin(idDelegate); require(_storage.pledgeAdminsCount() >= idxDelegate);
addr = delegate.addr; addr = _storage.getAdminAddr(idDelegate);
name = delegate.name; name = _storage.getAdminName(idDelegate);
} }
/// @notice A constant getter used to check how many total Admins exist /// @notice A constant getter used to check how many total Admins exist
/// @return The total number of admins (Givers, Delegates and Projects) . /// @return The total number of admins (Givers, Delegates and Projects) .
function numberOfPledgeAdmins() public constant returns(uint) { // function numberOfPledgeAdmins() public constant returns(uint) {
return admins.length - 1; // return _storage.pledgeAdminsCount();
} // }
/// @notice A constant getter to check the details of a specified Admin // can use _storage.getAdmin(idAdmin);
/// @return addr Account or contract address for admin // function getPledgeAdmin(uint64 idAdmin) public constant returns (
/// @return name Name of the pledgeAdmin // PledgeAdmins.PledgeAdminType adminType,
/// @return url The link to the Project's profile often an IPFS hash // address addr,
/// @return commitTime The length of time in seconds the Admin has to veto // string name,
/// when the Admin delegates to a Delegate and that Delegate pledges those // string url,
/// funds to a project // uint64 commitTime,
/// @return parentProject The Admin id number for the parent project or 0 // uint64 parentProject,
/// if there is no parentProject // bool canceled,
/// @return canceled 0 for Delegates & Givers, true if a Project has been // address plugin)
/// canceled // {
/// @return plugin This is Project's liquidPledging plugin allowing for // (adminType, addr, name, url, commitTime, parentProject, canceled, plugin) = _storage.getAdmin(idAdmin);
/// 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);
}
//////// ////////
// Private methods // Private methods
@ -449,14 +333,6 @@ contract LiquidPledgingBase is Escapable {
return idx; 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 /// @notice A getter to look up a Pledge's details
/// @param idPledge The id for the Pledge to lookup /// @param idPledge The id for the Pledge to lookup
/// @return The PledgeA struct for the specified Pledge /// @return The PledgeA struct for the specified Pledge
@ -498,53 +374,35 @@ contract LiquidPledgingBase is Escapable {
/// @param p The Pledge being queried /// @param p The Pledge being queried
/// @return The maximum commitTime out of the owner and all the delegates /// @return The maximum commitTime out of the owner and all the delegates
function maxCommitTime(Pledge p) internal view returns(uint commitTime) { function maxCommitTime(Pledge p) internal view returns(uint commitTime) {
PledgeAdmin storage m = findAdmin(p.owner); uint adminsSize = _storage.pledgeAdminsCount();
commitTime = m.commitTime; // start with the owner's commitTime require(adminsSize >= p.owner);
commitTime = _storage.getAdminCommitTime(p.owner); // start with the owner's commitTime
for (uint i=0; i<p.delegationChain.length; i++) { 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 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 /// @notice A getter to find the oldest pledge that hasn't been canceled
/// @param idPledge The starting place to lookup the pledges /// @param idPledge The starting place to lookup the pledges
/// @return The oldest idPledge that hasn't been canceled (DUH!) /// @return The oldest idPledge that hasn't been canceled (DUH!)
function getOldestPledgeNotCanceled(uint64 idPledge function getOldestPledgeNotCanceled(
) internal constant returns(uint64) { uint64 idPledge
) internal constant returns(uint64)
{
if (idPledge == 0) return 0; if (idPledge == 0) return 0;
Pledge storage p = findPledge(idPledge); 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); 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 /// @notice A check to see if the msg.sender is the owner or the
/// plugin contract for a specific Admin /// plugin contract for a specific Admin
/// @param m The Admin being checked /// @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))); 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 // Plugin Whitelist Methods
/////////////////////////// ///////////////////////////

411
contracts/PledgeAdmins.sol Normal file
View File

@ -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");
}
}

6
js/eternalStorage.js Normal file
View File

@ -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);