diff --git a/contracts/EscapableApp.sol b/contracts/EscapableApp.sol new file mode 100644 index 0000000..ddf2b39 --- /dev/null +++ b/contracts/EscapableApp.sol @@ -0,0 +1,89 @@ +pragma solidity ^0.4.15; +/* + Copyright 2016, Jordi Baylina + Contributor: AdriĆ  Massanet + + 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 . +*/ + +// 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); +} diff --git a/contracts/LPFactory.sol b/contracts/LPFactory.sol new file mode 100644 index 0000000..d7df7ff --- /dev/null +++ b/contracts/LPFactory.sol @@ -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)); + } +} \ No newline at end of file diff --git a/contracts/LPVault.sol b/contracts/LPVault.sol index d9cea8d..76fc2e6 100644 --- a/contracts/LPVault.sol +++ b/contracts/LPVault.sol @@ -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; + } } diff --git a/contracts/LiquidPledging.sol b/contracts/LiquidPledging.sol index 445c29b..31c27d5 100644 --- a/contracts/LiquidPledging.sol +++ b/contracts/LiquidPledging.sol @@ -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= 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[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; } diff --git a/contracts/LiquidPledgingBase.sol b/contracts/LiquidPledgingBase.sol index 8c73a33..bc14bec 100644 --- a/contracts/LiquidPledgingBase.sol +++ b/contracts/LiquidPledgingBase.sol @@ -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); } } diff --git a/contracts/LiquidPledgingMock.sol b/contracts/LiquidPledgingMock.sol index 763c95e..0fbed3a 100644 --- a/contracts/LiquidPledgingMock.sol +++ b/contracts/LiquidPledgingMock.sol @@ -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; } diff --git a/contracts/LiquidPledgingPlugins.sol b/contracts/LiquidPledgingPlugins.sol index 1f5a472..d28af85 100644 --- a/contracts/LiquidPledgingPlugins.sol +++ b/contracts/LiquidPledgingPlugins.sol @@ -19,31 +19,33 @@ pragma solidity ^0.4.18; along with this program. If not, see . */ -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; } diff --git a/contracts/PledgeAdmins.sol b/contracts/PledgeAdmins.sol index 437d101..1115fda 100644 --- a/contracts/PledgeAdmins.sol +++ b/contracts/PledgeAdmins.sol @@ -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"); - } -} +} \ No newline at end of file diff --git a/contracts/Pledges.sol b/contracts/Pledges.sol index e77d8a0..a94cc04 100644 --- a/contracts/Pledges.sol +++ b/contracts/Pledges.sol @@ -19,11 +19,9 @@ pragma solidity ^0.4.18; along with this program. If not, see . */ -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 } } diff --git a/package.json b/package.json index 6fa651c..419ebe7 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/test/NormalOperation.js b/test/NormalOperation.js index a76aca7..af1f484 100644 --- a/test/NormalOperation.js +++ b/test/NormalOperation.js @@ -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); });