diff --git a/apps/contribution/arapp.json b/apps/contribution/arapp.json index 96d78fd..942ec44 100644 --- a/apps/contribution/arapp.json +++ b/apps/contribution/arapp.json @@ -19,7 +19,7 @@ "environments": { "default": { "network": "development", - "appName": "kredits-contribution.aragonpm.eth" + "appName": "kredits-contribution.open.aragonpm.eth" }, "rinkeby": { "registry": "0x98df287b6c145399aaa709692c8d308357bc085d", diff --git a/apps/contribution/artifact.json b/apps/contribution/artifact.json new file mode 100644 index 0000000..5f8f197 --- /dev/null +++ b/apps/contribution/artifact.json @@ -0,0 +1,912 @@ +{ + "roles": [ + { + "name": "Add contributions", + "id": "ADD_CONTRIBUTION_ROLE", + "params": [], + "bytes": "0x493d28cd0d82bcb20db66e4f6390a00122ef772717e282b436ba3240af18bfb1" + }, + { + "name": "Manage token contract", + "id": "MANAGE_TOKEN_CONTRACT_ROLE", + "params": [], + "bytes": "0xdd275187bc43df45ce7b34f6716e572716c69ad44e5e496175008950f032854b" + }, + { + "name": "Veto contributions", + "id": "VETO_CONTRIBUTION_ROLE", + "params": [], + "bytes": "0x495a36de1ed34d5c1b9f8704e7d8bc8badb027221b09c79691d430bc54c4c88f" + } + ], + "environments": { + "default": { + "network": "development", + "appName": "kredits-contribution.open.aragonpm.eth" + } + }, + "path": "contracts/Contribution.sol", + "appName": "kredits-contribution.open.aragonpm.eth", + "appId": "0x09f5274cba299b46c5be722ef672d10eef7a2ef980b612aef529d74fb9da7643", + "abi": [ + { + "constant": true, + "inputs": [], + "name": "hasInitialized", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "ADD_CONTRIBUTION_ROLE", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_script", + "type": "bytes" + } + ], + "name": "getEVMScriptExecutor", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getRecoveryVault", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "contributionsCount", + "outputs": [ + { + "name": "", + "type": "uint32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "token", + "type": "address" + } + ], + "name": "allowRecoverability", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "appId", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getInitializationBlock", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "KERNEL_APP_ADDR_NAMESPACE", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_token", + "type": "address" + } + ], + "name": "transferToVault", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_sender", + "type": "address" + }, + { + "name": "_role", + "type": "bytes32" + }, + { + "name": "_params", + "type": "uint256[]" + } + ], + "name": "canPerform", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEVMScriptRegistry", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "uint32" + } + ], + "name": "contributionOwner", + "outputs": [ + { + "name": "", + "type": "uint32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "uint32" + } + ], + "name": "contributions", + "outputs": [ + { + "name": "contributorId", + "type": "uint32" + }, + { + "name": "amount", + "type": "uint32" + }, + { + "name": "claimed", + "type": "bool" + }, + { + "name": "hashDigest", + "type": "bytes32" + }, + { + "name": "hashFunction", + "type": "uint8" + }, + { + "name": "hashSize", + "type": "uint8" + }, + { + "name": "tokenMetadataURL", + "type": "string" + }, + { + "name": "confirmedAtBlock", + "type": "uint256" + }, + { + "name": "vetoed", + "type": "bool" + }, + { + "name": "exists", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "uint32" + }, + { + "name": "", + "type": "uint256" + } + ], + "name": "ownedContributions", + "outputs": [ + { + "name": "", + "type": "uint32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "kernel", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "blocksToWait", + "outputs": [ + { + "name": "", + "type": "uint32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isPetrified", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "uint256" + } + ], + "name": "appIds", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "VETO_CONTRIBUTION_ROLE", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "id", + "type": "uint32" + }, + { + "indexed": true, + "name": "contributorId", + "type": "uint32" + }, + { + "indexed": false, + "name": "amount", + "type": "uint32" + } + ], + "name": "ContributionAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "id", + "type": "uint32" + }, + { + "indexed": true, + "name": "contributorId", + "type": "uint32" + }, + { + "indexed": false, + "name": "amount", + "type": "uint32" + } + ], + "name": "ContributionClaimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "id", + "type": "uint32" + }, + { + "indexed": false, + "name": "vetoedByAccount", + "type": "address" + } + ], + "name": "ContributionVetoed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "executor", + "type": "address" + }, + { + "indexed": false, + "name": "script", + "type": "bytes" + }, + { + "indexed": false, + "name": "input", + "type": "bytes" + }, + { + "indexed": false, + "name": "returnData", + "type": "bytes" + } + ], + "name": "ScriptResult", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "vault", + "type": "address" + }, + { + "indexed": true, + "name": "token", + "type": "address" + }, + { + "indexed": false, + "name": "amount", + "type": "uint256" + } + ], + "name": "RecoverToVault", + "type": "event" + }, + { + "constant": false, + "inputs": [ + { + "name": "_appIds", + "type": "bytes32[4]" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getTokenContract", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getContributorContract", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "contributorAccount", + "type": "address" + } + ], + "name": "getContributorIdByAddress", + "outputs": [ + { + "name": "", + "type": "uint32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "contributorId", + "type": "uint32" + } + ], + "name": "getContributorAddressById", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "contributionId", + "type": "uint32" + } + ], + "name": "ownerOf", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "owner", + "type": "address" + }, + { + "name": "index", + "type": "uint32" + } + ], + "name": "tokenOfOwnerByIndex", + "outputs": [ + { + "name": "", + "type": "uint32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "contributionId", + "type": "uint32" + } + ], + "name": "tokenMetadata", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "confirmedOnly", + "type": "bool" + } + ], + "name": "totalKreditsEarned", + "outputs": [ + { + "name": "amount", + "type": "uint32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "contributorId", + "type": "uint32" + }, + { + "name": "confirmedOnly", + "type": "bool" + } + ], + "name": "totalKreditsEarnedByContributor", + "outputs": [ + { + "name": "amount", + "type": "uint32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "contributionId", + "type": "uint32" + } + ], + "name": "getContribution", + "outputs": [ + { + "name": "id", + "type": "uint32" + }, + { + "name": "contributorId", + "type": "uint32" + }, + { + "name": "amount", + "type": "uint32" + }, + { + "name": "claimed", + "type": "bool" + }, + { + "name": "hashDigest", + "type": "bytes32" + }, + { + "name": "hashFunction", + "type": "uint8" + }, + { + "name": "hashSize", + "type": "uint8" + }, + { + "name": "confirmedAtBlock", + "type": "uint256" + }, + { + "name": "exists", + "type": "bool" + }, + { + "name": "vetoed", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "amount", + "type": "uint32" + }, + { + "name": "contributorId", + "type": "uint32" + }, + { + "name": "hashDigest", + "type": "bytes32" + }, + { + "name": "hashFunction", + "type": "uint8" + }, + { + "name": "hashSize", + "type": "uint8" + } + ], + "name": "add", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "contributionId", + "type": "uint32" + } + ], + "name": "veto", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "contributionId", + "type": "uint32" + } + ], + "name": "claim", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "contributionId", + "type": "uint32" + } + ], + "name": "exists", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } + ], + "deployment": { + "contractName": "Contribution", + "compiledAt": "2019-06-13T12:39:07.659Z", + "compiler": { + "name": "solc", + "version": "0.4.24+commit.e67f0147.Emscripten.clang", + "optimizer": { + "enabled": false + } + }, + "flattenedCode": "./code.sol", + "transactionHash": "0x073057bb616243e415823fa9a8c8cc096b573fbd0bbf11c2f59bb75a84291689" + }, + "functions": [ + { + "sig": "mintFor(address,uint256,uint32)", + "roles": [], + "notice": null + }, + { + "sig": "initialize(bytes32[4])", + "roles": [], + "notice": null + }, + { + "sig": "add(uint32,uint32,bytes32,uint8,uint8)", + "roles": [ + "ADD_CONTRIBUTION_ROLE" + ], + "notice": null + }, + { + "sig": "veto(uint32)", + "roles": [ + "VETO_CONTRIBUTION_ROLE" + ], + "notice": null + }, + { + "sig": "claim(uint32)", + "roles": [], + "notice": null + } + ] +} diff --git a/apps/contribution/code.sol b/apps/contribution/code.sol new file mode 100644 index 0000000..0f5b216 --- /dev/null +++ b/apps/contribution/code.sol @@ -0,0 +1,1252 @@ + +// File: @aragon/os/contracts/common/UnstructuredStorage.sol + +/* + * SPDX-License-Identitifer: MIT + */ + +pragma solidity ^0.4.24; + + +library UnstructuredStorage { + function getStorageBool(bytes32 position) internal view returns (bool data) { + assembly { data := sload(position) } + } + + function getStorageAddress(bytes32 position) internal view returns (address data) { + assembly { data := sload(position) } + } + + function getStorageBytes32(bytes32 position) internal view returns (bytes32 data) { + assembly { data := sload(position) } + } + + function getStorageUint256(bytes32 position) internal view returns (uint256 data) { + assembly { data := sload(position) } + } + + function setStorageBool(bytes32 position, bool data) internal { + assembly { sstore(position, data) } + } + + function setStorageAddress(bytes32 position, address data) internal { + assembly { sstore(position, data) } + } + + function setStorageBytes32(bytes32 position, bytes32 data) internal { + assembly { sstore(position, data) } + } + + function setStorageUint256(bytes32 position, uint256 data) internal { + assembly { sstore(position, data) } + } +} + +// File: @aragon/os/contracts/acl/IACL.sol + +/* + * SPDX-License-Identitifer: MIT + */ + +pragma solidity ^0.4.24; + + +interface IACL { + function initialize(address permissionsCreator) external; + + // TODO: this should be external + // See https://github.com/ethereum/solidity/issues/4832 + function hasPermission(address who, address where, bytes32 what, bytes how) public view returns (bool); +} + +// File: @aragon/os/contracts/common/IVaultRecoverable.sol + +/* + * SPDX-License-Identitifer: MIT + */ + +pragma solidity ^0.4.24; + + +interface IVaultRecoverable { + event RecoverToVault(address indexed vault, address indexed token, uint256 amount); + + function transferToVault(address token) external; + + function allowRecoverability(address token) external view returns (bool); + function getRecoveryVault() external view returns (address); +} + +// File: @aragon/os/contracts/kernel/IKernel.sol + +/* + * SPDX-License-Identitifer: MIT + */ + +pragma solidity ^0.4.24; + + + + +interface IKernelEvents { + event SetApp(bytes32 indexed namespace, bytes32 indexed appId, address app); +} + + +// This should be an interface, but interfaces can't inherit yet :( +contract IKernel is IKernelEvents, IVaultRecoverable { + 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 appId, address app) public; + function getApp(bytes32 namespace, bytes32 appId) public view returns (address); +} + +// File: @aragon/os/contracts/apps/AppStorage.sol + +/* + * SPDX-License-Identitifer: MIT + */ + +pragma solidity ^0.4.24; + + + + +contract AppStorage { + using UnstructuredStorage for bytes32; + + /* Hardcoded constants to save gas + bytes32 internal constant KERNEL_POSITION = keccak256("aragonOS.appStorage.kernel"); + bytes32 internal constant APP_ID_POSITION = keccak256("aragonOS.appStorage.appId"); + */ + bytes32 internal constant KERNEL_POSITION = 0x4172f0f7d2289153072b0a6ca36959e0cbe2efc3afe50fc81636caa96338137b; + bytes32 internal constant APP_ID_POSITION = 0xd625496217aa6a3453eecb9c3489dc5a53e6c67b444329ea2b2cbc9ff547639b; + + function kernel() public view returns (IKernel) { + return IKernel(KERNEL_POSITION.getStorageAddress()); + } + + function appId() public view returns (bytes32) { + return APP_ID_POSITION.getStorageBytes32(); + } + + function setKernel(IKernel _kernel) internal { + KERNEL_POSITION.setStorageAddress(address(_kernel)); + } + + function setAppId(bytes32 _appId) internal { + APP_ID_POSITION.setStorageBytes32(_appId); + } +} + +// File: @aragon/os/contracts/acl/ACLSyntaxSugar.sol + +/* + * SPDX-License-Identitifer: MIT + */ + +pragma solidity ^0.4.24; + + +contract ACLSyntaxSugar { + function arr() internal pure returns (uint256[]) { + return new uint256[](0); + } + + 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, uint256 _c, uint256 _d) internal pure returns (uint256[] r) { + return arr(uint256(_a), _b, _c, _d); + } + + 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/common/Uint256Helpers.sol + +pragma solidity ^0.4.24; + + +library Uint256Helpers { + uint256 private constant MAX_UINT64 = uint64(-1); + + string private constant ERROR_NUMBER_TOO_BIG = "UINT64_NUMBER_TOO_BIG"; + + function toUint64(uint256 a) internal pure returns (uint64) { + require(a <= MAX_UINT64, ERROR_NUMBER_TOO_BIG); + return uint64(a); + } +} + +// File: @aragon/os/contracts/common/TimeHelpers.sol + +/* + * SPDX-License-Identitifer: MIT + */ + +pragma solidity ^0.4.24; + + + +contract TimeHelpers { + using Uint256Helpers for uint256; + + /** + * @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; + } + + /** + * @dev Returns the current block number, converted to uint64. + * Using a function rather than `block.number` allows us to easily mock the block number in + * tests. + */ + function getBlockNumber64() internal view returns (uint64) { + return getBlockNumber().toUint64(); + } + + /** + * @dev Returns the current timestamp. + * Using a function rather than `block.timestamp` allows us to easily mock it in + * tests. + */ + function getTimestamp() internal view returns (uint256) { + return block.timestamp; // solium-disable-line security/no-block-members + } + + /** + * @dev Returns the current timestamp, converted to uint64. + * Using a function rather than `block.timestamp` allows us to easily mock it in + * tests. + */ + function getTimestamp64() internal view returns (uint64) { + return getTimestamp().toUint64(); + } +} + +// File: @aragon/os/contracts/common/Initializable.sol + +/* + * SPDX-License-Identitifer: MIT + */ + +pragma solidity ^0.4.24; + + + + +contract Initializable is TimeHelpers { + using UnstructuredStorage for bytes32; + + // keccak256("aragonOS.initializable.initializationBlock") + bytes32 internal constant INITIALIZATION_BLOCK_POSITION = 0xebb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e; + + string private constant ERROR_ALREADY_INITIALIZED = "INIT_ALREADY_INITIALIZED"; + string private constant ERROR_NOT_INITIALIZED = "INIT_NOT_INITIALIZED"; + + modifier onlyInit { + require(getInitializationBlock() == 0, ERROR_ALREADY_INITIALIZED); + _; + } + + modifier isInitialized { + require(hasInitialized(), ERROR_NOT_INITIALIZED); + _; + } + + /** + * @return Block number in which the contract was initialized + */ + function getInitializationBlock() public view returns (uint256) { + return INITIALIZATION_BLOCK_POSITION.getStorageUint256(); + } + + /** + * @return Whether the contract has been initialized by the time of the current block + */ + function hasInitialized() public view returns (bool) { + uint256 initializationBlock = getInitializationBlock(); + return initializationBlock != 0 && getBlockNumber() >= initializationBlock; + } + + /** + * @dev Function to be called by top level contract after initialization has finished. + */ + function initialized() internal onlyInit { + INITIALIZATION_BLOCK_POSITION.setStorageUint256(getBlockNumber()); + } + + /** + * @dev Function to be called by top level contract after initialization to enable the contract + * at a future block number rather than immediately. + */ + function initializedAt(uint256 _blockNumber) internal onlyInit { + INITIALIZATION_BLOCK_POSITION.setStorageUint256(_blockNumber); + } +} + +// File: @aragon/os/contracts/common/Petrifiable.sol + +/* + * SPDX-License-Identitifer: MIT + */ + +pragma solidity ^0.4.24; + + + +contract Petrifiable is Initializable { + // Use block UINT256_MAX (which should be never) as the initializable date + uint256 internal constant PETRIFIED_BLOCK = uint256(-1); + + function isPetrified() public view returns (bool) { + return getInitializationBlock() == PETRIFIED_BLOCK; + } + + /** + * @dev Function to be called by top level contract to prevent being initialized. + * Useful for freezing base contracts when they're used behind proxies. + */ + function petrify() internal onlyInit { + initializedAt(PETRIFIED_BLOCK); + } +} + +// File: @aragon/os/contracts/common/Autopetrified.sol + +/* + * SPDX-License-Identitifer: MIT + */ + +pragma solidity ^0.4.24; + + + +contract Autopetrified is Petrifiable { + constructor() public { + // Immediately petrify base (non-proxy) instances of inherited contracts on deploy. + // This renders them uninitializable (and unusable without a proxy). + petrify(); + } +} + +// File: @aragon/os/contracts/common/ConversionHelpers.sol + +pragma solidity ^0.4.24; + + +library ConversionHelpers { + string private constant ERROR_IMPROPER_LENGTH = "CONVERSION_IMPROPER_LENGTH"; + + function dangerouslyCastUintArrayToBytes(uint256[] memory _input) internal pure returns (bytes memory output) { + // Force cast the uint256[] into a bytes array, by overwriting its length + // Note that the bytes array doesn't need to be initialized as we immediately overwrite it + // with the input and a new length. The input becomes invalid from this point forward. + uint256 byteLength = _input.length * 32; + assembly { + output := _input + mstore(output, byteLength) + } + } + + function dangerouslyCastBytesToUintArray(bytes memory _input) internal pure returns (uint256[] memory output) { + // Force cast the bytes array into a uint256[], by overwriting its length + // Note that the uint256[] doesn't need to be initialized as we immediately overwrite it + // with the input and a new length. The input becomes invalid from this point forward. + uint256 intsLength = _input.length / 32; + require(_input.length == intsLength * 32, ERROR_IMPROPER_LENGTH); + + assembly { + output := _input + mstore(output, intsLength) + } + } +} + +// File: @aragon/os/contracts/common/ReentrancyGuard.sol + +/* + * SPDX-License-Identitifer: MIT + */ + +pragma solidity ^0.4.24; + + + +contract ReentrancyGuard { + using UnstructuredStorage for bytes32; + + /* Hardcoded constants to save gas + bytes32 internal constant REENTRANCY_MUTEX_POSITION = keccak256("aragonOS.reentrancyGuard.mutex"); + */ + bytes32 private constant REENTRANCY_MUTEX_POSITION = 0xe855346402235fdd185c890e68d2c4ecad599b88587635ee285bce2fda58dacb; + + string private constant ERROR_REENTRANT = "REENTRANCY_REENTRANT_CALL"; + + modifier nonReentrant() { + // Ensure mutex is unlocked + require(!REENTRANCY_MUTEX_POSITION.getStorageBool(), ERROR_REENTRANT); + + // Lock mutex before function call + REENTRANCY_MUTEX_POSITION.setStorageBool(true); + + // Perform function call + _; + + // Unlock mutex after function call + REENTRANCY_MUTEX_POSITION.setStorageBool(false); + } +} + +// File: @aragon/os/contracts/lib/token/ERC20.sol + +// See https://github.com/OpenZeppelin/openzeppelin-solidity/blob/a9f910d34f0ab33a1ae5e714f69f9596a02b4d91/contracts/token/ERC20/ERC20.sol + +pragma solidity ^0.4.24; + + +/** + * @title ERC20 interface + * @dev see https://github.com/ethereum/EIPs/issues/20 + */ +contract ERC20 { + function totalSupply() public view returns (uint256); + + function balanceOf(address _who) public view returns (uint256); + + function allowance(address _owner, address _spender) + public view returns (uint256); + + function transfer(address _to, uint256 _value) public returns (bool); + + function approve(address _spender, uint256 _value) + public returns (bool); + + function transferFrom(address _from, address _to, uint256 _value) + public returns (bool); + + event Transfer( + address indexed from, + address indexed to, + uint256 value + ); + + event Approval( + address indexed owner, + address indexed spender, + uint256 value + ); +} + +// File: @aragon/os/contracts/common/EtherTokenConstant.sol + +/* + * SPDX-License-Identitifer: MIT + */ + +pragma solidity ^0.4.24; + + +// aragonOS and aragon-apps rely on address(0) to denote native ETH, in +// contracts where both tokens and ETH are accepted +contract EtherTokenConstant { + address internal constant ETH = address(0); +} + +// File: @aragon/os/contracts/common/IsContract.sol + +/* + * SPDX-License-Identitifer: MIT + */ + +pragma solidity ^0.4.24; + + +contract IsContract { + /* + * NOTE: this should NEVER be used for authentication + * (see pitfalls: https://github.com/fergarrui/ethereum-security/tree/master/contracts/extcodesize). + * + * This is only intended to be used as a sanity check that an address is actually a contract, + * RATHER THAN an address not being a contract. + */ + 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/SafeERC20.sol + +// Inspired by AdEx (https://github.com/AdExNetwork/adex-protocol-eth/blob/b9df617829661a7518ee10f4cb6c4108659dd6d5/contracts/libs/SafeERC20.sol) +// and 0x (https://github.com/0xProject/0x-monorepo/blob/737d1dc54d72872e24abce5a1dbe1b66d35fa21a/contracts/protocol/contracts/protocol/AssetProxy/ERC20Proxy.sol#L143) + +pragma solidity ^0.4.24; + + + +library SafeERC20 { + // Before 0.5, solidity has a mismatch between `address.transfer()` and `token.transfer()`: + // https://github.com/ethereum/solidity/issues/3544 + bytes4 private constant TRANSFER_SELECTOR = 0xa9059cbb; + + string private constant ERROR_TOKEN_BALANCE_REVERTED = "SAFE_ERC_20_BALANCE_REVERTED"; + string private constant ERROR_TOKEN_ALLOWANCE_REVERTED = "SAFE_ERC_20_ALLOWANCE_REVERTED"; + + function invokeAndCheckSuccess(address _addr, bytes memory _calldata) + private + returns (bool) + { + bool ret; + assembly { + let ptr := mload(0x40) // free memory pointer + + let success := call( + gas, // forward all gas + _addr, // address + 0, // no value + add(_calldata, 0x20), // calldata start + mload(_calldata), // calldata length + ptr, // write output over free memory + 0x20 // uint256 return + ) + + if gt(success, 0) { + // Check number of bytes returned from last function call + switch returndatasize + + // No bytes returned: assume success + case 0 { + ret := 1 + } + + // 32 bytes returned: check if non-zero + case 0x20 { + // Only return success if returned data was true + // Already have output in ptr + ret := eq(mload(ptr), 1) + } + + // Not sure what was returned: don't mark as success + default { } + } + } + return ret; + } + + function staticInvoke(address _addr, bytes memory _calldata) + private + view + returns (bool, uint256) + { + bool success; + uint256 ret; + assembly { + let ptr := mload(0x40) // free memory pointer + + success := staticcall( + gas, // forward all gas + _addr, // address + add(_calldata, 0x20), // calldata start + mload(_calldata), // calldata length + ptr, // write output over free memory + 0x20 // uint256 return + ) + + if gt(success, 0) { + ret := mload(ptr) + } + } + return (success, ret); + } + + /** + * @dev Same as a standards-compliant ERC20.transfer() that never reverts (returns false). + * Note that this makes an external call to the token. + */ + function safeTransfer(ERC20 _token, address _to, uint256 _amount) internal returns (bool) { + bytes memory transferCallData = abi.encodeWithSelector( + TRANSFER_SELECTOR, + _to, + _amount + ); + return invokeAndCheckSuccess(_token, transferCallData); + } + + /** + * @dev Same as a standards-compliant ERC20.transferFrom() that never reverts (returns false). + * Note that this makes an external call to the token. + */ + function safeTransferFrom(ERC20 _token, address _from, address _to, uint256 _amount) internal returns (bool) { + bytes memory transferFromCallData = abi.encodeWithSelector( + _token.transferFrom.selector, + _from, + _to, + _amount + ); + return invokeAndCheckSuccess(_token, transferFromCallData); + } + + /** + * @dev Same as a standards-compliant ERC20.approve() that never reverts (returns false). + * Note that this makes an external call to the token. + */ + function safeApprove(ERC20 _token, address _spender, uint256 _amount) internal returns (bool) { + bytes memory approveCallData = abi.encodeWithSelector( + _token.approve.selector, + _spender, + _amount + ); + return invokeAndCheckSuccess(_token, approveCallData); + } + + /** + * @dev Static call into ERC20.balanceOf(). + * Reverts if the call fails for some reason (should never fail). + */ + function staticBalanceOf(ERC20 _token, address _owner) internal view returns (uint256) { + bytes memory balanceOfCallData = abi.encodeWithSelector( + _token.balanceOf.selector, + _owner + ); + + (bool success, uint256 tokenBalance) = staticInvoke(_token, balanceOfCallData); + require(success, ERROR_TOKEN_BALANCE_REVERTED); + + return tokenBalance; + } + + /** + * @dev Static call into ERC20.allowance(). + * Reverts if the call fails for some reason (should never fail). + */ + function staticAllowance(ERC20 _token, address _owner, address _spender) internal view returns (uint256) { + bytes memory allowanceCallData = abi.encodeWithSelector( + _token.allowance.selector, + _owner, + _spender + ); + + (bool success, uint256 allowance) = staticInvoke(_token, allowanceCallData); + require(success, ERROR_TOKEN_ALLOWANCE_REVERTED); + + return allowance; + } +} + +// File: @aragon/os/contracts/common/VaultRecoverable.sol + +/* + * SPDX-License-Identitifer: MIT + */ + +pragma solidity ^0.4.24; + + + + + + + +contract VaultRecoverable is IVaultRecoverable, EtherTokenConstant, IsContract { + using SafeERC20 for ERC20; + + string private constant ERROR_DISALLOWED = "RECOVER_DISALLOWED"; + string private constant ERROR_VAULT_NOT_CONTRACT = "RECOVER_VAULT_NOT_CONTRACT"; + string private constant ERROR_TOKEN_TRANSFER_FAILED = "RECOVER_TOKEN_TRANSFER_FAILED"; + + /** + * @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), ERROR_DISALLOWED); + address vault = getRecoveryVault(); + require(isContract(vault), ERROR_VAULT_NOT_CONTRACT); + + uint256 balance; + if (_token == ETH) { + balance = address(this).balance; + vault.transfer(balance); + } else { + ERC20 token = ERC20(_token); + balance = token.staticBalanceOf(this); + require(token.safeTransfer(vault, balance), ERROR_TOKEN_TRANSFER_FAILED); + } + + emit RecoverToVault(vault, _token, balance); + } + + /** + * @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; + } + + // Cast non-implemented interface to be public so we can use it internally + function getRecoveryVault() public view returns (address); +} + +// File: @aragon/os/contracts/evmscript/IEVMScriptExecutor.sol + +/* + * SPDX-License-Identitifer: MIT + */ + +pragma solidity ^0.4.24; + + +interface IEVMScriptExecutor { + function execScript(bytes script, bytes input, address[] blacklist) external returns (bytes); + function executorType() external pure returns (bytes32); +} + +// File: @aragon/os/contracts/evmscript/IEVMScriptRegistry.sol + +/* + * SPDX-License-Identitifer: MIT + */ + +pragma solidity ^0.4.24; + + + +contract EVMScriptRegistryConstants { + /* Hardcoded constants to save gas + bytes32 internal constant EVMSCRIPT_REGISTRY_APP_ID = apmNamehash("evmreg"); + */ + bytes32 internal constant EVMSCRIPT_REGISTRY_APP_ID = 0xddbcfd564f642ab5627cf68b9b7d374fb4f8a36e941a75d89c87998cef03bd61; +} + + +interface IEVMScriptRegistry { + function addScriptExecutor(IEVMScriptExecutor executor) external returns (uint id); + function disableScriptExecutor(uint256 executorId) external; + + // TODO: this should be external + // See https://github.com/ethereum/solidity/issues/4832 + function getScriptExecutor(bytes script) public view returns (IEVMScriptExecutor); +} + +// File: @aragon/os/contracts/kernel/KernelConstants.sol + +/* + * SPDX-License-Identitifer: MIT + */ + +pragma solidity ^0.4.24; + + +contract KernelAppIds { + /* Hardcoded constants to save gas + bytes32 internal constant KERNEL_CORE_APP_ID = apmNamehash("kernel"); + bytes32 internal constant KERNEL_DEFAULT_ACL_APP_ID = apmNamehash("acl"); + bytes32 internal constant KERNEL_DEFAULT_VAULT_APP_ID = apmNamehash("vault"); + */ + bytes32 internal constant KERNEL_CORE_APP_ID = 0x3b4bf6bf3ad5000ecf0f989d5befde585c6860fea3e574a4fab4c49d1c177d9c; + bytes32 internal constant KERNEL_DEFAULT_ACL_APP_ID = 0xe3262375f45a6e2026b7e7b18c2b807434f2508fe1a2a3dfb493c7df8f4aad6a; + bytes32 internal constant KERNEL_DEFAULT_VAULT_APP_ID = 0x7e852e0fcfce6551c13800f1e7476f982525c2b5277ba14b24339c68416336d1; +} + + +contract KernelNamespaceConstants { + /* Hardcoded constants to save gas + bytes32 internal constant KERNEL_CORE_NAMESPACE = keccak256("core"); + bytes32 internal constant KERNEL_APP_BASES_NAMESPACE = keccak256("base"); + bytes32 internal constant KERNEL_APP_ADDR_NAMESPACE = keccak256("app"); + */ + bytes32 internal constant KERNEL_CORE_NAMESPACE = 0xc681a85306374a5ab27f0bbc385296a54bcd314a1948b6cf61c4ea1bc44bb9f8; + bytes32 internal constant KERNEL_APP_BASES_NAMESPACE = 0xf1f3eb40f5bc1ad1344716ced8b8a0431d840b5783aea1fd01786bc26f35ac0f; + bytes32 internal constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb; +} + +// File: @aragon/os/contracts/evmscript/EVMScriptRunner.sol + +/* + * SPDX-License-Identitifer: MIT + */ + +pragma solidity ^0.4.24; + + + + + + + +contract EVMScriptRunner is AppStorage, Initializable, EVMScriptRegistryConstants, KernelNamespaceConstants { + string private constant ERROR_EXECUTOR_UNAVAILABLE = "EVMRUN_EXECUTOR_UNAVAILABLE"; + string private constant ERROR_PROTECTED_STATE_MODIFIED = "EVMRUN_PROTECTED_STATE_MODIFIED"; + + /* This is manually crafted in assembly + string private constant ERROR_EXECUTOR_INVALID_RETURN = "EVMRUN_EXECUTOR_INVALID_RETURN"; + */ + + event ScriptResult(address indexed executor, bytes script, bytes input, bytes returnData); + + function getEVMScriptExecutor(bytes _script) public view returns (IEVMScriptExecutor) { + return IEVMScriptExecutor(getEVMScriptRegistry().getScriptExecutor(_script)); + } + + function getEVMScriptRegistry() public view returns (IEVMScriptRegistry) { + address registryAddr = kernel().getApp(KERNEL_APP_ADDR_NAMESPACE, EVMSCRIPT_REGISTRY_APP_ID); + return IEVMScriptRegistry(registryAddr); + } + + function runScript(bytes _script, bytes _input, address[] _blacklist) + internal + isInitialized + protectState + returns (bytes) + { + IEVMScriptExecutor executor = getEVMScriptExecutor(_script); + require(address(executor) != address(0), ERROR_EXECUTOR_UNAVAILABLE); + + bytes4 sig = executor.execScript.selector; + bytes memory data = abi.encodeWithSelector(sig, _script, _input, _blacklist); + + bytes memory output; + assembly { + let success := delegatecall( + gas, // forward all gas + executor, // address + add(data, 0x20), // calldata start + mload(data), // calldata length + 0, // don't write output (we'll handle this ourselves) + 0 // don't write output + ) + + output := mload(0x40) // free mem ptr get + + switch success + case 0 { + // If the call errored, forward its full error data + returndatacopy(output, 0, returndatasize) + revert(output, returndatasize) + } + default { + switch gt(returndatasize, 0x3f) + case 0 { + // Need at least 0x40 bytes returned for properly ABI-encoded bytes values, + // revert with "EVMRUN_EXECUTOR_INVALID_RETURN" + // See remix: doing a `revert("EVMRUN_EXECUTOR_INVALID_RETURN")` always results in + // this memory layout + mstore(output, 0x08c379a000000000000000000000000000000000000000000000000000000000) // error identifier + mstore(add(output, 0x04), 0x0000000000000000000000000000000000000000000000000000000000000020) // starting offset + mstore(add(output, 0x24), 0x000000000000000000000000000000000000000000000000000000000000001e) // reason length + mstore(add(output, 0x44), 0x45564d52554e5f4558454355544f525f494e56414c49445f52455455524e0000) // reason + + revert(output, 100) // 100 = 4 + 3 * 32 (error identifier + 3 words for the ABI encoded error) + } + default { + // Copy result + // + // Needs to perform an ABI decode for the expected `bytes` return type of + // `executor.execScript()` as solidity will automatically ABI encode the returned bytes as: + // [ position of the first dynamic length return value = 0x20 (32 bytes) ] + // [ output length (32 bytes) ] + // [ output content (N bytes) ] + // + // Perform the ABI decode by ignoring the first 32 bytes of the return data + let copysize := sub(returndatasize, 0x20) + returndatacopy(output, 0x20, copysize) + + mstore(0x40, add(output, copysize)) // free mem ptr set + } + } + } + + emit ScriptResult(address(executor), _script, _input, output); + + return output; + } + + modifier protectState { + address preKernel = address(kernel()); + bytes32 preAppId = appId(); + _; // exec + require(address(kernel()) == preKernel, ERROR_PROTECTED_STATE_MODIFIED); + require(appId() == preAppId, ERROR_PROTECTED_STATE_MODIFIED); + } +} + +// File: @aragon/os/contracts/apps/AragonApp.sol + +/* + * SPDX-License-Identitifer: MIT + */ + +pragma solidity ^0.4.24; + + + + + + + + + +// Contracts inheriting from AragonApp are, by default, immediately petrified upon deployment so +// that they can never be initialized. +// Unless overriden, this behaviour enforces those contracts to be usable only behind an AppProxy. +// ReentrancyGuard, EVMScriptRunner, and ACLSyntaxSugar are not directly used by this contract, but +// are included so that they are automatically usable by subclassing contracts +contract AragonApp is AppStorage, Autopetrified, VaultRecoverable, ReentrancyGuard, EVMScriptRunner, ACLSyntaxSugar { + string private constant ERROR_AUTH_FAILED = "APP_AUTH_FAILED"; + + modifier auth(bytes32 _role) { + require(canPerform(msg.sender, _role, new uint256[](0)), ERROR_AUTH_FAILED); + _; + } + + modifier authP(bytes32 _role, uint256[] _params) { + require(canPerform(msg.sender, _role, _params), ERROR_AUTH_FAILED); + _; + } + + /** + * @dev Check whether an action can be performed by a sender for a particular role on this app + * @param _sender Sender of the call + * @param _role Role on this app + * @param _params Permission params for the role + * @return Boolean indicating whether the sender has the permissions to perform the action. + * Always returns false if the app hasn't been initialized yet. + */ + function canPerform(address _sender, bytes32 _role, uint256[] _params) public view returns (bool) { + if (!hasInitialized()) { + return false; + } + + IKernel linkedKernel = kernel(); + if (address(linkedKernel) == address(0)) { + return false; + } + + return linkedKernel.hasPermission( + _sender, + address(this), + _role, + ConversionHelpers.dangerouslyCastUintArrayToBytes(_params) + ); + } + + /** + * @dev Get the recovery vault for the app + * @return Recovery vault address for the app + */ + function getRecoveryVault() public view returns (address) { + // Funds recovery via a vault is only available when used with a kernel + return kernel().getRecoveryVault(); // if kernel is not set, it will revert + } +} + +// File: contracts/Contribution.sol + +pragma solidity ^0.4.24; + + + +interface IToken { + function mintFor(address contributorAccount, uint256 amount, uint32 contributionId) public; +} + +interface ContributorInterface { + function getContributorAddressById(uint32 contributorId) public view returns (address); + function getContributorIdByAddress(address contributorAccount) public view returns (uint32); + // TODO Maybe use for validation + // function exists(uint32 contributorId) public view returns (bool); +} + +contract Contribution is AragonApp { + bytes32 public constant ADD_CONTRIBUTION_ROLE = keccak256("ADD_CONTRIBUTION_ROLE"); + bytes32 public constant VETO_CONTRIBUTION_ROLE = keccak256("VETO_CONTRIBUTION_ROLE"); + + bytes32 public constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb; + + // ensure alphabetic order + enum Apps { Contribution, Contributor, Proposal, Token } + bytes32[4] public appIds; + + struct ContributionData { + uint32 contributorId; + uint32 amount; + bool claimed; + bytes32 hashDigest; + uint8 hashFunction; + uint8 hashSize; + string tokenMetadataURL; + uint256 confirmedAtBlock; + bool vetoed; + bool exists; + } + + string internal name_; + string internal symbol_; + + // map contribution ID to contributor + mapping(uint32 => uint32) public contributionOwner; + // map contributor to contribution IDs + mapping(uint32 => uint32[]) public ownedContributions; + + mapping(uint32 => ContributionData) public contributions; + uint32 public contributionsCount; + + uint32 public blocksToWait; + + event ContributionAdded(uint32 id, uint32 indexed contributorId, uint32 amount); + event ContributionClaimed(uint32 id, uint32 indexed contributorId, uint32 amount); + event ContributionVetoed(uint32 id, address vetoedByAccount); + + function initialize(bytes32[4] _appIds) public onlyInit { + appIds = _appIds; + blocksToWait = 40320; // 7 days; 15 seconds block time + initialized(); + } + + // TODO refactor into a single function + function getTokenContract() public view returns (address) { + IKernel k = IKernel(kernel()); + return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[uint8(Apps.Token)]); + } + function getContributorContract() public view returns (address) { + IKernel k = IKernel(kernel()); + return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[uint8(Apps.Contributor)]); + } + + function getContributorIdByAddress(address contributorAccount) public view returns (uint32) { + address contributor = getContributorContract(); + return ContributorInterface(contributor).getContributorIdByAddress(contributorAccount); + } + + function getContributorAddressById(uint32 contributorId) public view returns (address) { + address contributor = getContributorContract(); + return ContributorInterface(contributor).getContributorAddressById(contributorId); + } + + // + // Token standard functions (ERC 721) + // + + function name() external view returns (string) { + return name_; + } + + function symbol() external view returns (string) { + return symbol_; + } + + // Balance is amount of ERC271 tokens, not amount of kredits + function balanceOf(address owner) public view returns (uint256) { + require(owner != address(0)); + uint32 contributorId = getContributorIdByAddress(owner); + return ownedContributions[contributorId].length; + } + + function ownerOf(uint32 contributionId) public view returns (address) { + require(exists(contributionId)); + uint32 contributorId = contributions[contributionId].contributorId; + return getContributorAddressById(contributorId); + } + + function tokenOfOwnerByIndex(address owner, uint32 index) public view returns (uint32) { + uint32 contributorId = getContributorIdByAddress(owner); + return ownedContributions[contributorId][index]; + } + + function tokenMetadata(uint32 contributionId) public view returns (string) { + return contributions[contributionId].tokenMetadataURL; + } + + // + // Custom functions + // + + function totalKreditsEarned(bool confirmedOnly) public view returns (uint32 amount) { + for (uint32 i = 1; i <= contributionsCount; i++) { + ContributionData memory c = contributions[i]; + if (!c.vetoed && (block.number >= c.confirmedAtBlock || !confirmedOnly)) { + amount += c.amount; // should use safemath + } + } + } + + function totalKreditsEarnedByContributor(uint32 contributorId, bool confirmedOnly) public view returns (uint32 amount) { + uint256 tokenCount = ownedContributions[contributorId].length; + for (uint256 i = 0; i < tokenCount; i++) { + uint32 cId = ownedContributions[contributorId][i]; + ContributionData memory c = contributions[cId]; + if (!c.vetoed && (block.number >= c.confirmedAtBlock || !confirmedOnly)) { + amount += c.amount; // should use safemath + } + } + } + + function getContribution(uint32 contributionId) public view returns (uint32 id, uint32 contributorId, uint32 amount, bool claimed, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint256 confirmedAtBlock, bool exists, bool vetoed) { + id = contributionId; + ContributionData storage c = contributions[id]; + return ( + id, + c.contributorId, + c.amount, + c.claimed, + c.hashDigest, + c.hashFunction, + c.hashSize, + c.confirmedAtBlock, + c.exists, + c.vetoed + ); + } + + function add(uint32 amount, uint32 contributorId, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(ADD_CONTRIBUTION_ROLE) { + //require(canPerform(msg.sender, ADD_CONTRIBUTION_ROLE, new uint32[](0)), 'nope'); + uint32 contributionId = contributionsCount + 1; + ContributionData storage c = contributions[contributionId]; + c.exists = true; + c.amount = amount; + c.claimed = false; + c.contributorId = contributorId; + c.hashDigest = hashDigest; + c.hashFunction = hashFunction; + c.hashSize = hashSize; + if (contributionId < 10) { + c.confirmedAtBlock = block.number; + } else { + c.confirmedAtBlock = block.number + blocksToWait; + } + + contributionsCount++; + + contributionOwner[contributionId] = contributorId; + ownedContributions[contributorId].push(contributionId); + + emit ContributionAdded(contributionId, contributorId, amount); + } + + function veto(uint32 contributionId) public isInitialized auth(VETO_CONTRIBUTION_ROLE) { + ContributionData storage c = contributions[contributionId]; + require(c.exists, 'NOT_FOUND'); + require(!c.claimed, 'ALREADY_CLAIMED'); + require(block.number < c.confirmedAtBlock, 'VETO_PERIOD_ENDED'); + c.vetoed = true; + + emit ContributionVetoed(contributionId, msg.sender); + } + + function claim(uint32 contributionId) public isInitialized { + ContributionData storage c = contributions[contributionId]; + require(c.exists, 'NOT_FOUND'); + require(!c.claimed, 'ALREADY_CLAIMED'); + require(!c.vetoed, 'VETOED'); + require(block.number >= c.confirmedAtBlock, 'NOT_CLAIMABLE'); + + c.claimed = true; + address token = getTokenContract(); + address contributorAccount = getContributorAddressById(c.contributorId); + uint256 amount = uint256(c.amount); + IToken(token).mintFor(contributorAccount, amount, contributionId); + emit ContributionClaimed(contributionId, c.contributorId, c.amount); + } + + function exists(uint32 contributionId) view public returns (bool) { + return contributions[contributionId].exists; + } +} diff --git a/apps/contributor/arapp.json b/apps/contributor/arapp.json index 58c090e..e26fa44 100644 --- a/apps/contributor/arapp.json +++ b/apps/contributor/arapp.json @@ -9,7 +9,7 @@ "environments": { "default": { "network": "development", - "appName": "kredits-contributor.aragonpm.eth" + "appName": "kredits-contributor.open.aragonpm.eth" }, "rinkeby": { "registry": "0x98df287b6c145399aaa709692c8d308357bc085d", diff --git a/apps/proposal/arapp.json b/apps/proposal/arapp.json index 71172d5..255d894 100644 --- a/apps/proposal/arapp.json +++ b/apps/proposal/arapp.json @@ -14,7 +14,7 @@ "environments": { "default": { "network": "development", - "appName": "kredits-proposal.aragonpm.eth" + "appName": "kredits-proposal.open.aragonpm.eth" }, "rinkeby": { "registry": "0x98df287b6c145399aaa709692c8d308357bc085d", diff --git a/apps/token/arapp.json b/apps/token/arapp.json index d3918ae..2197661 100644 --- a/apps/token/arapp.json +++ b/apps/token/arapp.json @@ -9,7 +9,7 @@ "environments": { "default": { "network": "development", - "appName": "kredits-token.aragonpm.eth" + "appName": "kredits-token.open.aragonpm.eth" }, "rinkeby": { "registry": "0x98df287b6c145399aaa709692c8d308357bc085d", diff --git a/arapp.json b/arapp.json index 6e0f2fc..4e3013e 100644 --- a/arapp.json +++ b/arapp.json @@ -21,7 +21,7 @@ "params": [] }, { - "name": "Add proposal", + "name": "Add proposal", "id": "ADD_PROPOSAL_ROLE", "params": [] }, @@ -34,15 +34,15 @@ "environments": { "development": { "network": "development", - "apm": "aragonpm.eth", + "apm": "open.aragonpm.eth", "registry": "0x5f6f7e8cc7346a11ca2def8f827b7a0b612c56a1", - "appName": "dummy.aragonpm.eth" + "appName": "dummy.open.aragonpm.eth" }, "rinkeby": { "network": "rinkeby", "registry": "0x98Df287B6C145399Aaa709692c8D308357bC085D", "wsRPC": "wss://rinkeby.eth.aragon.network/ws", - "daoFactory": "0x2298d27a9b847c681d2b2c2828ab9d79013f5f1d", + "daoFactory": "0x2298d27a9b847c681d2b2c2828ab9d79013f5f1d", "appName": "dummy.open.aragonpm.eth", "apm": "open.aragonpm.eth" }, diff --git a/lib/contracts/kernel.js b/lib/contracts/kernel.js index a9e875a..630736a 100644 --- a/lib/contracts/kernel.js +++ b/lib/contracts/kernel.js @@ -6,7 +6,7 @@ const KERNEL_APP_ADDR_NAMESPACE = '0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da class Kernel extends Base { constructor (contract) { super(contract); - this.apm = 'aragonpm.eth'; // can be overwritten if needed + this.apm = 'open.aragonpm.eth'; // can be overwritten if needed } getApp (appName) {