1072 lines
36 KiB
Solidity
1072 lines
36 KiB
Solidity
|
|
|
|
///File: @aragon/os/contracts/acl/IACL.sol
|
|
|
|
pragma solidity ^0.4.18;
|
|
|
|
|
|
interface IACL {
|
|
function initialize(address permissionsCreator) public;
|
|
function hasPermission(address who, address where, bytes32 what, bytes how) public view returns (bool);
|
|
}
|
|
|
|
|
|
///File: @aragon/os/contracts/kernel/IKernel.sol
|
|
|
|
pragma solidity ^0.4.18;
|
|
|
|
|
|
|
|
interface IKernel {
|
|
event SetApp(bytes32 indexed namespace, bytes32 indexed name, bytes32 indexed id, address app);
|
|
|
|
function acl() public view returns (IACL);
|
|
function hasPermission(address who, address where, bytes32 what, bytes how) public view returns (bool);
|
|
|
|
function setApp(bytes32 namespace, bytes32 name, address app) public returns (bytes32 id);
|
|
function getApp(bytes32 id) public view returns (address);
|
|
}
|
|
|
|
///File: @aragon/os/contracts/apps/AppStorage.sol
|
|
|
|
pragma solidity ^0.4.18;
|
|
|
|
|
|
|
|
|
|
contract AppStorage {
|
|
IKernel public kernel;
|
|
bytes32 public appId;
|
|
address internal pinnedCode; // used by Proxy Pinned
|
|
uint256 internal initializationBlock; // used by Initializable
|
|
uint256[95] private storageOffset; // forces App storage to start at after 100 slots
|
|
uint256 private offset;
|
|
}
|
|
|
|
|
|
///File: @aragon/os/contracts/common/Initializable.sol
|
|
|
|
pragma solidity ^0.4.18;
|
|
|
|
|
|
|
|
|
|
contract Initializable is AppStorage {
|
|
modifier onlyInit {
|
|
require(initializationBlock == 0);
|
|
_;
|
|
}
|
|
|
|
/**
|
|
* @return Block number in which the contract was initialized
|
|
*/
|
|
function getInitializationBlock() public view returns (uint256) {
|
|
return initializationBlock;
|
|
}
|
|
|
|
/**
|
|
* @dev Function to be called by top level contract after initialization has finished.
|
|
*/
|
|
function initialized() internal onlyInit {
|
|
initializationBlock = getBlockNumber();
|
|
}
|
|
|
|
/**
|
|
* @dev Returns the current block number.
|
|
* Using a function rather than `block.number` allows us to easily mock the block number in
|
|
* tests.
|
|
*/
|
|
function getBlockNumber() internal view returns (uint256) {
|
|
return block.number;
|
|
}
|
|
}
|
|
|
|
|
|
///File: @aragon/os/contracts/evmscript/IEVMScriptExecutor.sol
|
|
|
|
pragma solidity ^0.4.18;
|
|
|
|
|
|
interface IEVMScriptExecutor {
|
|
function execScript(bytes script, bytes input, address[] blacklist) external returns (bytes);
|
|
}
|
|
|
|
|
|
///File: @aragon/os/contracts/evmscript/IEVMScriptRegistry.sol
|
|
|
|
pragma solidity 0.4.18;
|
|
|
|
|
|
contract EVMScriptRegistryConstants {
|
|
bytes32 constant public EVMSCRIPT_REGISTRY_APP_ID = keccak256("evmreg.aragonpm.eth");
|
|
bytes32 constant public EVMSCRIPT_REGISTRY_APP = keccak256(keccak256("app"), EVMSCRIPT_REGISTRY_APP_ID);
|
|
}
|
|
|
|
|
|
interface IEVMScriptRegistry {
|
|
function addScriptExecutor(address executor) external returns (uint id);
|
|
function disableScriptExecutor(uint256 executorId) external;
|
|
|
|
function getScriptExecutor(bytes script) public view returns (address);
|
|
}
|
|
|
|
///File: @aragon/os/contracts/evmscript/ScriptHelpers.sol
|
|
|
|
pragma solidity 0.4.18;
|
|
|
|
|
|
library ScriptHelpers {
|
|
// To test with JS and compare with actual encoder. Maintaining for reference.
|
|
// t = function() { return IEVMScriptExecutor.at('0x4bcdd59d6c77774ee7317fc1095f69ec84421e49').contract.execScript.getData(...[].slice.call(arguments)).slice(10).match(/.{1,64}/g) }
|
|
// run = function() { return ScriptHelpers.new().then(sh => { sh.abiEncode.call(...[].slice.call(arguments)).then(a => console.log(a.slice(2).match(/.{1,64}/g)) ) }) }
|
|
// This is truly not beautiful but lets no daydream to the day solidity gets reflection features
|
|
|
|
function abiEncode(bytes _a, bytes _b, address[] _c) public pure returns (bytes d) {
|
|
return encode(_a, _b, _c);
|
|
}
|
|
|
|
function encode(bytes memory _a, bytes memory _b, address[] memory _c) internal pure returns (bytes memory d) {
|
|
// A is positioned after the 3 position words
|
|
uint256 aPosition = 0x60;
|
|
uint256 bPosition = aPosition + 32 * abiLength(_a);
|
|
uint256 cPosition = bPosition + 32 * abiLength(_b);
|
|
uint256 length = cPosition + 32 * abiLength(_c);
|
|
|
|
d = new bytes(length);
|
|
assembly {
|
|
// Store positions
|
|
mstore(add(d, 0x20), aPosition)
|
|
mstore(add(d, 0x40), bPosition)
|
|
mstore(add(d, 0x60), cPosition)
|
|
}
|
|
|
|
// Copy memory to correct position
|
|
copy(d, getPtr(_a), aPosition, _a.length);
|
|
copy(d, getPtr(_b), bPosition, _b.length);
|
|
copy(d, getPtr(_c), cPosition, _c.length * 32); // 1 word per address
|
|
}
|
|
|
|
function abiLength(bytes memory _a) internal pure returns (uint256) {
|
|
// 1 for length +
|
|
// memory words + 1 if not divisible for 32 to offset word
|
|
return 1 + (_a.length / 32) + (_a.length % 32 > 0 ? 1 : 0);
|
|
}
|
|
|
|
function abiLength(address[] _a) internal pure returns (uint256) {
|
|
// 1 for length + 1 per item
|
|
return 1 + _a.length;
|
|
}
|
|
|
|
function copy(bytes _d, uint256 _src, uint256 _pos, uint256 _length) internal pure {
|
|
uint dest;
|
|
assembly {
|
|
dest := add(add(_d, 0x20), _pos)
|
|
}
|
|
memcpy(dest, _src, _length + 32);
|
|
}
|
|
|
|
function getPtr(bytes memory _x) internal pure returns (uint256 ptr) {
|
|
assembly {
|
|
ptr := _x
|
|
}
|
|
}
|
|
|
|
function getPtr(address[] memory _x) internal pure returns (uint256 ptr) {
|
|
assembly {
|
|
ptr := _x
|
|
}
|
|
}
|
|
|
|
function getSpecId(bytes _script) internal pure returns (uint32) {
|
|
return uint32At(_script, 0);
|
|
}
|
|
|
|
function uint256At(bytes _data, uint256 _location) internal pure returns (uint256 result) {
|
|
assembly {
|
|
result := mload(add(_data, add(0x20, _location)))
|
|
}
|
|
}
|
|
|
|
function addressAt(bytes _data, uint256 _location) internal pure returns (address result) {
|
|
uint256 word = uint256At(_data, _location);
|
|
|
|
assembly {
|
|
result := div(and(word, 0xffffffffffffffffffffffffffffffffffffffff000000000000000000000000),
|
|
0x1000000000000000000000000)
|
|
}
|
|
}
|
|
|
|
function uint32At(bytes _data, uint256 _location) internal pure returns (uint32 result) {
|
|
uint256 word = uint256At(_data, _location);
|
|
|
|
assembly {
|
|
result := div(and(word, 0xffffffff00000000000000000000000000000000000000000000000000000000),
|
|
0x100000000000000000000000000000000000000000000000000000000)
|
|
}
|
|
}
|
|
|
|
function locationOf(bytes _data, uint256 _location) internal pure returns (uint256 result) {
|
|
assembly {
|
|
result := add(_data, add(0x20, _location))
|
|
}
|
|
}
|
|
|
|
function toBytes(bytes4 _sig) internal pure returns (bytes) {
|
|
bytes memory payload = new bytes(4);
|
|
payload[0] = bytes1(_sig);
|
|
payload[1] = bytes1(_sig << 8);
|
|
payload[2] = bytes1(_sig << 16);
|
|
payload[3] = bytes1(_sig << 24);
|
|
return payload;
|
|
}
|
|
|
|
function memcpy(uint _dest, uint _src, uint _len) public pure {
|
|
uint256 src = _src;
|
|
uint256 dest = _dest;
|
|
uint256 len = _len;
|
|
|
|
// Copy word-length chunks while possible
|
|
for (; len >= 32; len -= 32) {
|
|
assembly {
|
|
mstore(dest, mload(src))
|
|
}
|
|
dest += 32;
|
|
src += 32;
|
|
}
|
|
|
|
// Copy remaining bytes
|
|
uint mask = 256 ** (32 - len) - 1;
|
|
assembly {
|
|
let srcpart := and(mload(src), not(mask))
|
|
let destpart := and(mload(dest), mask)
|
|
mstore(dest, or(destpart, srcpart))
|
|
}
|
|
}
|
|
}
|
|
|
|
///File: @aragon/os/contracts/evmscript/EVMScriptRunner.sol
|
|
|
|
pragma solidity ^0.4.18;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
contract EVMScriptRunner is AppStorage, EVMScriptRegistryConstants {
|
|
using ScriptHelpers for bytes;
|
|
|
|
function runScript(bytes _script, bytes _input, address[] _blacklist) protectState internal returns (bytes output) {
|
|
// TODO: Too much data flying around, maybe extracting spec id here is cheaper
|
|
address executorAddr = getExecutor(_script);
|
|
require(executorAddr != address(0));
|
|
|
|
bytes memory calldataArgs = _script.encode(_input, _blacklist);
|
|
bytes4 sig = IEVMScriptExecutor(0).execScript.selector;
|
|
|
|
require(executorAddr.delegatecall(sig, calldataArgs));
|
|
|
|
return returnedDataDecoded();
|
|
}
|
|
|
|
function getExecutor(bytes _script) public view returns (IEVMScriptExecutor) {
|
|
return IEVMScriptExecutor(getExecutorRegistry().getScriptExecutor(_script));
|
|
}
|
|
|
|
// TODO: Internal
|
|
function getExecutorRegistry() internal view returns (IEVMScriptRegistry) {
|
|
address registryAddr = kernel.getApp(EVMSCRIPT_REGISTRY_APP);
|
|
return IEVMScriptRegistry(registryAddr);
|
|
}
|
|
|
|
/**
|
|
* @dev copies and returns last's call data. Needs to ABI decode first
|
|
*/
|
|
function returnedDataDecoded() internal view returns (bytes ret) {
|
|
assembly {
|
|
let size := returndatasize
|
|
switch size
|
|
case 0 {}
|
|
default {
|
|
ret := mload(0x40) // free mem ptr get
|
|
mstore(0x40, add(ret, add(size, 0x20))) // free mem ptr set
|
|
returndatacopy(ret, 0x20, sub(size, 0x20)) // copy return data
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
modifier protectState {
|
|
address preKernel = kernel;
|
|
bytes32 preAppId = appId;
|
|
_; // exec
|
|
require(kernel == preKernel);
|
|
require(appId == preAppId);
|
|
}
|
|
}
|
|
|
|
///File: @aragon/os/contracts/acl/ACLSyntaxSugar.sol
|
|
|
|
pragma solidity 0.4.18;
|
|
|
|
|
|
contract ACLSyntaxSugar {
|
|
function arr() internal pure returns (uint256[] r) {}
|
|
|
|
function arr(bytes32 _a) internal pure returns (uint256[] r) {
|
|
return arr(uint256(_a));
|
|
}
|
|
|
|
function arr(bytes32 _a, bytes32 _b) internal pure returns (uint256[] r) {
|
|
return arr(uint256(_a), uint256(_b));
|
|
}
|
|
|
|
function arr(address _a) internal pure returns (uint256[] r) {
|
|
return arr(uint256(_a));
|
|
}
|
|
|
|
function arr(address _a, address _b) internal pure returns (uint256[] r) {
|
|
return arr(uint256(_a), uint256(_b));
|
|
}
|
|
|
|
function arr(address _a, uint256 _b, uint256 _c) internal pure returns (uint256[] r) {
|
|
return arr(uint256(_a), _b, _c);
|
|
}
|
|
|
|
function arr(address _a, uint256 _b) internal pure returns (uint256[] r) {
|
|
return arr(uint256(_a), uint256(_b));
|
|
}
|
|
|
|
function arr(address _a, address _b, uint256 _c, uint256 _d, uint256 _e) internal pure returns (uint256[] r) {
|
|
return arr(uint256(_a), uint256(_b), _c, _d, _e);
|
|
}
|
|
|
|
function arr(address _a, address _b, address _c) internal pure returns (uint256[] r) {
|
|
return arr(uint256(_a), uint256(_b), uint256(_c));
|
|
}
|
|
|
|
function arr(address _a, address _b, uint256 _c) internal pure returns (uint256[] r) {
|
|
return arr(uint256(_a), uint256(_b), uint256(_c));
|
|
}
|
|
|
|
function arr(uint256 _a) internal pure returns (uint256[] r) {
|
|
r = new uint256[](1);
|
|
r[0] = _a;
|
|
}
|
|
|
|
function arr(uint256 _a, uint256 _b) internal pure returns (uint256[] r) {
|
|
r = new uint256[](2);
|
|
r[0] = _a;
|
|
r[1] = _b;
|
|
}
|
|
|
|
function arr(uint256 _a, uint256 _b, uint256 _c) internal pure returns (uint256[] r) {
|
|
r = new uint256[](3);
|
|
r[0] = _a;
|
|
r[1] = _b;
|
|
r[2] = _c;
|
|
}
|
|
|
|
function arr(uint256 _a, uint256 _b, uint256 _c, uint256 _d) internal pure returns (uint256[] r) {
|
|
r = new uint256[](4);
|
|
r[0] = _a;
|
|
r[1] = _b;
|
|
r[2] = _c;
|
|
r[3] = _d;
|
|
}
|
|
|
|
function arr(uint256 _a, uint256 _b, uint256 _c, uint256 _d, uint256 _e) internal pure returns (uint256[] r) {
|
|
r = new uint256[](5);
|
|
r[0] = _a;
|
|
r[1] = _b;
|
|
r[2] = _c;
|
|
r[3] = _d;
|
|
r[4] = _e;
|
|
}
|
|
}
|
|
|
|
|
|
contract ACLHelpers {
|
|
function decodeParamOp(uint256 _x) internal pure returns (uint8 b) {
|
|
return uint8(_x >> (8 * 30));
|
|
}
|
|
|
|
function decodeParamId(uint256 _x) internal pure returns (uint8 b) {
|
|
return uint8(_x >> (8 * 31));
|
|
}
|
|
|
|
function decodeParamsList(uint256 _x) internal pure returns (uint32 a, uint32 b, uint32 c) {
|
|
a = uint32(_x);
|
|
b = uint32(_x >> (8 * 4));
|
|
c = uint32(_x >> (8 * 8));
|
|
}
|
|
}
|
|
|
|
|
|
///File: @aragon/os/contracts/apps/AragonApp.sol
|
|
|
|
pragma solidity ^0.4.18;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
contract AragonApp is AppStorage, Initializable, ACLSyntaxSugar, EVMScriptRunner {
|
|
modifier auth(bytes32 _role) {
|
|
require(canPerform(msg.sender, _role, new uint256[](0)));
|
|
_;
|
|
}
|
|
|
|
modifier authP(bytes32 _role, uint256[] params) {
|
|
require(canPerform(msg.sender, _role, params));
|
|
_;
|
|
}
|
|
|
|
function canPerform(address _sender, bytes32 _role, uint256[] params) public view returns (bool) {
|
|
bytes memory how; // no need to init memory as it is never used
|
|
if (params.length > 0) {
|
|
uint256 byteLength = params.length * 32;
|
|
assembly {
|
|
how := params // forced casting
|
|
mstore(how, byteLength)
|
|
}
|
|
}
|
|
return address(kernel) == 0 || kernel.hasPermission(_sender, address(this), _role, how);
|
|
}
|
|
}
|
|
|
|
|
|
///File: ./contracts/ILiquidPledgingPlugin.sol
|
|
|
|
pragma solidity ^0.4.11;
|
|
|
|
/*
|
|
Copyright 2017, Jordi Baylina
|
|
Contributors: Adrià Massanet <adria@codecontext.io>, RJ Ewing, Griff
|
|
Green, Arthur Lunn
|
|
|
|
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/>.
|
|
*/
|
|
|
|
|
|
/// @dev `ILiquidPledgingPlugin` is the basic interface for any
|
|
/// liquid pledging plugin
|
|
contract ILiquidPledgingPlugin {
|
|
|
|
/// @notice Plugins are used (much like web hooks) to initiate an action
|
|
/// upon any donation, delegation, or transfer; this is an optional feature
|
|
/// and allows for extreme customization of the contract. This function
|
|
/// implements any action that should be initiated before a transfer.
|
|
/// @param pledgeManager The admin or current manager of the pledge
|
|
/// @param pledgeFrom This is the Id from which value will be transfered.
|
|
/// @param pledgeTo This is the Id that value will be transfered to.
|
|
/// @param context The situation that is triggering the plugin:
|
|
/// 0 -> Plugin for the owner transferring pledge to another party
|
|
/// 1 -> Plugin for the first delegate transferring pledge to another party
|
|
/// 2 -> Plugin for the second delegate transferring pledge to another party
|
|
/// ...
|
|
/// 255 -> Plugin for the intendedProject transferring pledge to another party
|
|
///
|
|
/// 256 -> Plugin for the owner receiving pledge to another party
|
|
/// 257 -> Plugin for the first delegate receiving pledge to another party
|
|
/// 258 -> Plugin for the second delegate receiving pledge to another party
|
|
/// ...
|
|
/// 511 -> Plugin for the intendedProject receiving pledge to another party
|
|
/// @param amount The amount of value that will be transfered.
|
|
function beforeTransfer(
|
|
uint64 pledgeManager,
|
|
uint64 pledgeFrom,
|
|
uint64 pledgeTo,
|
|
uint64 context,
|
|
address token,
|
|
uint amount ) public returns (uint maxAllowed);
|
|
|
|
/// @notice Plugins are used (much like web hooks) to initiate an action
|
|
/// upon any donation, delegation, or transfer; this is an optional feature
|
|
/// and allows for extreme customization of the contract. This function
|
|
/// implements any action that should be initiated after a transfer.
|
|
/// @param pledgeManager The admin or current manager of the pledge
|
|
/// @param pledgeFrom This is the Id from which value will be transfered.
|
|
/// @param pledgeTo This is the Id that value will be transfered to.
|
|
/// @param context The situation that is triggering the plugin:
|
|
/// 0 -> Plugin for the owner transferring pledge to another party
|
|
/// 1 -> Plugin for the first delegate transferring pledge to another party
|
|
/// 2 -> Plugin for the second delegate transferring pledge to another party
|
|
/// ...
|
|
/// 255 -> Plugin for the intendedProject transferring pledge to another party
|
|
///
|
|
/// 256 -> Plugin for the owner receiving pledge to another party
|
|
/// 257 -> Plugin for the first delegate receiving pledge to another party
|
|
/// 258 -> Plugin for the second delegate receiving pledge to another party
|
|
/// ...
|
|
/// 511 -> Plugin for the intendedProject receiving pledge to another party
|
|
/// @param amount The amount of value that will be transfered.
|
|
function afterTransfer(
|
|
uint64 pledgeManager,
|
|
uint64 pledgeFrom,
|
|
uint64 pledgeTo,
|
|
uint64 context,
|
|
address token,
|
|
uint amount
|
|
) public;
|
|
}
|
|
|
|
|
|
///File: ./contracts/LiquidPledgingStorage.sol
|
|
|
|
pragma solidity ^0.4.18;
|
|
|
|
|
|
|
|
/// @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 ILPVault {
|
|
function authorizePayment(bytes32 _ref, address _dest, address _token, uint _amount) public;
|
|
}
|
|
|
|
/// This contract contains all state variables used in LiquidPledging contracts
|
|
/// This is done to have everything in 1 location, b/c state variable layout
|
|
/// is MUST have be the same when performing an upgrade.
|
|
contract LiquidPledgingStorage {
|
|
enum PledgeAdminType { Giver, Delegate, Project }
|
|
enum PledgeState { Pledged, Paying, Paid }
|
|
|
|
/// @dev This struct defines the details of a `PledgeAdmin` which are
|
|
/// commonly referenced by their index in the `admins` array
|
|
/// and can own pledges and act as delegates
|
|
struct PledgeAdmin {
|
|
PledgeAdminType adminType; // Giver, Delegate or Project
|
|
address addr; // Account or contract address for admin
|
|
uint64 commitTime; // In seconds, used for time Givers' & Delegates' have to veto
|
|
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;
|
|
string name;
|
|
string url; // Can be IPFS hash
|
|
}
|
|
|
|
struct Pledge {
|
|
uint amount;
|
|
uint64[] delegationChain; // List of delegates in order of authority
|
|
uint64 owner; // PledgeAdmin
|
|
uint64 intendedProject; // Used when delegates are sending to projects
|
|
uint64 commitTime; // When the intendedProject will become the owner
|
|
uint64 oldPledge; // Points to the id that this Pledge was derived from
|
|
address token;
|
|
PledgeState pledgeState; // Pledged, Paying, Paid
|
|
}
|
|
|
|
PledgeAdmin[] admins; //The list of pledgeAdmins 0 means there is no admin
|
|
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;
|
|
|
|
// this whitelist is for non-proxied plugins
|
|
mapping (bytes32 => bool) pluginContractWhitelist;
|
|
// this whitelist is for proxied plugins
|
|
mapping (address => bool) pluginInstanceWhitelist;
|
|
bool public whitelistDisabled = false;
|
|
|
|
ILPVault public vault;
|
|
|
|
// reserve 50 slots for future upgrades. I'm not sure if this is necessary
|
|
// but b/c of multiple inheritance used in lp, better safe then sorry.
|
|
// especially since it is free
|
|
uint[50] private storageOffset;
|
|
}
|
|
|
|
///File: ./contracts/LiquidPledgingACLHelpers.sol
|
|
|
|
pragma solidity ^0.4.18;
|
|
|
|
contract LiquidPledgingACLHelpers {
|
|
function arr(uint64 a, uint64 b, address c, uint d, address e) internal pure returns(uint[] r) {
|
|
r = new uint[](4);
|
|
r[0] = uint(a);
|
|
r[1] = uint(b);
|
|
r[2] = uint(c);
|
|
r[3] = d;
|
|
r[4] = uint(e);
|
|
}
|
|
|
|
function arr(bool a) internal pure returns (uint[] r) {
|
|
r = new uint[](1);
|
|
uint _a;
|
|
assembly {
|
|
_a := a // forced casting
|
|
}
|
|
r[0] = _a;
|
|
}
|
|
}
|
|
|
|
///File: ./contracts/LiquidPledgingPlugins.sol
|
|
|
|
pragma solidity ^0.4.18;
|
|
|
|
/*
|
|
Copyright 2017, Jordi Baylina, RJ Ewing
|
|
Contributors: Adrià Massanet <adria@codecontext.io>, Griff Green,
|
|
Arthur Lunn
|
|
|
|
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/>.
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
contract LiquidPledgingPlugins is AragonApp, LiquidPledgingStorage, LiquidPledgingACLHelpers {
|
|
|
|
bytes32 constant public PLUGIN_MANAGER_ROLE = keccak256("PLUGIN_MANAGER_ROLE");
|
|
|
|
function addValidPluginInstance(address addr) auth(PLUGIN_MANAGER_ROLE) public {
|
|
pluginInstanceWhitelist[addr] = true;
|
|
}
|
|
|
|
function addValidPluginContract(bytes32 contractHash) auth(PLUGIN_MANAGER_ROLE) public {
|
|
pluginContractWhitelist[contractHash] = true;
|
|
}
|
|
|
|
function addValidPluginContracts(bytes32[] contractHashes) external auth(PLUGIN_MANAGER_ROLE) {
|
|
for (uint8 i = 0; i < contractHashes.length; i++) {
|
|
addValidPluginContract(contractHashes[i]);
|
|
}
|
|
}
|
|
|
|
function removeValidPluginContract(bytes32 contractHash) external authP(PLUGIN_MANAGER_ROLE, arr(contractHash)) {
|
|
pluginContractWhitelist[contractHash] = false;
|
|
}
|
|
|
|
function removeValidPluginInstance(address addr) external auth(PLUGIN_MANAGER_ROLE) {
|
|
pluginInstanceWhitelist[addr] = false;
|
|
}
|
|
|
|
function useWhitelist(bool useWhitelist) external auth(PLUGIN_MANAGER_ROLE) {
|
|
whitelistDisabled = !useWhitelist;
|
|
}
|
|
|
|
function isValidPlugin(address addr) public view returns(bool) {
|
|
if (whitelistDisabled || addr == 0x0) {
|
|
return true;
|
|
}
|
|
|
|
// first check pluginInstances
|
|
if (pluginInstanceWhitelist[addr]) {
|
|
return true;
|
|
}
|
|
|
|
// if the addr isn't a valid instance, check the contract code
|
|
bytes32 contractHash = getCodeHash(addr);
|
|
|
|
return pluginContractWhitelist[contractHash];
|
|
}
|
|
|
|
function getCodeHash(address addr) public view returns(bytes32) {
|
|
bytes memory o_code;
|
|
assembly {
|
|
// retrieve the size of the code, this needs assembly
|
|
let size := extcodesize(addr)
|
|
// allocate output byte array - this could also be done without assembly
|
|
// by using o_code = new bytes(size)
|
|
o_code := mload(0x40)
|
|
mstore(o_code, size) // store length in memory
|
|
// actually retrieve the code, this needs assembly
|
|
extcodecopy(addr, add(o_code, 0x20), 0, size)
|
|
}
|
|
return keccak256(o_code);
|
|
}
|
|
}
|
|
|
|
///File: ./contracts/PledgeAdmins.sol
|
|
|
|
pragma solidity ^0.4.18;
|
|
|
|
/*
|
|
Copyright 2017, Jordi Baylina, RJ Ewing
|
|
Contributors: Adrià Massanet <adria@codecontext.io>, Griff Green,
|
|
Arthur Lunn
|
|
|
|
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/>.
|
|
*/
|
|
|
|
|
|
|
|
contract PledgeAdmins is AragonApp, LiquidPledgingPlugins {
|
|
|
|
// Limits inserted to prevent large loops that could prevent canceling
|
|
uint constant MAX_SUBPROJECT_LEVEL = 20;
|
|
uint constant MAX_INTERPROJECT_LEVEL = 20;
|
|
|
|
// Events
|
|
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);
|
|
|
|
////////////////////
|
|
// Public functions
|
|
////////////////////
|
|
|
|
/// @notice Creates a Giver Admin with the `msg.sender` as the Admin address
|
|
/// @param name The name used to identify the Giver
|
|
/// @param url The link to the Giver's profile often an IPFS hash
|
|
/// @param commitTime The length of time in seconds the Giver has to
|
|
/// veto when the Giver's delegates Pledge funds to a project
|
|
/// @param plugin This is Giver's liquid pledge plugin allowing for
|
|
/// extended functionality
|
|
/// @return idGiver The id number used to reference this Admin
|
|
function addGiver(
|
|
string name,
|
|
string url,
|
|
uint64 commitTime,
|
|
ILiquidPledgingPlugin plugin
|
|
) public returns (uint64 idGiver)
|
|
{
|
|
return addGiver(
|
|
msg.sender,
|
|
name,
|
|
url,
|
|
commitTime,
|
|
plugin
|
|
);
|
|
}
|
|
|
|
// TODO: is there an issue w/ allowing anyone to create a giver on behalf of another addy?
|
|
function addGiver(
|
|
address addr,
|
|
string name,
|
|
string url,
|
|
uint64 commitTime,
|
|
ILiquidPledgingPlugin plugin
|
|
) public returns (uint64 idGiver)
|
|
{
|
|
require(isValidPlugin(plugin)); // Plugin check
|
|
|
|
idGiver = uint64(admins.length);
|
|
|
|
// Save the fields
|
|
admins.push(
|
|
PledgeAdmin(
|
|
PledgeAdminType.Giver,
|
|
addr,
|
|
commitTime,
|
|
0,
|
|
false,
|
|
plugin,
|
|
name,
|
|
url)
|
|
);
|
|
|
|
GiverAdded(idGiver);
|
|
}
|
|
|
|
/// @notice Updates a Giver's info to change the address, name, url, or
|
|
/// commitTime, it cannot be used to change a plugin, and it must be called
|
|
/// by the current address of the Giver
|
|
/// @param idGiver This is the Admin id number used to specify the Giver
|
|
/// @param newAddr The new address that represents this Giver
|
|
/// @param newName The new name used to identify the Giver
|
|
/// @param newUrl The new link to the Giver's profile often an IPFS hash
|
|
/// @param newCommitTime Sets the length of time in seconds the Giver has to
|
|
/// veto when the Giver's delegates Pledge funds to a project
|
|
function updateGiver(
|
|
uint64 idGiver,
|
|
address newAddr,
|
|
string newName,
|
|
string newUrl,
|
|
uint64 newCommitTime
|
|
) public
|
|
{
|
|
PledgeAdmin storage giver = _findAdmin(idGiver);
|
|
require(msg.sender == giver.addr);
|
|
require(giver.adminType == PledgeAdminType.Giver); // Must be a Giver
|
|
giver.addr = newAddr;
|
|
giver.name = newName;
|
|
giver.url = newUrl;
|
|
giver.commitTime = newCommitTime;
|
|
|
|
GiverUpdated(idGiver);
|
|
}
|
|
|
|
/// @notice Creates a Delegate Admin with the `msg.sender` as the Admin addr
|
|
/// @param name The name used to identify the Delegate
|
|
/// @param url The link to the Delegate's profile often an IPFS hash
|
|
/// @param commitTime Sets the length of time in seconds that this delegate
|
|
/// can be vetoed. Whenever this delegate is in a delegate chain the time
|
|
/// allowed to veto any event must be greater than or equal to this time.
|
|
/// @param plugin This is Delegate's liquid pledge plugin allowing for
|
|
/// extended functionality
|
|
/// @return idxDelegate The id number used to reference this Delegate within
|
|
/// the PLEDGE_ADMIN array
|
|
function addDelegate(
|
|
string name,
|
|
string url,
|
|
uint64 commitTime,
|
|
ILiquidPledgingPlugin plugin
|
|
) public returns (uint64 idDelegate)
|
|
{
|
|
require(isValidPlugin(plugin)); // Plugin check
|
|
|
|
idDelegate = uint64(admins.length);
|
|
|
|
admins.push(
|
|
PledgeAdmin(
|
|
PledgeAdminType.Delegate,
|
|
msg.sender,
|
|
commitTime,
|
|
0,
|
|
false,
|
|
plugin,
|
|
name,
|
|
url)
|
|
);
|
|
|
|
DelegateAdded(idDelegate);
|
|
}
|
|
|
|
/// @notice Updates a Delegate's info to change the address, name, url, or
|
|
/// commitTime, it cannot be used to change a plugin, and it must be called
|
|
/// by the current address of the Delegate
|
|
/// @param idDelegate The Admin id number used to specify the Delegate
|
|
/// @param newAddr The new address that represents this Delegate
|
|
/// @param newName The new name used to identify the Delegate
|
|
/// @param newUrl The new link to the Delegate's profile often an IPFS hash
|
|
/// @param newCommitTime Sets the length of time in seconds that this
|
|
/// delegate can be vetoed. Whenever this delegate is in a delegate chain
|
|
/// the time allowed to veto any event must be greater than or equal to
|
|
/// this time.
|
|
function updateDelegate(
|
|
uint64 idDelegate,
|
|
address newAddr,
|
|
string newName,
|
|
string newUrl,
|
|
uint64 newCommitTime
|
|
) public
|
|
{
|
|
PledgeAdmin storage delegate = _findAdmin(idDelegate);
|
|
require(msg.sender == delegate.addr);
|
|
require(delegate.adminType == PledgeAdminType.Delegate);
|
|
delegate.addr = newAddr;
|
|
delegate.name = newName;
|
|
delegate.url = newUrl;
|
|
delegate.commitTime = newCommitTime;
|
|
|
|
DelegateUpdated(idDelegate);
|
|
}
|
|
|
|
/// @notice Creates a Project Admin with the `msg.sender` as the Admin addr
|
|
/// @param name The name used to identify the Project
|
|
/// @param url The link to the Project's profile often an IPFS hash
|
|
/// @param projectAdmin The address for the trusted project manager
|
|
/// @param parentProject The Admin id number for the parent project or 0 if
|
|
/// there is no parentProject
|
|
/// @param commitTime Sets the length of time in seconds the Project has to
|
|
/// veto when the Project delegates to another Delegate and they pledge
|
|
/// those funds to a project
|
|
/// @param plugin This is Project's liquid pledge plugin allowing for
|
|
/// extended functionality
|
|
/// @return idProject The id number used to reference this Admin
|
|
function addProject(
|
|
string name,
|
|
string url,
|
|
address projectAdmin,
|
|
uint64 parentProject,
|
|
uint64 commitTime,
|
|
ILiquidPledgingPlugin plugin
|
|
) public returns (uint64 idProject)
|
|
{
|
|
require(isValidPlugin(plugin));
|
|
|
|
if (parentProject != 0) {
|
|
PledgeAdmin storage a = _findAdmin(parentProject);
|
|
// getProjectLevel will check that parentProject has a `Project` adminType
|
|
require(_getProjectLevel(a) < MAX_SUBPROJECT_LEVEL);
|
|
}
|
|
|
|
idProject = uint64(admins.length);
|
|
|
|
admins.push(
|
|
PledgeAdmin(
|
|
PledgeAdminType.Project,
|
|
projectAdmin,
|
|
commitTime,
|
|
parentProject,
|
|
false,
|
|
plugin,
|
|
name,
|
|
url)
|
|
);
|
|
|
|
ProjectAdded(idProject);
|
|
}
|
|
|
|
/// @notice Updates a Project's info to change the address, name, url, or
|
|
/// commitTime, it cannot be used to change a plugin or a parentProject,
|
|
/// and it must be called by the current address of the Project
|
|
/// @param idProject The Admin id number used to specify the Project
|
|
/// @param newAddr The new address that represents this Project
|
|
/// @param newName The new name used to identify the Project
|
|
/// @param newUrl The new link to the Project's profile often an IPFS hash
|
|
/// @param newCommitTime Sets the length of time in seconds the Project has
|
|
/// to veto when the Project delegates to a Delegate and they pledge those
|
|
/// funds to a project
|
|
function updateProject(
|
|
uint64 idProject,
|
|
address newAddr,
|
|
string newName,
|
|
string newUrl,
|
|
uint64 newCommitTime
|
|
) public
|
|
{
|
|
PledgeAdmin storage project = _findAdmin(idProject);
|
|
|
|
require(msg.sender == project.addr);
|
|
require(project.adminType == PledgeAdminType.Project);
|
|
|
|
project.addr = newAddr;
|
|
project.name = newName;
|
|
project.url = newUrl;
|
|
project.commitTime = newCommitTime;
|
|
|
|
ProjectUpdated(idProject);
|
|
}
|
|
|
|
/////////////////////////////
|
|
// Public constant functions
|
|
/////////////////////////////
|
|
|
|
/// @notice A constant getter used to check how many total Admins exist
|
|
/// @return The total number of admins (Givers, Delegates and Projects) .
|
|
function numberOfPledgeAdmins() public constant returns(uint) {
|
|
return admins.length - 1;
|
|
}
|
|
|
|
/// @notice A constant getter to check the details of a specified Admin
|
|
/// @return addr Account or contract address for admin
|
|
/// @return name Name of the pledgeAdmin
|
|
/// @return url The link to the Project's profile often an IPFS hash
|
|
/// @return commitTime The length of time in seconds the Admin has to veto
|
|
/// when the Admin delegates to a Delegate and that Delegate pledges those
|
|
/// funds to a project
|
|
/// @return parentProject The Admin id number for the parent project or 0
|
|
/// if there is no parentProject
|
|
/// @return canceled 0 for Delegates & Givers, true if a Project has been
|
|
/// canceled
|
|
/// @return plugin This is Project's liquidPledging plugin allowing for
|
|
/// extended functionality
|
|
function getPledgeAdmin(uint64 idAdmin) public view returns (
|
|
PledgeAdminType adminType,
|
|
address addr,
|
|
string name,
|
|
string url,
|
|
uint64 commitTime,
|
|
uint64 parentProject,
|
|
bool canceled,
|
|
address plugin
|
|
) {
|
|
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);
|
|
}
|
|
|
|
/// @notice A getter to find if a specified Project has been canceled
|
|
/// @param projectId The Admin id number used to specify the Project
|
|
/// @return True if the Project has been canceled
|
|
function isProjectCanceled(uint64 projectId)
|
|
public constant returns (bool)
|
|
{
|
|
PledgeAdmin storage a = _findAdmin(projectId);
|
|
|
|
if (a.adminType == PledgeAdminType.Giver) {
|
|
return false;
|
|
}
|
|
|
|
assert(a.adminType == PledgeAdminType.Project);
|
|
|
|
if (a.canceled) {
|
|
return true;
|
|
}
|
|
if (a.parentProject == 0) {
|
|
return false;
|
|
}
|
|
|
|
return isProjectCanceled(a.parentProject);
|
|
}
|
|
|
|
///////////////////
|
|
// 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 view returns (PledgeAdmin storage) {
|
|
require(idAdmin < admins.length);
|
|
return admins[idAdmin];
|
|
}
|
|
|
|
/// @notice Find the level of authority a specific Project has
|
|
/// using a recursive loop
|
|
/// @param a The project admin being queried
|
|
/// @return The level of authority a specific Project has
|
|
function _getProjectLevel(PledgeAdmin a) internal returns(uint64) {
|
|
assert(a.adminType == PledgeAdminType.Project);
|
|
|
|
if (a.parentProject == 0) {
|
|
return(1);
|
|
}
|
|
|
|
PledgeAdmin storage parent = _findAdmin(a.parentProject);
|
|
return _getProjectLevel(parent) + 1;
|
|
}
|
|
} |