first pass as AragonApp

This commit is contained in:
perissology 2018-02-10 15:14:52 +01:00
parent 6cdceb62cf
commit 369e6ae6cd
11 changed files with 696 additions and 624 deletions

View File

@ -0,0 +1,89 @@
pragma solidity ^0.4.15;
/*
Copyright 2016, Jordi Baylina
Contributor: Adrià Massanet <adria@codecontext.io>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// import "./Owned.sol";
import "giveth-common-contracts/contracts/ERC20.sol";
import "@aragon/os/contracts/apps/AragonApp.sol";
/// @dev `Escapable` is a base level contract built off of the `Owned`
/// contract; it creates an escape hatch function that can be called in an
/// emergency that will allow designated addresses to send any ether or tokens
/// held in the contract to an `escapeHatchDestination` as long as they were
/// not blacklisted
contract EscapableApp is AragonApp {
// warning whoever has this role can move all funds to the `escapeHatchDestination`
bytes32 constant public ESCAPE_HATCH_CALLER_ROLE = bytes32(1);
address public escapeHatchDestination;
mapping (address=>bool) private escapeBlacklist; // Token contract addresses
/// @param _escapeHatchDestination The address of a safe location (usu a
/// Multisig) to send the ether held in this contract; if a neutral address
/// is required, the WHG Multisig is an option:
/// 0x8Ff920020c8AD673661c8117f2855C384758C572
function initialize(address _escapeHatchDestination) onlyInit external {
initialized();
require(_escapeHatchDestination != 0x0);
escapeHatchDestination = _escapeHatchDestination;
}
/// @notice Creates the blacklist of tokens that are not able to be taken
/// out of the contract; can only be done at the deployment, and the logic
/// to add to the blacklist will be in the constructor of a child contract
/// @param _token the token contract address that is to be blacklisted
function blacklistEscapeToken(address _token) internal {
escapeBlacklist[_token] = true;
EscapeHatchBlackistedToken(_token);
}
/// @notice Checks to see if `_token` is in the blacklist of tokens
/// @param _token the token address being queried
/// @return False if `_token` is in the blacklist and can't be taken out of
/// the contract via the `escapeHatch()`
function isTokenEscapable(address _token) constant public returns (bool) {
return !escapeBlacklist[_token];
}
/// @notice The `escapeHatch()` should only be called as a last resort if a
/// security issue is uncovered or something unexpected happened
/// @param _token to transfer, use 0x0 for ether
function escapeHatch(address _token) public authP(ESCAPE_HATCH_CALLER_ROLE, arr(_token)) {
require(escapeBlacklist[_token]==false);
uint256 balance;
/// @dev Logic for ether
if (_token == 0x0) {
balance = this.balance;
escapeHatchDestination.transfer(balance);
EscapeHatchCalled(_token, balance);
return;
}
/// @dev Logic for tokens
ERC20 token = ERC20(_token);
balance = token.balanceOf(this);
require(token.transfer(escapeHatchDestination, balance));
EscapeHatchCalled(_token, balance);
}
event EscapeHatchBlackistedToken(address token);
event EscapeHatchCalled(address token, uint amount);
}

66
contracts/LPFactory.sol Normal file
View File

@ -0,0 +1,66 @@
pragma solidity ^0.4.18;
import "@aragon/os/contracts/factory/DAOFactory.sol";
import "./LPVault.sol";
import "./LiquidPledging.sol";
contract LPFactory is DAOFactory {
address public vaultBase;
address public lpBase;
bytes32 constant public VAULT_APP_ID = keccak256("vault");
bytes32 constant public LP_APP_ID = keccak256("liquidPledging");
event DeployVault(address vault);
event DeployLiquidPledging(address liquidPledging);
function LPFactory(address _vaultBase, address _lpBase) public DAOFactory(0) {
require(_vaultBase != 0);
require(_lpBase != 0);
vaultBase = _vaultBase;
lpBase = _lpBase;
}
function newLP(address _root, address _escapeHatchDestination) public {
Kernel kernel = newDAO(this);
ACL acl = ACL(kernel.acl());
bytes32 appManagerRole = kernel.APP_MANAGER_ROLE();
acl.createPermission(this, address(kernel), appManagerRole, this);
LPVault v = LPVault(kernel.newAppInstance(VAULT_APP_ID, vaultBase));
LiquidPledging lp = LiquidPledging(kernel.newAppInstance(LP_APP_ID, lpBase));
v.initialize(address(lp), _escapeHatchDestination);
lp.initialize(address(v), _escapeHatchDestination);
_setPermissions(_root, acl, kernel, v, lp);
}
function _setPermissions(address _root, ACL acl, Kernel kernel, LPVault v, LiquidPledging lp) internal {
bytes32 appManagerRole = kernel.APP_MANAGER_ROLE();
bytes32 permRole = acl.CREATE_PERMISSIONS_ROLE();
bytes32 hatchCallerRole = v.ESCAPE_HATCH_CALLER_ROLE();
bytes32 authPaymentRole = v.AUTHORIZE_PAYMENT_ROLE();
bytes32 pledgeAdminRole = lp.PLEDGE_ADMIN_ROLE();
bytes32 pluginManagerRole = lp.PLUGIN_MANAGER_ROLE();
acl.createPermission(_root, address(v), hatchCallerRole, _root);
acl.createPermission(_root, address(lp), hatchCallerRole, _root);
acl.createPermission(_root, address(lp), pluginManagerRole, _root);
acl.createPermission(address(lp), address(v), authPaymentRole, _root);
acl.createPermission(0x0, address(lp), pledgeAdminRole, address(lp));
// TODO: set pledgeAdminRole manager to 0x0? maybe it doesn't matter b/c it can be recreated by _root anyways
acl.grantPermission(_root, address(kernel), appManagerRole);
acl.grantPermission(_root, address(acl), permRole);
acl.revokePermission(this, address(kernel), appManagerRole);
acl.revokePermission(this, address(acl), permRole);
acl.setPermissionManager(_root, address(kernel), appManagerRole);
acl.setPermissionManager(_root, address(acl), permRole);
DeployVault(address(v));
DeployLiquidPledging(address(lp));
}
}

View File

@ -27,28 +27,41 @@ pragma solidity ^0.4.11;
/// be a safe place to store funds equipped with optional variable time delays
/// to allow for an optional escapeHatch to be implemented in case of issues;
/// future versions of this contract will be enabled for tokens
import "giveth-common-contracts/contracts/Escapable.sol";
import "./EscapableApp.sol";
/// @dev `LiquidPledging` is a basic interface to allow the `LPVault` contract
/// to confirm and cancel payments in the `LiquidPledging` contract.
contract LiquidPledging {
contract ILiquidPledging {
function confirmPayment(uint64 idPledge, uint amount) public;
function cancelPayment(uint64 idPledge, uint amount) public;
}
/// @dev `LPVault` is a higher level contract built off of the `Escapable`
/// contract that holds funds for the liquid pledging system.
contract LPVault is Escapable {
contract LPVault is EscapableApp {
LiquidPledging public liquidPledging; // LiquidPledging contract's address
bool public autoPay; // If false, payments will take 2 txs to be completed
bytes32 constant public CONFIRM_PAYMENT_ROLE = bytes32(1);
bytes32 constant public CANCEL_PAYMENT_ROLE = bytes32(2);
bytes32 constant public AUTHORIZE_PAYMENT_ROLE = bytes32(3);
bytes32 constant public SET_AUTOPAY_ROLE = bytes32(4);
event AutoPaySet(bool autoPay);
event EscapeFundsCalled(address token, uint amount);
event ConfirmPayment(uint indexed idPayment, bytes32 indexed ref);
event CancelPayment(uint indexed idPayment, bytes32 indexed ref);
event AuthorizePayment(
uint indexed idPayment,
bytes32 indexed ref,
address indexed dest,
uint amount
);
enum PaymentStatus {
Pending, // When the payment is awaiting confirmation
Paid, // When the payment has been sent
Canceled // When the payment will never be sent
}
/// @dev `Payment` is a public structure that describes the details of
/// each payment the `ref` param makes it easy to track the movements of
/// funds transparently by its connection to other `Payment` structs
@ -59,13 +72,11 @@ contract LPVault is Escapable {
uint amount; // amount of ETH (in wei) to be sent
}
bool public autoPay; // If false, payments will take 2 txs to be completed
// @dev An array that contains all the payments for this LPVault
Payment[] public payments;
function LPVault(address _escapeHatchCaller, address _escapeHatchDestination)
Escapable(_escapeHatchCaller, _escapeHatchDestination) public
{
}
ILiquidPledging public liquidPledging;
/// @dev The attached `LiquidPledging` contract is the only address that can
/// call a function with this modifier
@ -74,31 +85,39 @@ contract LPVault is Escapable {
_;
}
function initialize(address _escapeHatchDestination) onlyInit external {
require(false); // overload the EscapableApp
}
/// @param _liquidPledging
/// @param _escapeHatchDestination The address of a safe location (usu a
/// Multisig) to send the ether held in this contract; if a neutral address
/// is required, the WHG Multisig is an option:
/// 0x8Ff920020c8AD673661c8117f2855C384758C572
function initialize(address _liquidPledging, address _escapeHatchDestination) onlyInit external {
initialized();
require(_escapeHatchDestination != 0x0);
require(_liquidPledging != 0x0);
escapeHatchDestination = _escapeHatchDestination;
liquidPledging = ILiquidPledging(_liquidPledging);
}
/// @dev The fall back function allows ETH to be deposited into the LPVault
/// through a simple send
function () public payable {}
/// @notice `onlyOwner` used to attach a specific liquidPledging instance
/// to this LPvault; keep in mind that once a liquidPledging contract is
/// attached it cannot be undone, this vault will be forever connected
/// @param _newLiquidPledging A full liquid pledging contract
function setLiquidPledging(address _newLiquidPledging) public onlyOwner {
require(address(liquidPledging) == 0x0);
liquidPledging = LiquidPledging(_newLiquidPledging);
}
/// @notice Used to decentralize, toggles whether the LPVault will
/// automatically confirm a payment after the payment has been authorized
/// @param _automatic If true, payments will confirm instantly, if false
/// the training wheels are put on and the owner must manually approve
/// every payment
function setAutopay(bool _automatic) public onlyOwner {
function setAutopay(bool _automatic) external authP(SET_AUTOPAY_ROLE, ar(_automatic)) {
autoPay = _automatic;
AutoPaySet();
AutoPaySet(autoPay);
}
/// @notice `onlyLiquidPledging` authorizes payments from this contract, if
/// `autoPay == true` the transfer happens automatically `else` the `owner`
/// @notice If `autoPay == true` the transfer happens automatically `else` the `owner`
/// must call `confirmPayment()` for a transfer to occur (training wheels);
/// either way, a new payment is added to `payments[]`
/// @param _ref References the payment will normally be the pledgeID
@ -109,7 +128,7 @@ contract LPVault is Escapable {
bytes32 _ref,
address _dest,
uint _amount
) public onlyLiquidPledging returns (uint)
) external authP(AUTHORIZE_PAYMENT_ROLE, arr(_dest, _amount)) returns (uint)
{
uint idPayment = payments.length;
payments.length ++;
@ -132,51 +151,20 @@ contract LPVault is Escapable {
/// this is generally used when `autopay` is `false` after a payment has
/// has been authorized
/// @param _idPayment Array lookup for the payment.
function confirmPayment(uint _idPayment) public onlyOwner {
function confirmPayment(uint _idPayment) public {
doConfirmPayment(_idPayment);
}
/// @notice Transfers ETH according to the data held within the specified
/// payment id (internal function)
/// @param _idPayment id number for the payment about to be fulfilled
function doConfirmPayment(uint _idPayment) internal {
require(_idPayment < payments.length);
Payment storage p = payments[_idPayment];
require(p.state == PaymentStatus.Pending);
p.state = PaymentStatus.Paid;
liquidPledging.confirmPayment(uint64(p.ref), p.amount);
p.dest.transfer(p.amount); // Transfers ETH denominated in wei
ConfirmPayment(_idPayment, p.ref);
}
/// @notice When `autopay` is `false` and after a payment has been authorized
/// to allow the owner to cancel a payment instead of confirming it.
/// @param _idPayment Array lookup for the payment.
function cancelPayment(uint _idPayment) public onlyOwner {
function cancelPayment(uint _idPayment) public {
doCancelPayment(_idPayment);
}
/// @notice Cancels a pending payment (internal function)
/// @param _idPayment id number for the payment
function doCancelPayment(uint _idPayment) internal {
require(_idPayment < payments.length);
Payment storage p = payments[_idPayment];
require(p.state == PaymentStatus.Pending);
p.state = PaymentStatus.Canceled;
liquidPledging.cancelPayment(uint64(p.ref), p.amount);
CancelPayment(_idPayment, p.ref);
}
/// @notice `onlyOwner` An efficient way to confirm multiple payments
/// @param _idPayments An array of multiple payment ids
function multiConfirm(uint[] _idPayments) public onlyOwner {
function multiConfirm(uint[] _idPayments) external {
for (uint i = 0; i < _idPayments.length; i++) {
doConfirmPayment(_idPayments[i]);
}
@ -184,23 +172,18 @@ contract LPVault is Escapable {
/// @notice `onlyOwner` An efficient way to cancel multiple payments
/// @param _idPayments An array of multiple payment ids
function multiCancel(uint[] _idPayments) public onlyOwner {
function multiCancel(uint[] _idPayments) external {
for (uint i = 0; i < _idPayments.length; i++) {
doCancelPayment(_idPayments[i]);
}
}
/// @return The total number of payments that have ever been authorized
function nPayments() constant public returns (uint) {
return payments.length;
}
/// Transfer eth or tokens to the escapeHatchDestination.
/// Used as a safety mechanism to prevent the vault from holding too much value
/// before being thoroughly battle-tested.
/// @param _token to transfer, use 0x0 for ether
/// @param _amount to transfer
function escapeFunds(address _token, uint _amount) public onlyOwner {
function escapeFunds(address _token, uint _amount) public authP(ESCAPE_HATCH_CALLER_ROLE, arr(_token)) {
/// @dev Logic for ether
if (_token == 0x0) {
require(this.balance >= _amount);
@ -216,14 +199,48 @@ contract LPVault is Escapable {
EscapeFundsCalled(_token, _amount);
}
event AutoPaySet();
event EscapeFundsCalled(address token, uint amount);
event ConfirmPayment(uint indexed idPayment, bytes32 indexed ref);
event CancelPayment(uint indexed idPayment, bytes32 indexed ref);
event AuthorizePayment(
uint indexed idPayment,
bytes32 indexed ref,
address indexed dest,
uint amount
);
/// @return The total number of payments that have ever been authorized
function nPayments() public view returns (uint) {
return payments.length;
}
/// @notice Transfers ETH according to the data held within the specified
/// payment id (internal function)
/// @param _idPayment id number for the payment about to be fulfilled
function doConfirmPayment(uint _idPayment) internal {
require(_idPayment < payments.length);
Payment storage p = payments[_idPayment];
require(p.state == PaymentStatus.Pending);
require(canPerform(msg.sender, CONFIRM_PAYMENT_ROLE, arr(_idPayment, p.amount)));
p.state = PaymentStatus.Paid;
liquidPledging.confirmPayment(uint64(p.ref), p.amount);
p.dest.transfer(p.amount); // Transfers ETH denominated in wei
ConfirmPayment(_idPayment, p.ref);
}
/// @notice Cancels a pending payment (internal function)
/// @param _idPayment id number for the payment
function doCancelPayment(uint _idPayment) internal authP(CANCEL_PAYMENT_ROLE, arr(_idPayment)) {
require(_idPayment < payments.length);
Payment storage p = payments[_idPayment];
require(p.state == PaymentStatus.Pending);
p.state = PaymentStatus.Canceled;
liquidPledging.cancelPayment(uint64(p.ref), p.amount);
CancelPayment(_idPayment, p.ref);
}
function ar(bool a) internal pure returns (uint256[] r) {
r = new uint256[](1);
uint _a;
assembly {
_a := a // forced casting
}
r[0] = _a;
}
}

View File

@ -36,13 +36,8 @@ contract LiquidPledging is LiquidPledgingBase {
/// LiquidPledgingBase contract
/// @dev This constructor also calls the constructor
/// for `LiquidPledgingBase`
/// @param _vault The vault where ETH backing this pledge is stored
function LiquidPledging(
address _storage,
address _vault,
address _escapeHatchCaller,
address _escapeHatchDestination
) LiquidPledgingBase(_storage, _vault, _escapeHatchCaller, _escapeHatchDestination) public
function LiquidPledging()
LiquidPledgingBase() public
{
}
@ -54,23 +49,24 @@ contract LiquidPledging is LiquidPledgingBase {
/// @param idGiver The id of the Giver donating; if 0, a new id is created
/// @param idReceiver The Admin receiving the donation; can be any Admin:
/// the Giver themselves, another Giver, a Delegate or a Project
function donate(uint64 idGiver, uint64 idReceiver) public payable {
function donate(uint64 idGiver, uint64 idReceiver) authP(PLEDGE_ADMIN_ROLE, arr(uint(idGiver)))
public payable
{
// TODO: maybe need a separate role here to allow giver creation
if (idGiver == 0) {
// default to a 3 day (259200 seconds) commitTime
idGiver = uint64(addGiver("", "", 259200, ILiquidPledgingPlugin(0x0)));
}
checkAdminOwner(idGiver);
PledgeAdminType adminType = getAdminType(idGiver);
require(adminType == PledgeAdminType.Giver);
PledgeAdmins.PledgeAdmin storage sender = _findAdmin(idGiver);
// _checkAdminOwner(sender);
require(sender.adminType == PledgeAdminType.Giver);
uint amount = msg.value;
require(amount > 0);
vault.transfer(amount); // Sends the `msg.value` (in wei) to the `vault`
Pledges.Pledge memory p = findOrCreatePledge(
uint64 idPledge = _findOrCreatePledge(
idGiver,
new uint64[](0), // Creates empty array for delegationChain
0,
@ -79,13 +75,12 @@ contract LiquidPledging is LiquidPledgingBase {
Pledges.PledgeState.Pledged
);
Pledges.Pledge storage pTo = _findPledge(idPledge);
pTo.amount += amount;
p.amount += amount;
setPledgeAmount(p.id, p.amount);
Transfer(0, idPledge, amount); // An event
Transfer(0, p.id, amount); // An event
transfer(idGiver, uint64(p.id), amount, idReceiver); // LP accounting
transfer(idGiver, idPledge, amount, idReceiver); // LP accounting
}
/// @notice Transfers amounts between pledges for internal accounting
@ -102,52 +97,53 @@ contract LiquidPledging is LiquidPledgingBase {
uint64 idPledge,
uint amount,
uint64 idReceiver
) public
) authP(PLEDGE_ADMIN_ROLE, arr(uint(idSender), amount)) public
{
checkAdminOwner(idSender);
idPledge = normalizePledge(idPledge);
Pledges.Pledge memory p = normalizePledge(idPledge);
Pledges.Pledge storage p = _findPledge(idPledge);
PledgeAdmins.PledgeAdmin storage receiver = _findAdmin(idReceiver);
// PledgeAdmins.PledgeAdmin storage sender = _findAdmin(idSender);
PledgeAdminType receiverAdminType = getAdminType(idReceiver);
require(p.pledgeState == Pledges.PledgeState.Pledged);
// _checkAdminOwner(sender);
require(p.pledgeState == PledgeState.Pledged);
// If the sender is the owner of the Pledge
if (p.owner == idSender) {
if (receiverAdminType == PledgeAdminType.Giver) {
transferOwnershipToGiver(p, amount, idReceiver);
} else if (receiverAdminType == PledgeAdminType.Project) {
transferOwnershipToProject(p, amount, idReceiver);
} else if (receiverAdminType == PledgeAdminType.Delegate) {
if (receiver.adminType == PledgeAdmins.PledgeAdminType.Giver) {
_transferOwnershipToGiver(idPledge, amount, idReceiver);
} else if (receiver.adminType == PledgeAdmins.PledgeAdminType.Project) {
_transferOwnershipToProject(idPledge, amount, idReceiver);
} else if (receiver.adminType == PledgeAdmins.PledgeAdminType.Delegate) {
uint recieverDIdx = getDelegateIdx(p, idReceiver);
uint recieverDIdx = _getDelegateIdx(p, idReceiver);
if (p.intendedProject > 0 && recieverDIdx != NOTFOUND) {
// if there is an intendedProject and the receiver is in the delegationChain,
// then we want to preserve the delegationChain as this is a veto of the
// intendedProject by the owner
if (recieverDIdx == p.delegationChain.length - 1) {
Pledges.Pledge memory toPledge = findOrCreatePledge(
uint64 toPledge = _findOrCreatePledge(
p.owner,
p.delegationChain,
0,
0,
p.oldPledge,
Pledges.PledgeState.Pledged);
doTransfer(p, toPledge, amount);
_doTransfer(idPledge, toPledge, amount);
} else {
undelegate(p, amount, p.delegationChain.length - receiverDIdx - 1);
_undelegate(idPledge, amount, p.delegationChain.length - receiverDIdx - 1);
}
} else {
// owner is not vetoing an intendedProject and is transferring the pledge to a delegate,
// so we want to reset the delegationChain
p = undelegate(
p,
idPledge = _undelegate(
idPledge,
amount,
p.delegationChain.length
);
appendDelegate(p, amount, idReceiver);
_appendDelegate(idPledge, amount, idReceiver);
}
} else {
@ -159,47 +155,47 @@ contract LiquidPledging is LiquidPledgingBase {
}
// If the sender is a Delegate
uint senderDIdx = getDelegateIdx(p, idSender);
uint senderDIdx = _getDelegateIdx(p, idSender);
if (senderDIdx != NOTFOUND) {
// And the receiver is another Giver
if (receiverAdminType == PledgeAdminType.Giver) {
if (receiver.adminType == PledgeAdmins.PledgeAdminType.Giver) {
// Only transfer to the Giver who owns the pldege
assert(p.owner == idReceiver);
undelegate(p, amount, p.delegationChain.length);
_undelegate(idPledge, amount, p.delegationChain.length);
return;
}
// And the receiver is another Delegate
if (receiverAdminType == PledgeAdminType.Delegate) {
uint receiverDIdx = getDelegateIdx(p, idReceiver);
if (receiver.adminType == PledgeAdmins.PledgeAdminType.Delegate) {
uint receiverDIdx = _getDelegateIdx(p, idReceiver);
// And not in the delegationChain
if (receiverDIdx == NOTFOUND) {
p = undelegate(
p,
idPledge = _undelegate(
idPledge,
amount,
p.delegationChain.length - senderDIdx - 1
);
appendDelegate(p, amount, idReceiver);
_appendDelegate(idPledge, amount, idReceiver);
// And part of the delegationChain and is after the sender, then
// all of the other delegates after the sender are removed and
// the receiver is appended at the end of the delegationChain
} else if (receiverDIdx > senderDIdx) {
p = undelegate(
p,
idPledge = _undelegate(
idPledge,
amount,
p.delegationChain.length - senderDIdx - 1
);
appendDelegate(p, amount, idReceiver);
_appendDelegate(idPledge, amount, idReceiver);
// 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?
undelegate(
p,
_undelegate(
idPledge,
amount,
p.delegationChain.length - receiverDIdx - 1
);
@ -209,13 +205,13 @@ 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 (receiverAdminType == PledgeAdminType.Project) {
p = undelegate(
p,
if (receiver.adminType == PledgeAdmins.PledgeAdminType.Project) {
idPledge = _undelegate(
idPledge,
amount,
p.delegationChain.length - senderDIdx - 1
);
proposeAssignProject(p, amount, idReceiver);
_proposeAssignProject(idPledge, amount, idReceiver);
return;
}
}
@ -227,13 +223,16 @@ contract LiquidPledging is LiquidPledgingBase {
/// intendedProject
/// @param idPledge Id of the pledge that is to be redeemed into ether
/// @param amount Quantity of ether (in wei) to be authorized
function withdraw(uint64 idPledge, uint amount) public {
Pledges.Pledge memory p = normalizePledge(idPledge); // Updates pledge info
// = findPledge(idPledge);
require(p.pledgeState == Pledges.PledgeState.Pledged);
checkAdminOwner(p.owner);
function withdraw(uint64 idPledge, uint amount) authP(PLEDGE_ADMIN_ROLE, arr(uint(idPledge), amount)) public {
idPledge = normalizePledge(idPledge); // Updates pledge info
Pledges.Pledge memory newPledge = findOrCreatePledge(
Pledges.Pledge storage p = _findPledge(idPledge);
require(p.pledgeState == PledgeState.Pledged);
PledgeAdmins.PledgeAdmin storage owner = _findAdmin(p.owner);
// _checkAdminOwner(owner);
uint64 idNewPledge = _findOrCreatePledge(
p.owner,
p.delegationChain,
0,
@ -242,9 +241,9 @@ contract LiquidPledging is LiquidPledgingBase {
Pledges.PledgeState.Paying
);
doTransfer(p, newPledge, amount);
_doTransfer(idPledge, idNewPledge, amount);
vault.authorizePayment(bytes32(newPledge.id), getAdminAddr(p.owner), amount);
vault.authorizePayment(bytes32(idNewPledge), owner.addr, amount);
}
/// @notice `onlyVault` Confirms a withdraw request changing the Pledges.PledgeState
@ -253,11 +252,11 @@ contract LiquidPledging is LiquidPledgingBase {
/// @param amount Quantity of ether (in wei) to be withdrawn
function confirmPayment(uint64 idPledge, uint amount) public onlyVault {
//TODO don't fetch entire pledge
Pledges.Pledge memory p = findPledge(idPledge);
Pledges.Pledge storage p = _findPledge(idPledge);
require(p.pledgeState == Pledges.PledgeState.Paying);
Pledges.Pledge memory newPledge = findOrCreatePledge(
uint64 idNewPledge = _findOrCreatePledge(
p.owner,
p.delegationChain,
0,
@ -266,7 +265,7 @@ contract LiquidPledging is LiquidPledgingBase {
Pledges.PledgeState.Paid
);
doTransfer(p, newPledge, amount);
_doTransfer(idPledge, idNewPledge, amount);
}
/// @notice `onlyVault` Cancels a withdraw request, changing the Pledges.PledgeState
@ -274,12 +273,12 @@ contract LiquidPledging is LiquidPledgingBase {
/// @param idPledge Id of the pledge that's withdraw is to be canceled
/// @param amount Quantity of ether (in wei) to be canceled
function cancelPayment(uint64 idPledge, uint amount) public onlyVault {
Pledges.Pledge memory p = findPledge(idPledge);
Pledges.Pledge storage p = _findPledge(idPledge);
require(p.pledgeState == Pledges.PledgeState.Paying);
// When a payment is canceled, never is assigned to a project.
Pledges.Pledge memory oldPledge = findOrCreatePledge(
uint64 idOldPledge = _findOrCreatePledge(
p.owner,
p.delegationChain,
0,
@ -288,17 +287,18 @@ contract LiquidPledging is LiquidPledgingBase {
Pledges.PledgeState.Pledged
);
oldPledge = _normalizePledge(oldPledge);
idOldPledge = normalizePledge(idOldPledge);
doTransfer(p, oldPledge, amount);
_doTransfer(idPledge, idOldPledge, amount);
}
/// @notice Changes the `project.canceled` flag to `true`; cannot be undone
/// @param idProject Id of the project that is to be canceled
function cancelProject(uint64 idProject) public {
checkAdminOwner(idProject);
function cancelProject(uint64 idProject) authP(PLEDGE_ADMIN_ROLE, arr(uint(idProject))) public {
PledgeAdmins.PledgeAdmin storage project = _findAdmin(idProject);
// _checkAdminOwner(project);
project.canceled = true;
_storage.stgObjectSetBoolean(PLEDGE_ADMIN, idProject, "canceled", true);
CancelProject(idProject);
}
@ -307,19 +307,17 @@ contract LiquidPledging is LiquidPledgingBase {
/// @param idPledge Id of the pledge that is to be canceled
/// @param amount Quantity of ether (in wei) to be transfered to the
/// `oldPledge`
function cancelPledge(uint64 idPledge, uint amount) public {
//TODO fetch idPledge? OR return Pledge from this call?
// idPledge = normalizePledge(idPledge);
function cancelPledge(uint64 idPledge, uint amount) authP(PLEDGE_ADMIN_ROLE, arr(uint(idPledge))) public {
idPledge = normalizePledge(idPledge);
// Pledges.Pledge memory p = findPledge(idPledge);
Pledges.Pledge memory p = normalizePledge(idPledge);
Pledges.Pledge storage p = _findPledge(idPledge);
require(p.oldPledge != 0);
checkAdminOwner(p.owner);
// PledgeAdmins.PledgeAdmin storage a = _findAdmin(p.owner);
// _checkAdminOwner(a);
uint64 oldPledgeId = getOldestPledgeNotCanceled(p.oldPledge);
Pledges.Pledge memory oldPledge = findPledge(oldPledgeId);
doTransfer(p, oldPledge, amount);
uint64 oldPledge = _getOldestPledgeNotCanceled(p.oldPledge);
_doTransfer(idPledge, oldPledge, amount);
}
@ -414,21 +412,23 @@ contract LiquidPledging is LiquidPledgingBase {
/// @notice `transferOwnershipToProject` allows for the transfer of
/// ownership to the project, but it can also be called by a project
/// to un-delegate everyone by setting one's own id for the idReceiver
/// @param p the pledge to be transfered.
/// @param idPledge the id of the pledge to be transfered.
/// @param amount Quantity of value that's being transfered
/// @param idReceiver The new owner of the project (or self to un-delegate)
function transferOwnershipToProject(
Pledges.Pledge p,
function _transferOwnershipToProject(
uint64 idPledge,
uint amount,
uint64 idReceiver
) internal
{
Pledges.Pledge storage p = _findPledge(idPledge);
// Ensure that the pledge is not already at max pledge depth
// and the project has not been canceled
require(getPledgeLevel(p.oldPledge) < MAX_INTERPROJECT_LEVEL);
require(!isProjectCanceled(idReceiver));
require(_getPledgeLevel(p) < MAX_INTERPROJECT_LEVEL);
require(!_isProjectCanceled(idReceiver));
Pledges.Pledge memory oldPledge = findOrCreatePledge(
uint64 oldPledge = _findOrCreatePledge(
p.owner,
p.delegationChain,
0,
@ -436,31 +436,31 @@ contract LiquidPledging is LiquidPledgingBase {
p.oldPledge,
Pledges.PledgeState.Pledged
);
Pledges.Pledge memory toPledge = findOrCreatePledge(
uint64 toPledge = _findOrCreatePledge(
idReceiver, // Set the new owner
new uint64[](0), // clear the delegation chain
0,
0,
uint64(oldPledge.id),
uint64(oldPledge),
Pledges.PledgeState.Pledged
);
doTransfer(p, toPledge, amount);
_doTransfer(idPledge, toPledge, amount);
}
/// @notice `transferOwnershipToGiver` allows for the transfer of
/// value back to the Giver, value is placed in a pledged state
/// without being attached to a project, delegation chain, or time line.
/// @param p the pledge to be transfered.
/// @param idPledge the id of the pledge to be transfered.
/// @param amount Quantity of value that's being transfered
/// @param idReceiver The new owner of the pledge
function transferOwnershipToGiver(
Pledges.Pledge p,
function _transferOwnershipToGiver(
uint64 idPledge,
uint amount,
uint64 idReceiver
) internal
{
Pledges.Pledge memory toPledge = findOrCreatePledge(
uint64 toPledge = _findOrCreatePledge(
idReceiver,
new uint64[](0),
0,
@ -468,32 +468,34 @@ contract LiquidPledging is LiquidPledgingBase {
0,
Pledges.PledgeState.Pledged
);
doTransfer(p, toPledge, amount);
_doTransfer(idPledge, toPledge, amount);
}
/// @notice `appendDelegate` allows for a delegate to be added onto the
/// end of the delegate chain for a given Pledge.
/// @param p the pledge thats delegate chain will be modified.
/// @param idPledge the id of the pledge thats delegate chain will be modified.
/// @param amount Quantity of value that's being chained.
/// @param idReceiver The delegate to be added at the end of the chain
function appendDelegate(
Pledges.Pledge p,
function _appendDelegate(
uint64 idPledge,
uint amount,
uint64 idReceiver
) internal
{
Pledges.Pledge storage p = _findPledge(idPledge);
require(p.delegationChain.length < MAX_DELEGATES);
uint64[] memory newDelegationChain = new uint64[](
p.delegationChain.length + 1
);
for (uint i = 0; i<p.delegationChain.length; i++) {
for (uint i = 0; i < p.delegationChain.length; i++) {
newDelegationChain[i] = p.delegationChain[i];
}
// Make the last item in the array the idReceiver
newDelegationChain[p.delegationChain.length] = idReceiver;
Pledges.Pledge memory toPledge = findOrCreatePledge(
uint64 toPledge = _findOrCreatePledge(
p.owner,
newDelegationChain,
0,
@ -501,21 +503,22 @@ contract LiquidPledging is LiquidPledgingBase {
p.oldPledge,
Pledges.PledgeState.Pledged
);
doTransfer(p, toPledge, amount);
_doTransfer(idPledge, toPledge, amount);
}
/// @notice `appendDelegate` allows for a delegate to be added onto the
/// end of the delegate chain for a given Pledge.
/// @param p the pledge thats delegate chain will be modified.
/// @param idPledge the id of the pledge thats delegate chain will be modified.
/// @param amount Quantity of value that's shifted from delegates.
/// @param q Number (or depth) of delegates to remove
/// @return toPledge The id for the pledge being adjusted or created
function undelegate(
Pledges.Pledge p,
function _undelegate(
uint64 idPledge,
uint amount,
uint q
) internal returns (Pledges.Pledge)
) internal returns (uint64 toPledge)
{
Pledges.Pledge storage p = _findPledge(idPledge);
uint64[] memory newDelegationChain = new uint64[](
p.delegationChain.length - q
);
@ -523,7 +526,7 @@ contract LiquidPledging is LiquidPledgingBase {
for (uint i = 0; i < p.delegationChain.length - q; i++) {
newDelegationChain[i] = p.delegationChain[i];
}
Pledges.Pledge memory toPledge = findOrCreatePledge(
toPledge = _findOrCreatePledge(
p.owner,
newDelegationChain,
0,
@ -531,60 +534,61 @@ contract LiquidPledging is LiquidPledgingBase {
p.oldPledge,
Pledges.PledgeState.Pledged
);
doTransfer(p, toPledge, amount);
return toPledge;
_doTransfer(idPledge, toPledge, amount);
}
/// @notice `proposeAssignProject` proposes the assignment of a pledge
/// to a specific project.
/// @dev This function should potentially be named more specifically.
/// @param p the pledge that will be assigned.
/// @param idPledge the id of the pledge that will be assigned.
/// @param amount Quantity of value this pledge leader would be assigned.
/// @param idReceiver The project this pledge will potentially
/// be assigned to.
function proposeAssignProject(
Pledges.Pledge p,
function _proposeAssignProject(
uint64 idPledge,
uint amount,
uint64 idReceiver
) internal
{
require(getPledgeLevel(p.oldPledge) < MAX_INTERPROJECT_LEVEL);
require(!isProjectCanceled(idReceiver));
Pledges.Pledge storage p = _findPledge(idPledge);
Pledges.Pledge memory toPledge = findOrCreatePledge(
require(_getPledgeLevel(p) < MAX_INTERPROJECT_LEVEL);
require(!_isProjectCanceled(idReceiver));
uint64 toPledge = _findOrCreatePledge(
p.owner,
p.delegationChain,
idReceiver,
uint64(getTime() + maxCommitTime(p)),
uint64(_getTime() + _maxCommitTime(p)),
p.oldPledge,
Pledges.PledgeState.Pledged
);
doTransfer(p, toPledge, amount);
_doTransfer(idPledge, toPledge, amount);
}
/// @notice `doTransfer` is designed to allow for pledge amounts to be
/// shifted around internally.
/// @param pFrom This is the pledge from which value will be transfered.
/// @param pTo This is the pledge that value will be transfered to.
/// @param from This is the id of the pledge from which value will be transfered.
/// @param to This is the id of the pledge that value will be transfered to.
/// @param _amount The amount of value that will be transfered.
function doTransfer(Pledges.Pledge pFrom, Pledges.Pledge pTo, uint _amount) internal {
uint amount = callPlugins(true, pFrom, pTo, _amount);
if (pFrom.id == pTo.id) {
function _doTransfer(uint64 from, uint64 to, uint _amount) internal {
uint amount = _callPlugins(true, from, to, _amount);
if (from == to) {
return;
}
if (amount == 0) {
return;
}
Pledges.Pledge storage pFrom = _findPledge(from);
Pledges.Pledge storage pTo = _findPledge(to);
require(pFrom.amount >= amount);
pFrom.amount -= amount;
setPledgeAmount(pFrom.id, pFrom.amount);
pTo.amount += amount;
setPledgeAmount(pTo.id, pTo.amount);
Transfer(pFrom.id, pTo.id, amount);
callPlugins(false, pFrom, pTo, amount);
Transfer(from, to, amount);
_callPlugins(false, from, to, amount);
}
/// @notice Only affects pledges with the Pledged Pledges.PledgeState for 2 things:
@ -604,22 +608,18 @@ contract LiquidPledging is LiquidPledgingBase {
/// plugins, which also need to be predicted by the UI
/// @param idPledge This is the id of the pledge that will be normalized
/// @return The normalized Pledge!
function normalizePledge(uint64 idPledge) public returns(Pledges.Pledge) {
Pledges.Pledge memory p = findPledge(idPledge);
return _normalizePledge(p);
}
function _normalizePledge(Pledges.Pledge p) internal returns(Pledges.Pledge) {
function normalizePledge(uint64 idPledge) public returns(uint64) {
Pledges.Pledge storage p = _findPledge(idPledge);
// Check to make sure this pledge hasn't already been used
// or is in the process of being used
if (p.pledgeState != Pledges.PledgeState.Pledged) {
return p;
return idPledge;
}
// First send to a project if it's proposed and committed
if ((p.intendedProject > 0) && ( getTime() > p.commitTime)) {
Pledges.Pledge memory oldPledge = findOrCreatePledge(
if ((p.intendedProject > 0) && ( _getTime() > p.commitTime)) {
uint64 oldPledge = _findOrCreatePledge(
p.owner,
p.delegationChain,
0,
@ -627,26 +627,25 @@ contract LiquidPledging is LiquidPledgingBase {
p.oldPledge,
Pledges.PledgeState.Pledged
);
Pledges.Pledge memory toPledge = findOrCreatePledge(
uint64 toPledge = _findOrCreatePledge(
p.intendedProject,
new uint64[](0),
0,
0,
uint64(oldPledge.id),
oldPledge,
Pledges.PledgeState.Pledged
);
doTransfer(p, toPledge, p.amount);
p = toPledge;
_doTransfer(idPledge, toPledge, p.amount);
idPledge = toPledge;
p = _findPledge(idPledge);
}
uint64 oldestPledgeId = getOldestPledgeNotCanceled(uint64(p.id));
if (p.id != oldestPledgeId) {
toPledge = findPledge(oldestPledgeId);
doTransfer(p, toPledge, p.amount);
p = toPledge;
toPledge = _getOldestPledgeNotCanceled(idPledge);
if (toPledge != idPledge) {
_doTransfer(idPledge, toPledge, p.amount);
}
return p;
return toPledge;
}
/////////////
@ -667,7 +666,7 @@ contract LiquidPledging is LiquidPledgingBase {
/// @param context The situation that is triggering the plugin. See plugin
/// for a full description of contexts.
/// @param amount The amount of value that is being transfered.
function callPlugin(
function _callPlugin(
bool before,
uint64 adminId,
uint64 fromPledge,
@ -678,14 +677,14 @@ contract LiquidPledging is LiquidPledgingBase {
uint newAmount;
allowedAmount = amount;
address plugin = getAdminPlugin(adminId);
PledgeAdmins.PledgeAdmin storage admin = _findAdmin(adminId);
// Checks admin has a plugin assigned and a non-zero amount is requested
if (plugin != 0 && allowedAmount > 0) {
if (address(admin.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 = ILiquidPledgingPlugin(plugin).beforeTransfer(
newAmount = admin.plugin.beforeTransfer(
adminId,
fromPledge,
toPledge,
@ -695,7 +694,7 @@ contract LiquidPledging is LiquidPledgingBase {
require(newAmount <= allowedAmount);
allowedAmount = newAmount;
} else {
ILiquidPledgingPlugin(plugin).afterTransfer(
admin.plugin.afterTransfer(
adminId,
fromPledge,
toPledge,
@ -712,29 +711,26 @@ contract LiquidPledging is LiquidPledgingBase {
/// on the `p` and `fromPledge` parameters.
/// @param before This toggle determines whether the plugin call is occuring
/// before or after a transfer.
/// @param p This is the pledge on which this plugin
/// @param idPledge This is the id of the pledge on which this plugin
/// is being called.
/// @param fromPledge This is the Id from which value is being transfered.
/// @param toPledge This is the Id that value is being transfered to.
/// @param amount The amount of value that is being transfered.
function callPluginsPledge(
function _callPluginsPledge(
bool before,
Pledges.Pledge p,
uint64 idPledge,
uint64 fromPledge,
uint64 toPledge,
uint amount
) internal returns (uint allowedAmount) {
// Determine if callPlugin is being applied in a receiving
// or transferring context
uint64 offset = p.id == fromPledge ? 0 : 256;
uint64 offset = idPledge == fromPledge ? 0 : 256;
allowedAmount = amount;
// 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 = pledgeAdminsCount();
// require(adminsSize >= p.owner);
Pledges.Pledge storage p = _findPledge(idPledge);
// Always call the plugin on the owner
allowedAmount = callPlugin(
allowedAmount = _callPlugin(
before,
p.owner,
fromPledge,
@ -744,14 +740,13 @@ 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(
for (uint64 i = 0; i < p.delegationChain.length; i++) {
allowedAmount = _callPlugin(
before,
p.delegationChain[i],
fromPledge,
toPledge,
offset + i+1,
offset + i + 1,
allowedAmount
);
}
@ -760,8 +755,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(
allowedAmount = _callPlugin(
before,
p.intendedProject,
fromPledge,
@ -781,29 +775,29 @@ contract LiquidPledging is LiquidPledgingBase {
/// @param fromPledge This is the Id from which value is being transferred.
/// @param toPledge This is the Id that value is being transferred to.
/// @param amount The amount of value that is being transferred.
function callPlugins(
function _callPlugins(
bool before,
Pledges.Pledge fromPledge,
Pledges.Pledge toPledge,
uint64 fromPledge,
uint64 toPledge,
uint amount
) internal returns (uint allowedAmount) {
allowedAmount = amount;
// Call the pledges plugins in the transfer context
allowedAmount = callPluginsPledge(
allowedAmount = _callPluginsPledge(
before,
fromPledge,
uint64(fromPledge.id),
uint64(toPledge.id),
fromPledge,
toPledge,
allowedAmount
);
// Call the pledges plugins in the receive context
allowedAmount = callPluginsPledge(
allowedAmount = _callPluginsPledge(
before,
toPledge,
uint64(fromPledge.id),
uint64(toPledge.id),
fromPledge,
toPledge,
allowedAmount
);
}
@ -813,7 +807,7 @@ contract LiquidPledging is LiquidPledgingBase {
/////////////
/// @notice Basic helper function to return the current time
function getTime() internal view returns (uint) {
function _getTime() internal view returns (uint) {
return now;
}

View File

@ -20,15 +20,15 @@ pragma solidity ^0.4.11;
*/
import "./ILiquidPledgingPlugin.sol";
import "giveth-common-contracts/contracts/Escapable.sol";
// import "giveth-common-contracts/contracts/Escapable.sol";
import "./EscapableApp.sol";
import "./PledgeAdmins.sol";
import "./Pledges.sol";
import "./LiquidPledgingStorage.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
/// payments can Pledges be converted for ETH
interface LPVault {
interface ILPVault {
function authorizePayment(bytes32 _ref, address _dest, uint _amount) public;
function () public payable;
}
@ -36,11 +36,10 @@ interface LPVault {
/// @dev `LiquidPledgingBase` is the base level contract used to carry out
/// liquidPledging's most basic functions, mostly handling and searching the
/// data structures
contract LiquidPledgingBase is LiquidPledgingStorage, PledgeAdmins, Pledges, Escapable {
contract LiquidPledgingBase is PledgeAdmins, Pledges, EscapableApp {
LPVault public vault;
ILPVault public vault;
/////////////
// Modifiers
/////////////
@ -57,21 +56,31 @@ contract LiquidPledgingBase is LiquidPledgingStorage, PledgeAdmins, Pledges, Esc
// Constructor
///////////////
/// @notice The Constructor creates `LiquidPledgingBase` on the blockchain
/// @param _vault The vault where the ETH backing the pledges is stored
function LiquidPledgingBase(
address _storage,
address _vault,
address _escapeHatchCaller,
address _escapeHatchDestination
) LiquidPledgingStorage(_storage)
PledgeAdmins(_storage)
Pledges(_storage)
Escapable(_escapeHatchCaller, _escapeHatchDestination) public
function LiquidPledgingBase()
PledgeAdmins()
Pledges() public
{
vault = LPVault(_vault); // Assigns the specified vault
}
function initialize(address _escapeHatchDestination) onlyInit external {
require(false); // overload the EscapableApp
}
/// @param _vault The vault where the ETH backing the pledges is stored
/// @param _escapeHatchDestination The address of a safe location (usu a
/// Multisig) to send the ether held in this contract; if a neutral address
/// is required, the WHG Multisig is an option:
/// 0x8Ff920020c8AD673661c8117f2855C384758C572
function initialize(address _vault, address _escapeHatchDestination) onlyInit external {
initialized();
require(_escapeHatchDestination != 0x0);
require(_vault != 0x0);
escapeHatchDestination = _escapeHatchDestination;
vault = ILPVault(_vault);
}
/////////////////////////////
// Public constant functions
/////////////////////////////
@ -79,14 +88,16 @@ contract LiquidPledgingBase is LiquidPledgingStorage, PledgeAdmins, Pledges, Esc
/// @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 getDelegate(uint idPledge, uint idxDelegate) public view returns(
uint idDelegate,
function getPledgeDelegate(uint64 idPledge, uint64 idxDelegate) public view returns(
uint64 idDelegate,
address addr,
string name
) {
idDelegate = getPledgeDelegate(idPledge, idxDelegate);
addr = getAdminAddr(idDelegate);
name = getAdminName(idDelegate);
Pledge storage p = _findPledge(idPledge);
idDelegate = p.delegationChain[idxDelegate - 1];
PledgeAdmin storage delegate = _findAdmin(idDelegate);
addr = delegate.addr;
name = delegate.name;
}
////////////////////
@ -95,28 +106,25 @@ contract LiquidPledgingBase is LiquidPledgingStorage, PledgeAdmins, Pledges, Esc
/// @notice A check to see if the msg.sender is the owner or the
/// plugin contract for a specific Admin
/// @param idAdmin The id of the admin being checked
function checkAdminOwner(uint idAdmin) internal constant {
require(msg.sender == getAdminAddr(idAdmin) || msg.sender == getAdminPlugin(idAdmin));
}
/// @param a The admin being checked
// function _checkAdminOwner(PledgeAdmin a) internal constant {
// require(msg.sender == a.addr || msg.sender == address(a.plugin));
// }
/// @notice A getter to find the longest commitTime out of the owner and all
/// the delegates for a specified pledge
/// @param p The Pledge being queried
/// @return The maximum commitTime out of the owner and all the delegates
function maxCommitTime(Pledge p) internal view returns(uint commitTime) {
uint adminsSize = numberOfPledgeAdmins();
require(adminsSize >= p.owner);
commitTime = getAdminCommitTime(p.owner); // start with the owner's commitTime
function _maxCommitTime(Pledge p) internal view returns(uint64 commitTime) {
PledgeAdmin storage a = _findAdmin(p.owner);
commitTime = a.commitTime; // start with the owner's commitTime
for (uint i = 0; i < p.delegationChain.length; i++) {
require(adminsSize >= p.delegationChain[i]);
uint delegateCommitTime = getAdminCommitTime(p.delegationChain[i]);
a = _findAdmin(p.delegationChain[i]);
// If a delegate's commitTime is longer, make it the new commitTime
if (delegateCommitTime > commitTime) {
commitTime = delegateCommitTime;
if (a.commitTime > commitTime) {
commitTime = a.commitTime;
}
}
}
@ -124,7 +132,7 @@ contract LiquidPledgingBase is LiquidPledgingStorage, PledgeAdmins, Pledges, Esc
/// @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(
function _getOldestPledgeNotCanceled(
uint64 idPledge
) internal view returns(uint64)
{
@ -132,19 +140,18 @@ contract LiquidPledgingBase is LiquidPledgingStorage, PledgeAdmins, Pledges, Esc
return 0;
}
uint owner = getPledgeOwner(idPledge);
PledgeAdminType adminType = getAdminType(owner);
if (adminType == PledgeAdminType.Giver) {
return idPledge;
}
assert(adminType == PledgeAdminType.Project);
if (!isProjectCanceled(owner)) {
Pledge storage p = _findPledge(idPledge);
PledgeAdmin storage admin = _findAdmin(p.owner);
if (admin.adminType == PledgeAdminType.Giver) {
return idPledge;
}
uint64 oldPledge = uint64(getPledgeOldPledge(idPledge));
return getOldestPledgeNotCanceled(oldPledge);
assert(admin.adminType == PledgeAdminType.Project);
if (!_isProjectCanceled(p.owner)) {
return idPledge;
}
return _getOldestPledgeNotCanceled(p.oldPledge);
}
}

View File

@ -28,13 +28,7 @@ contract LiquidPledgingMock is LiquidPledging {
/// @dev `LiquidPledgingMock` creates a standard `LiquidPledging`
/// instance and sets the mocked time to the current blocktime.
/// @param _vault The vault where ETH backing this pledge is stored
function LiquidPledgingMock(
address _storage,
address _vault,
address _escapeHatchCaller,
address _escapeHatchDestination
) LiquidPledging(_storage, _vault, _escapeHatchCaller, _escapeHatchDestination) public {
function LiquidPledgingMock() LiquidPledging() public {
mock_time = now;
}

View File

@ -19,31 +19,33 @@ pragma solidity ^0.4.18;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import "giveth-common-contracts/contracts/Owned.sol";
import "@aragon/os/contracts/apps/AragonApp.sol";
/// NOTICE: This contract is not using EternalStorage. This is done to save gas. The pluginWhitelist
/// should be fairly small, and would be trivial and relatively cheap to re-add all valid plugins
/// when the LiquidPledging contract is upgraded
contract LiquidPledgingPlugins is Owned {
contract LiquidPledgingPlugins is AragonApp {
bytes32 constant public PLUGIN_MANAGER_ROLE = keccak256("PLUGIN_MANAGER_ROLE");
mapping (bytes32 => bool) pluginWhitelist;
bool public whitelistDisabled = false;
function addValidPlugin(bytes32 contractHash) public onlyOwner {
function addValidPlugin(bytes32 contractHash) auth(PLUGIN_MANAGER_ROLE) public {
pluginWhitelist[contractHash] = true;
}
function addValidPlugins(bytes32[] contractHashes) external onlyOwner {
function addValidPlugins(bytes32[] contractHashes) external auth(PLUGIN_MANAGER_ROLE) {
for (uint8 i = 0; i < contractHashes.length; i++) {
addValidPlugin(contractHashes[i]);
}
}
function removeValidPlugin(bytes32 contractHash) external onlyOwner {
function removeValidPlugin(bytes32 contractHash) external auth(PLUGIN_MANAGER_ROLE) {
pluginWhitelist[contractHash] = false;
}
function useWhitelist(bool useWhitelist) external onlyOwner {
function useWhitelist(bool useWhitelist) external auth(PLUGIN_MANAGER_ROLE) {
whitelistDisabled = !useWhitelist;
}

View File

@ -20,42 +20,53 @@ pragma solidity ^0.4.18;
*/
import "./ILiquidPledgingPlugin.sol";
import "./EternallyPersistentLib.sol";
import "./LiquidPledgingStorage.sol";
import "./LiquidPledgingPlugins.sol";
import "@aragon/os/contracts/apps/AragonApp.sol";
import "@aragon/os/contracts/acl/ACL.sol";
contract PledgeAdmins is LiquidPledgingStorage, LiquidPledgingPlugins {
using EternallyPersistentLib for EternalStorage;
contract PledgeAdmins is AragonApp, LiquidPledgingPlugins {
bytes32 constant public PLEDGE_ADMIN_ROLE = keccak256("PLEDGE_ADMIN_ROLE");
// Limits inserted to prevent large loops that could prevent canceling
uint constant MAX_SUBPROJECT_LEVEL = 20;
uint constant MAX_INTERPROJECT_LEVEL = 20;
// Constants used when dealing with storage/retrieval of PledgeAdmins
string constant PLEDGE_ADMIN = "PledgeAdmin";
bytes32 constant PLEDGE_ADMINS_ARRAY = keccak256("pledgeAdmins");
//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?
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;
}
// Events
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);
event GiverAdded(uint64 indexed idGiver);
event GiverUpdated(uint64 indexed idGiver);
event DelegateAdded(uint64 indexed idDelegate);
event DelegateUpdated(uint64 indexed idDelegate);
event ProjectAdded(uint64 indexed idProject);
event ProjectUpdated(uint64 indexed idProject);
PledgeAdmin[] admins; //The list of pledgeAdmins 0 means there is no admin
///////////////
// Constructor
///////////////
function PledgeAdmins(address _storage)
LiquidPledgingStorage(_storage)
function PledgeAdmins()
LiquidPledgingPlugins() public
{
}
@ -75,21 +86,31 @@ contract PledgeAdmins is LiquidPledgingStorage, LiquidPledgingPlugins {
function addGiver(
string name,
string url,
uint commitTime,
uint64 commitTime,
ILiquidPledgingPlugin plugin
) public returns (uint idGiver)
) public returns (uint64 idGiver)
{
require(isValidPlugin(plugin)); // Plugin check
idGiver = _storage.stgCollectionAddItem(PLEDGE_ADMINS_ARRAY);
idGiver = uint64(admins.length);
// Save the fields
// don't set adminType to save gas, b/c 0 is Giver
_storage.stgObjectSetAddress(PLEDGE_ADMIN, idGiver, "addr", msg.sender);
_storage.stgObjectSetString(PLEDGE_ADMIN, idGiver, "name", name);
_storage.stgObjectSetString(PLEDGE_ADMIN, idGiver, "url", url);
_storage.stgObjectSetUInt(PLEDGE_ADMIN, idGiver, "commitTime", commitTime);
_storage.stgObjectSetAddress(PLEDGE_ADMIN, idGiver, "plugin", address(plugin));
admins.push(
PledgeAdmin(
PledgeAdminType.Giver,
msg.sender, // TODO: is this needed?
name,
url,
commitTime,
0,
false,
plugin)
);
_grantPledgeAdminPermission(msg.sender, idGiver);
if (address(plugin) != 0) {
_grantPledgeAdminPermission(address(plugin), idGiver);
}
GiverAdded(idGiver);
}
@ -104,21 +125,20 @@ contract PledgeAdmins is LiquidPledgingStorage, LiquidPledgingPlugins {
/// @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(
uint idGiver,
uint64 idGiver,
address newAddr,
string newName,
string newUrl,
uint64 newCommitTime
) public
) authP(PLEDGE_ADMIN_ROLE, arr(uint(idGiver))) public
{
require(getAdminType(idGiver) == PledgeAdminType.Giver); // Must be a Giver
require(getAdminAddr(idGiver) == msg.sender); // Current addr had to send this tx
// Save the fields
_storage.stgObjectSetAddress(PLEDGE_ADMIN, idGiver, "addr", newAddr);
_storage.stgObjectSetString(PLEDGE_ADMIN, idGiver, "name", newName);
_storage.stgObjectSetString(PLEDGE_ADMIN, idGiver, "url", newUrl);
_storage.stgObjectSetUInt(PLEDGE_ADMIN, idGiver, "commitTime", newCommitTime);
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);
}
@ -138,19 +158,28 @@ contract PledgeAdmins is LiquidPledgingStorage, LiquidPledgingPlugins {
string url,
uint64 commitTime,
ILiquidPledgingPlugin plugin
) public returns (uint idDelegate)
) public returns (uint64 idDelegate)
{
require(isValidPlugin(plugin)); // Plugin check
idDelegate = _storage.stgCollectionAddItem(PLEDGE_ADMINS_ARRAY);
idDelegate = uint64(admins.length);
// Save the fields
_storage.stgObjectSetUInt(PLEDGE_ADMIN, idDelegate, "adminType", uint(PledgeAdminType.Delegate));
_storage.stgObjectSetAddress(PLEDGE_ADMIN, idDelegate, "addr", msg.sender);
_storage.stgObjectSetString(PLEDGE_ADMIN, idDelegate, "name", name);
_storage.stgObjectSetString(PLEDGE_ADMIN, idDelegate, "url", url);
_storage.stgObjectSetUInt(PLEDGE_ADMIN, idDelegate, "commitTime", commitTime);
_storage.stgObjectSetAddress(PLEDGE_ADMIN, idDelegate, "plugin", address(plugin));
admins.push(
PledgeAdmin(
PledgeAdminType.Delegate,
msg.sender,
name,
url,
commitTime,
0,
false,
plugin)
);
_grantPledgeAdminPermission(msg.sender, idDelegate);
if (address(plugin) != 0) {
_grantPledgeAdminPermission(address(plugin), idDelegate);
}
DelegateAdded(idDelegate);
}
@ -167,21 +196,20 @@ contract PledgeAdmins is LiquidPledgingStorage, LiquidPledgingPlugins {
/// the time allowed to veto any event must be greater than or equal to
/// this time.
function updateDelegate(
uint idDelegate,
uint64 idDelegate,
address newAddr,
string newName,
string newUrl,
uint64 newCommitTime
) public
) authP(PLEDGE_ADMIN_ROLE, arr(uint(idDelegate))) public
{
require(getAdminType(idDelegate) == PledgeAdminType.Delegate);
require(getAdminAddr(idDelegate) == msg.sender); // Current addr had to send this tx
// Save the fields
_storage.stgObjectSetAddress(PLEDGE_ADMIN, idDelegate, "addr", newAddr);
_storage.stgObjectSetString(PLEDGE_ADMIN, idDelegate, "name", newName);
_storage.stgObjectSetString(PLEDGE_ADMIN, idDelegate, "url", newUrl);
_storage.stgObjectSetUInt(PLEDGE_ADMIN, idDelegate, "commitTime", newCommitTime);
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);
}
@ -205,27 +233,35 @@ contract PledgeAdmins is LiquidPledgingStorage, LiquidPledgingPlugins {
uint64 parentProject,
uint64 commitTime,
ILiquidPledgingPlugin plugin
) public returns (uint idProject)
) public returns (uint64 idProject)
{
require(isValidPlugin(plugin));
if (parentProject != 0) {
PledgeAdmin storage a = _findAdmin(parentProject);
// require(a.adminType == PledgeAdminType.Project);
// getProjectLevel will check that parentProject has a `Project` adminType
require(getProjectLevel(parentProject) < MAX_SUBPROJECT_LEVEL);
require(_getProjectLevel(a) < MAX_SUBPROJECT_LEVEL);
}
idProject = _storage.stgCollectionAddItem(PLEDGE_ADMINS_ARRAY);//, idProject);
idProject = uint64(admins.length);
// Save the fields
_storage.stgObjectSetUInt(PLEDGE_ADMIN, idProject, "adminType", uint(PledgeAdminType.Project));
_storage.stgObjectSetAddress(PLEDGE_ADMIN, idProject, "addr", projectAdmin);
_storage.stgObjectSetString(PLEDGE_ADMIN, idProject, "name", name);
_storage.stgObjectSetString(PLEDGE_ADMIN, idProject, "url", url);
admins.push(
PledgeAdmin(
PledgeAdminType.Project,
projectAdmin,
name,
url,
commitTime,
parentProject,
false,
plugin)
);
_storage.stgObjectSetUInt(PLEDGE_ADMIN, idProject, "parentProject", parentProject);
_storage.stgObjectSetUInt(PLEDGE_ADMIN, idProject, "commitTime", commitTime);
_storage.stgObjectSetAddress(PLEDGE_ADMIN, idProject, "plugin", address(plugin));
_grantPledgeAdminPermission(msg.sender, idProject);
if (address(plugin) != 0) {
_grantPledgeAdminPermission(address(plugin), idProject);
}
ProjectAdded(idProject);
}
@ -241,21 +277,22 @@ contract PledgeAdmins is LiquidPledgingStorage, LiquidPledgingPlugins {
/// to veto when the Project delegates to a Delegate and they pledge those
/// funds to a project
function updateProject(
uint idProject,
uint64 idProject,
address newAddr,
string newName,
string newUrl,
uint64 newCommitTime
) public
) authP(PLEDGE_ADMIN_ROLE, arr(uint(idProject))) public
{
require(getAdminType(idProject) == PledgeAdminType.Project);
require(getAdminAddr(idProject) == msg.sender); // Current addr had to send this tx
PledgeAdmin storage project = _findAdmin(idProject);
// Save the fields
_storage.stgObjectSetAddress(PLEDGE_ADMIN, idProject, "addr", newAddr);
_storage.stgObjectSetString(PLEDGE_ADMIN, idProject, "name", newName);
_storage.stgObjectSetString(PLEDGE_ADMIN, idProject, "url", newUrl);
_storage.stgObjectSetUInt(PLEDGE_ADMIN, idProject, "commitTime", newCommitTime);
require(project.adminType == PledgeAdminType.Project);
// require(project.addr == msg.sender);
project.addr = newAddr;
project.name = newName;
project.url = newUrl;
project.commitTime = newCommitTime;
ProjectUpdated(idProject);
}
@ -268,7 +305,7 @@ contract PledgeAdmins is LiquidPledgingStorage, LiquidPledgingPlugins {
/// @notice A constant getter used to check how many total Admins exist
/// @return The total number of admins (Givers, Delegates and Projects) .
function numberOfPledgeAdmins() public constant returns(uint) {
return _storage.stgCollectionLength(PLEDGE_ADMINS_ARRAY);
return admins.length - 1;
}
/// @notice A constant getter to check the details of a specified Admin
@ -284,7 +321,7 @@ contract PledgeAdmins is LiquidPledgingStorage, LiquidPledgingPlugins {
/// canceled
/// @return plugin This is Project's liquidPledging plugin allowing for
/// extended functionality
function getPledgeAdmin(uint idAdmin) public view returns (
function getPledgeAdmin(uint64 idAdmin) public view returns (
PledgeAdminType adminType,
address addr,
string name,
@ -294,20 +331,15 @@ contract PledgeAdmins is LiquidPledgingStorage, LiquidPledgingPlugins {
bool canceled,
address plugin
) {
adminType = getAdminType(idAdmin);
addr = getAdminAddr(idAdmin);
name = getAdminName(idAdmin);
url = _storage.stgObjectGetString(PLEDGE_ADMIN, idAdmin, "url");
commitTime = uint64(getAdminCommitTime(idAdmin));
// parentProject & canceled only belong to Project admins,
// so don't waste the gas to fetch the data
if (adminType == PledgeAdminType.Project) {
parentProject = uint64(getAdminParentProject(idAdmin));
canceled = getAdminCanceled(idAdmin);
}
plugin = getAdminPlugin(idAdmin);
PledgeAdmin storage a = _findAdmin(idAdmin);
adminType = a.adminType;
addr = a.addr;
name = a.name;
url = a.url;
commitTime = a.commitTime;
parentProject = a.parentProject;
canceled = a.canceled;
plugin = address(a.plugin);
}
@ -315,97 +347,60 @@ contract PledgeAdmins is LiquidPledgingStorage, LiquidPledgingPlugins {
// Internal methods
///////////////////
/// @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 returns (PledgeAdmin storage) {
require(idAdmin < admins.length);
return admins[idAdmin];
}
/// @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(uint projectId)
function _isProjectCanceled(uint64 projectId)
internal constant returns (bool)
{
require(numberOfPledgeAdmins() >= projectId);
PledgeAdmin storage a = _findAdmin(projectId);
PledgeAdminType adminType = getAdminType(projectId);
if (adminType == PledgeAdminType.Giver) {
if (a.adminType == PledgeAdminType.Giver) {
return false;
}
assert(adminType == PledgeAdminType.Project);
if (getAdminCanceled(projectId)) {
assert(a.adminType == PledgeAdminType.Project);
if (a.canceled) {
return true;
}
uint parentProject = getAdminParentProject(projectId);
if (parentProject == 0) {
if (a.parentProject == 0) {
return false;
}
return isProjectCanceled(parentProject);
return _isProjectCanceled(a.parentProject);
}
/// @notice Find the level of authority a specific Project has
/// using a recursive loop
/// @param idProject The id of the Project being queried
/// @param a The project admin being queried
/// @return The level of authority a specific Project has
function getProjectLevel(uint idProject) internal returns(uint) {
assert(getAdminType(idProject) == PledgeAdminType.Project);
uint parentProject = getAdminParentProject(idProject);
if (parentProject == 0) {
function _getProjectLevel(PledgeAdmin a) internal returns(uint64) {
assert(a.adminType == PledgeAdminType.Project);
if (a.parentProject == 0) {
return(1);
}
return getProjectLevel(parentProject) + 1;
PledgeAdmin storage parentA = _findAdmin(a.parentProject);
return _getProjectLevel(parentA) + 1;
}
function _grantPledgeAdminPermission(address _who, uint64 idPledge) internal {
bytes32 id;
assembly { id := idPledge }
//////////////////////////////////////////////////////
// Getters for individual attributes of a PledgeAdmin
//////////////////////////////////////////////////////
uint[] memory params = new uint[](1);
params[0] = uint(bytes32(1 << 8 * 30) | id);
function getAdminType(
uint idAdmin
) internal view returns (PledgeAdminType)
{
return PledgeAdminType(_storage.stgObjectGetUInt(PLEDGE_ADMIN, idAdmin, "adminType"));
ACL(kernel.acl()).grantPermissionP(_who, address(this), PLEDGE_ADMIN_ROLE, params);
}
function getAdminAddr(
uint idAdmin
) internal view returns (address)
{
return _storage.stgObjectGetAddress(PLEDGE_ADMIN, idAdmin, "addr");
}
function getAdminName(
uint idAdmin
) internal view returns (string)
{
return _storage.stgObjectGetString(PLEDGE_ADMIN, idAdmin, "name");
}
function getAdminParentProject(
uint idAdmin
) internal view returns (uint)
{
return _storage.stgObjectGetUInt(PLEDGE_ADMIN, idAdmin, "parentProject");
}
function getAdminCanceled(
uint idAdmin
) internal view returns (bool)
{
return _storage.stgObjectGetBoolean(PLEDGE_ADMIN, idAdmin, "canceled");
}
function getAdminPlugin(
uint idAdmin
) internal view returns (address)
{
return _storage.stgObjectGetAddress(PLEDGE_ADMIN, idAdmin, "plugin");
}
function getAdminCommitTime(
uint idAdmin
) internal view returns (uint)
{
return _storage.stgObjectGetUInt(PLEDGE_ADMIN, idAdmin, "commitTime");
}
}
}

View File

@ -19,11 +19,9 @@ pragma solidity ^0.4.18;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import "./EternallyPersistentLib.sol";
import "./LiquidPledgingStorage.sol";
import "@aragon/os/contracts/apps/AragonApp.sol";
contract Pledges is LiquidPledgingStorage {
using EternallyPersistentLib for EternalStorage;
contract Pledges is AragonApp {
// Limits inserted to prevent large loops that could prevent canceling
uint constant MAX_DELEGATES = 10;
@ -31,14 +29,10 @@ contract Pledges is LiquidPledgingStorage {
// a constant for when a delegate is requested that is not in the system
uint64 constant NOTFOUND = 0xFFFFFFFFFFFFFFFF;
// Constants used when dealing with storage/retrieval of Pledges
string constant PLEDGE = "Pledge";
bytes32 constant PLEDGES_ARRAY = keccak256("pledges");
enum PledgeState { Pledged, Paying, Paid }
struct Pledge {
uint id; // the id of this Pledge
// uint id; // the id of this Pledge
uint amount;
uint64 owner; // PledgeAdmin
uint64[] delegationChain; // List of delegates in order of authority
@ -48,13 +42,17 @@ contract Pledges is LiquidPledgingStorage {
PledgeState pledgeState; // Pledged, Paying, Paid
}
Pledge[] pledges;
/// @dev this mapping allows you to search for a specific pledge's
/// index number by the hash of that pledge
mapping (bytes32 => uint64) hPledge2idx;
///////////////
// Constructor
///////////////
function Pledges(address _storage)
LiquidPledgingStorage(_storage) public
{
function Pledges() public {
pledges.length = 1; // we reserve the 0 pledge
}
@ -65,7 +63,7 @@ contract Pledges is LiquidPledgingStorage {
/// @notice A constant getter that returns the total number of pledges
/// @return The total number of Pledges in the system
function numberOfPledges() public view returns (uint) {
return _storage.stgCollectionLength(PLEDGES_ARRAY);
return pledges.length - 1;
}
/// @notice A getter that returns the details of the specified pledge
@ -82,10 +80,10 @@ contract Pledges is LiquidPledgingStorage {
uint64 oldPledge,
PledgeState pledgeState
) {
Pledge memory p = findPledge(idPledge);
Pledge memory p = _findPledge(idPledge);
amount = p.amount;
owner = p.owner;
nDelegates = uint64(getPledgeDelegateCount(idPledge));
nDelegates = uint64(p.delegationChain.length);
intendedProject = p.intendedProject;
commitTime = p.commitTime;
oldPledge = p.oldPledge;
@ -113,89 +111,42 @@ contract Pledges is LiquidPledgingStorage {
/// will revert back to it's previous state
/// @param state The pledge state: Pledged, Paying, or state
/// @return The hPledge2idx index number
function findOrCreatePledge(
function _findOrCreatePledge(
uint64 owner,
uint64[] delegationChain,
uint64 intendedProject,
uint64 commitTime,
uint64 oldPledge,
PledgeState state
) internal returns (Pledge)
) internal returns (uint64)
{
bytes32 hPledge = keccak256(owner, delegationChain, intendedProject, commitTime, oldPledge, state);
uint id = _storage.getUIntValue(hPledge);
uint64 id = hPledge2idx[hPledge];
if (id > 0) {
return Pledge(
id,
getPledgeAmount(id), //TODO don't fetch this here b/c it may not be needed?
return id;
}
id = uint64(pledges.length);
hPledge2idx[hPledge] = id;
pledges.push(
Pledge(
0,
owner,
delegationChain,
intendedProject,
commitTime,
oldPledge,
state);
}
id = _storage.stgCollectionAddItem(PLEDGES_ARRAY);
_storage.setUIntValue(hPledge, id);
_storage.stgObjectSetUInt(PLEDGE, id, "owner", owner);
if (intendedProject > 0) {
_storage.stgObjectSetUInt(PLEDGE, id, "intendedProject", intendedProject);
}
if (commitTime > 0) {
_storage.stgObjectSetUInt(PLEDGE, id, "commitTime", commitTime);
}
if (oldPledge > 0) {
_storage.stgObjectSetUInt(PLEDGE, id, "oldPledge", oldPledge);
}
if (state != PledgeState.Pledged) {
_storage.stgObjectSetUInt(PLEDGE, id, "state", uint(state));
}
if (delegationChain.length > 0) {
_storage.setUIntValue(keccak256("delegationChain", id, "length"), delegationChain.length);
// TODO pack these? possibly add array method to EternalStorage in anticipation of the new solidity abi encoder
for (uint i = 0; i < delegationChain.length; i++) {
_storage.setUIntValue(keccak256("delegationChain", id, i), delegationChain[i]);
}
}
return Pledge(
id,
0,
owner,
delegationChain,
intendedProject,
commitTime,
oldPledge,
state);
state
)
);
return id;
}
/// @param idPledge the id of the pledge to load from storage
/// @return The Pledge
function findPledge(uint idPledge) internal view returns(Pledge) {
require(idPledge <= numberOfPledges());
uint amount = getPledgeAmount(idPledge);
uint owner = getPledgeOwner(idPledge);
uint intendedProject = getPledgeIntendedProject(idPledge);
uint commitTime = getPledgeCommitTime(idPledge);
uint oldPledge = getPledgeOldPledge(idPledge);
PledgeState state = getPledgeState(idPledge);
uint64[] memory delegates = getPledgeDelegates(idPledge);
return Pledge(
idPledge,
amount,
uint64(owner),
delegates,
uint64(intendedProject),
uint64(commitTime),
uint64(oldPledge),
state
);
function _findPledge(uint64 idPledge) internal view returns(Pledge storage) {
require(idPledge < pledges.length);
return pledges[idPledge];
}
/// @notice A getter that searches the delegationChain for the level of
@ -206,7 +157,7 @@ contract Pledges is LiquidPledgingStorage {
/// `admins` array index `idDelegate` this returns that delegates
/// corresponding index in the delegationChain. Otherwise it returns
/// the NOTFOUND constant
function getDelegateIdx(Pledge p, uint64 idDelegate) internal pure returns(uint64) {
function _getDelegateIdx(Pledge p, uint64 idDelegate) internal pure returns(uint64) {
for (uint i = 0; i < p.delegationChain.length; i++) {
if (p.delegationChain[i] == idDelegate) {
return uint64(i);
@ -217,64 +168,13 @@ contract Pledges is LiquidPledgingStorage {
/// @notice A getter to find how many old "parent" pledges a specific Pledge
/// had using a self-referential loop
/// @param idOldPledge The Pledge being queried
/// @param p The Pledge being queried
/// @return The number of old "parent" pledges a specific Pledge had
function getPledgeLevel(uint idOldPledge) internal view returns(uint) {
if (idOldPledge == 0) {
function _getPledgeLevel(Pledge p) internal view returns(uint) {
if (p.oldPledge == 0) {
return 0;
}
idOldPledge = _storage.stgObjectGetUInt(PLEDGE, idOldPledge, "oldPledge");
return getPledgeLevel(idOldPledge) + 1; // a loop lookup
}
//////////////////////////////////////////////////////
// Getters for individual attributes of a PledgeAdmin
//////////////////////////////////////////////////////
function getPledgeOwner(uint idPledge) internal view returns(uint) {
return _storage.stgObjectGetUInt(PLEDGE, idPledge, "owner");
}
function getPledgeDelegate(uint idPledge, uint index) internal view returns(uint) {
return _storage.getUIntValue(keccak256("delegationChain", idPledge, index));
}
function getPledgeOldPledge(uint idPledge) internal view returns(uint) {
return _storage.stgObjectGetUInt(PLEDGE, idPledge, "oldPledge");
}
function getPledgeAmount(uint idPledge) internal view returns(uint) {
return _storage.stgObjectGetUInt(PLEDGE, idPledge, "amount");
}
function getPledgeIntendedProject(uint idPledge) internal view returns(uint) {
return _storage.stgObjectGetUInt(PLEDGE, idPledge, "intendedProject");
}
function getPledgeCommitTime(uint idPledge) internal view returns(uint) {
return _storage.stgObjectGetUInt(PLEDGE, idPledge, "commitTime");
}
function getPledgeState(uint idPledge) internal view returns(PledgeState) {
return PledgeState(_storage.stgObjectGetUInt(PLEDGE, idPledge, "state"));
}
function getPledgeDelegates(uint idPledge) internal view returns(uint64[]) {
//TODO pack/unpack chain
uint length = getPledgeDelegateCount(idPledge);
uint64[] memory delegates = new uint64[](length);
for (uint i = 0; i < length; i++) {
delegates[i] = uint64(getPledgeDelegate(idPledge, i));
}
return delegates;
}
function getPledgeDelegateCount(uint idPledge) internal view returns(uint) {
return _storage.getUIntValue(keccak256("delegationChain", idPledge, "length"));
}
function setPledgeAmount(uint idPledge, uint amount) internal {
_storage.stgObjectSetUInt(PLEDGE, idPledge, "amount", amount);
Pledge storage oldP = _findPledge(p.oldPledge);
return _getPledgeLevel(oldP) + 1; // a loop lookup
}
}

View File

@ -42,13 +42,14 @@
"eslint-plugin-react": "^7.1.0",
"ganache-cli": "^7.0.0-beta.0",
"lerna": "^2.2.0",
"random-bytes": "^1.0.0",
"mocha": "^3.5.0",
"solcpiler": "0.0.10",
"random-bytes": "^1.0.0",
"solcpiler": "^0.0.15",
"web3": "1.0.0-beta.24"
},
"homepage": "https://github.com/Giveth/liquidpledging#readme",
"dependencies": {
"@aragon/os": "3.0.1",
"async": "^2.4.0",
"chai": "^4.1.0",
"eth-contract-class": "0.0.6",

View File

@ -3,15 +3,12 @@
const TestRPC = require('ganache-cli');
const Web3 = require('web3');
const chai = require('chai');
const liquidpledging = require('../index.js');
const EternalStorage = require('../js/eternalStorage');
const assertFail = require('./helpers/assertFail');
const contracts = require("../build/contracts.js");
const { utils } = Web3;
const LiquidPledging = liquidpledging.LiquidPledgingMock;
const LPVault = liquidpledging.LPVault;
const LiquidPledgingState = liquidpledging.LiquidPledgingState;
const LiquidPledgingState = require('../index').LiquidPledgingState;
const assert = chai.assert;
@ -36,6 +33,7 @@ describe('LiquidPledging test', function () {
let adminProject2a;
let adminProject3;
let delegate2;
let escapeHatchDestination;
before(async () => {
testrpc = TestRPC.server({
ws: true,
@ -55,6 +53,7 @@ describe('LiquidPledging test', function () {
delegate2 = accounts[6];
giver2 = accounts[7];
adminProject3 = accounts[8];
escapeHatchDestination = accounts[9];
});
after((done) => {
@ -63,13 +62,21 @@ describe('LiquidPledging test', function () {
});
it('Should deploy LiquidPledging contract', async () => {
vault = await LPVault.new(web3, accounts[0], accounts[1]);
const storage = await EternalStorage.new(web3, accounts[0], accounts[1]);
const baseVault = await contracts.LPVault.new(web3);
const baseLP = await contracts.LiquidPledgingMock.new(web3);
lpFactory = await contracts.LPFactory.new(web3, baseVault.$address, baseLP.$address);
liquidPledging = await LiquidPledging.new(web3, storage.$address, vault.$address, accounts[0], accounts[0], {gas: 6700000})
const r = await lpFactory.newLP(accounts[0], escapeHatchDestination);
await storage.changeOwnership(liquidPledging.$address);
await vault.setLiquidPledging(liquidPledging.$address);
const vaultAddress = r.events.DeployVault.returnValues.vault;
vault = new contracts.LPVault(web3, vaultAddress);
const lpAddress = r.events.DeployLiquidPledging.returnValues.liquidPledging;
lp = new contracts.LiquidPledgingMock(web3, lpAddress);
// setup permissions
// const kernel = new contracts.Kernel(web3, await vault.kernel());
// const acl = new contracts.ACL(web3, await kernel.acl());
liquidPledgingState = new LiquidPledgingState(liquidPledging);
});