///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/common/IVaultRecoverable.sol
pragma solidity ^0.4.18;
interface IVaultRecoverable {
function transferToVault(address token) external;
function allowRecoverability(address token) public view returns (bool);
function getRecoveryVault() public view returns (address);
}
///File: @aragon/os/contracts/kernel/IKernel.sol
pragma solidity ^0.4.18;
// This should be an interface, but interfaces can't inherit yet :(
contract IKernel is IVaultRecoverable {
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/kernel/KernelStorage.sol
pragma solidity 0.4.18;
contract KernelConstants {
/*
bytes32 constant public CORE_NAMESPACE = keccak256("core");
bytes32 constant public APP_BASES_NAMESPACE = keccak256("base");
bytes32 constant public APP_ADDR_NAMESPACE = keccak256("app");
bytes32 constant public KERNEL_APP_ID = apmNamehash("kernel");
bytes32 constant public KERNEL_APP = keccak256(CORE_NAMESPACE, KERNEL_APP_ID);
bytes32 constant public ACL_APP_ID = apmNamehash("acl");
bytes32 constant public ACL_APP = keccak256(APP_ADDR_NAMESPACE, ACL_APP_ID);
*/
bytes32 constant public CORE_NAMESPACE = 0xc681a85306374a5ab27f0bbc385296a54bcd314a1948b6cf61c4ea1bc44bb9f8;
bytes32 constant public APP_BASES_NAMESPACE = 0xf1f3eb40f5bc1ad1344716ced8b8a0431d840b5783aea1fd01786bc26f35ac0f;
bytes32 constant public APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb;
bytes32 constant public ETH_NODE = 0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae;
bytes32 constant public APM_NODE = 0x9065c3e7f7b7ef1ef4e53d2d0b8e0cef02874ab020c1ece79d5f0d3d0111c0ba;
bytes32 constant public KERNEL_APP_ID = 0x3b4bf6bf3ad5000ecf0f989d5befde585c6860fea3e574a4fab4c49d1c177d9c;
bytes32 constant public KERNEL_APP = 0x2b7d19d0575c228f8d9326801e14149d284dc5bb7b1541c5ad712ae4b2fcaadb;
bytes32 constant public ACL_APP_ID = 0xe3262375f45a6e2026b7e7b18c2b807434f2508fe1a2a3dfb493c7df8f4aad6a;
bytes32 constant public ACL_APP = 0x4b8e03a458a6ccec5d9077c2490964c1333dd3c72e2db408d7d9a7a36ef5c41a;
}
contract KernelStorage is KernelConstants {
mapping (bytes32 => address) public apps;
bytes32 public recoveryVaultId;
}
///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/lib/misc/ERCProxy.sol
pragma solidity ^0.4.18;
contract ERCProxy {
uint256 constant public FORWARDING = 1;
uint256 constant public UPGRADEABLE = 2;
function proxyType() public pure returns (uint256 proxyTypeId);
function implementation() public view returns (address codeAddr);
}
///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);
_;
}
modifier isInitialized {
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/common/IsContract.sol
pragma solidity ^0.4.18;
contract IsContract {
function isContract(address _target) internal view returns (bool) {
if (_target == address(0)) {
return false;
}
uint256 size;
assembly { size := extcodesize(_target) }
return size > 0;
}
}
///File: @aragon/os/contracts/common/EtherTokenConstant.sol
pragma solidity ^0.4.18;
// aragonOS and aragon-apps rely on address(0) to denote native ETH, in
// contracts where both tokens and ETH are accepted
contract EtherTokenConstant {
address constant public ETH = address(0);
}
///File: @aragon/os/contracts/lib/zeppelin/token/ERC20Basic.sol
pragma solidity ^0.4.11;
/**
* @title ERC20Basic
* @dev Simpler version of ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/179
*/
contract ERC20Basic {
function totalSupply() public view returns (uint256);
function balanceOf(address who) public constant returns (uint256);
function transfer(address to, uint256 value) public returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
}
///File: @aragon/os/contracts/lib/zeppelin/token/ERC20.sol
pragma solidity ^0.4.11;
import './ERC20Basic.sol';
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20 is ERC20Basic {
function allowance(address owner, address spender) public constant returns (uint256);
function transferFrom(address from, address to, uint256 value) public returns (bool);
function approve(address spender, uint256 value) public returns (bool);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
///File: @aragon/os/contracts/common/VaultRecoverable.sol
pragma solidity ^0.4.18;
contract VaultRecoverable is IVaultRecoverable, EtherTokenConstant, IsContract {
/**
* @notice Send funds to recovery Vault. This contract should never receive funds,
* but in case it does, this function allows one to recover them.
* @param _token Token balance to be sent to recovery vault.
*/
function transferToVault(address _token) external {
require(allowRecoverability(_token));
address vault = getRecoveryVault();
require(isContract(vault));
if (_token == ETH) {
vault.transfer(this.balance);
} else {
uint256 amount = ERC20(_token).balanceOf(this);
ERC20(_token).transfer(vault, amount);
}
}
/**
* @dev By default deriving from AragonApp makes it recoverable
* @param token Token address that would be recovered
* @return bool whether the app allows the recovery
*/
function allowRecoverability(address token) public view returns (bool) {
return true;
}
}
///File: @aragon/os/contracts/common/DelegateProxy.sol
pragma solidity 0.4.18;
contract DelegateProxy is ERCProxy, IsContract {
uint256 constant public FWD_GAS_LIMIT = 10000;
/**
* @dev Performs a delegatecall and returns whatever the delegatecall returned (entire context execution will return!)
* @param _dst Destination address to perform the delegatecall
* @param _calldata Calldata for the delegatecall
*/
function delegatedFwd(address _dst, bytes _calldata) internal {
delegatedFwd(_dst, _calldata, 0);
}
/**
* @dev Performs a delegatecall and returns whatever the delegatecall returned (entire context execution will return!)
* @param _dst Destination address to perform the delegatecall
* @param _calldata Calldata for the delegatecall
* @param _minReturnSize Minimum size the call needs to return, if less than that it will revert
*/
function delegatedFwd(address _dst, bytes _calldata, uint256 _minReturnSize) internal {
require(isContract(_dst));
uint256 size;
uint256 result;
uint256 fwd_gas_limit = FWD_GAS_LIMIT;
assembly {
result := delegatecall(sub(gas, fwd_gas_limit), _dst, add(_calldata, 0x20), mload(_calldata), 0, 0)
size := returndatasize
}
require(size >= _minReturnSize);
assembly {
let ptr := mload(0x40)
returndatacopy(ptr, 0, size)
// revert instead of invalid() bc if the underlying call failed with invalid() it already wasted gas.
// if the call returned error data, forward it
switch result case 0 { revert(ptr, size) }
default { return(ptr, size) }
}
}
}
///File: @aragon/os/contracts/common/DepositableDelegateProxy.sol
pragma solidity 0.4.18;
contract DepositableDelegateProxy is DelegateProxy {
event ProxyDeposit(address sender, uint256 value);
function () payable public {
// send / transfer
if (msg.gas < FWD_GAS_LIMIT) {
require(msg.value > 0 && msg.data.length == 0);
ProxyDeposit(msg.sender, msg.value);
} else { // all calls except for send or transfer
address target = implementation();
delegatedFwd(target, msg.data);
}
}
}
///File: @aragon/os/contracts/apps/AppProxyBase.sol
pragma solidity 0.4.18;
contract AppProxyBase is AppStorage, DepositableDelegateProxy, KernelConstants {
/**
* @dev Initialize AppProxy
* @param _kernel Reference to organization kernel for the app
* @param _appId Identifier for app
* @param _initializePayload Payload for call to be made after setup to initialize
*/
function AppProxyBase(IKernel _kernel, bytes32 _appId, bytes _initializePayload) public {
kernel = _kernel;
appId = _appId;
// Implicit check that kernel is actually a Kernel
// The EVM doesn't actually provide a way for us to make sure, but we can force a revert to
// occur if the kernel is set to 0x0 or a non-code address when we try to call a method on
// it.
address appCode = getAppBase(appId);
// If initialize payload is provided, it will be executed
if (_initializePayload.length > 0) {
require(isContract(appCode));
// Cannot make delegatecall as a delegateproxy.delegatedFwd as it
// returns ending execution context and halts contract deployment
require(appCode.delegatecall(_initializePayload));
}
}
function getAppBase(bytes32 _appId) internal view returns (address) {
return kernel.getApp(keccak256(APP_BASES_NAMESPACE, _appId));
}
}
///File: @aragon/os/contracts/apps/AppProxyUpgradeable.sol
pragma solidity 0.4.18;
contract AppProxyUpgradeable is AppProxyBase {
/**
* @dev Initialize AppProxyUpgradeable (makes it an upgradeable Aragon app)
* @param _kernel Reference to organization kernel for the app
* @param _appId Identifier for app
* @param _initializePayload Payload for call to be made after setup to initialize
*/
function AppProxyUpgradeable(IKernel _kernel, bytes32 _appId, bytes _initializePayload)
AppProxyBase(_kernel, _appId, _initializePayload) public
{
}
/**
* @dev ERC897, the address the proxy would delegate calls to
*/
function implementation() public view returns (address) {
return getAppBase(appId);
}
/**
* @dev ERC897, whether it is a forwarding (1) or an upgradeable (2) proxy
*/
function proxyType() public pure returns (uint256 proxyTypeId) {
return UPGRADEABLE;
}
}
///File: @aragon/os/contracts/apps/AppProxyPinned.sol
pragma solidity 0.4.18;
contract AppProxyPinned is AppProxyBase {
/**
* @dev Initialize AppProxyPinned (makes it an un-upgradeable Aragon app)
* @param _kernel Reference to organization kernel for the app
* @param _appId Identifier for app
* @param _initializePayload Payload for call to be made after setup to initialize
*/
function AppProxyPinned(IKernel _kernel, bytes32 _appId, bytes _initializePayload)
AppProxyBase(_kernel, _appId, _initializePayload) public
{
pinnedCode = getAppBase(appId);
require(pinnedCode != address(0));
}
/**
* @dev ERC897, the address the proxy would delegate calls to
*/
function implementation() public view returns (address) {
return pinnedCode;
}
/**
* @dev ERC897, whether it is a forwarding (1) or an upgradeable (2) proxy
*/
function proxyType() public pure returns (uint256 proxyTypeId) {
return FORWARDING;
}
}
///File: @aragon/os/contracts/factory/AppProxyFactory.sol
pragma solidity 0.4.18;
contract AppProxyFactory {
event NewAppProxy(address proxy, bool isUpgradeable, bytes32 appId);
function newAppProxy(IKernel _kernel, bytes32 _appId) public returns (AppProxyUpgradeable) {
return newAppProxy(_kernel, _appId, new bytes(0));
}
function newAppProxy(IKernel _kernel, bytes32 _appId, bytes _initializePayload) public returns (AppProxyUpgradeable) {
AppProxyUpgradeable proxy = new AppProxyUpgradeable(_kernel, _appId, _initializePayload);
NewAppProxy(address(proxy), true, _appId);
return proxy;
}
function newAppProxyPinned(IKernel _kernel, bytes32 _appId) public returns (AppProxyPinned) {
return newAppProxyPinned(_kernel, _appId, new bytes(0));
}
function newAppProxyPinned(IKernel _kernel, bytes32 _appId, bytes _initializePayload) public returns (AppProxyPinned) {
AppProxyPinned proxy = new AppProxyPinned(_kernel, _appId, _initializePayload);
NewAppProxy(address(proxy), false, _appId);
return proxy;
}
}
///File: @aragon/os/contracts/kernel/Kernel.sol
pragma solidity 0.4.18;
contract Kernel is IKernel, KernelStorage, Initializable, IsContract, AppProxyFactory, ACLSyntaxSugar, VaultRecoverable {
// Hardocde constant to save gas
//bytes32 constant public APP_MANAGER_ROLE = keccak256("APP_MANAGER_ROLE");
//bytes32 constant public DEFAULT_VAULT_ID = keccak256(APP_ADDR_NAMESPACE, apmNamehash("vault"));
bytes32 constant public APP_MANAGER_ROLE = 0xb6d92708f3d4817afc106147d969e229ced5c46e65e0a5002a0d391287762bd0;
bytes32 constant public DEFAULT_VAULT_ID = 0x4214e5fd6d0170d69ea641b5614f5093ebecc9928af51e95685c87617489800e;
/**
* @dev Initialize can only be called once. It saves the block number in which it was initialized.
* @notice Initializes a kernel instance along with its ACL and sets `_permissionsCreator` as the entity that can create other permissions
* @param _baseAcl Address of base ACL app
* @param _permissionsCreator Entity that will be given permission over createPermission
*/
function initialize(address _baseAcl, address _permissionsCreator) onlyInit public {
initialized();
IACL acl = IACL(newAppProxy(this, ACL_APP_ID));
_setApp(APP_BASES_NAMESPACE, ACL_APP_ID, _baseAcl);
_setApp(APP_ADDR_NAMESPACE, ACL_APP_ID, acl);
acl.initialize(_permissionsCreator);
recoveryVaultId = DEFAULT_VAULT_ID;
}
/**
* @dev Create a new instance of an app linked to this kernel
* @param _name Name of the app
* @param _appBase Address of the app's base implementation
* @return AppProxy instance
*/
function newAppInstance(bytes32 _name, address _appBase) auth(APP_MANAGER_ROLE, arr(APP_BASES_NAMESPACE, _name)) public returns (ERCProxy appProxy) {
return newAppInstance(_name, _appBase, false);
}
/**
* @dev Create a new instance of an app linked to this kernel and set its base
* implementation if it was not already set
* @param _name Name of the app
* @param _appBase Address of the app's base implementation
* @param _setDefault Whether the app proxy app is the default one.
* Useful when the Kernel needs to know of an instance of a particular app,
* like Vault for escape hatch mechanism.
* @return AppProxy instance
*/
function newAppInstance(bytes32 _name, address _appBase, bool _setDefault) auth(APP_MANAGER_ROLE, arr(APP_BASES_NAMESPACE, _name)) public returns (ERCProxy appProxy) {
_setAppIfNew(APP_BASES_NAMESPACE, _name, _appBase);
appProxy = newAppProxy(this, _name);
// By calling setApp directly and not the internal functions, we make sure the params are checked
// and it will only succeed if sender has permissions to set something to the namespace.
if (_setDefault) {
setApp(APP_ADDR_NAMESPACE, _name, appProxy);
}
}
/**
* @dev Create a new pinned instance of an app linked to this kernel
* @param _name Name of the app
* @param _appBase Address of the app's base implementation
* @return AppProxy instance
*/
function newPinnedAppInstance(bytes32 _name, address _appBase) auth(APP_MANAGER_ROLE, arr(APP_BASES_NAMESPACE, _name)) public returns (ERCProxy appProxy) {
return newPinnedAppInstance(_name, _appBase, false);
}
/**
* @dev Create a new pinned instance of an app linked to this kernel and set
* its base implementation if it was not already set
* @param _name Name of the app
* @param _appBase Address of the app's base implementation
* @param _setDefault Whether the app proxy app is the default one.
* Useful when the Kernel needs to know of an instance of a particular app,
* like Vault for escape hatch mechanism.
* @return AppProxy instance
*/
function newPinnedAppInstance(bytes32 _name, address _appBase, bool _setDefault) auth(APP_MANAGER_ROLE, arr(APP_BASES_NAMESPACE, _name)) public returns (ERCProxy appProxy) {
_setAppIfNew(APP_BASES_NAMESPACE, _name, _appBase);
appProxy = newAppProxyPinned(this, _name);
// By calling setApp directly and not the internal functions, we make sure the params are checked
// and it will only succeed if sender has permissions to set something to the namespace.
if (_setDefault) {
setApp(APP_ADDR_NAMESPACE, _name, appProxy);
}
}
/**
* @dev Set the resolving address of an app instance or base implementation
* @param _namespace App namespace to use
* @param _name Name of the app
* @param _app Address of the app
* @return ID of app
*/
function setApp(bytes32 _namespace, bytes32 _name, address _app) auth(APP_MANAGER_ROLE, arr(_namespace, _name)) kernelIntegrity public returns (bytes32 id) {
return _setApp(_namespace, _name, _app);
}
/**
* @dev Get the address of an app instance or base implementation
* @param _id App identifier
* @return Address of the app
*/
function getApp(bytes32 _id) public view returns (address) {
return apps[_id];
}
/**
* @dev Get the address of the recovery Vault instance (to recover funds)
* @return Address of the Vault
*/
function getRecoveryVault() public view returns (address) {
return apps[recoveryVaultId];
}
/**
* @dev Set the default vault id for the escape hatch mechanism
* @param _name Name of the app
*/
function setRecoveryVaultId(bytes32 _name) auth(APP_MANAGER_ROLE, arr(APP_ADDR_NAMESPACE, _name)) public {
recoveryVaultId = keccak256(APP_ADDR_NAMESPACE, _name);
}
/**
* @dev Get the installed ACL app
* @return ACL app
*/
function acl() public view returns (IACL) {
return IACL(getApp(ACL_APP));
}
/**
* @dev Function called by apps to check ACL on kernel or to check permission status
* @param _who Sender of the original call
* @param _where Address of the app
* @param _what Identifier for a group of actions in app
* @param _how Extra data for ACL auth
* @return boolean indicating whether the ACL allows the role or not
*/
function hasPermission(address _who, address _where, bytes32 _what, bytes _how) public view returns (bool) {
return acl().hasPermission(_who, _where, _what, _how);
}
function _setApp(bytes32 _namespace, bytes32 _name, address _app) internal returns (bytes32 id) {
require(isContract(_app));
id = keccak256(_namespace, _name);
apps[id] = _app;
SetApp(_namespace, _name, id, _app);
}
function _setAppIfNew(bytes32 _namespace, bytes32 _name, address _app) internal returns (bytes32 id) {
require(isContract(_app));
id = keccak256(_namespace, _name);
address app = getApp(id);
if (app != address(0)) {
require(app == _app);
} else {
apps[id] = _app;
SetApp(_namespace, _name, id, _app);
}
}
modifier auth(bytes32 _role, uint256[] memory params) {
bytes memory how;
uint256 byteLength = params.length * 32;
assembly {
how := params // forced casting
mstore(how, byteLength)
}
// Params is invalid from this point fwd
require(hasPermission(msg.sender, address(this), _role, how));
_;
}
modifier kernelIntegrity {
_; // After execution check integrity
address kernel = getApp(KERNEL_APP);
uint256 size;
assembly { size := extcodesize(kernel) }
require(size > 0);
}
}
///File: @aragon/os/contracts/kernel/KernelProxy.sol
pragma solidity 0.4.18;
contract KernelProxy is KernelStorage, DepositableDelegateProxy {
/**
* @dev KernelProxy is a proxy contract to a kernel implementation. The implementation
* can update the reference, which effectively upgrades the contract
* @param _kernelImpl Address of the contract used as implementation for kernel
*/
function KernelProxy(address _kernelImpl) public {
apps[keccak256(CORE_NAMESPACE, KERNEL_APP_ID)] = _kernelImpl;
}
/**
* @dev ERC897, whether it is a forwarding (1) or an upgradeable (2) proxy
*/
function proxyType() public pure returns (uint256 proxyTypeId) {
return UPGRADEABLE;
}
/**
* @dev ERC897, the address the proxy would delegate calls to
*/
function implementation() public view returns (address) {
return apps[KERNEL_APP];
}
}
///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);
assembly { mstore(add(payload, 0x20), _sig) }
return payload;
}
function memcpy(uint _dest, uint _src, uint _len) internal 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/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 {
/* Hardcoded constants to save gas
// repeated definitions from KernelStorage, to avoid out of gas issues
bytes32 constant public APP_ADDR_NAMESPACE = keccak256("app");
bytes32 constant public EVMSCRIPT_REGISTRY_APP_ID = apmNamehash("evmreg");
bytes32 constant public EVMSCRIPT_REGISTRY_APP = keccak256(APP_ADDR_NAMESPACE, EVMSCRIPT_REGISTRY_APP_ID);
*/
bytes32 constant public APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb;
bytes32 constant public EVMSCRIPT_REGISTRY_APP_ID = 0xddbcfd564f642ab5627cf68b9b7d374fb4f8a36e941a75d89c87998cef03bd61;
bytes32 constant public EVMSCRIPT_REGISTRY_APP = 0x34f01c17e9be6ddbf2c61f37b5b1fb9f1a090a975006581ad19bda1c4d382871;
}
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/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));
bytes memory ret = returnedDataDecoded();
require(ret.length > 0);
return ret;
}
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 pure 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/apps/AragonApp.sol
pragma solidity ^0.4.18;
// ACLSyntaxSugar and EVMScriptRunner are not directly used by this contract, but are included so
// that they are automatically usable by subclassing contracts
contract AragonApp is AppStorage, Initializable, ACLSyntaxSugar, VaultRecoverable, 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);
}
function getRecoveryVault() public view returns (address) {
// Funds recovery via a vault is only available when used with a kernel
require(address(kernel) != 0);
return kernel.getRecoveryVault();
}
}
///File: @aragon/os/contracts/acl/ACL.sol
pragma solidity 0.4.18;
interface ACLOracle {
function canPerform(address who, address where, bytes32 what, uint256[] how) public view returns (bool);
}
contract ACL is IACL, AragonApp, ACLHelpers {
// Hardcoded constant to save gas
//bytes32 constant public CREATE_PERMISSIONS_ROLE = keccak256("CREATE_PERMISSIONS_ROLE");
bytes32 constant public CREATE_PERMISSIONS_ROLE = 0x0b719b33c83b8e5d300c521cb8b54ae9bd933996a14bef8c2f4e0285d2d2400a;
// whether a certain entity has a permission
mapping (bytes32 => bytes32) permissions; // 0 for no permission, or parameters id
mapping (bytes32 => Param[]) public permissionParams;
// who is the manager of a permission
mapping (bytes32 => address) permissionManager;
enum Op { NONE, EQ, NEQ, GT, LT, GTE, LTE, RET, NOT, AND, OR, XOR, IF_ELSE } // op types
struct Param {
uint8 id;
uint8 op;
uint240 value; // even though value is an uint240 it can store addresses
// in the case of 32 byte hashes losing 2 bytes precision isn't a huge deal
// op and id take less than 1 byte each so it can be kept in 1 sstore
}
uint8 constant BLOCK_NUMBER_PARAM_ID = 200;
uint8 constant TIMESTAMP_PARAM_ID = 201;
uint8 constant SENDER_PARAM_ID = 202;
uint8 constant ORACLE_PARAM_ID = 203;
uint8 constant LOGIC_OP_PARAM_ID = 204;
uint8 constant PARAM_VALUE_PARAM_ID = 205;
// TODO: Add execution times param type?
// Hardcoded constant to save gas
//bytes32 constant public EMPTY_PARAM_HASH = keccak256(uint256(0));
bytes32 constant public EMPTY_PARAM_HASH = 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563;
address constant ANY_ENTITY = address(-1);
modifier onlyPermissionManager(address _app, bytes32 _role) {
require(msg.sender == getPermissionManager(_app, _role));
_;
}
event SetPermission(address indexed entity, address indexed app, bytes32 indexed role, bool allowed);
event ChangePermissionManager(address indexed app, bytes32 indexed role, address indexed manager);
/**
* @dev Initialize can only be called once. It saves the block number in which it was initialized.
* @notice Initializes an ACL instance and sets `_permissionsCreator` as the entity that can create other permissions
* @param _permissionsCreator Entity that will be given permission over createPermission
*/
function initialize(address _permissionsCreator) onlyInit public {
initialized();
require(msg.sender == address(kernel));
_createPermission(_permissionsCreator, this, CREATE_PERMISSIONS_ROLE, _permissionsCreator);
}
/**
* @dev Creates a permission that wasn't previously set and managed. Access is limited by the ACL.
* If a created permission is removed it is possible to reset it with createPermission.
* @notice Create a new permission granting `_entity` the ability to perform actions of role `_role` on `_app` (setting `_manager` as the permission manager)
* @param _entity Address of the whitelisted entity that will be able to perform the role
* @param _app Address of the app in which the role will be allowed (requires app to depend on kernel for ACL)
* @param _role Identifier for the group of actions in app given access to perform
* @param _manager Address of the entity that will be able to grant and revoke the permission further.
*/
function createPermission(address _entity, address _app, bytes32 _role, address _manager) external {
require(hasPermission(msg.sender, address(this), CREATE_PERMISSIONS_ROLE));
_createPermission(_entity, _app, _role, _manager);
}
/**
* @dev Grants permission if allowed. This requires `msg.sender` to be the permission manager
* @notice Grants `_entity` the ability to perform actions of role `_role` on `_app`
* @param _entity Address of the whitelisted entity that will be able to perform the role
* @param _app Address of the app in which the role will be allowed (requires app to depend on kernel for ACL)
* @param _role Identifier for the group of actions in app given access to perform
*/
function grantPermission(address _entity, address _app, bytes32 _role)
external
{
grantPermissionP(_entity, _app, _role, new uint256[](0));
}
/**
* @dev Grants a permission with parameters if allowed. This requires `msg.sender` to be the permission manager
* @notice Grants `_entity` the ability to perform actions of role `_role` on `_app`
* @param _entity Address of the whitelisted entity that will be able to perform the role
* @param _app Address of the app in which the role will be allowed (requires app to depend on kernel for ACL)
* @param _role Identifier for the group of actions in app given access to perform
* @param _params Permission parameters
*/
function grantPermissionP(address _entity, address _app, bytes32 _role, uint256[] _params)
onlyPermissionManager(_app, _role)
public
{
bytes32 paramsHash = _params.length > 0 ? _saveParams(_params) : EMPTY_PARAM_HASH;
_setPermission(_entity, _app, _role, paramsHash);
}
/**
* @dev Revokes permission if allowed. This requires `msg.sender` to be the the permission manager
* @notice Revokes `_entity` the ability to perform actions of role `_role` on `_app`
* @param _entity Address of the whitelisted entity to revoke access from
* @param _app Address of the app in which the role will be revoked
* @param _role Identifier for the group of actions in app being revoked
*/
function revokePermission(address _entity, address _app, bytes32 _role)
onlyPermissionManager(_app, _role)
external
{
_setPermission(_entity, _app, _role, bytes32(0));
}
/**
* @notice Sets `_newManager` as the manager of the permission `_role` in `_app`
* @param _newManager Address for the new manager
* @param _app Address of the app in which the permission management is being transferred
* @param _role Identifier for the group of actions being transferred
*/
function setPermissionManager(address _newManager, address _app, bytes32 _role)
onlyPermissionManager(_app, _role)
external
{
_setPermissionManager(_newManager, _app, _role);
}
/**
* @notice Removes the manager of the permission `_role` in `_app`
* @param _app Address of the app in which the permission is being unmanaged
* @param _role Identifier for the group of actions being unmanaged
*/
function removePermissionManager(address _app, bytes32 _role)
onlyPermissionManager(_app, _role)
external
{
_setPermissionManager(address(0), _app, _role);
}
/**
* @notice Get parameters for permission array length
* @param _entity Address of the whitelisted entity that will be able to perform the role
* @param _app Address of the app
* @param _role Identifier for a group of actions in app
* @return Length of the array
*/
function getPermissionParamsLength(address _entity, address _app, bytes32 _role) external view returns (uint) {
return permissionParams[permissions[permissionHash(_entity, _app, _role)]].length;
}
/**
* @notice Get parameter for permission
* @param _entity Address of the whitelisted entity that will be able to perform the role
* @param _app Address of the app
* @param _role Identifier for a group of actions in app
* @param _index Index of parameter in the array
* @return Parameter (id, op, value)
*/
function getPermissionParam(address _entity, address _app, bytes32 _role, uint _index) external view returns (uint8 id, uint8 op, uint240 value) {
Param storage param = permissionParams[permissions[permissionHash(_entity, _app, _role)]][_index];
id = param.id;
op = param.op;
value = param.value;
}
/**
* @dev Get manager for permission
* @param _app Address of the app
* @param _role Identifier for a group of actions in app
* @return address of the manager for the permission
*/
function getPermissionManager(address _app, bytes32 _role) public view returns (address) {
return permissionManager[roleHash(_app, _role)];
}
/**
* @dev Function called by apps to check ACL on kernel or to check permission statu
* @param _who Sender of the original call
* @param _where Address of the app
* @param _where Identifier for a group of actions in app
* @param _how Permission parameters
* @return boolean indicating whether the ACL allows the role or not
*/
function hasPermission(address _who, address _where, bytes32 _what, bytes memory _how) public view returns (bool) {
uint256[] memory how;
uint256 intsLength = _how.length / 32;
assembly {
how := _how // forced casting
mstore(how, intsLength)
}
// _how is invalid from this point fwd
return hasPermission(_who, _where, _what, how);
}
function hasPermission(address _who, address _where, bytes32 _what, uint256[] memory _how) public view returns (bool) {
bytes32 whoParams = permissions[permissionHash(_who, _where, _what)];
if (whoParams != bytes32(0) && evalParams(whoParams, _who, _where, _what, _how)) {
return true;
}
bytes32 anyParams = permissions[permissionHash(ANY_ENTITY, _where, _what)];
if (anyParams != bytes32(0) && evalParams(anyParams, ANY_ENTITY, _where, _what, _how)) {
return true;
}
return false;
}
function hasPermission(address _who, address _where, bytes32 _what) public view returns (bool) {
uint256[] memory empty = new uint256[](0);
return hasPermission(_who, _where, _what, empty);
}
function evalParams(
bytes32 _paramsHash,
address _who,
address _where,
bytes32 _what,
uint256[] _how
) public view returns (bool)
{
if (_paramsHash == EMPTY_PARAM_HASH) {
return true;
}
return evalParam(_paramsHash, 0, _who, _where, _what, _how);
}
/**
* @dev Internal createPermission for access inside the kernel (on instantiation)
*/
function _createPermission(address _entity, address _app, bytes32 _role, address _manager) internal {
// only allow permission creation (or re-creation) when there is no manager
require(getPermissionManager(_app, _role) == address(0));
_setPermission(_entity, _app, _role, EMPTY_PARAM_HASH);
_setPermissionManager(_manager, _app, _role);
}
/**
* @dev Internal function called to actually save the permission
*/
function _setPermission(address _entity, address _app, bytes32 _role, bytes32 _paramsHash) internal {
permissions[permissionHash(_entity, _app, _role)] = _paramsHash;
SetPermission(_entity, _app, _role, _paramsHash != bytes32(0));
}
function _saveParams(uint256[] _encodedParams) internal returns (bytes32) {
bytes32 paramHash = keccak256(_encodedParams);
Param[] storage params = permissionParams[paramHash];
if (params.length == 0) { // params not saved before
for (uint256 i = 0; i < _encodedParams.length; i++) {
uint256 encodedParam = _encodedParams[i];
Param memory param = Param(decodeParamId(encodedParam), decodeParamOp(encodedParam), uint240(encodedParam));
params.push(param);
}
}
return paramHash;
}
function evalParam(
bytes32 _paramsHash,
uint32 _paramId,
address _who,
address _where,
bytes32 _what,
uint256[] _how
) internal view returns (bool)
{
if (_paramId >= permissionParams[_paramsHash].length) {
return false; // out of bounds
}
Param memory param = permissionParams[_paramsHash][_paramId];
if (param.id == LOGIC_OP_PARAM_ID) {
return evalLogic(param, _paramsHash, _who, _where, _what, _how);
}
uint256 value;
uint256 comparedTo = uint256(param.value);
// get value
if (param.id == ORACLE_PARAM_ID) {
value = checkOracle(address(param.value), _who, _where, _what, _how) ? 1 : 0;
comparedTo = 1;
} else if (param.id == BLOCK_NUMBER_PARAM_ID) {
value = blockN();
} else if (param.id == TIMESTAMP_PARAM_ID) {
value = time();
} else if (param.id == SENDER_PARAM_ID) {
value = uint256(msg.sender);
} else if (param.id == PARAM_VALUE_PARAM_ID) {
value = uint256(param.value);
} else {
if (param.id >= _how.length) {
return false;
}
value = uint256(uint240(_how[param.id])); // force lost precision
}
if (Op(param.op) == Op.RET) {
return uint256(value) > 0;
}
return compare(value, Op(param.op), comparedTo);
}
function evalLogic(Param _param, bytes32 _paramsHash, address _who, address _where, bytes32 _what, uint256[] _how) internal view returns (bool) {
if (Op(_param.op) == Op.IF_ELSE) {
var (condition, success, failure) = decodeParamsList(uint256(_param.value));
bool result = evalParam(_paramsHash, condition, _who, _where, _what, _how);
return evalParam(_paramsHash, result ? success : failure, _who, _where, _what, _how);
}
var (v1, v2,) = decodeParamsList(uint256(_param.value));
bool r1 = evalParam(_paramsHash, v1, _who, _where, _what, _how);
if (Op(_param.op) == Op.NOT) {
return !r1;
}
if (r1 && Op(_param.op) == Op.OR) {
return true;
}
if (!r1 && Op(_param.op) == Op.AND) {
return false;
}
bool r2 = evalParam(_paramsHash, v2, _who, _where, _what, _how);
if (Op(_param.op) == Op.XOR) {
return r1 != r2;
}
return r2; // both or and and depend on result of r2 after checks
}
function compare(uint256 _a, Op _op, uint256 _b) internal pure returns (bool) {
if (_op == Op.EQ) return _a == _b; // solium-disable-line lbrace
if (_op == Op.NEQ) return _a != _b; // solium-disable-line lbrace
if (_op == Op.GT) return _a > _b; // solium-disable-line lbrace
if (_op == Op.LT) return _a < _b; // solium-disable-line lbrace
if (_op == Op.GTE) return _a >= _b; // solium-disable-line lbrace
if (_op == Op.LTE) return _a <= _b; // solium-disable-line lbrace
return false;
}
function checkOracle(address _oracleAddr, address _who, address _where, bytes32 _what, uint256[] _how) internal view returns (bool) {
bytes4 sig = ACLOracle(_oracleAddr).canPerform.selector;
// a raw call is required so we can return false if the call reverts, rather than reverting
bool ok = _oracleAddr.call(sig, _who, _where, _what, 0x80, _how.length, _how);
// 0x80 is the position where the array that goes there starts
if (!ok) {
return false;
}
uint256 size;
assembly { size := returndatasize }
if (size != 32) {
return false;
}
bool result;
assembly {
let ptr := mload(0x40) // get next free memory ptr
returndatacopy(ptr, 0, size) // copy return from above `call`
result := mload(ptr) // read data at ptr and set it to result
mstore(ptr, 0) // set pointer memory to 0 so it still is the next free ptr
}
return result;
}
/**
* @dev Internal function that sets management
*/
function _setPermissionManager(address _newManager, address _app, bytes32 _role) internal {
permissionManager[roleHash(_app, _role)] = _newManager;
ChangePermissionManager(_app, _role, _newManager);
}
function roleHash(address _where, bytes32 _what) pure internal returns (bytes32) {
return keccak256(uint256(1), _where, _what);
}
function permissionHash(address _who, address _where, bytes32 _what) pure internal returns (bytes32) {
return keccak256(uint256(2), _who, _where, _what);
}
function time() internal view returns (uint64) { return uint64(block.timestamp); } // solium-disable-line security/no-block-members
function blockN() internal view returns (uint256) { return block.number; }
}
///File: @aragon/os/contracts/evmscript/EVMScriptRegistry.sol
pragma solidity 0.4.18;
contract EVMScriptRegistry is IEVMScriptRegistry, EVMScriptRegistryConstants, AragonApp {
using ScriptHelpers for bytes;
// WARN: Manager can censor all votes and the like happening in an org
// bytes32 constant public REGISTRY_MANAGER_ROLE = keccak256("REGISTRY_MANAGER_ROLE");
bytes32 constant public REGISTRY_MANAGER_ROLE = 0xf7a450ef335e1892cb42c8ca72e7242359d7711924b75db5717410da3f614aa3;
struct ExecutorEntry {
address executor;
bool enabled;
}
ExecutorEntry[] public executors;
function initialize() onlyInit public {
initialized();
// Create empty record to begin executor IDs at 1
executors.push(ExecutorEntry(address(0), false));
}
function addScriptExecutor(address _executor) external auth(REGISTRY_MANAGER_ROLE) returns (uint id) {
return executors.push(ExecutorEntry(_executor, true));
}
function disableScriptExecutor(uint256 _executorId) external auth(REGISTRY_MANAGER_ROLE) {
executors[_executorId].enabled = false;
}
function getScriptExecutor(bytes _script) public view returns (address) {
uint256 id = _script.getSpecId();
if (id == 0 || id >= executors.length) {
return address(0);
}
ExecutorEntry storage entry = executors[id];
return entry.enabled ? entry.executor : address(0);
}
}
///File: @aragon/os/contracts/evmscript/executors/CallsScript.sol
pragma solidity 0.4.18;
// Inspired by https://github.com/reverendus/tx-manager
contract CallsScript is IEVMScriptExecutor {
using ScriptHelpers for bytes;
uint256 constant internal SCRIPT_START_LOCATION = 4;
event LogScriptCall(address indexed sender, address indexed src, address indexed dst);
/**
* @notice Executes a number of call scripts
* @param _script [ specId (uint32) ] many calls with this structure ->
* [ to (address: 20 bytes) ] [ calldataLength (uint32: 4 bytes) ] [ calldata (calldataLength bytes) ]
* @param _input Input is ignored in callscript
* @param _blacklist Addresses the script cannot call to, or will revert.
* @return always returns empty byte array
*/
function execScript(bytes _script, bytes _input, address[] _blacklist) external returns (bytes) {
uint256 location = SCRIPT_START_LOCATION; // first 32 bits are spec id
while (location < _script.length) {
address contractAddress = _script.addressAt(location);
// Check address being called is not blacklist
for (uint i = 0; i < _blacklist.length; i++) {
require(contractAddress != _blacklist[i]);
}
// logged before execution to ensure event ordering in receipt
// if failed entire execution is reverted regardless
LogScriptCall(msg.sender, address(this), contractAddress);
uint256 calldataLength = uint256(_script.uint32At(location + 0x14));
uint256 startOffset = location + 0x14 + 0x04;
uint256 calldataStart = _script.locationOf(startOffset);
// compute end of script / next location
location = startOffset + calldataLength;
require(location <= _script.length);
assembly {
let success := call(sub(gas, 5000), contractAddress, 0, calldataStart, calldataLength, 0, 0)
switch success case 0 { revert(0, 0) }
}
}
bytes memory ret = new bytes(1);
ret[0] = 0x01;
return ret;
}
}
///File: @aragon/os/contracts/evmscript/executors/DelegateScript.sol
pragma solidity 0.4.18;
interface DelegateScriptTarget {
function exec() public returns (bool);
}
contract DelegateScript is IEVMScriptExecutor, IsContract {
using ScriptHelpers for *;
uint256 constant internal SCRIPT_START_LOCATION = 4;
/**
* @notice Executes script by delegatecall into a contract
* @param _script [ specId (uint32) ][ contract address (20 bytes) ]
* @param _input ABI encoded call to be made to contract (if empty executes default exec() function)
* @param _blacklist If any address is passed, will revert.
* @return Call return data
*/
function execScript(bytes _script, bytes _input, address[] _blacklist) external returns (bytes) {
require(_blacklist.length == 0); // dont have ability to control bans, so fail.
// Script should be spec id + address (20 bytes)
require(_script.length == SCRIPT_START_LOCATION + 20);
return delegate(_script.addressAt(SCRIPT_START_LOCATION), _input);
}
/**
* @dev Delegatecall to contract with input data
*/
function delegate(address _addr, bytes memory _input) internal returns (bytes memory output) {
require(isContract(_addr));
require(_addr.delegatecall(_input.length > 0 ? _input : defaultInput()));
bytes memory ret = returnedData();
require(ret.length > 0);
return ret;
}
function defaultInput() internal pure returns (bytes) {
return DelegateScriptTarget(0).exec.selector.toBytes();
}
/**
* @dev copies and returns last's call data
*/
function returnedData() internal pure returns (bytes ret) {
assembly {
let size := returndatasize
ret := mload(0x40) // free mem ptr get
mstore(0x40, add(ret, add(size, 0x20))) // free mem ptr set
mstore(ret, size) // set array length
returndatacopy(add(ret, 0x20), 0, size) // copy return data
}
return ret;
}
}
///File: @aragon/os/contracts/evmscript/executors/DeployDelegateScript.sol
pragma solidity 0.4.18;
// Inspired by: https://github.com/dapphub/ds-proxy/blob/master/src/proxy.sol
contract DeployDelegateScript is DelegateScript {
uint256 constant internal SCRIPT_START_LOCATION = 4;
uint256[2**254] private cacheStorageOffset;
mapping (bytes32 => address) cache;
/**
* @notice Executes script by delegatecall into a deployed contract (exec() function)
* @param _script [ specId (uint32) ][ contractInitcode (bytecode) ]
* @param _input ABI encoded call to be made to contract (if empty executes default exec() function)
* @param _blacklist If any address is passed, will revert.
* @return Call return data
*/
function execScript(bytes _script, bytes _input, address[] _blacklist) external returns (bytes) {
require(_blacklist.length == 0); // dont have ability to control bans, so fail.
bytes32 id = keccak256(_script);
address deployed = cache[id];
if (deployed == address(0)) {
deployed = deploy(_script);
cache[id] = deployed;
}
return DelegateScript.delegate(deployed, _input);
}
/**
* @dev Deploys contract byte code to network
*/
function deploy(bytes _script) internal returns (address addr) {
assembly {
// 0x24 = 0x20 (length) + 0x04 (spec id uint32)
// Length of code is 4 bytes less than total script size
addr := create(0, add(_script, 0x24), sub(mload(_script), 0x04))
switch iszero(extcodesize(addr))
case 1 { revert(0, 0) } // throw if contract failed to deploy
}
}
}
///File: @aragon/os/contracts/factory/EVMScriptRegistryFactory.sol
pragma solidity 0.4.18;
contract EVMScriptRegistryFactory is AppProxyFactory, EVMScriptRegistryConstants {
address public baseReg;
address public baseCalls;
address public baseDel;
address public baseDeployDel;
function EVMScriptRegistryFactory() public {
baseReg = address(new EVMScriptRegistry());
baseCalls = address(new CallsScript());
baseDel = address(new DelegateScript());
baseDeployDel = address(new DeployDelegateScript());
}
function newEVMScriptRegistry(Kernel _dao, address _root) public returns (EVMScriptRegistry reg) {
reg = EVMScriptRegistry(_dao.newPinnedAppInstance(EVMSCRIPT_REGISTRY_APP_ID, baseReg, true));
reg.initialize();
ACL acl = ACL(_dao.acl());
acl.createPermission(this, reg, reg.REGISTRY_MANAGER_ROLE(), this);
reg.addScriptExecutor(baseCalls); // spec 1 = CallsScript
reg.addScriptExecutor(baseDel); // spec 2 = DelegateScript
reg.addScriptExecutor(baseDeployDel); // spec 3 = DeployDelegateScript
acl.revokePermission(this, reg, reg.REGISTRY_MANAGER_ROLE());
acl.setPermissionManager(_root, reg, reg.REGISTRY_MANAGER_ROLE());
return reg;
}
}
///File: @aragon/os/contracts/factory/DAOFactory.sol
pragma solidity 0.4.18;
contract DAOFactory {
address public baseKernel;
address public baseACL;
EVMScriptRegistryFactory public regFactory;
event DeployDAO(address dao);
event DeployEVMScriptRegistry(address reg);
function DAOFactory(address _baseKernel, address _baseACL, address _regFactory) public {
// No need to init as it cannot be killed by devops199
if (_regFactory != address(0)) {
regFactory = EVMScriptRegistryFactory(_regFactory);
}
baseKernel = _baseKernel;
baseACL = _baseACL;
}
/**
* @param _root Address that will be granted control to setup DAO permissions
*/
function newDAO(address _root) public returns (Kernel dao) {
dao = Kernel(new KernelProxy(baseKernel));
if (address(regFactory) == address(0)) {
dao.initialize(baseACL, _root);
} else {
dao.initialize(baseACL, this);
ACL acl = ACL(dao.acl());
bytes32 permRole = acl.CREATE_PERMISSIONS_ROLE();
bytes32 appManagerRole = dao.APP_MANAGER_ROLE();
acl.grantPermission(regFactory, acl, permRole);
acl.createPermission(regFactory, dao, appManagerRole, this);
EVMScriptRegistry reg = regFactory.newEVMScriptRegistry(dao, _root);
DeployEVMScriptRegistry(address(reg));
acl.revokePermission(regFactory, dao, appManagerRole);
acl.revokePermission(regFactory, acl, permRole);
acl.revokePermission(this, acl, permRole);
acl.grantPermission(_root, acl, permRole);
acl.removePermissionManager(dao, appManagerRole);
acl.setPermissionManager(_root, acl, permRole);
}
DeployDAO(dao);
}
}
///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/LPVault.sol
pragma solidity ^0.4.18;
/*
Copyright 2017, Jordi Baylina
Contributors: 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 .
*/
/// @dev This contract holds ether securely for liquid pledging systems; for
/// this iteration the funds will come often be escaped to the Giveth Multisig
/// (safety precaution), but once fully tested and optimized this contract will
/// 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
/// @dev `LiquidPledging` is a basic interface to allow the `LPVault` contract
/// to confirm and cancel payments in the `LiquidPledging` contract.
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 AragonApp, LiquidPledgingACLHelpers {
bytes32 constant public CONFIRM_PAYMENT_ROLE = keccak256("CONFIRM_PAYMENT_ROLE");
bytes32 constant public CANCEL_PAYMENT_ROLE = keccak256("CANCEL_PAYMENT_ROLE");
bytes32 constant public SET_AUTOPAY_ROLE = keccak256("SET_AUTOPAY_ROLE");
bytes32 constant public ESCAPE_HATCH_CALLER_ROLE = keccak256("ESCAPE_HATCH_CALLER_ROLE");
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,
address token,
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
struct Payment {
bytes32 ref; // an input that references details from other contracts
address dest; // recipient of the ETH
PaymentStatus state; // Pending, Paid or Canceled
address token;
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;
ILiquidPledging public liquidPledging;
/// @dev The attached `LiquidPledging` contract is the only address that can
/// call a function with this modifier
modifier onlyLiquidPledging() {
require(msg.sender == address(liquidPledging));
_;
}
/// @param _liquidPledging Address of the liquidPledging instance associated
/// with this LPVault
function initialize(address _liquidPledging) onlyInit external {
require(_liquidPledging != 0x0);
initialized();
liquidPledging = ILiquidPledging(_liquidPledging);
}
/// @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) external authP(SET_AUTOPAY_ROLE, arr(_automatic)) {
autoPay = _automatic;
AutoPaySet(autoPay);
}
/// @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
/// @param _dest The address that payments will be sent to
/// @param _amount The amount that the payment is being authorized for
/// @return idPayment The id of the payment (needed by the owner to confirm)
function authorizePayment(
bytes32 _ref,
address _dest,
address _token,
uint _amount
) external onlyLiquidPledging returns (uint)
{
uint idPayment = payments.length;
payments.length ++;
payments[idPayment].state = PaymentStatus.Pending;
payments[idPayment].ref = _ref;
payments[idPayment].dest = _dest;
payments[idPayment].token = _token;
payments[idPayment].amount = _amount;
AuthorizePayment(idPayment, _ref, _dest, _token, _amount);
if (autoPay) {
_doConfirmPayment(idPayment);
}
return idPayment;
}
/// @notice Allows the owner to confirm payments; since
/// `authorizePayment` is the only way to populate the `payments[]` array
/// 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 {
Payment storage p = payments[_idPayment];
require(canPerform(msg.sender, CONFIRM_PAYMENT_ROLE, arr(_idPayment, p.amount)));
_doConfirmPayment(_idPayment);
}
/// @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) external {
_doCancelPayment(_idPayment);
}
/// @notice `onlyOwner` An efficient way to confirm multiple payments
/// @param _idPayments An array of multiple payment ids
function multiConfirm(uint[] _idPayments) external {
for (uint i = 0; i < _idPayments.length; i++) {
confirmPayment(_idPayments[i]);
}
}
/// @notice `onlyOwner` An efficient way to cancel multiple payments
/// @param _idPayments An array of multiple payment ids
function multiCancel(uint[] _idPayments) external {
for (uint i = 0; i < _idPayments.length; i++) {
_doCancelPayment(_idPayments[i]);
}
}
/**
* @dev By default, AragonApp will allow anyone to call transferToVault
* Because this app is designed to hold funds, we only want to call
* transferToVault in the case of an emergency. Only senders with the
* ESCAPE_HATCH_CALLER_ROLE are allowed to pull the "escapeHatch"
* @param token Token address that would be recovered
* @return bool whether the app allows the recovery
*/
function allowRecoverability(address token) public view returns (bool) {
return canPerform(msg.sender, ESCAPE_HATCH_CALLER_ROLE, arr(token));
}
/// @return The total number of payments that have ever been authorized
function nPayments() external 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);
p.state = PaymentStatus.Paid;
liquidPledging.confirmPayment(uint64(p.ref), p.amount);
ERC20 token = ERC20(p.token);
require(token.transfer(p.dest, p.amount)); // Transfers token to dest
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);
}
}
///File: ./contracts/ILiquidPledgingPlugin.sol
pragma solidity ^0.4.0;
/*
Copyright 2018, Jordi Baylina
Contributors: Adrià Massanet , 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 .
*/
/// @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.
uint[50] private storageOffset;
}
///File: ./contracts/LiquidPledgingPlugins.sol
pragma solidity ^0.4.18;
/*
Copyright 2017, Jordi Baylina, RJ Ewing
Contributors: Adrià Massanet , 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 .
*/
contract LiquidPledgingPlugins is AragonApp, LiquidPledgingStorage, LiquidPledgingACLHelpers {
bytes32 constant public PLUGIN_MANAGER_ROLE = keccak256("PLUGIN_MANAGER_ROLE");
/**
* @dev adds an instance of a plugin to the whitelist
*/
function addValidPluginInstance(address addr) auth(PLUGIN_MANAGER_ROLE) external {
pluginInstanceWhitelist[addr] = true;
}
/**
* @dev add a contract to the plugin whitelist.
* @notice Proxy contracts should never be added using this method. Each individual
* proxy instance should be added by calling `addValidPluginInstance`
*/
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]);
}
}
/**
* @dev removes a contract from the plugin whitelist
*/
function removeValidPluginContract(bytes32 contractHash) external authP(PLUGIN_MANAGER_ROLE, arr(contractHash)) {
pluginContractWhitelist[contractHash] = false;
}
/**
* @dev removes an instance of a plugin to the whitelist
*/
function removeValidPluginInstance(address addr) external authP(PLUGIN_MANAGER_ROLE, arr(addr)) {
pluginInstanceWhitelist[addr] = false;
}
/**
* @dev enable/disable the plugin whitelist.
* @notice you better know what you're doing if you are going to disable it
*/
function useWhitelist(bool useWhitelist) external auth(PLUGIN_MANAGER_ROLE) {
whitelistDisabled = !useWhitelist;
}
/**
* check if the contract at the provided address is in the plugin whitelist
*/
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];
}
/**
* @return the hash of the code for the given address
*/
function getCodeHash(address addr) public view returns(bytes32) {
bytes memory o_code;
assembly {
// retrieve the size of the code
let size := extcodesize(addr)
// allocate output byte array
o_code := mload(0x40)
mstore(o_code, size) // store length in memory
// actually retrieve the code
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 , 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 .
*/
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, string url);
event GiverUpdated(uint64 indexed idGiver, string url);
event DelegateAdded(uint64 indexed idDelegate, string url);
event DelegateUpdated(uint64 indexed idDelegate, string url);
event ProjectAdded(uint64 indexed idProject, string url);
event ProjectUpdated(uint64 indexed idProject, string url);
////////////////////
// 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
) external returns (uint64 idGiver)
{
return addGiver(
msg.sender,
name,
url,
commitTime,
plugin
);
}
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, url);
}
/// @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
) external
{
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, newUrl);
}
/// @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
) external 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, url);
}
/// @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
) external
{
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, newUrl);
}
/// @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
) external 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, url);
}
/// @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
) external
{
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, newUrl);
}
/////////////////////////////
// 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() external view 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) external 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 view 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 view returns(uint64) {
assert(a.adminType == PledgeAdminType.Project);
if (a.parentProject == 0) {
return(1);
}
PledgeAdmin storage parent = _findAdmin(a.parentProject);
return _getProjectLevel(parent) + 1;
}
}
///File: ./contracts/Pledges.sol
pragma solidity ^0.4.18;
/*
Copyright 2017, Jordi Baylina, RJ Ewing
Contributors: Adrià Massanet , 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 .
*/
contract Pledges is AragonApp, LiquidPledgingStorage {
// Limits inserted to prevent large loops that could prevent canceling
uint constant MAX_DELEGATES = 10;
// a constant for when a delegate is requested that is not in the system
uint64 constant NOTFOUND = 0xFFFFFFFFFFFFFFFF;
/////////////////////////////
// Public constant functions
////////////////////////////
/// @notice A constant getter that returns the total number of pledges
/// @return The total number of Pledges in the system
function numberOfPledges() external view returns (uint) {
return pledges.length - 1;
}
/// @notice A getter that returns the details of the specified pledge
/// @param idPledge the id number of the pledge being queried
/// @return the amount, owner, the number of delegates (but not the actual
/// delegates, the intendedProject (if any), the current commit time and
/// the previous pledge this pledge was derived from
function getPledge(uint64 idPledge) external view returns(
uint amount,
uint64 owner,
uint64 nDelegates,
uint64 intendedProject,
uint64 commitTime,
uint64 oldPledge,
address token,
PledgeState pledgeState
) {
Pledge memory p = _findPledge(idPledge);
amount = p.amount;
owner = p.owner;
nDelegates = uint64(p.delegationChain.length);
intendedProject = p.intendedProject;
commitTime = p.commitTime;
oldPledge = p.oldPledge;
token = p.token;
pledgeState = p.pledgeState;
}
////////////////////
// Internal methods
////////////////////
/// @notice This creates a Pledge with an initial amount of 0 if one is not
/// created already; otherwise it finds the pledge with the specified
/// attributes; all pledges technically exist, if the pledge hasn't been
/// created in this system yet it simply isn't in the hash array
/// hPledge2idx[] yet
/// @param owner The owner of the pledge being looked up
/// @param delegationChain The list of delegates in order of authority
/// @param intendedProject The project this pledge will Fund after the
/// commitTime has passed
/// @param commitTime The length of time in seconds the Giver has to
/// veto when the Giver's delegates Pledge funds to a project
/// @param oldPledge This value is used to store the pledge the current
/// pledge was came from, and in the case a Project is canceled, the Pledge
/// will revert back to it's previous state
/// @param state The pledge state: Pledged, Paying, or state
/// @return The hPledge2idx index number
function _findOrCreatePledge(
uint64 owner,
uint64[] delegationChain,
uint64 intendedProject,
uint64 commitTime,
uint64 oldPledge,
address token,
PledgeState state
) internal returns (uint64)
{
bytes32 hPledge = keccak256(delegationChain, owner, intendedProject, commitTime, oldPledge, token, state);
uint64 id = hPledge2idx[hPledge];
if (id > 0) {
return id;
}
id = uint64(pledges.length);
hPledge2idx[hPledge] = id;
pledges.push(
Pledge(
0,
delegationChain,
owner,
intendedProject,
commitTime,
oldPledge,
token,
state
)
);
return id;
}
/// @param idPledge the id of the pledge to load from storage
/// @return The Pledge
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
/// authority a specific delegate has within a Pledge
/// @param p The Pledge that will be searched
/// @param idDelegate The specified delegate that's searched for
/// @return If the delegate chain contains the delegate with the
/// `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) {
for (uint i = 0; i < p.delegationChain.length; i++) {
if (p.delegationChain[i] == idDelegate) {
return uint64(i);
}
}
return NOTFOUND;
}
/// @notice A getter to find how many old "parent" pledges a specific Pledge
/// had using a self-referential loop
/// @param p The Pledge being queried
/// @return The number of old "parent" pledges a specific Pledge had
function _getPledgeLevel(Pledge p) internal view returns(uint) {
if (p.oldPledge == 0) {
return 0;
}
Pledge storage oldP = _findPledge(p.oldPledge);
return _getPledgeLevel(oldP) + 1; // a loop lookup
}
}
///File: ./contracts/LiquidPledgingBase.sol
pragma solidity ^0.4.18;
/*
Copyright 2017, Jordi Baylina
Contributors: Adrià Massanet , 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 .
*/
/// @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 AragonApp, LiquidPledgingStorage, PledgeAdmins, Pledges {
event Transfer(uint indexed from, uint indexed to, uint amount);
event CancelProject(uint indexed idProject);
/////////////
// Modifiers
/////////////
/// @dev The `vault`is the only addresses that can call a function with this
/// modifier
modifier onlyVault() {
require(msg.sender == address(vault));
_;
}
///////////////
// Constructor
///////////////
/// @param _vault The vault where the ETH backing the pledges is stored
function initialize(address _vault) onlyInit public {
require(_vault != 0x0);
initialized();
vault = ILPVault(_vault);
admins.length = 1; // we reserve the 0 admin
pledges.length = 1; // we reserve the 0 pledge
}
/////////////////////////////
// Public constant functions
/////////////////////////////
/// @notice Getter to find Delegate w/ the Pledge ID & the Delegate index
/// @param idPledge The id number representing the pledge being queried
/// @param idxDelegate The index number for the delegate in this Pledge
function getPledgeDelegate(uint64 idPledge, uint64 idxDelegate) external view returns(
uint64 idDelegate,
address addr,
string name
) {
Pledge storage p = _findPledge(idPledge);
idDelegate = p.delegationChain[idxDelegate - 1];
PledgeAdmin storage delegate = _findAdmin(idDelegate);
addr = delegate.addr;
name = delegate.name;
}
///////////////////
// Public functions
///////////////////
/// @notice Only affects pledges with the Pledged PledgeState for 2 things:
/// #1: Checks if the pledge should be committed. This means that
/// if the pledge has an intendedProject and it is past the
/// commitTime, it changes the owner to be the proposed project
/// (The UI will have to read the commit time and manually do what
/// this function does to the pledge for the end user
/// at the expiration of the commitTime)
///
/// #2: Checks to make sure that if there has been a cancellation in the
/// chain of projects, the pledge's owner has been changed
/// appropriately.
///
/// This function can be called by anybody at anytime on any pledge.
/// In general it can be called to force the calls of the affected
/// 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(uint64) {
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 != PledgeState.Pledged) {
return idPledge;
}
// First send to a project if it's proposed and committed
if ((p.intendedProject > 0) && ( _getTime() > p.commitTime)) {
uint64 oldPledge = _findOrCreatePledge(
p.owner,
p.delegationChain,
0,
0,
p.oldPledge,
p.token,
PledgeState.Pledged
);
uint64 toPledge = _findOrCreatePledge(
p.intendedProject,
new uint64[](0),
0,
0,
oldPledge,
p.token,
PledgeState.Pledged
);
_doTransfer(idPledge, toPledge, p.amount);
idPledge = toPledge;
p = _findPledge(idPledge);
}
toPledge = _getOldestPledgeNotCanceled(idPledge);
if (toPledge != idPledge) {
_doTransfer(idPledge, toPledge, p.amount);
}
return toPledge;
}
////////////////////
// Internal methods
////////////////////
/// @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(uint64 idAdmin) internal view {
PledgeAdmin storage a = _findAdmin(idAdmin);
require(msg.sender == address(a.plugin) || msg.sender == a.addr);
}
function _transfer(
uint64 idSender,
uint64 idPledge,
uint amount,
uint64 idReceiver
) internal
{
require(idReceiver > 0); // prevent burning value
idPledge = normalizePledge(idPledge);
Pledge storage p = _findPledge(idPledge);
PledgeAdmin storage receiver = _findAdmin(idReceiver);
require(p.pledgeState == PledgeState.Pledged);
// If the sender is the owner of the Pledge
if (p.owner == idSender) {
if (receiver.adminType == PledgeAdminType.Giver) {
_transferOwnershipToGiver(idPledge, amount, idReceiver);
return;
} else if (receiver.adminType == PledgeAdminType.Project) {
_transferOwnershipToProject(idPledge, amount, idReceiver);
return;
} else if (receiver.adminType == PledgeAdminType.Delegate) {
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) {
uint64 toPledge = _findOrCreatePledge(
p.owner,
p.delegationChain,
0,
0,
p.oldPledge,
p.token,
PledgeState.Pledged);
_doTransfer(idPledge, toPledge, amount);
return;
}
_undelegate(idPledge, amount, p.delegationChain.length - receiverDIdx - 1);
return;
}
// owner is not vetoing an intendedProject and is transferring the pledge to a delegate,
// so we want to reset the delegationChain
idPledge = _undelegate(
idPledge,
amount,
p.delegationChain.length
);
_appendDelegate(idPledge, amount, idReceiver);
return;
}
// This should never be reached as the receiver.adminType
// should always be either a Giver, Project, or Delegate
assert(false);
}
// If the sender is a Delegate
uint senderDIdx = _getDelegateIdx(p, idSender);
if (senderDIdx != NOTFOUND) {
// And the receiver is another Giver
if (receiver.adminType == PledgeAdminType.Giver) {
// Only transfer to the Giver who owns the pledge
assert(p.owner == idReceiver);
_undelegate(idPledge, amount, p.delegationChain.length);
return;
}
// And the receiver is another Delegate
if (receiver.adminType == PledgeAdminType.Delegate) {
uint receiverDIdx = _getDelegateIdx(p, idReceiver);
// And not in the delegationChain or part of the delegationChain
// 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
if (receiverDIdx == NOTFOUND || receiverDIdx > senderDIdx) {
idPledge = _undelegate(
idPledge,
amount,
p.delegationChain.length - senderDIdx - 1
);
_appendDelegate(idPledge, amount, idReceiver);
return;
}
// 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
_undelegate(
idPledge,
amount,
p.delegationChain.length - receiverDIdx - 1
);
return;
}
// And the receiver is a Project, all the delegates after the sender
// are removed and the amount is pre-committed to the project
if (receiver.adminType == PledgeAdminType.Project) {
idPledge = _undelegate(
idPledge,
amount,
p.delegationChain.length - senderDIdx - 1
);
_proposeAssignProject(idPledge, amount, idReceiver);
return;
}
}
assert(false); // When the sender is not an owner or a delegate
}
/// @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 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(
uint64 idPledge,
uint amount,
uint64 idReceiver
) internal
{
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) < MAX_INTERPROJECT_LEVEL);
require(!isProjectCanceled(idReceiver));
uint64 oldPledge = _findOrCreatePledge(
p.owner,
p.delegationChain,
0,
0,
p.oldPledge,
p.token,
PledgeState.Pledged
);
uint64 toPledge = _findOrCreatePledge(
idReceiver, // Set the new owner
new uint64[](0), // clear the delegation chain
0,
0,
oldPledge,
p.token,
PledgeState.Pledged
);
_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 idPledge the id of the pledge to be transferred.
/// @param amount Quantity of value that's being transferred
/// @param idReceiver The new owner of the pledge
function _transferOwnershipToGiver(
uint64 idPledge,
uint amount,
uint64 idReceiver
) internal
{
Pledge storage p = _findPledge(idPledge);
uint64 toPledge = _findOrCreatePledge(
idReceiver,
new uint64[](0),
0,
0,
0,
p.token,
PledgeState.Pledged
);
_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 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(
uint64 idPledge,
uint amount,
uint64 idReceiver
) internal
{
Pledge storage p = _findPledge(idPledge);
require(p.delegationChain.length < MAX_DELEGATES);
uint64[] memory newDelegationChain = new uint64[](
p.delegationChain.length + 1
);
for (uint i = 0; i < p.delegationChain.length; i++) {
newDelegationChain[i] = p.delegationChain[i];
}
// Make the last item in the array the idReceiver
newDelegationChain[p.delegationChain.length] = idReceiver;
uint64 toPledge = _findOrCreatePledge(
p.owner,
newDelegationChain,
0,
0,
p.oldPledge,
p.token,
PledgeState.Pledged
);
_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 idPledge the id of the pledge thats delegate chain will be modified.
/// @param amount Quantity of value that's shifted from delegates.
/// @param q Number (or depth) of delegates to remove
/// @return toPledge The id for the pledge being adjusted or created
function _undelegate(
uint64 idPledge,
uint amount,
uint q
) internal returns (uint64 toPledge)
{
Pledge storage p = _findPledge(idPledge);
uint64[] memory newDelegationChain = new uint64[](
p.delegationChain.length - q
);
for (uint i = 0; i < p.delegationChain.length - q; i++) {
newDelegationChain[i] = p.delegationChain[i];
}
toPledge = _findOrCreatePledge(
p.owner,
newDelegationChain,
0,
0,
p.oldPledge,
p.token,
PledgeState.Pledged
);
_doTransfer(idPledge, toPledge, amount);
}
/// @notice `proposeAssignProject` proposes the assignment of a pledge
/// to a specific project.
/// @dev This function should potentially be named more specifically.
/// @param idPledge the id of the pledge that will be assigned.
/// @param amount Quantity of value this pledge leader would be assigned.
/// @param idReceiver The project this pledge will potentially
/// be assigned to.
function _proposeAssignProject(
uint64 idPledge,
uint amount,
uint64 idReceiver
) internal
{
Pledge storage p = _findPledge(idPledge);
require(_getPledgeLevel(p) < MAX_INTERPROJECT_LEVEL);
require(!isProjectCanceled(idReceiver));
uint64 toPledge = _findOrCreatePledge(
p.owner,
p.delegationChain,
idReceiver,
uint64(_getTime() + _maxCommitTime(p)),
p.oldPledge,
p.token,
PledgeState.Pledged
);
_doTransfer(idPledge, toPledge, amount);
}
/// @notice `doTransfer` is designed to allow for pledge amounts to be
/// shifted around internally.
/// @param from This is the id of the pledge from which value will be transferred.
/// @param to This is the id of the pledge that value will be transferred to.
/// @param _amount The amount of value that will be transferred.
function _doTransfer(uint64 from, uint64 to, uint _amount) internal {
uint amount = _callPlugins(true, from, to, _amount);
if (from == to) {
return;
}
if (amount == 0) {
return;
}
Pledge storage pFrom = _findPledge(from);
Pledge storage pTo = _findPledge(to);
require(pFrom.amount >= amount);
pFrom.amount -= amount;
pTo.amount += amount;
require(pTo.amount >= amount);
Transfer(from, to, amount);
_callPlugins(false, from, to, amount);
}
/// @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(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++) {
a = _findAdmin(p.delegationChain[i]);
// If a delegate's commitTime is longer, make it the new commitTime
if (a.commitTime > commitTime) {
commitTime = a.commitTime;
}
}
}
/// @notice A getter to find the oldest pledge that hasn't been canceled
/// @param idPledge The starting place to lookup the pledges
/// @return The oldest idPledge that hasn't been canceled (DUH!)
function _getOldestPledgeNotCanceled(
uint64 idPledge
) internal view returns(uint64)
{
if (idPledge == 0) {
return 0;
}
Pledge storage p = _findPledge(idPledge);
PledgeAdmin storage admin = _findAdmin(p.owner);
if (admin.adminType == PledgeAdminType.Giver) {
return idPledge;
}
assert(admin.adminType == PledgeAdminType.Project);
if (!isProjectCanceled(p.owner)) {
return idPledge;
}
return _getOldestPledgeNotCanceled(p.oldPledge);
}
/// @notice `callPlugin` is used to trigger the general functions in the
/// plugin for any actions needed before and after a transfer happens.
/// Specifically what this does in relation to the plugin is something
/// that largely depends on the functions of that plugin. This function
/// is generally called in pairs, once before, and once after a transfer.
/// @param before This toggle determines whether the plugin call is occurring
/// before or after a transfer.
/// @param adminId This should be the Id of the *trusted* individual
/// who has control over this plugin.
/// @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 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(
bool before,
uint64 adminId,
uint64 fromPledge,
uint64 toPledge,
uint64 context,
address token,
uint amount
) internal returns (uint allowedAmount)
{
uint newAmount;
allowedAmount = amount;
PledgeAdmin storage admin = _findAdmin(adminId);
// Checks admin has a plugin assigned and a non-zero amount is requested
if (address(admin.plugin) != 0 && allowedAmount > 0) {
// There are two separate functions called in the plugin.
// One is called before the transfer and one after
if (before) {
newAmount = admin.plugin.beforeTransfer(
adminId,
fromPledge,
toPledge,
context,
token,
amount
);
require(newAmount <= allowedAmount);
allowedAmount = newAmount;
} else {
admin.plugin.afterTransfer(
adminId,
fromPledge,
toPledge,
context,
token,
amount
);
}
}
}
/// @notice `callPluginsPledge` is used to apply plugin calls to
/// the delegate chain and the intended project if there is one.
/// It does so in either a transferring or receiving context based
/// on the `p` and `fromPledge` parameters.
/// @param before This toggle determines whether the plugin call is occuring
/// before or after a transfer.
/// @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(
bool before,
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 = idPledge == fromPledge ? 0 : 256;
allowedAmount = amount;
Pledge storage p = _findPledge(idPledge);
// Always call the plugin on the owner
allowedAmount = _callPlugin(
before,
p.owner,
fromPledge,
toPledge,
offset,
p.token,
allowedAmount
);
// Apply call plugin to all delegates
for (uint64 i = 0; i < p.delegationChain.length; i++) {
allowedAmount = _callPlugin(
before,
p.delegationChain[i],
fromPledge,
toPledge,
offset + i + 1,
p.token,
allowedAmount
);
}
// If there is an intended project also call the plugin in
// either a transferring or receiving context based on offset
// on the intended project
if (p.intendedProject > 0) {
allowedAmount = _callPlugin(
before,
p.intendedProject,
fromPledge,
toPledge,
offset + 255,
p.token,
allowedAmount
);
}
}
/// @notice `callPlugins` calls `callPluginsPledge` once for the transfer
/// context and once for the receiving context. The aggregated
/// allowed amount is then returned.
/// @param before This toggle determines whether the plugin call is occurring
/// before or after a transfer.
/// @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(
bool before,
uint64 fromPledge,
uint64 toPledge,
uint amount
) internal returns (uint allowedAmount)
{
allowedAmount = amount;
// Call the plugins in the transfer context
allowedAmount = _callPluginsPledge(
before,
fromPledge,
fromPledge,
toPledge,
allowedAmount
);
// Call the plugins in the receive context
allowedAmount = _callPluginsPledge(
before,
toPledge,
fromPledge,
toPledge,
allowedAmount
);
}
/////////////
// Test functions
/////////////
/// @notice Basic helper function to return the current time
function _getTime() internal view returns (uint) {
return now;
}
}
///File: ./contracts/LiquidPledging.sol
pragma solidity ^0.4.18;
/*
Copyright 2017, Jordi Baylina, RJ Ewing
Contributors: Adrià Massanet , 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 .
*/
/// @dev `LiquidPledging` allows for liquid pledging through the use of
/// internal id structures and delegate chaining. All basic operations for
/// handling liquid pledging are supplied as well as plugin features
/// to allow for expanded functionality.
contract LiquidPledging is LiquidPledgingBase {
/// Create a "giver" pledge admin for the sender & donate
/// @param idReceiver The Admin receiving the donation; can be any Admin:
/// the Giver themselves, another Giver, a Delegate or a Project
/// @param token The address of the token being donated.
/// @param amount The amount of tokens being donated
function addGiverAndDonate(uint64 idReceiver, address token, uint amount)
public
{
addGiverAndDonate(idReceiver, msg.sender, token, amount);
}
/// Create a "giver" pledge admin for the given `donorAddress` & donate
/// @param idReceiver The Admin receiving the donation; can be any Admin:
/// the Giver themselves, another Giver, a Delegate or a Project
/// @param donorAddress The address of the "giver" of this donation
/// @param token The address of the token being donated.
/// @param amount The amount of tokens being donated
function addGiverAndDonate(uint64 idReceiver, address donorAddress, address token, uint amount)
public
{
require(donorAddress != 0);
// default to a 3 day (259200 seconds) commitTime
uint64 idGiver = addGiver(donorAddress, "", "", 259200, ILiquidPledgingPlugin(0));
donate(idGiver, idReceiver, token, amount);
}
/// @notice This is how value enters the system and how pledges are created;
/// the ether is sent to the vault, an pledge for the Giver is created (or
/// found), the amount of ETH donated in wei is added to the `amount` in
/// the Giver's Pledge, and an LP transfer is done to the idReceiver for
/// the full amount
/// @param idGiver The id of the Giver donating
/// @param idReceiver The Admin receiving the donation; can be any Admin:
/// the Giver themselves, another Giver, a Delegate or a Project
/// @param token The address of the token being donated.
/// @param amount The amount of tokens being donated
function donate(uint64 idGiver, uint64 idReceiver, address token, uint amount)
public
{
require(idGiver > 0); // prevent burning donations. idReceiver is checked in _transfer
require(amount > 0);
require(token != 0x0);
PledgeAdmin storage sender = _findAdmin(idGiver);
require(sender.adminType == PledgeAdminType.Giver);
require(ERC20(token).transferFrom(msg.sender, address(vault), amount)); // transfer the token to the `vault`
uint64 idPledge = _findOrCreatePledge(
idGiver,
new uint64[](0), // Creates empty array for delegationChain
0,
0,
0,
token,
PledgeState.Pledged
);
Pledge storage pTo = _findPledge(idPledge);
pTo.amount += amount;
Transfer(0, idPledge, amount);
_transfer(idGiver, idPledge, amount, idReceiver);
}
/// @notice Transfers amounts between pledges for internal accounting
/// @param idSender Id of the Admin that is transferring the amount from
/// Pledge to Pledge; this admin must have permissions to move the value
/// @param idPledge Id of the pledge that's moving the value
/// @param amount Quantity of ETH (in wei) that this pledge is transferring
/// the authority to withdraw from the vault
/// @param idReceiver Destination of the `amount`, can be a Giver/Project sending
/// to a Giver, a Delegate or a Project; a Delegate sending to another
/// Delegate, or a Delegate pre-commiting it to a Project
function transfer(
uint64 idSender,
uint64 idPledge,
uint amount,
uint64 idReceiver
) public
{
_checkAdminOwner(idSender);
_transfer(idSender, idPledge, amount, idReceiver);
}
/// @notice Authorizes a payment be made from the `vault` can be used by the
/// Giver to veto a pre-committed donation from a Delegate to an
/// 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 {
idPledge = normalizePledge(idPledge); // Updates pledge info
Pledge storage p = _findPledge(idPledge);
require(p.pledgeState == PledgeState.Pledged);
_checkAdminOwner(p.owner);
uint64 idNewPledge = _findOrCreatePledge(
p.owner,
p.delegationChain,
0,
0,
p.oldPledge,
p.token,
PledgeState.Paying
);
_doTransfer(idPledge, idNewPledge, amount);
PledgeAdmin storage owner = _findAdmin(p.owner);
vault.authorizePayment(bytes32(idNewPledge), owner.addr, p.token, amount);
}
/// @notice `onlyVault` Confirms a withdraw request changing the PledgeState
/// from Paying to Paid
/// @param idPledge Id of the pledge that is to be withdrawn
/// @param amount Quantity of ether (in wei) to be withdrawn
function confirmPayment(uint64 idPledge, uint amount) public onlyVault {
Pledge storage p = _findPledge(idPledge);
require(p.pledgeState == PledgeState.Paying);
uint64 idNewPledge = _findOrCreatePledge(
p.owner,
p.delegationChain,
0,
0,
p.oldPledge,
p.token,
PledgeState.Paid
);
_doTransfer(idPledge, idNewPledge, amount);
}
/// @notice `onlyVault` Cancels a withdraw request, changing the PledgeState
/// from Paying back to Pledged
/// @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 {
Pledge storage p = _findPledge(idPledge);
require(p.pledgeState == PledgeState.Paying);
// When a payment is canceled, never is assigned to a project.
uint64 idOldPledge = _findOrCreatePledge(
p.owner,
p.delegationChain,
0,
0,
p.oldPledge,
p.token,
PledgeState.Pledged
);
idOldPledge = normalizePledge(idOldPledge);
_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 {
PledgeAdmin storage project = _findAdmin(idProject);
_checkAdminOwner(idProject);
project.canceled = true;
CancelProject(idProject);
}
/// @notice Transfers `amount` in `idPledge` back to the `oldPledge` that
/// that sent it there in the first place, a Ctrl-z
/// @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 {
idPledge = normalizePledge(idPledge);
Pledge storage p = _findPledge(idPledge);
require(p.oldPledge != 0);
require(p.pledgeState == PledgeState.Pledged);
_checkAdminOwner(p.owner);
uint64 oldPledge = _getOldestPledgeNotCanceled(p.oldPledge);
_doTransfer(idPledge, oldPledge, amount);
}
////////
// Multi pledge methods
////////
// @dev This set of functions makes moving a lot of pledges around much more
// efficient (saves gas) than calling these functions in series
/// @dev Bitmask used for dividing pledge amounts in Multi pledge methods
uint constant D64 = 0x10000000000000000;
/// @notice Transfers multiple amounts within multiple Pledges in an
/// efficient single call
/// @param idSender Id of the Admin that is transferring the amounts from
/// all the Pledges; this admin must have permissions to move the value
/// @param pledgesAmounts An array of Pledge amounts and the idPledges with
/// which the amounts are associated; these are extrapolated using the D64
/// bitmask
/// @param idReceiver Destination of the `pledesAmounts`, can be a Giver or
/// Project sending to a Giver, a Delegate or a Project; a Delegate sending
/// to another Delegate, or a Delegate pre-commiting it to a Project
function mTransfer(
uint64 idSender,
uint[] pledgesAmounts,
uint64 idReceiver
) public
{
for (uint i = 0; i < pledgesAmounts.length; i++ ) {
uint64 idPledge = uint64(pledgesAmounts[i] & (D64-1));
uint amount = pledgesAmounts[i] / D64;
transfer(idSender, idPledge, amount, idReceiver);
}
}
/// @notice Authorizes multiple amounts within multiple Pledges to be
/// withdrawn from the `vault` in an efficient single call
/// @param pledgesAmounts An array of Pledge amounts and the idPledges with
/// which the amounts are associated; these are extrapolated using the D64
/// bitmask
function mWithdraw(uint[] pledgesAmounts) public {
for (uint i = 0; i < pledgesAmounts.length; i++ ) {
uint64 idPledge = uint64(pledgesAmounts[i] & (D64-1));
uint amount = pledgesAmounts[i] / D64;
withdraw(idPledge, amount);
}
}
/// @notice `mNormalizePledge` allows for multiple pledges to be
/// normalized efficiently
/// @param pledges An array of pledge IDs
function mNormalizePledge(uint64[] pledges) public {
for (uint i = 0; i < pledges.length; i++ ) {
normalizePledge(pledges[i]);
}
}
}
///File: ./contracts/LPConstants.sol
pragma solidity ^0.4.18;
contract LPConstants is KernelConstants {
bytes32 constant public VAULT_APP_ID = keccak256("vault");
bytes32 constant public LP_APP_ID = keccak256("liquidPledging");
}
///File: ./contracts/LPFactory.sol
pragma solidity ^0.4.18;
contract LPFactory is LPConstants, DAOFactory(new Kernel(), new ACL(), 0) {
bytes32 public constant RECOVERY_VAULT_ID = keccak256("recoveryVault");
address public vaultBase;
address public lpBase;
event DeployVault(address vault);
event DeployLiquidPledging(address liquidPledging);
function LPFactory(address _vaultBase, address _lpBase) public {
require(_vaultBase != 0);
require(_lpBase != 0);
vaultBase = _vaultBase;
lpBase = _lpBase;
}
function newLP(address _root, address _escapeHatchDestination) external {
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));
// deploy & register the lp instance w/ the kernel
LiquidPledging lp = LiquidPledging(kernel.newAppInstance(LP_APP_ID, lpBase, true));
v.initialize(address(lp));
lp.initialize(address(v));
// set the recoveryVault to the escapeHatchDestination
kernel.setRecoveryVaultId(RECOVERY_VAULT_ID);
kernel.setApp(APP_ADDR_NAMESPACE, RECOVERY_VAULT_ID, _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 pluginManagerRole = lp.PLUGIN_MANAGER_ROLE();
acl.createPermission(_root, address(v), hatchCallerRole, _root);
acl.createPermission(_root, address(lp), pluginManagerRole, _root);
// 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));
}
}