Compare commits

..

1 Commits

Author SHA1 Message Date
c5f98a1b69 Add balance when loading contributor
This is a regression introduced by the latest contract updates.
Now we return the balance again when loading contributor data.
2019-04-03 13:37:46 +02:00
97 changed files with 53938 additions and 146377 deletions

View File

@@ -1 +0,0 @@
/scripts/

View File

@@ -1,33 +0,0 @@
module.exports = {
'env': {
'browser': true,
'es6': true,
'node': true
},
'extends': 'eslint:recommended',
'globals': {
'Atomics': 'readonly',
'SharedArrayBuffer': 'readonly'
},
'parserOptions': {
'ecmaVersion': 2018,
'sourceType': 'module'
},
'rules': {
'comma-dangle': ['error', {
arrays: 'always-multiline',
objects: 'always-multiline',
imports: 'never',
exports: 'never',
functions: 'ignore',
}],
'eol-last': ['error', 'always'],
semi: ['error', 'always'],
'space-before-function-paren': ['error', {
anonymous: 'never',
named: 'always',
asyncArrow: 'always',
}],
'indent': ['error', 2]
}
}

2
.gitignore vendored
View File

@@ -4,5 +4,3 @@ node_modules
**/node_modules
.ganache-db
.tm_properties
yarn-error.log
.DS_Store

View File

@@ -1,9 +0,0 @@
{
"extends": [
"solhint:default",
"solhint:recommended"
],
"rules": {
"indent": "2"
}
}

View File

@@ -1,35 +0,0 @@
---
language: node_js
node_js:
- "lts/*"
sudo: false
dist: xenial
cache:
directories:
- node_modules
- apps/contribution/node_modules
- apps/contributor/node_modules
- apps/proposal/node_modules
- apps/token/node_modules
- apps/vault/node_modules
install:
- npm install -g @aragon/cli
- npm install -g truffle
- npm install
before_script:
- npm run devchain &
- ipfs daemon --offline &
script:
- npm run lint:wrapper
- npm run lint:contract-tests
- npm run test
- npm run bootstrap
branches:
only:
- master

View File

@@ -2,20 +2,17 @@
# Kredits Contracts
This repository contains the Solidity smart contracts organized as
[Aragon](https://hack.aragon.org/) apps and JavaScript API wrapper for [Kosmos
Kredits](https://wiki.kosmos.org/Kredits).
This repository contains the Solidity smart contracts organized as [Aragon](https://hack.aragon.org/)
apps and JavaScript API wrapper for [Kosmos Kredits](https://wiki.kosmos.org/Kredits).
It is based on [aragonOS](https://hack.aragon.org/docs/aragonos-intro.html) and
follows the aragonOS conventions. Aragon itself uses the [Truffle
framework](http://truffleframework.com/) for some things.
It is based on [aragonOS](https://hack.aragon.org/docs/aragonos-intro.html) and
follows the aragonOS conventions.
Aragon itself uses the [Truffle framework](http://truffleframework.com/) for some things.
## Development
### Installation
#### App dependencies
All requirements are defined in `package.json`.
$ npm install
@@ -25,43 +22,35 @@ Each of the aragon apps are separate packages:
$ cd apps/[app]
$ npm install
You can use `npm run install-all` to install all app dependencies at once.
#### Sytem dependencies
Aragon CLI and Truffle need to be installed on your sytem as well:
npm install -g @aragon/cli
npm install -g truffle
or use the bootstrap command (see below)
### Local development chain
For local development it is recommended to use
[ganache](http://truffleframework.com/ganache/) to run a local development
For local development it is recommended to use
[ganache](http://truffleframework.com/ganache/) to run a local development
chain. Using the ganache simulator no full Ethereum node is required.
We use the default aragon-cli devchain command to configure and run a local
We use the default aragon-cli devchain command to confgure and run a local
development ganache.
$ npm run devchain (or aragon devchain --port 7545)
To clear/reset the chain use (e.g. if you run out of funds on your devchain)
To clear/reset the chain use:
$ npm run devchain -- --reset (or aragon devchain --port 7545 --reset)
We default to port 7545 for development to not get in conflict with the default
We default to port 7545 for development to not get in conflict with the default
Ethereum RPC port.
### Bootstrap
1. Run an Ethereum node and ipfs
$ npm run devchain
$ ipfs daemon
2. Deploy each app to the devchain
(make sure you've run `npm install` for every app - see installation)
$ npm run deploy:apps
3. Deploy a new KreditsKit and create a new DAO with the latest app versions
@@ -69,28 +58,23 @@ Ethereum RPC port.
$ npm run deploy:kit
$ npm run deploy:dao
4. Execute seeds to create demo contributors, contributions, etc. (optional)
4. Execute seeds to create demo contributors, contributons, etc. (optional)
$ npm run seeds
**Step 2-4 is also summarized in `npm run bootstrap`**
If you want to reset your local setup:
$ npm run reset // deploys a new kit and a new DAO
$ npm run reset:hard // deploys all apps and does reset
## Contract architecture
Contracts are organized in independent apps (see `/apps`) and are developed and
deployed independently. Each app has a version and can be "installed" on the
Kredits DAO independently.
Contracts are organized in independent apps (see `/apps`) and are developed
and deployed independently. Each app has a version and can be "installed"
on the Kredits DAO independently.
![](docs/kredits-diagram.png)
A DAO can be deployed using the `scripts/deploy-kit.js` script or with the
`npm run deploy:dao` command. This deploys a new Kredits DAO, installs the
latest app versions and sets the required permissions.
A DAO can be deployed using the `scripts/deploy-kit.js` script or with the
`npm run deploy:dao` command. This deploys a new Kredits DAO, installs
the latest app versions and sets the required permissions.
See each app in `/apps/*` for details.
@@ -103,8 +87,6 @@ CLI. _At some point these should be moved into a real nice CLI._
To run these scripts use `truffle exec`. For example: `truffle exec
scripts/add-proposal.js`.
Some scripts are also defined as npm script, see package.json.
### cli.js
Call any function on any contract:
@@ -124,11 +106,11 @@ Script to add a new entries to the contracts using the JS wrapper
$ truffle exec scripts/add-{contributor, contribution, proposal}.js
### list-{contributors, contributions, proposals}.js
### list-{contributor, contribution, proposal}.js
List contract entries
$ truffle exec scripts/list-{contributors, contributions, proposals}.js
$ truffle exec scripts/list-{contributor, contribution, proposal}.js
### send-funds.js
@@ -161,7 +143,7 @@ Deploys a new KreditsKit that allows to create a new DAO
or
$ npm run deploy:kit
`ENS` address is required as environment variable.
`ENS` address is required as environment variable.
`DAO_FACTORY` can optionally be set as environment variable. (see aragon)
### new-dao.js
@@ -172,7 +154,7 @@ Creates and configures a new DAO instance.
or
$ npm run deploy:dao
KreditsKit address is loaded from `lib/addresses/KreditsKit.json` or can be
KreditsKit address is load from `lib/addresses/KreditsKit.json` or can be
configured through the `KREDITS_KIT` environment variable.
### deploy-apps.sh
@@ -183,32 +165,14 @@ Runs `npm install` for each app and publishes a new version.
or
$ npm run deploy:apps
## Deployment
### Apps deployment
To deploy a new app version run:
$ aragon apm publish major --environment=NETWORK_TO_DEPLOY
### KreditsKit
deploy the KreditsKit as Kit to create new DAOs
$ truffle exec scripts/deploy-kit.js --network=NETWORK_TO_DEPLOY
### Creating a new DAO
make sure all apps and the KreditsKit are deployed, then create a new DAO:
$ truffle exec scripts/new-dao.js --network=NETWORK_TO_DEPLOY
## ACL / Permissions
## Upgradeable contracts
We use aragonOS for upgradeability of the different contracts. Refer to the
[aragonOS upgradeablity documentation](https://hack.aragon.org/docs/upgradeability-intro)
We use aragonOS for upgradeablity of the different contracts.
Refer to the [aragonOS upgradeablity documentation](https://hack.aragon.org/docs/upgradeability-intro)
for more details.
### Example

View File

@@ -1,12 +0,0 @@
module.exports = {
'globals': {
contract: true,
describe: true,
it: true,
},
rules: {
'no-unused-vars': ['error', {
'argsIgnorePattern': '^_',
}],
}
}

View File

@@ -19,7 +19,7 @@
"environments": {
"default": {
"network": "development",
"appName": "kredits-contribution.open.aragonpm.eth"
"appName": "kredits-contribution.aragonpm.eth"
},
"rinkeby": {
"registry": "0x98df287b6c145399aaa709692c8d308357bc085d",

View File

@@ -1,912 +0,0 @@
{
"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
}
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -4,14 +4,7 @@ import "@aragon/os/contracts/apps/AragonApp.sol";
import "@aragon/os/contracts/kernel/IKernel.sol";
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);
function mintFor(address contributorAccount, uint256 amount, uint256 contributionId) public;
}
contract Contribution is AragonApp {
@@ -19,66 +12,48 @@ contract Contribution is AragonApp {
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;
address contributor;
uint256 amount;
bool claimed;
bytes32 hashDigest;
uint8 hashFunction;
uint8 hashSize;
string tokenMetadataURL;
uint256 confirmedAtBlock;
uint claimAfterBlock;
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(uint256 => address) contributionOwner;
mapping(address => uint256[]) ownedContributions;
mapping(uint32 => ContributionData) public contributions;
uint32 public contributionsCount;
mapping(uint256 => ContributionData) public contributions;
uint256 public contributionsCount;
uint32 public blocksToWait;
uint256 public blocksToWait = 0;
event ContributionAdded(uint32 id, uint32 indexed contributorId, uint32 amount);
event ContributionClaimed(uint32 id, uint32 indexed contributorId, uint32 amount);
event ContributionVetoed(uint32 id, address vetoedByAccount);
event ContributionAdded(uint256 id, address indexed contributor, uint256 amount);
event ContributionClaimed(uint256 id, address indexed contributor, uint256 amount);
event ContributionVetoed(uint256 id, address vetoedByAccount);
function initialize(bytes32[4] _appIds) public onlyInit {
appIds = _appIds;
blocksToWait = 40320; // 7 days; 15 seconds block time
initialized();
}
function getContract(uint8 appId) public view returns (address) {
function getTokenContract() public view returns (address) {
IKernel k = IKernel(kernel());
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[appId]);
}
function getContributorIdByAddress(address contributorAccount) public view returns (uint32) {
address contributor = getContract(uint8(Apps.Contributor));
return ContributorInterface(contributor).getContributorIdByAddress(contributorAccount);
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[uint8(Apps.Token)]);
}
function getContributorAddressById(uint32 contributorId) public view returns (address) {
address contributor = getContract(uint8(Apps.Contributor));
return ContributorInterface(contributor).getContributorAddressById(contributorId);
}
//
// Token standard functions (ERC 721)
//
function name() external view returns (string) {
return name_;
}
@@ -87,120 +62,85 @@ contract Contribution is AragonApp {
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;
return ownedContributions[owner].length;
}
function ownerOf(uint32 contributionId) public view returns (address) {
function ownerOf(uint256 contributionId) public view returns (address) {
require(exists(contributionId));
uint32 contributorId = contributions[contributionId].contributorId;
return getContributorAddressById(contributorId);
return contributions[contributionId].contributor;
}
function tokenOfOwnerByIndex(address owner, uint32 index) public view returns (uint32) {
uint32 contributorId = getContributorIdByAddress(owner);
return ownedContributions[contributorId][index];
function tokenOfOwnerByIndex(address contributor, uint256 index) public view returns (uint256) {
return ownedContributions[contributor][index];
}
function tokenMetadata(uint32 contributionId) public view returns (string) {
function tokenMetadata(uint256 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) {
function getContribution(uint256 contributionId) public view returns (uint256 id, address contributor, uint256 amount, bool claimed, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint claimAfterBlock, bool exists, bool vetoed) {
id = contributionId;
ContributionData storage c = contributions[id];
return (
id,
c.contributorId,
c.amount,
c.claimed,
c.contributor,
c.amount,
c.claimed,
c.hashDigest,
c.hashFunction,
c.hashSize,
c.confirmedAtBlock,
c.claimAfterBlock,
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;
function add(uint256 amount, address contributorAccount, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(ADD_CONTRIBUTION_ROLE) {
//require(canPerform(msg.sender, ADD_CONTRIBUTION_ROLE, new uint256[](0)), 'nope');
uint256 contributionId = contributionsCount + 1;
ContributionData storage c = contributions[contributionId];
c.exists = true;
c.amount = amount;
c.claimed = false;
c.contributorId = contributorId;
c.contributor = contributorAccount;
c.hashDigest = hashDigest;
c.hashFunction = hashFunction;
c.hashSize = hashSize;
if (contributionId < 10) {
c.confirmedAtBlock = block.number;
} else {
c.confirmedAtBlock = block.number + blocksToWait;
}
c.claimAfterBlock = block.number; // + blocksToWait;
contributionsCount++;
contributionOwner[contributionId] = contributorId;
ownedContributions[contributorId].push(contributionId);
emit ContributionAdded(contributionId, contributorId, amount);
contributionOwner[contributionId] = contributorAccount;
ownedContributions[contributorAccount].push(contributionId);
emit ContributionAdded(contributionId, contributorAccount, amount);
}
function veto(uint32 contributionId) public isInitialized auth(VETO_CONTRIBUTION_ROLE) {
function veto(uint256 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 {
function claim(uint256 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');
require(block.number > c.claimAfterBlock, 'NOT_CLAIMABLE');
c.claimed = true;
address token = getContract(uint8(Apps.Token));
address contributorAccount = getContributorAddressById(c.contributorId);
uint256 amount = uint256(c.amount);
IToken(token).mintFor(contributorAccount, amount, contributionId);
emit ContributionClaimed(contributionId, c.contributorId, c.amount);
address token = getTokenContract();
IToken(token).mintFor(c.contributor, c.amount, contributionId);
emit ContributionClaimed(contributionId, c.contributor, c.amount);
}
function exists(uint32 contributionId) public view returns (bool) {
function exists(uint256 contributionId) view public returns (bool) {
return contributions[contributionId].exists;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -3,30 +3,25 @@
"version": "1.0.0",
"description": "",
"dependencies": {
"@aragon/os": "^4.2.0",
"@aragon/cli": "^5.9.6"
},
"devDependencies": {
"@aragon/test-helpers": "^2.0.0",
"eth-gas-reporter": "^0.2.0",
"ganache-cli": "^6.4.3",
"solidity-coverage": "^0.5.11"
"@aragon/os": "^4.1.0",
"@aragon/cli": "^5.5.0"
},
"devDependencies": {},
"scripts": {
"start": "npm run start:aragon:ipfs",
"start:aragon:ipfs": "aragon run",
"start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist",
"start:app": "",
"start:app": "npm run sync-assets && npm run build:script -- --no-minify && parcel serve app/index.html -p 8001 --out-dir dist/ --no-cache",
"test": "aragon contracts test",
"compile": "aragon contracts compile",
"sync-assets": "",
"sync-assets": "copy-aragon-ui-assets -n aragon-ui ./dist",
"build:app": "",
"build:script": "",
"build": "",
"publish:patch": "aragon apm publish patch",
"publish:minor": "aragon apm publish minor",
"publish:major": "aragon apm publish major",
"versions": "aragon apm versions",
"test": "aragon contracts test"
"versions": "aragon apm versions"
},
"keywords": []
}

View File

@@ -1,5 +1,5 @@
// const Contribution = artifacts.require('Contribution.sol');
const CounterApp = artifacts.require('Contribution.sol')
contract('Contribution', (_accounts) => {
it('should be tested');
});
contract('Contribution', (accounts) => {
it('should be tested')
})

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,7 @@
"environments": {
"default": {
"network": "development",
"appName": "kredits-contributor.open.aragonpm.eth"
"appName": "kredits-contributor.aragonpm.eth"
},
"rinkeby": {
"registry": "0x98df287b6c145399aaa709692c8d308357bc085d",

View File

@@ -6,83 +6,87 @@ import "@aragon/os/contracts/kernel/IKernel.sol";
interface ITokenBalance {
function balanceOf(address contributorAccount) public view returns (uint256);
}
interface IContributionBalance {
function totalKreditsEarnedByContributor(uint32 contributorId, bool confirmedOnly) public view returns (uint32 amount);
function balanceOf(address owner) public view returns (uint256);
}
contract Contributor is AragonApp {
bytes32 public constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb;
bytes32 public constant MANAGE_CONTRIBUTORS_ROLE = keccak256("MANAGE_CONTRIBUTORS_ROLE");
struct Contributor {
address account;
bytes32 hashDigest;
bytes32 ipfsHash;
uint8 hashFunction;
uint8 hashSize;
bool isCore;
bool exists;
}
mapping (address => uint32) public contributorIds;
mapping (uint32 => Contributor) public contributors;
uint32 public contributorsCount;
mapping (address => uint) public contributorIds;
mapping (uint => Contributor) public contributors;
uint256 public contributorsCount;
// ensure alphabetic order
enum Apps { Contribution, Contributor, Proposal, Token }
bytes32[4] public appIds;
event ContributorProfileUpdated(uint32 id, bytes32 oldHashDigest, bytes32 newHashDigest); // what should be logged
event ContributorAccountUpdated(uint32 id, address oldAccount, address newAccount);
event ContributorAdded(uint32 id, address account);
event ContributorProfileUpdated(uint id, bytes32 oldIpfsHash, bytes32 newIpfsHash);
event ContributorAccountUpdated(uint id, address oldAccount, address newAccount);
event ContributorAdded(uint id, address account);
function initialize(address root,bytes32[4] _appIds) public onlyInit {
uint _id = contributorsCount + 1;
Contributor storage c = contributors[_id];
c.exists = true;
c.isCore = true;
c.account = root;
contributorIds[root] = _id;
contributorsCount += 1;
appIds = _appIds;
initialized();
}
function getContract(uint8 appId) public view returns (address) {
function getTokenContract() public view returns (address) {
IKernel k = IKernel(kernel());
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[appId]);
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[uint8(Apps.Token)]);
}
function coreContributorsCount() public view returns (uint32) {
uint32 count = 0;
for (uint32 i = 1; i <= contributorsCount; i++) {
if (isCoreTeam(i)) {
function coreContributorsCount() view public returns (uint) {
uint count = 0;
for (uint256 i = 1; i <= contributorsCount; i++) {
if (contributors[i].isCore) {
count += 1;
}
}
return count;
}
function updateContributorAccount(uint32 id, address oldAccount, address newAccount) public auth(MANAGE_CONTRIBUTORS_ROLE) {
require(newAccount != address(0), "invalid new account address");
require(getContributorAddressById(id) == oldAccount, "contributor does not exist");
function updateContributorAccount(uint id, address oldAccount, address newAccount) public auth(MANAGE_CONTRIBUTORS_ROLE) {
contributorIds[oldAccount] = 0;
contributorIds[newAccount] = id;
contributors[id].account = newAccount;
emit ContributorAccountUpdated(id, oldAccount, newAccount);
ContributorAccountUpdated(id, oldAccount, newAccount);
}
function updateContributorProfileHash(uint32 id, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(MANAGE_CONTRIBUTORS_ROLE) {
function updateContributorIpfsHash(uint id, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize) public isInitialized auth(MANAGE_CONTRIBUTORS_ROLE) {
Contributor storage c = contributors[id];
bytes32 oldHashDigest = c.hashDigest;
c.hashDigest = hashDigest;
bytes32 oldIpfsHash = c.ipfsHash;
c.ipfsHash = ipfsHash;
c.hashFunction = hashFunction;
c.hashSize = hashSize;
ContributorProfileUpdated(id, oldHashDigest, c.hashDigest);
ContributorProfileUpdated(id, oldIpfsHash, c.ipfsHash);
}
function addContributor(address account, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(MANAGE_CONTRIBUTORS_ROLE) {
function addContributor(address account, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize, bool isCore) public isInitialized auth(MANAGE_CONTRIBUTORS_ROLE) {
require(!addressExists(account));
uint32 _id = contributorsCount + 1;
uint _id = contributorsCount + 1;
assert(!contributors[_id].exists); // this can not be acually
Contributor storage c = contributors[_id];
c.exists = true;
c.hashDigest = hashDigest;
c.isCore = isCore;
c.ipfsHash = ipfsHash;
c.hashFunction = hashFunction;
c.hashSize = hashSize;
c.account = account;
@@ -92,64 +96,49 @@ contract Contributor is AragonApp {
emit ContributorAdded(_id, account);
}
function isCoreTeam(uint32 id) view public returns (bool) {
// TODO: for simplicity we simply define the first contributors as core
// later this needs to be changed to something more dynamic
return id < 7;
function isCore(uint id) view public returns (bool) {
return contributors[id].isCore;
}
function exists(uint32 id) view public returns (bool) {
function exists(uint id) view public returns (bool) {
return contributors[id].exists;
}
function addressIsCore(address account) view public returns (bool) {
uint32 id = getContributorIdByAddress(account);
return isCoreTeam(id);
return getContributorByAddress(account).isCore;
}
function addressExists(address account) view public returns (bool) {
return getContributorByAddress(account).exists;
}
function getContributorIdByAddress(address account) view public returns (uint32) {
function getContributorIdByAddress(address account) view public returns (uint) {
return contributorIds[account];
}
function getContributorAddressById(uint32 id) view public returns (address) {
function getContributorAddressById(uint id) view public returns (address) {
return contributors[id].account;
}
function getContributorByAddress(address account) internal view returns (Contributor) {
uint32 id = contributorIds[account];
uint id = contributorIds[account];
return contributors[id];
}
function getContributorById(uint32 _id) public view returns (uint32 id, address account, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, bool isCore, uint256 balance, uint32 totalKreditsEarned, uint256 contributionsCount, bool exists ) {
function getContributorById(uint _id) public view returns (uint id, address account, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize, bool isCore, uint256 balance, bool exists ) {
id = _id;
Contributor storage c = contributors[_id];
account = c.account;
hashDigest = c.hashDigest;
ipfsHash = c.ipfsHash;
hashFunction = c.hashFunction;
hashSize = c.hashSize;
isCore = isCoreTeam(id);
address token = getContract(uint8(Apps.Token));
isCore = c.isCore;
address token = getTokenContract();
balance = ITokenBalance(token).balanceOf(c.account);
address contribution = getContract(uint8(Apps.Contribution));
totalKreditsEarned = IContributionBalance(contribution).totalKreditsEarnedByContributor(_id, true);
contributionsCount = IContributionBalance(contribution).balanceOf(c.account);
exists = c.exists;
}
function canPerform(address _who, address _where, bytes32 _what, uint256[] memory _how) public returns (bool) {
address sender = _who;
if (sender == address(-1)) {
sender = tx.origin;
}
// _what == keccak256('VOTE_PROPOSAL_ROLE')
if (_what == 0xd61216798314d2fc33e42ff2021d66707b1e38517d3f7166798a9d3a196a9c96) {
return contributorIds[sender] != uint256(0);
}
return addressIsCore(sender);
function canPerform(address _who, address _where, bytes32 _what, uint256[] _how) public view returns (bool) {
return addressExists(_who);
}
}

View File

@@ -1,16 +0,0 @@
pragma solidity ^0.4.24;
import "@aragon/os/contracts/acl/ACL.sol";
import "@aragon/os/contracts/kernel/Kernel.sol";
import "@aragon/os/contracts/factory/DAOFactory.sol";
// You might think this file is a bit odd, but let me explain.
// We only use for now those imported contracts in our tests, which
// means Truffle will not compile them for us, because they are from
// an external dependency.
// solium-disable-next-line no-empty-blocks
contract Spoof {
// ...
}

File diff suppressed because it is too large Load Diff

View File

@@ -3,20 +3,16 @@
"version": "1.0.0",
"description": "",
"dependencies": {
"@aragon/os": "^4.2.0",
"@aragon/cli": "^5.9.6"
},
"devDependencies": {
"eth-gas-reporter": "^0.2.0",
"ganache-cli": "^6.4.3",
"solidity-coverage": "^0.5.11",
"@aragon/test-helpers": "^2.0.0"
"@aragon/os": "^4.1.0",
"@aragon/cli": "^5.5.0"
},
"devDependencies": {},
"scripts": {
"start": "npm run start:aragon:ipfs",
"start:aragon:ipfs": "aragon run",
"start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist",
"start:app": "",
"test": "aragon contracts test",
"compile": "aragon contracts compile",
"sync-assets": "",
"build:app": "",
@@ -25,8 +21,7 @@
"publish:patch": "aragon apm publish patch",
"publish:minor": "aragon apm publish minor",
"publish:major": "aragon apm publish major",
"versions": "aragon apm versions",
"test": "aragon contracts test"
"versions": "aragon apm versions"
},
"keywords": []
}

View File

@@ -0,0 +1,5 @@
const CounterApp = artifacts.require('CounterApp.sol')
contract('CounterApp', (accounts) => {
it('should be tested')
})

View File

@@ -1,170 +0,0 @@
const namehash = require('ethers').utils.namehash;
// eslint-disable-next-line no-undef
const Contributor = artifacts.require("Contributor.sol");
// eslint-disable-next-line no-undef
const getContract = name => artifacts.require(name);
const { assertRevert } = require('@aragon/test-helpers/assertThrow');
const ZERO_ADDR = '0x0000000000000000000000000000000000000000';
contract('Contributor app', (accounts) => {
let kernelBase, aclBase, daoFactory, r, dao, acl, contributor;
const root = accounts[0];
const member1 = accounts[1];
// eslint-disable-next-line no-undef
before(async () => {
kernelBase = await getContract('Kernel').new(true); // petrify immediately
aclBase = await getContract('ACL').new();
daoFactory = await getContract('DAOFactory').new(kernelBase.address, aclBase.address, ZERO_ADDR);
r = await daoFactory.newDAO(root);
dao = getContract('Kernel').at(r.logs.filter(l => l.event == 'DeployDAO')[0].args.dao);
acl = getContract('ACL').at(await dao.acl());
//create dao mamnager permission for coin owner
await acl.createPermission(
root,
dao.address,
await dao.APP_MANAGER_ROLE(),
root,
{ from: root }
);
//get new app instance from DAO
const receipt = await dao.newAppInstance(
'0x1234',
(await Contributor.new()).address,
0x0,
false,
{ from: root }
);
contributor = Contributor.at(
receipt.logs.filter(l => l.event == 'NewAppProxy')[0].args.proxy
);
//apps id
let appsId = [];
appsId[0] = namehash("kredits-contribution");
appsId[1] = namehash("kredits-contributor");
appsId[2] = namehash("kredits-proposal");
appsId[3] = namehash("kredits-token");
//init contributor (app)
await contributor.initialize(root, appsId);
//create manage contributors role
await acl.createPermission(
root,
contributor.address,
await contributor.MANAGE_CONTRIBUTORS_ROLE(),
root,
{ from: root }
);
});
describe("Owner default permissions", async () => {
it('check owner is contributors manager', async () => {
let manageContributorPermission = await acl.hasPermission(root, contributor.address, await contributor.MANAGE_CONTRIBUTORS_ROLE());
// eslint-disable-next-line no-undef
assert.equal(manageContributorPermission, true);
});
});
describe("Add contributor", async () => {
let account = root;
let hashDigest = '0x0';
let hashFunction = 0;
let hashSize = 0;
it("should revert when add contributor from an address that does not have permission", async () => {
return assertRevert(async () => {
await contributor.addContributor(account, hashDigest, hashFunction, hashSize, { from: member1});
'sender does not have permission';
});
});
it('add contributor', async () => {
let contributorCount = await contributor.coreContributorsCount();
await contributor.addContributor(account, hashDigest, hashFunction, hashSize);
// eslint-disable-next-line no-undef
assert.equal(await contributor.addressExists(account), true);
let contributorCountAfter = await contributor.coreContributorsCount();
// eslint-disable-next-line no-undef
assert.equal(await contributorCountAfter.toNumber(), parseInt(contributorCount)+1);
});
it("should revert when add contributor with an address that already exist", async () => {
return assertRevert(async () => {
await contributor.addContributor(account, hashDigest, hashFunction, hashSize, { from: member1});
'address already exist';
});
});
});
describe("Update contributor", async () => {
let id;
let oldAccount;
let newAccount;
let hashDigest;
let hashFunction;
let hashSize;
// eslint-disable-next-line no-undef
before(async () => {
id = await contributor.coreContributorsCount();
oldAccount = root;
newAccount = member1;
hashDigest = '0x1000000000000000000000000000000000000000000000000000000000000000';
hashFunction = 1;
hashSize = 1;
});
it('update contributor account', async () => {
await contributor.updateContributorAccount(id.toNumber(), oldAccount, newAccount);
let contributorId = await contributor.getContributorIdByAddress(oldAccount);
// eslint-disable-next-line no-undef
assert.equal(contributorId.toNumber(), 0);
});
it("should revert when update contributor account from address that does not have permission", async () => {
return assertRevert(async () => {
await contributor.updateContributorAccount(id.toNumber(), oldAccount, newAccount, {from: member1});
'sender does not have permission';
});
});
it("should revert when update contributor account that does not exist", async () => {
return assertRevert(async () => {
await contributor.updateContributorAccount(id.toNumber(), accounts[3], newAccount);
'contributor does not exist';
});
});
it("should revert when update contributor account with address(0)", async () => {
return assertRevert(async () => {
await contributor.updateContributorAccount(id.toNumber(), oldAccount, ZERO_ADDR);
'contributor does not exist';
});
});
it('update contributor profile hash', async () => {
await contributor.updateContributorProfileHash(id.toNumber(), hashDigest, hashFunction, hashSize);
let contributorProfile = await contributor.contributors(id.toNumber());
assert.equal(hashDigest, contributorProfile[1]); // eslint-disable-line no-undef
assert.equal(hashFunction, contributorProfile[2].toNumber()); // eslint-disable-line no-undef
assert.equal(hashSize, contributorProfile[3].toNumber()); // eslint-disable-line no-undef
});
it("should revert when update contributor profile hash from address that does not have permission", async () => {
return assertRevert(async () => {
await contributor.updateContributorProfileHash(id.toNumber(), hashDigest, hashFunction, hashSize, {from: member1});
'sender does not have permission';
});
});
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -14,7 +14,7 @@
"environments": {
"default": {
"network": "development",
"appName": "kredits-proposal.open.aragonpm.eth"
"appName": "kredits-proposal.aragonpm.eth"
},
"rinkeby": {
"registry": "0x98df287b6c145399aaa709692c8d308357bc085d",

View File

@@ -4,13 +4,13 @@ import "@aragon/os/contracts/apps/AragonApp.sol";
import "@aragon/os/contracts/kernel/IKernel.sol";
interface IContributor {
function getContributorAddressById(uint32 contributorId) public view returns (address);
function getContributorIdByAddress(address contributorAccount) public view returns (uint32);
function exists(uint32 contributorId) public view returns (bool);
function getContributorAddressById(uint256 contributorId) public view returns (address);
function getContributorIdByAddress(address contributorAccount) public view returns (uint256);
function exists(uint256 contributorId) public view returns (bool);
}
interface IContribution {
function add(uint32 amount, uint32 contributorId, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public;
function add(uint256 amount, address contributor, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public;
}
contract Proposal is AragonApp {
@@ -25,70 +25,73 @@ contract Proposal is AragonApp {
struct Proposal {
address creatorAccount;
uint32 contributorId;
uint16 votesCount;
uint16 votesNeeded;
uint32 amount;
uint contributorId;
uint votesCount;
uint votesNeeded;
uint256 amount;
bool executed;
bytes32 hashDigest;
uint8 hashFunction;
uint8 hashSize;
uint32[] voterIds;
mapping (uint32 => bool) votes;
uint256[] voterIds;
mapping (uint256 => bool) votes;
bool exists;
}
mapping(uint32 => Proposal) public proposals;
uint32 public proposalsCount;
mapping(uint256 => Proposal) public proposals;
uint256 public proposalsCount;
event ProposalCreated(uint32 id, address creatorAccount, uint32 contributorId, uint32 amount);
event ProposalCreated(uint256 id, address creatorAccount, uint256 contributorId, uint256 amount);
event ProposalVoted(uint32 id, uint32 voterId, uint16 totalVotes);
event ProposalExecuted(uint32 id, uint32 contributorId, uint32 amount);
event ProposalVoted(uint256 id, uint256 voterId, uint256 totalVotes);
event ProposalExecuted(uint256 id, uint256 contributorId, uint256 amount);
function initialize(bytes32[4] _appIds) public onlyInit {
appIds = _appIds;
initialized();
}
function getContract(uint8 appId) public view returns (address) {
IKernel k = IKernel(kernel());
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[appId]);
function getContributorContract() public view returns (address) {
return IKernel(kernel()).getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[uint8(Apps.Contributor)]);
}
function addProposal(uint32 contributorId, uint32 amount, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(ADD_PROPOSAL_ROLE) {
require(IContributor(getContract(uint8(Apps.Contributor))).exists(contributorId), 'CONTRIBUTOR_NOT_FOUND');
function getContributionContract() public view returns (address) {
return IKernel(kernel()).getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[uint8(Apps.Contribution)]);
}
uint32 proposalId = proposalsCount + 1;
uint16 _votesNeeded = 1; //contributorsContract().coreContributorsCount() / 100 * 75;
function addProposal(uint contributorId, uint256 amount, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(ADD_PROPOSAL_ROLE) {
require(IContributor(getContributorContract()).exists(contributorId), 'CONTRIBUTOR_NOT_FOUND');
uint256 proposalId = proposalsCount + 1;
uint256 _votesNeeded = 1; //contributorsContract().coreContributorsCount() / 100 * 75;
Proposal storage p = proposals[proposalId];
p.creatorAccount = msg.sender;
p.contributorId = contributorId;
p.amount = amount;
p.hashDigest = hashDigest;
p.hashDigest = hashDigest;
p.hashFunction = hashFunction;
p.hashSize = hashSize;
p.votesCount = 0;
p.hashSize = hashSize;
p.votesCount = 0;
p.votesNeeded = _votesNeeded;
p.exists = true;
proposalsCount++;
emit ProposalCreated(proposalId, msg.sender, p.contributorId, p.amount);
}
function getProposal(uint32 proposalId) public view returns (uint32 id, address creatorAccount, uint32 contributorId, uint16 votesCount, uint16 votesNeeded, uint32 amount, bool executed, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint32[] voterIds, bool exists) {
function getProposal(uint proposalId) public view returns (uint256 id, address creatorAccount, uint256 contributorId, uint256 votesCount, uint256 votesNeeded, uint256 amount, bool executed, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint256[] voterIds, bool exists) {
id = proposalId;
Proposal storage p = proposals[id];
return (
id,
p.creatorAccount,
p.contributorId,
p.votesCount,
p.contributorId,
p.votesCount,
p.votesNeeded,
p.amount,
p.executed,
p.hashDigest,
p.executed,
p.hashDigest,
p.hashFunction,
p.hashSize,
p.voterIds,
@@ -96,10 +99,10 @@ contract Proposal is AragonApp {
);
}
function vote(uint32 proposalId) public isInitialized auth(VOTE_PROPOSAL_ROLE) {
function vote(uint256 proposalId) public isInitialized auth(VOTE_PROPOSAL_ROLE) {
Proposal storage p = proposals[proposalId];
require(!p.executed, 'ALREADY_EXECUTED');
uint32 voterId = IContributor(getContract(uint8(Apps.Contributor))).getContributorIdByAddress(msg.sender);
uint256 voterId = IContributor(getContributorContract()).getContributorIdByAddress(msg.sender);
require(p.votes[voterId] != true, 'ALREADY_VOTED');
p.voterIds.push(voterId);
p.votes[voterId] = true;
@@ -111,19 +114,20 @@ contract Proposal is AragonApp {
emit ProposalVoted(proposalId, voterId, p.votesCount);
}
function batchVote(uint32[] _proposalIds) public isInitialized auth(VOTE_PROPOSAL_ROLE) {
for (uint32 i = 0; i < _proposalIds.length; i++) {
function batchVote(uint256[] _proposalIds) public isInitialized auth(VOTE_PROPOSAL_ROLE) {
for (uint256 i = 0; i < _proposalIds.length; i++) {
vote(_proposalIds[i]);
}
}
function executeProposal(uint32 proposalId) private {
function executeProposal(uint proposalId) private {
Proposal storage p = proposals[proposalId];
require(!p.executed, 'ALREADY_EXECUTED');
require(p.votesCount >= p.votesNeeded, 'MISSING_VOTES');
p.executed = true;
IContribution(getContract(uint8(Apps.Contribution))).add(p.amount, p.contributorId, p.hashDigest, p.hashFunction, p.hashSize);
address contributorAccount = IContributor(getContributorContract()).getContributorAddressById(p.contributorId);
IContribution(getContributionContract()).add(p.amount, contributorAccount, p.hashDigest, p.hashFunction, p.hashSize);
emit ProposalExecuted(proposalId, p.contributorId, p.amount);
}

View File

@@ -1,4 +1,4 @@
{
"name": "Proposal",
"description": "Kredits Proposal app"
"description": "Kredits proposal app"
}

File diff suppressed because it is too large Load Diff

View File

@@ -3,20 +3,16 @@
"version": "1.0.0",
"description": "",
"dependencies": {
"@aragon/os": "^4.2.0",
"@aragon/cli": "^5.9.6"
},
"devDependencies": {
"@aragon/test-helpers": "^2.0.0",
"eth-gas-reporter": "^0.2.0",
"ganache-cli": "^6.4.3",
"solidity-coverage": "^0.5.11"
"@aragon/os": "^4.1.0",
"@aragon/cli": "^5.5.0"
},
"devDependencies": {},
"scripts": {
"start": "npm run start:aragon:ipfs",
"start:aragon:ipfs": "aragon run",
"start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist",
"start:app": "",
"test": "aragon contracts test",
"compile": "aragon contracts compile",
"sync-assets": "",
"build:app": "",
@@ -25,8 +21,7 @@
"publish:patch": "aragon apm publish patch",
"publish:minor": "aragon apm publish minor",
"publish:major": "aragon apm publish major",
"versions": "aragon apm versions",
"test": "aragon contracts test"
"versions": "aragon apm versions"
},
"keywords": []
}

View File

@@ -1,5 +0,0 @@
// const Proposal = artifacts.require('Proposal.sol');
contract('Proposal', (_accounts) => {
it('should be tested');
});

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,7 @@
"environments": {
"default": {
"network": "development",
"appName": "kredits-token.open.aragonpm.eth"
"appName": "kredits-token.aragonpm.eth"
},
"rinkeby": {
"registry": "0x98df287b6c145399aaa709692c8d308357bc085d",

View File

@@ -9,22 +9,16 @@ contract Token is ERC20Token, AragonApp {
// ensure alphabetic order
enum Apps { Contribution, Contributor, Proposal, Token }
bytes32[4] public appIds;
event LogMint(address indexed recipient, uint256 amount, uint32 contributionId);
event LogMint(address indexed recipient, uint256 amount, uint256 contributionId);
function initialize(bytes32[4] _appIds) public onlyInit {
appIds = _appIds;
name = 'Kredits';
symbol = '₭S';
decimals = 18;
initialized();
}
function mintFor(address contributorAccount, uint256 amount, uint32 contributionId) public isInitialized auth(MINT_TOKEN_ROLE) {
require(amount > 0, "INVALID_AMOUNT");
uint256 amountInWei = amount.mul(1 ether);
_mint(contributorAccount, amountInWei);
function mintFor(address contributorAccount, uint256 amount, uint256 contributionId) public isInitialized auth(MINT_TOKEN_ROLE) {
_mint(contributorAccount, amount);
emit LogMint(contributorAccount, amount, contributionId);
}

View File

@@ -1,16 +0,0 @@
pragma solidity ^0.4.24;
import "@aragon/os/contracts/acl/ACL.sol";
import "@aragon/os/contracts/kernel/Kernel.sol";
import "@aragon/os/contracts/factory/DAOFactory.sol";
// You might think this file is a bit odd, but let me explain.
// We only use for now those imported contracts in our tests, which
// means Truffle will not compile them for us, because they are from
// an external dependency.
// solium-disable-next-line no-empty-blocks
contract Spoof {
// ...
}

View File

@@ -1,4 +1,4 @@
{
"name": "Token",
"description": "Kredits Token app"
"description": "Kredits token app"
}

File diff suppressed because it is too large Load Diff

View File

@@ -3,20 +3,16 @@
"version": "1.0.0",
"description": "",
"dependencies": {
"@aragon/os": "^4.2.0",
"@aragon/cli": "^5.9.6"
},
"devDependencies": {
"@aragon/test-helpers": "^2.0.0",
"eth-gas-reporter": "^0.2.0",
"ganache-cli": "^6.4.3",
"solidity-coverage": "^0.5.11"
"@aragon/os": "^4.1.0",
"@aragon/cli": "^5.5.0"
},
"devDependencies": {},
"scripts": {
"start": "npm run start:aragon:ipfs",
"start:aragon:ipfs": "aragon run",
"start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist",
"start:app": "",
"test": "aragon contracts test",
"compile": "aragon contracts compile",
"sync-assets": "",
"build:app": "",
@@ -25,8 +21,7 @@
"publish:patch": "aragon apm publish patch",
"publish:minor": "aragon apm publish minor",
"publish:major": "aragon apm publish major",
"versions": "aragon apm versions",
"test": "aragon contracts test"
"versions": "aragon apm versions"
},
"keywords": []
}

5
apps/token/test/app.js Normal file
View File

@@ -0,0 +1,5 @@
const CounterApp = artifacts.require('CounterApp.sol')
contract('CounterApp', (accounts) => {
it('should be tested')
})

View File

@@ -1,123 +0,0 @@
const namehash = require('ethers').utils.namehash;
// eslint-disable-next-line no-undef
const Token = artifacts.require("Token.sol");
// eslint-disable-next-line no-undef
const getContract = name => artifacts.require(name);
const { assertRevert } = require('@aragon/test-helpers/assertThrow');
const ZERO_ADDR = '0x0000000000000000000000000000000000000000';
contract('Token app', (accounts) => {
let kernelBase, aclBase, daoFactory, dao, r, acl, token;
const root = accounts[0];
const member1 = accounts[1];
// eslint-disable-next-line no-undef
before(async () => {
kernelBase = await getContract('Kernel').new(true); // petrify immediately
aclBase = await getContract('ACL').new();
daoFactory = await getContract('DAOFactory').new(kernelBase.address, aclBase.address, ZERO_ADDR);
r = await daoFactory.newDAO(root);
dao = getContract('Kernel').at(r.logs.filter(l => l.event == 'DeployDAO')[0].args.dao);
acl = getContract('ACL').at(await dao.acl());
//create dao mamnager permission for coin owner
await acl.createPermission(
root,
dao.address,
await dao.APP_MANAGER_ROLE(),
root,
{ from: root }
);
//get new app instance from DAO
const receipt = await dao.newAppInstance(
'0x1234',
(await Token.new()).address,
0x0,
false,
{ from: root }
);
token = Token.at(
receipt.logs.filter(l => l.event == 'NewAppProxy')[0].args.proxy
);
//apps id
let appsId = [];
appsId[0] = namehash("kredits-contribution");
appsId[1] = namehash("kredits-contributor");
appsId[2] = namehash("kredits-proposal");
appsId[3] = namehash("kredits-token");
//init token (app)
await token.initialize(appsId);
//create token mint permission for coin owner
await acl.createPermission(
root,
token.address,
await token.MINT_TOKEN_ROLE(),
root,
{ from: root }
);
});
describe("Owner default space permissions", async () => {
it('check owner is token issuer', async () => {
let tokenIssuerPermission = await acl.hasPermission(root, token.address, await token.MINT_TOKEN_ROLE());
// eslint-disable-next-line no-undef
assert.equal(tokenIssuerPermission, true);
});
});
describe("Token issuing", async () => {
let name = "Kredits";
let symbol = "₭S";
let decimals = 18;
it("check token properties", async () => {
assert.equal(await token.name(), name); // eslint-disable-line no-undef
assert.equal(await token.symbol(), symbol); // eslint-disable-line no-undef
assert.equal(await token.decimals(), decimals); // eslint-disable-line no-undef
});
});
describe("Token minting", async () => {
let tokenToMint = 250;
let ether = 1000000000000000000;
it("should revert when mint tokens from an address that does not have minting permission", async () => {
return assertRevert(async () => {
await token.mintFor(root, tokenToMint, 1, { from: member1});
'address does not have permission to mint tokens';
});
});
it("should revert when mint tokens to address(0)", async () => {
return assertRevert(async () => {
await token.mintFor(ZERO_ADDR, tokenToMint, 1, { from: root});
'invalid contributor address';
});
});
it("should revert when mint amount of tokens equal to 0", async () => {
return assertRevert(async () => {
await token.mintFor(root, 0, 1, { from: root});
'amount to mint should be greater than zero';
});
});
it("mint tokens", async () => {
await token.mintFor(root, tokenToMint, 1, { from: root });
let ownerBalance = await token.balanceOf(root);
let totalSupply = await token.totalSupply();
assert.equal(ownerBalance.toNumber(), tokenToMint*ether); // eslint-disable-line no-undef
assert.equal(totalSupply.toNumber(), tokenToMint*ether); // eslint-disable-line no-undef
});
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -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": "open.aragonpm.eth",
"apm": "aragonpm.eth",
"registry": "0x5f6f7e8cc7346a11ca2def8f827b7a0b612c56a1",
"appName": "dummy.open.aragonpm.eth"
"appName": "dummy.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"
},

View File

@@ -1,48 +1,14 @@
const contractCalls = [
['Contributor', 'add', [{
account: '0x7e8f313c56f809188313aa274fa67ee58c31515d',
name: 'bumi',
kind: 'person',
url: '',
github_username: 'bumi',
github_uid: 318,
gitea_username: 'bumi',
wiki_username: 'Bumi',
}, { gasLimit: 200000 }]],
['Contributor', 'add', [{
account: '0x49575f3DD9a0d60aE661BC992f72D837A77f05Bc',
name: 'raucao',
kind: 'person',
url: '',
github_username: 'skddc',
github_uid: 842,
gitea_username: 'raucao',
wiki_username: 'Basti',
}, { gasLimit: 200000 }]],
['Contributor', 'add', [{
account: '0xF722709ECC3B05c19d02E82a2a4A4021B8F48C62',
name: 'Manuel',
kind: 'person',
url: '',
github_username: 'fsmanuel',
github_uid: 54812,
wiki_username: 'Manuel',
}, { gasLimit: 200000 }]],
['Proposal', 'add', [{ contributorId: 1, contributorIpfsHash: 'QmWKCYGr2rSf6abUPaTYqf98urvoZxGrb7dbspFZA6oyVF', date: '2019-04-09', amount: 500, kind: 'dev', description: '[67P/kredits-contracts] Ran the seeds', url: '' }, { gasLimit: 350000 }]],
['Proposal', 'add', [{ contributorId: 2, contributorIpfsHash: 'QmcHzEeAM26HV2zHTf5HnZrCtCtGdEccL5kUtDakAB7ozB', date: '2019-04-10', amount: 500, kind: 'dev', description: '[67P/kredits-contracts] Ran the seeds', url: '' }, { gasLimit: 350000 }]],
['Proposal', 'add', [{ contributorId: 2, contributorIpfsHash: 'QmcHzEeAM26HV2zHTf5HnZrCtCtGdEccL5kUtDakAB7ozB', date: '2019-04-11', amount: 500, kind: 'dev', description: '[67P/kredits-contracts] Hacked on kredits', url: '' }, { gasLimit: 350000 }]],
['Proposal', 'vote', [1, { gasLimit: 550000 }]],
['Contribution', 'add', [{ contributorId: 1, contributorIpfsHash: 'QmWKCYGr2rSf6abUPaTYqf98urvoZxGrb7dbspFZA6oyVF', date: '2019-04-11', amount: 5000, kind: 'dev', description: '[67P/kredits-contracts] Introduce contribution token', url: '' }, { gasLimit: 350000 }]],
['Contribution', 'add', [{ contributorId: 2, contributorIpfsHash: 'QmcHzEeAM26HV2zHTf5HnZrCtCtGdEccL5kUtDakAB7ozB', date: '2019-04-11', amount: 1500, kind: 'dev', description: '[67P/kredits-web] Reviewed stuff', url: '' }, { gasLimit: 350000 }]],
['Contribution', 'claim', [1, { gasLimit: 300000 }]],
let contractCalls = [
['Contributor', 'add', [{ account: '0x7e8f313c56f809188313aa274fa67ee58c31515d', name: 'bumi', isCore: true, kind: 'preson', url: '', github_username: 'bumi', github_uid: 318, wiki_username: 'bumi' }, {gasLimit: 200000}]],
['Contributor', 'add', [{ account: '0xa502eb4021f3b9ab62f75b57a94e1cfbf81fd827', name: 'raucau', isCore: true, kind: 'person', url: '', github_username: 'skddc', github_uid: 842, wiki_username: 'raucau' }, {gasLimit: 200000}]],
['Proposal', 'addProposal', [{ contributorId: 2, amount: 42, kind: 'code', description: 'runs the seeds', url: '' }, {gasLimit: 350000}]],
['Proposal', 'addProposal', [{ contributorId: 3, amount: 23, kind: 'code', description: 'runs the seeds', url: '' }, {gasLimit: 350000}]],
['Proposal', 'addProposal', [{contributorId: 3, amount: 100, kind: 'code', description: 'hacks on kredits', url: '' }, {gasLimit: 350000}]],
['Proposal', 'vote', [1, {gasLimit: 550000}]],
['Contribution', 'addContribution', [{contributorAccount: '0xa502eb4021f3b9ab62f75b57a94e1cfbf81fd827', amount: 100, kind: 'code', description: 'hacks on kredits', url: '' }, {gasLimit: 350000}]],
];
const funds = [
let funds = [
'0x7e8f313c56f809188313aa274fa67ee58c31515d',
'0xa502eb4021f3b9ab62f75b57a94e1cfbf81fd827',
'0xa502eb4021f3b9ab62f75b57a94e1cfbf81fd827'
];
module.exports = { contractCalls, funds };

View File

@@ -33,43 +33,40 @@ contract KreditsKit is KitBase {
Contributor contributor = Contributor(_installApp(dao, appIds[uint8(Apps.Contributor)]));
contributor.initialize(root, appIds);
acl.createPermission(root, contributor, contributor.MANAGE_CONTRIBUTORS_ROLE(), this);
acl.createPermission(root, contributor, contributor.MANAGE_CONTRIBUTORS_ROLE(), root);
Token token = Token(_installApp(dao, appIds[uint8(Apps.Token)]));
token.initialize(appIds);
Contribution contribution = Contribution(_installApp(dao, appIds[uint8(Apps.Contribution)]));
contribution.initialize(appIds);
Proposal proposal = Proposal(_installApp(dao, appIds[uint8(Apps.Proposal)]));
proposal.initialize(appIds);
acl.createPermission(root, contribution, contribution.ADD_CONTRIBUTION_ROLE(), this);
acl.createPermission(root, contribution, contribution.VETO_CONTRIBUTION_ROLE(), this);
acl.grantPermission(proposal, contribution, contribution.ADD_CONTRIBUTION_ROLE());
uint256[] memory params = new uint256[](1);
params[0] = uint256(203) << 248 | uint256(1) << 240 | uint240(contributor);
acl.grantPermissionP(acl.ANY_ENTITY(), contribution, contribution.ADD_CONTRIBUTION_ROLE(), params);
acl.grantPermissionP(acl.ANY_ENTITY(), contribution, contribution.VETO_CONTRIBUTION_ROLE(), params);
acl.grantPermissionP(acl.ANY_ENTITY(), contributor, contributor.MANAGE_CONTRIBUTORS_ROLE(), params);
acl.grantPermissionP(root, contribution, contribution.ADD_CONTRIBUTION_ROLE(), params);
acl.grantPermissionP(root, contribution, contribution.VETO_CONTRIBUTION_ROLE(), params);
//acl.setPermissionManager(this, proposal, proposal.VOTE_PROPOSAL_ROLE();
acl.createPermission(root, proposal, proposal.VOTE_PROPOSAL_ROLE(), this);
acl.grantPermissionP(acl.ANY_ENTITY(), proposal, proposal.VOTE_PROPOSAL_ROLE(), params);
acl.grantPermissionP(root, proposal, proposal.VOTE_PROPOSAL_ROLE(), params);
acl.createPermission(root, proposal, proposal.ADD_PROPOSAL_ROLE(), this);
//acl.grantPermissionP(address(-1), proposal, proposal.ADD_PROPOSAL_ROLE(), params);
acl.grantPermission(acl.ANY_ENTITY(), proposal, proposal.ADD_PROPOSAL_ROLE());
acl.grantPermissionP(root, proposal, proposal.ADD_PROPOSAL_ROLE(), params);
acl.setPermissionManager(root, proposal, proposal.VOTE_PROPOSAL_ROLE());
acl.setPermissionManager(root, proposal, proposal.ADD_PROPOSAL_ROLE());
acl.setPermissionManager(root, contribution, contribution.ADD_CONTRIBUTION_ROLE());
acl.setPermissionManager(root, contribution, contribution.VETO_CONTRIBUTION_ROLE());
acl.setPermissionManager(root, contributor, contributor.MANAGE_CONTRIBUTORS_ROLE());
acl.createPermission(root, token, token.MINT_TOKEN_ROLE(), this);
acl.grantPermission(contribution, token, token.MINT_TOKEN_ROLE());
acl.grantPermission(contribution, token, token.MINT_TOKEN_ROLE());
acl.setPermissionManager(root, token, token.MINT_TOKEN_ROLE());

View File

@@ -1,30 +1,5 @@
# Contribution deployments
aragon apm publish major --environment=rinkeby"
## 2019-04-24 update balances
✔ Successfully published kredits-contribution.open.aragonpm.eth v6.0.0:
Contract address: 0x2c083EEA83fd3a99C93759D97D0317A43261c758
Content (ipfs): QmULpSqz7BgTFmDu8AL7YZZEz525xkcEzf3dPKtbRdUtFs
Transaction hash: 0x8b01c4c00162e918659d267a2beaf33b578e2aaf9f427f1aa9a43029333c5cd7
## 2019-04-10 - Weltempfänger release
✔ Successfully published kredits-contribution.open.aragonpm.eth v5.0.0:
Contract address: 0xe0f7dB486321b917e3A986Bdb2F2b9d51BA98fa9
Content (ipfs): QmU3XEBb4f5jU8MFFEpwaa95C1mhc82UeYLRWLrKsvcQNw
Transaction hash: 0xd736ff5f79f8142be3fad1a50580fb40aa468838da397f8630285fd91a445af3
## 2019-04-04
✔ Successfully published kredits-contribution.open.aragonpm.eth v4.0.0:
Contract address: 0x7485e8fbde0112C53587079db450Eb002D359372
Content (ipfs): QmZKxYPm8wz4phgL4428Gh7MjmbyFFDMP2st7qhNL9qGDQ
Transaction hash: 0x7d9be7920db675be88e8f60ebf08dc62ee77c4e6e454c5fbf0f91f6ac97e4c26
## 2019-03-26
### v3.0.0 appids

View File

@@ -1,37 +1,5 @@
# Contributor deployments
aragon apm publish major --environment=rinkeby
## 2019-04-25 canPerform fix
✔ Successfully published kredits-contributor.open.aragonpm.eth v6.0.0:
Contract address: 0xA5379D49C718845A1BD7720c6BE3872bA69906cc
Content (ipfs): QmdennNV6s2FNpe6QNYxrUsUXPVdnQGvh1vCi22Tqs8ojq
Transaction hash: 0x51077afeff70a24e87c78bb23ea13bdb9b4445bd43ea7a74a4178fadfeeb6c35
## 2019-04-24 update balances
✔ Successfully published kredits-contributor.open.aragonpm.eth v5.0.0:
Contract address: 0xadefa3b66b68a127Fe38bEa1813b844EE69CFD86
Content (ipfs): QmeygbQgoj2McLWzo9hJayLWuBZqFaK4HTpa5qLeQdkn5K
Transaction hash: 0x4237a9636f6e4a8190e0d5bcfa85a452da097bf654a173a88e0e1de3d078f08d
## 2019-04-10 - Weltempfänger release
✔ Successfully published kredits-contributor.open.aragonpm.eth v4.0.0:
Contract address: 0x08a6D4D915FCAA5524F05F5F715a6C17cB6eeA6B
Content (ipfs): QmR62PWwe1EzommfkhJDYcTvHoZjbXuv9dTG6vCn5dWCsb
Transaction hash: 0xd5317c9e207a413485c55ec3046b09d467d978443680304737a6d7d3db0c90e1
## 2019-04-04
✔ Successfully published kredits-contributor.open.aragonpm.eth v3.0.0:
Contract address: 0x95D8458E28C8de7216279512601A3B9Dc512D70B
Content (ipfs): QmSpHnmsf8FybvqfD49kwxvDixwiBUR7D5yG4BWSnnwCPS
Transaction hash: 0x736f9751b07a91178f453c279ecb57604b3b5686ae5d7115880f3b27fbeb50f2
## 2019-03-26
### v2.0.0 support for appids

View File

@@ -1,35 +1,5 @@
# Kredits deployment
## 2019-04-25 canPerfom fix
aragon dao upgrade 0xc34edf7d11b7f8433d597f0bb0697acdff55ef14 kredits-contributor.open.aragonpm.eth --environment=rinkeby
✔ Fetching kredits-contributor.open.aragonpm.eth@latest
✔ Upgrading app
✔ Successfully executed: "Set the resolving address of 'kredits-contributor.open.aragonpm.eth' in namespace 'App code' to 0xA5379D49C718845A1BD7720c6BE3872bA69906cc"
## 2019-04-24 upgrade contributor and contribution
aragon dao upgrade 0xc34edf7d11b7f8433d597f0bb0697acdff55ef14 kredits-contributor.open.aragonpm.eth --environment=rinkeby
eth-provider | Invalid provider preset/location: "local"
✔ Fetching kredits-contributor.open.aragonpm.eth@latest
✔ Upgrading app
✔ Successfully executed: "Set the resolving address of 'kredits-contributor.open.aragonpm.eth' in namespace 'App code' to 0xadefa3b66b68a127Fe38bEa1813b844EE69CFD86"
aragon dao upgrade 0xc34edf7d11b7f8433d597f0bb0697acdff55ef14 kredits-contribution.open.aragonpm.eth --environment=rinkeby
✔ Fetching kredits-contribution.open.aragonpm.eth@latest
✔ Upgrading app
✔ Successfully executed: "Set the resolving address of 'kredits-contribution.open.aragonpm.eth' in namespace 'App code' to 0x2c083EEA83fd3a99C93759D97D0317A43261c758"
## 2019-04-10 - Weltempfänger release
Using KreditsKit at: 0x76e069b47b79442657eaf0555a32c6b16fa1b8b4
Created new DAO at: 0xc34edf7d11b7f8433d597f0bb0697acdff55ef14
## 2019-04-04
Using KreditsKit at: 0x76e069b47b79442657eaf0555a32c6b16fa1b8b4
Created new DAO at: 0xcd75458fbc4aa2231252d5b21f1391fd031e5cb2
### 2019-03-26
kredits/truffle-kredits@aragonos » truffle exec scripts/new-dao.js --network=rinkeby

View File

@@ -1,13 +1,5 @@
# KreditsKit deployments
## 2019-04-04
Deploying to networkId: 4
Using ENS at: 0x98Df287B6C145399Aaa709692c8D308357bC085D
Using DAOFactory at: 0x2298d27a9b847c681d2b2c2828ab9d79013f5f1d
Found apps: [contribution,contributor,proposal,token].open.aragonpm.eth
Deployed KreditsKit at: 0x76e069b47b79442657eaf0555a32c6b16fa1b8b4
## 2019-03-26
Using network 'rinkeby'.

View File

@@ -1,21 +1,5 @@
# Proposal deployments
## 2019-04-10 - Weltempfänger release
✔ Successfully published kredits-proposal.open.aragonpm.eth v5.0.0:
Contract address: 0x4ce5b0286483c66b861e5599a199054687434552
Content (ipfs): QmNYXEcmvKTGxYiob7WUf85oZhdmFDCuGiA6TsRrDE9TYb
Transaction hash: 0x0482b58a1ba87d494c6391026399d0ac41b45384330d916f3f99ba70e501584b
## 2019-04-04
✔ Successfully published kredits-proposal.open.aragonpm.eth v4.0.0:
Contract address: 0x4993275362Ba50D76f349A262B7b842e7FbD7490
Content (ipfs): QmesCPKLapASPTB3rnPpSdD7ULkg8BQwRcpdE1B8WwfSh7
Transaction hash: 0xe4be7b1a75b663eb345b957b216a8476b0f75e2bbfc5880943b05c82fab15dff
## 2019-03-26
### v3.0.0 appids

View File

@@ -1,21 +1,5 @@
# Token deployments
## 2019-04-10 - Weltempfänger release
✔ Successfully published kredits-token.open.aragonpm.eth v4.0.0:
Contract address: 0x05E0C2bbdA8e5BeE22AC1E20C1457dA4de63aE26
Content (ipfs): QmUuYLRMRNZcundUk2pxVaSjMNvtB7hzdf3eyoaUNqDPow
Transaction hash: 0x98c28b5ca645904d56eb83c4783682f018c0fcee015b3a3d5fa8bd609223fb89
## 2019-04-04
✔ Successfully published kredits-token.open.aragonpm.eth v3.0.0:
Contract address: 0xd9913A96e087f50E71BF14c62cBCa3b9635392A9
Content (ipfs): QmfZ7LwjkQRuLsEnVjrEF3ryTaHsL6YTxffiiDS5KWmHQG
Transaction hash: 0x102b27e154d1e144ec084f5e8dc78e1626fda9f26c20db3e3b230fac381c317a
## 2019-03-26
### v2.0.0 support for appIds

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
[{"constant":true,"inputs":[],"name":"ens","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"fac","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"appId","type":"bytes32"}],"name":"latestVersionAppBase","outputs":[{"name":"base","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"appIds","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_fac","type":"address"},{"name":"_ens","type":"address"},{"name":"_appIds","type":"bytes32[4]"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"dao","type":"address"}],"name":"DeployInstance","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"dao","type":"address"},{"indexed":false,"name":"appProxy","type":"address"},{"indexed":false,"name":"appId","type":"bytes32"}],"name":"InstalledApp","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"appProxy","type":"address"},{"indexed":false,"name":"appId","type":"bytes32"}],"name":"InstalledApp","type":"event"},{"constant":false,"inputs":[],"name":"newInstance","outputs":[{"name":"dao","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,4 @@
{
"4": "0x76e069b47b79442657eaf0555a32c6b16fa1b8b4"
}
"4": "0x1d6a9c2146a330575ee860eef9a012b5ff7caa68",
"41787949": "0xa35aacdfccac54d3d96e0d29050c773b251c2c83"
}

View File

@@ -1,3 +1,4 @@
{
"4": "0xc34edf7d11b7f8433d597f0bb0697acdff55ef14"
}
"4": "0x95a7ce185efc2d1f13efd2a00ee9247c51ea7009",
"41787949": "0x183af3950364390a266edff2a0e7c4c2f95c0691"
}

View File

@@ -2,15 +2,11 @@ const Base = require('./base');
const EthersUtils = require('ethers').utils;
class Acl extends Base {
hasPermission (fromAddress, contractAddress, roleID, params = null) {
let roleHash = EthersUtils.keccak256(EthersUtils.toUtf8Bytes(roleID));
return this.functions.hasPermission(
fromAddress,
contractAddress,
roleHash,
params
);
hasPermission(fromAddress, contractAddress, roleID, params = null) {
let roleHash = EthersUtils.keccak256(EthersUtils.toUtf8Bytes(roleID));
console.log(roleHash)
return this.functions.hasPermission(fromAddress, contractAddress, roleHash, params);
}
}

View File

@@ -1,28 +1,23 @@
class Base {
constructor (contract) {
constructor(contract) {
this.contract = contract;
}
get functions () {
get functions() {
return this.contract.functions;
}
get address () {
return this.contract.address;
}
get ipfs () {
get ipfs() {
if (!this._ipfsAPI) { throw new Error('IPFS API not configured; please set an ipfs instance'); }
return this._ipfsAPI;
}
set ipfs (ipfsAPI) {
set ipfs(ipfsAPI) {
this._ipfsAPI = ipfsAPI;
}
on (type, callback) {
on(type, callback) {
return this.contract.on(type, callback);
}
}
module.exports = Base;

View File

@@ -1,58 +1,62 @@
const Record = require('./record');
const ethers = require('ethers');
const RSVP = require('rsvp');
const ContributionSerializer = require('../serializers/contribution');
const deprecate = require('../utils/deprecate');
const Base = require('./base');
class Contribution extends Record {
get count () {
return this.functions.contributionsCount();
}
class Contribution extends Base {
all() {
return this.functions.contributionsCount()
.then((count) => {
count = count.toNumber();
let contributions = [];
getById (id) {
return this.functions.getContribution(id)
.then(data => {
return this.ipfs.catAndMerge(data, ContributionSerializer.deserialize);
});
}
getData (id) {
return this.functions.getContribution(id);
}
getByContributorId (contributorId) {
return this.functions.getContributorAddressById(contributorId)
.then(address => this.getByContributorAddress(address));
}
getByContributorAddress (address) {
return this.functions.balanceOf(address)
.then(async (balance) => {
const count = balance.toNumber();
const contributions = [];
for (let index = 0; index < count; index++) {
const id = await this.functions.tokenOfOwnerByIndex(address, index);
const contribution = await this.getById(id);
contributions.push(contribution);
for (let id = 1; id <= count; id++) {
contributions.push(this.getById(id));
}
return contributions;
return RSVP.all(contributions);
});
}
async add (contributionAttr, callOptions = {}) {
const contribution = new ContributionSerializer(contributionAttr);
getById(id) {
id = ethers.utils.bigNumberify(id);
try { await contribution.validate(); }
catch (error) { return Promise.reject(error); }
return this.functions.getContribution(id)
.then((data) => {
return this.ipfs.catAndMerge(data, ContributionSerializer.deserialize);
});
const jsonStr = contribution.serialize();
}
getByContributor(contributor) {
return this.functions.balanceOf(contributor)
then((balance) => {
count = balance.toNumber();
let contributions = [];
for (let index = 0; index <= count; index++) {
this.functions.tokenOfOwnerByIndex(contributor, index)
.then((id) => {
contributions.push(this.getById(id));
});
}
return RSVP.all(contributions);
});
}
addContribution(contributionAttr, callOptions = {}) {
let json = ContributionSerializer.serialize(contributionAttr);
// TODO: validate against schema
return this.ipfs
.add(jsonStr)
.then(ipfsHashAttr => {
.add(json)
.then((ipfsHashAttr) => {
let contribution = [
contributionAttr.amount,
contributionAttr.contributorId,
contributionAttr.contributorAccount,
ipfsHashAttr.hashDigest,
ipfsHashAttr.hashFunction,
ipfsHashAttr.hashSize,
@@ -61,12 +65,6 @@ class Contribution extends Record {
return this.functions.add(...contribution, callOptions);
});
}
addContribution () {
deprecate('The function `addContribution()` is deprecated and will be removed in the next major version. Use `add()` instead');
return this.add(...arguments);
}
}
module.exports = Contribution;

View File

@@ -1,33 +1,48 @@
const Record = require('./record');
const ContributorSerializer = require('../serializers/contributor');
const formatKredits = require('../utils/format-kredits');
const ethers = require('ethers');
const RSVP = require('rsvp');
class Contributor extends Record {
get count () {
return this.functions.contributorsCount();
const ContributorSerializer = require('../serializers/contributor');
const Base = require('./base');
class Contributor extends Base {
all() {
return this.functions.contributorsCount()
.then((count) => {
count = count.toNumber();
let contributors = [];
for (let id = 1; id <= count; id++) {
contributors.push(this.getById(id));
}
return RSVP.all(contributors);
});
}
getById (id) {
getById(id) {
id = ethers.utils.bigNumberify(id);
return this.functions.getContributorById(id)
.then(data => {
data.balanceInt = formatKredits(data.balance);
.then((data) => {
// TODO: remove when naming updated on the contract
data.hashDigest = data.ipfsHash;
return data;
})
// Fetch IPFS data if available
.then((data) => {
return this.ipfs.catAndMerge(data, ContributorSerializer.deserialize);
});
}
getData (id) {
return this.functions.getContributorById(id);
}
filterByAccount (search) {
filterByAccount(search) {
return this._byAccount(search, 'filter');
}
findByAccount (search) {
findByAccount(search) {
return this._byAccount(search, 'find');
}
_byAccount (search, method = 'filter') {
_byAccount(search, method = 'filter') {
return this.all().then((contributors) => {
const searchEntries = Object.entries(search);
@@ -43,52 +58,24 @@ class Contributor extends Record {
});
}
async add (contributorAttr, callOptions = {}) {
let contributor = new ContributorSerializer(contributorAttr);
try { await contributor.validate(); }
catch (error) { return Promise.reject(error); }
const jsonStr = contributor.serialize();
add(contributorAttr, callOptions = {}) {
let json = ContributorSerializer.serialize(contributorAttr);
// TODO: validate against schema
return this.ipfs
.add(jsonStr)
.add(json)
.then((ipfsHashAttr) => {
let contributor = [
contributorAttr.account,
ipfsHashAttr.hashDigest,
ipfsHashAttr.hashFunction,
ipfsHashAttr.hashSize,
contributorAttr.isCore,
];
return this.functions.addContributor(...contributor, callOptions);
});
}
updateProfile (contributorId, updateAttr, callOptions = {}) {
return this.getById(contributorId).then(async (contributor) => {
let updatedContributorAttr = Object.assign(contributor, updateAttr);
let updatedContributor = new ContributorSerializer(updatedContributorAttr);
try { await updatedContributor.validate(); }
catch (error) { return Promise.reject(error); }
const jsonStr = updatedContributor.serialize();
return this.ipfs
.add(jsonStr)
.then(ipfsHashAttr => {
return this.functions.updateContributorProfileHash(
contributorId,
ipfsHashAttr.hashDigest,
ipfsHashAttr.hashFunction,
ipfsHashAttr.hashSize,
callOptions
);
});
});
}
}
module.exports = Contributor;

View File

@@ -4,5 +4,5 @@ module.exports = {
Proposal: require('./proposal'),
Token: require('./token'),
Kernel: require('./kernel'),
Acl: require('./acl'),
Acl: require('./acl')
};

View File

@@ -1,22 +1,22 @@
const namehash = require('ethers').utils.namehash;
const namehash = require('eth-ens-namehash').hash;
const Base = require('./base');
const KERNEL_APP_ADDR_NAMESPACE = '0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb';
class Kernel extends Base {
constructor (contract) {
constructor(contract) {
super(contract);
this.apm = 'open.aragonpm.eth'; // can be overwritten if needed
this.apm = 'aragonpm.eth'; // can be overwritten if needed
}
getApp (appName) {
getApp(appName) {
if (appName === 'Acl') {
return this.functions.acl();
}
return this.functions.getApp(KERNEL_APP_ADDR_NAMESPACE, this.appNamehash(appName));
}
appNamehash (appName) {
appNamehash(appName) {
return namehash(`kredits-${appName.toLowerCase()}.${this.apm}`);
}
}

View File

@@ -1,29 +1,39 @@
const Record = require('./record');
const ContributionSerializer = require('../serializers/contribution');
const deprecate = require('../utils/deprecate');
const ethers = require('ethers');
const RSVP = require('rsvp');
class Proposal extends Record {
get count () {
return this.functions.proposalsCount();
const ContributionSerializer = require('../serializers/contribution');
const Base = require('./base');
class Proposal extends Base {
all() {
return this.functions.proposalsCount()
.then((count) => {
count = count.toNumber();
let proposals = [];
for (let id = 1; id <= count; id++) {
proposals.push(this.getById(id));
}
return RSVP.all(proposals);
});
}
getById (id) {
getById(id) {
id = ethers.utils.bigNumberify(id);
return this.functions.getProposal(id)
.then(data => {
.then((data) => {
return this.ipfs.catAndMerge(data, ContributionSerializer.deserialize);
});
}
async add (proposalAttr, callOptions = {}) {
const contribution = new ContributionSerializer(proposalAttr);
try { await contribution.validate(); }
catch (error) { return Promise.reject(error); }
const jsonStr = contribution.serialize();
addProposal(proposalAttr, callOptions = {}) {
let json = ContributionSerializer.serialize(proposalAttr);
// TODO: validate against schema
return this.ipfs
.add(jsonStr)
.add(json)
.then((ipfsHashAttr) => {
let proposal = [
proposalAttr.contributorId,
@@ -36,11 +46,6 @@ class Proposal extends Record {
return this.functions.addProposal(...proposal, callOptions);
});
}
addProposal () {
deprecate('The function `addProposal()` is deprecated and will be removed in the next major version. Use `add()` instead');
return this.add(...arguments);
}
}
module.exports = Proposal;
module.exports = Proposal

View File

@@ -1,14 +0,0 @@
const Base = require('./base');
const paged = require('../utils/pagination');
class Record extends Base {
all (options = {}) {
return this.count
.then((count) => {
let records = paged(count, options).map((id) => this.getById(id));
return Promise.all(records);
});
}
}
module.exports = Record;

View File

@@ -4,3 +4,4 @@ class Token extends Base {
}
module.exports = Token;

View File

@@ -1,7 +1,7 @@
const ethers = require('ethers');
const RSVP = require('rsvp');
const Preflight = require('./utils/preflight');
const deprecate = require('./utils/deprecate');
const ABIS = {
Contributor: require('./abis/Contributor.json'),
@@ -9,29 +9,29 @@ const ABIS = {
Token: require('./abis/Token.json'),
Proposal: require('./abis/Proposal.json'),
Kernel: require('./abis/Kernel.json'),
Acl: require('./abis/ACL.json'),
Acl: require('./abis/ACL.json')
};
const APP_CONTRACTS = [
'Contributor',
'Contribution',
'Token',
'Proposal',
'Acl',
'Acl'
];
const DaoAddresses = require('./addresses/dao.json');
const Contracts = require('./contracts');
const IPFS = require('./utils/ipfs');
const IPFS = require('./utils/ipfs')
// Helpers
function capitalize (word) {
function capitalize(word) {
let [first, ...rest] = word;
return `${first.toUpperCase()}${rest.join('')}`;
}
class Kredits {
constructor (provider, signer, options = {}) {
constructor(provider, signer, options = {}) {
let { addresses, abis, ipfsConfig } = options;
this.provider = provider;
@@ -43,7 +43,7 @@ class Kredits {
this.contracts = {};
}
init (names) {
init(names) {
let contractsToLoad = names || APP_CONTRACTS;
return this.provider.getNetwork().then(network => {
this.addresses['Kernel'] = this.addresses['Kernel'] || DaoAddresses[network.chainId.toString()];
@@ -51,43 +51,22 @@ class Kredits {
return this.Kernel.getApp(contractName).then((address) => {
this.addresses[contractName] = address;
}).catch((error) => {
console.log(error);
throw new Error(`Failed to get address for ${contractName} from DAO at ${this.Kernel.contract.address}
- ${error.message}`
);
});
});
return Promise.all(addressPromises).then(() => { return this; });
return RSVP.all(addressPromises).then(() => { return this });
});
}
static setup (provider, signer, ipfsConfig = null) {
deprecate('Kredits.setup() is deprecated use new Kredits().init() instead');
static setup(provider, signer, ipfsConfig = null) {
console.log('Kredits.setup() is deprecated use new Kredits().init() instead');
return new Kredits(provider, signer, { ipfsConfig: ipfsConfig }).init();
}
static for (connectionOptions, kreditsOptions) {
let { network, rpcUrl, wallet } = connectionOptions;
if (!rpcUrl && network === 'local') { rpcUrl = 'http://localhost:8545'; }
let ethProvider, signer;
if (rpcUrl) {
ethProvider = new ethers.providers.JsonRpcProvider(rpcUrl);
} else {
ethProvider = new ethers.getDefaultProvider(network);
}
if (wallet) {
signer = wallet.connect(ethProvider);
} else if (ethProvider.getSigner) {
signer = ethProvider.getSigner();
}
return new Kredits(ethProvider, signer, kreditsOptions);
}
static availableNetworks () {
return Object.keys(DaoAddresses);
}
get Kernel () {
get Kernel() {
let k = this.contractFor('Kernel');
// in case we want to use a special apm (e.g. development vs. production)
if (this.options.apm) {
@@ -96,37 +75,37 @@ class Kredits {
return k;
}
get Contributor () {
get Contributor() {
return this.contractFor('Contributor');
}
get Contributors () {
deprecate('Contributors is deprecated use Contributor instead');
get Contributors() {
console.log('Contributors is deprecated use Contributor instead');
return this.Contributor;
}
get Proposal () {
get Proposal() {
return this.contractFor('Proposal');
}
get Operator () {
get Operator() {
return this.Proposal;
}
get Token () {
get Token() {
return this.contractFor('Token');
}
get Contribution () {
get Contribution() {
return this.contractFor('Contribution');
}
get Acl () {
get Acl() {
return this.contractFor('Acl');
}
// Should be private
contractFor (name) {
contractFor(name) {
if (this.contracts[name]) {
return this.contracts[name];
}
@@ -146,7 +125,7 @@ class Kredits {
return this.contracts[name];
}
preflightChecks () {
preflightChecks() {
return new Preflight(this).check();
}
}

View File

@@ -1,49 +0,0 @@
const ethers = require('ethers');
const ABI = require('./abis/KreditsKit.json');
const Addresses = require('./addresses/KreditsKit.json');
class KreditsKit {
constructor (provider, signer, options = {}) {
let { address, abi } = options;
this.provider = provider;
this.signer = signer;
this.options = options;
this.address = address;
this.abi = abi || ABI;
}
init () {
return this.provider.getNetwork().then((network) => {
this.address = this.address || Addresses[network.chainId.toString()];
this.contract = new ethers.Contract(
this.address,
this.abi,
(this.signer || this.provider)
);
return this;
});
}
appIdFor (contractName) {
// see appIds in KreditsKit.sol for more details
const knownContracts = ['Contribution', 'Contributor', 'Proposal', 'Token'];
return this.contract.functions.appIds(knownContracts.indexOf(contractName));
}
newDAO (options = {}) {
return this.contract.functions.newInstance(options).then(transaction => {
return transaction.wait().then(result => {
const deployEvent = result.events.find(e => e.event === 'DeployInstance');
return {
daoAddress: deployEvent.args.dao,
transactionHash: transaction.hash,
};
});
});
}
}
module.exports = KreditsKit;

View File

@@ -1,75 +1,19 @@
const schemas = require('kosmos-schemas');
const validator = require('../utils/validator');
/**
* Serialization and validation for JSON-LD document of the contribution.
* Handle serialization for JSON-LD object of the contribution, according to
* https://github.com/67P/kosmos-schemas/blob/master/schemas/contribution.json
*
* @class
* @public
*/
class Contribution {
constructor (attrs) {
Object.keys(attrs).forEach(a => this[a] = attrs[a]);
}
/**
* Serialize object to JSON
*
* @public
*/
serialize () {
/**
* Deserialize JSON to object
*
* @method
* @public
*/
static deserialize(serialized) {
let {
contributorIpfsHash,
date,
time,
kind,
description,
url,
details,
} = this;
let data = {
'@context': 'https://schema.kosmos.org',
'@type': 'Contribution',
'contributor': {
'ipfs': contributorIpfsHash,
},
date,
time,
kind,
description,
'details': details || {},
};
if (url) {
data['url'] = url;
}
// Write it pretty to ipfs
return JSON.stringify(data, null, 2);
}
/**
* Validate serialized data against schema
*
* @public
*/
validate () {
const serialized = JSON.parse(this.serialize());
const valid = validator.validate(serialized, schemas['contribution']);
return valid ? Promise.resolve() : Promise.reject(validator.error);
}
/**
* Deserialize JSON to object
*
* @public
*/
static deserialize (serialized) {
let {
date,
time,
kind,
description,
details,
@@ -77,8 +21,6 @@ class Contribution {
} = JSON.parse(serialized.toString('utf8'));
return {
date,
time,
kind,
description,
details,
@@ -87,6 +29,39 @@ class Contribution {
};
}
/**
* Serialize object to JSON
*
* @method
* @public
*/
static serialize(deserialized) {
let {
contributorIpfsHash,
kind,
description,
url,
details
} = deserialized;
let data = {
"@context": "https://schema.kosmos.org",
"@type": "Contribution",
"contributor": {
"ipfs": contributorIpfsHash
},
kind,
description,
"details": details || {}
};
if (url) {
data["url"] = url;
}
// Write it pretty to ipfs
return JSON.stringify(data, null, 2);
}
}
module.exports = Contribution;

View File

@@ -1,5 +1,3 @@
const schemas = require('kosmos-schemas');
const validator = require('../utils/validator');
/**
* Handle serialization for JSON-LD object of the contributor, according to
* https://github.com/67P/kosmos-schemas/blob/master/schemas/contributor.json
@@ -8,88 +6,13 @@ const validator = require('../utils/validator');
* @public
*/
class Contributor {
constructor (attrs) {
Object.keys(attrs).forEach(a => this[a] = attrs[a]);
}
/**
* Serialize object to JSON
*
* @method
* @public
*/
serialize () {
let {
name,
kind,
url,
github_uid,
github_username,
gitea_username,
wiki_username,
accounts,
} = this;
let data = {
'@context': 'https://schema.kosmos.org',
'@type': 'Contributor',
kind,
name,
accounts: accounts || [],
};
if (url) {
data['url'] = url;
}
if (github_uid) {
data.accounts.push({
'site': 'github.com',
'uid': github_uid,
'username': github_username,
'url': `https://github.com/${github_username}`,
});
}
if (gitea_username) {
data.accounts.push({
'site': 'gitea.kosmos.org',
'username': gitea_username,
'url': `https://gitea.kosmos.org/${gitea_username}`,
});
}
if (wiki_username) {
data.accounts.push({
'site': 'wiki.kosmos.org',
'username': wiki_username,
'url': `https://wiki.kosmos.org/User:${wiki_username}`,
});
}
// Write it pretty to ipfs
return JSON.stringify(data, null, 2);
}
/**
* Validate serialized data against schema
*
* @public
*/
validate () {
const serialized = JSON.parse(this.serialize());
const valid = validator.validate(serialized, schemas['contributor']);
return valid ? Promise.resolve() : Promise.reject(validator.error);
}
/**
/**
* Deserialize JSON to object
*
* @method
* @public
*/
static deserialize (serialized) {
static deserialize(serialized) {
let {
name,
kind,
@@ -97,17 +20,13 @@ class Contributor {
accounts,
} = JSON.parse(serialized.toString('utf8'));
let github_username, github_uid, gitea_username, wiki_username;
let github = accounts.find(a => a.site === 'github.com');
let gitea = accounts.find(a => a.site === 'gitea.kosmos.org');
let wiki = accounts.find(a => a.site === 'wiki.kosmos.org');
let github_username, github_uid, wiki_username;
let github = accounts.find((a) => a.site === 'github.com');
let wiki = accounts.find((a) => a.site === 'wiki.kosmos.org');
if (github) {
(({ username: github_username, uid: github_uid} = github));
}
if (gitea) {
(({ username: gitea_username } = gitea));
}
if (wiki) {
(({ username: wiki_username } = wiki));
}
@@ -119,12 +38,59 @@ class Contributor {
accounts,
github_uid,
github_username,
gitea_username,
wiki_username,
ipfsData: serialized,
};
}
/**
* Serialize object to JSON
*
* @method
* @public
*/
static serialize(deserialized) {
let {
name,
kind,
url,
github_uid,
github_username,
wiki_username,
} = deserialized;
let data = {
"@context": "https://schema.kosmos.org",
"@type": "Contributor",
kind,
name,
"accounts": []
};
if (url) {
data["url"] = url;
}
if (github_uid) {
data.accounts.push({
"site": "github.com",
"uid": github_uid,
"username": github_username,
"url": `https://github.com/${github_username}`
});
}
if (wiki_username) {
data.accounts.push({
"site": "wiki.kosmos.org",
"username": wiki_username,
"url": `https://wiki.kosmos.org/User:${wiki_username}`
});
}
// Write it pretty to ipfs
return JSON.stringify(data, null, 2);
}
}
module.exports = Contributor;

View File

@@ -1,5 +0,0 @@
/*eslint no-console: ["error", { allow: ["warn"] }] */
module.exports = function deprecate (msg) {
console.warn(msg);
};

View File

@@ -1,10 +0,0 @@
const ethersUtils = require('ethers').utils;
module.exports = function(value, options = {}) {
let etherValue = ethersUtils.formatEther(value);
if (options.asFloat) {
return parseFloat(etherValue);
} else {
return parseInt(etherValue);
}
};

View File

@@ -1,17 +1,17 @@
const ipfsClient = require('ipfs-http-client');
const ipfsAPI = require('ipfs-api');
const multihashes = require('multihashes');
const fetch = require('node-fetch');
class IPFS {
constructor (config) {
constructor(config) {
if (!config) {
config = { host: 'localhost', port: '5001', protocol: 'http' };
}
this._ipfsAPI = ipfsAPI(config);
this._config = config;
this._ipfsAPI = ipfsClient(config);
}
catAndMerge (data, deserialize) {
catAndMerge(data, deserialize) {
// if no hash details are found simply return the data; nothing to merge
if (!data.hashSize || data.hashSize === 0) {
return data;
@@ -26,48 +26,37 @@ class IPFS {
});
}
add (data) {
add(data) {
return this._ipfsAPI
.add(ipfsClient.Buffer.from(data))
.add(new this._ipfsAPI.Buffer(data))
.then((res) => {
return this.decodeHash(res[0].hash);
});
}
cat (hashData) {
cat(hashData) {
let ipfsHash = hashData; // default - if it is a string
if (hashData.hasOwnProperty('hashSize')) {
ipfsHash = this.encodeHash(hashData);
}
if (this._config['gatewayUrl']) {
return fetch(`${this._config['gatewayUrl']}/${ipfsHash}`).then(r => r.text());
} else {
return this._ipfsAPI.cat(ipfsHash);
}
return this._ipfsAPI.cat(ipfsHash);
}
pin (hashData) {
let ipfsHash = hashData; // default - if it is a string
if (hashData.hasOwnProperty('hashSize')) {
ipfsHash = this.encodeHash(hashData);
}
return this._ipfsAPI.pin.add(multihashes.toB58String(ipfsHash));
}
decodeHash (ipfsHash) {
decodeHash(ipfsHash) {
let multihash = multihashes.decode(multihashes.fromB58String(ipfsHash));
return {
hashDigest: '0x' + multihashes.toHexString(multihash.digest),
hashSize: multihash.length,
hashFunction: multihash.code,
ipfsHash: ipfsHash,
ipfsHash: ipfsHash
};
}
encodeHash (hashData) {
let digest = ipfsClient.Buffer.from(hashData.hashDigest.slice(2), 'hex');
encodeHash(hashData) {
let digest = this._ipfsAPI.Buffer.from(hashData.hashDigest.slice(2), 'hex');
return multihashes.encode(digest, hashData.hashFunction, hashData.hashSize);
}
}
module.exports = IPFS;

View File

@@ -1,46 +0,0 @@
function pageNumber (number, size, recordCount) {
let numberOfPages = Math.ceil(recordCount / size);
number = parseInt(number) || 1;
// Ensure page number is in range
number = number > numberOfPages ? numberOfPages : number;
number = number < 1 ? 1 : number;
return number;
}
function buildIds (order, number, size, recordCount) {
let offset = size * (number - 1);
let start;
let mapFunction;
if (order === 'asc') {
start = 1 + offset;
mapFunction = (_, i) => start + i;
} else {
start = recordCount - offset;
mapFunction = (_, i) => start - i;
}
// Ensure size is in range
let end = offset + size;
if (end > recordCount) {
let diff = end - recordCount;
size = size - diff;
}
return Array.from({ length: size }, mapFunction);
}
module.exports = function paged (recordCount, options = {}) {
let { order, page } = options;
order = order || 'desc';
page = page || {};
let size = parseInt(page.size) || 25;
let number = pageNumber(page.number, size, recordCount);
return buildIds(order, number, size, recordCount);
};

View File

@@ -1,9 +1,9 @@
class Preflight {
constructor (kredits) {
constructor(kredits) {
this.kredits = kredits;
}
check () {
check() {
return this.kredits.ipfs._ipfsAPI.id()
.catch((error) => {
throw new Error(`IPFS node not available; config: ${JSON.stringify(this.kredits.ipfs.config)} - ${error.message}`);

View File

@@ -1,15 +0,0 @@
const tv4 = require('tv4');
const validator = tv4.freshApi();
validator.addFormat({
'date': function(value) {
const dateRegexp = /^[0-9]{4,}-[0-9]{2}-[0-9]{2}$/;
return dateRegexp.test(value) ? null : 'A valid ISO 8601 full-date string is expected';
},
'time': function(value) {
const timeRegexp = /^([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([Zz])|([+|-]([01][0-9]|2[0-3]):[0-5][0-9]))$/;
return timeRegexp.test(value) ? null : 'A valid ISO 8601 full-time string is expected';
},
});
module.exports = validator;

12750
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,35 +1,24 @@
{
"name": "kredits-contracts",
"version": "5.4.0",
"version": "3.0.2",
"description": "Ethereum contracts and npm wrapper for Kredits",
"main": "./lib/kredits.js",
"directories": {
"test": "test"
},
"scripts": {
"install-all": "./scripts/every-app.sh \"npm install\"",
"postshrinkwrap": "node scripts/fix-package-lock.js &>/dev/null || true",
"build-json": "npm run compile-contracts && node ./scripts/build-json.js",
"repl": "truffle exec scripts/repl.js",
"seeds": "truffle exec scripts/seeds.js",
"compile-contracts": "truffle compile --all",
"bootstrap": "npm run reset:hard && npm run seeds",
"reset": "npm run deploy:kit && npm run deploy:dao",
"reset:hard": "npm run deploy:apps && npm run reset",
"deploy:kit": "aragon contracts exec scripts/deploy-kit.js",
"compile-contracts": "aragon contracts compile --all",
"bootstrap": "npm run reset && truffle exec scripts/seeds.js",
"reset": "npm run deploy:apps && npm run deploy:kit && npm run deploy:dao",
"deploy:kit": "npm run compile-contracts && ENS=0x5f6f7e8cc7346a11ca2def8f827b7a0b612c56a1 aragon contracts exec scripts/deploy-kit.js",
"deploy:dao": "aragon contracts exec scripts/new-dao.js",
"deploy:apps": "./scripts/every-app.sh \"aragon apm publish major --propagate-content=false --build=false --prepublish=false --skip-confirmation\"",
"deploy:apps": "./scripts/deploy-apps.sh",
"devchain": "aragon devchain --port 7545",
"dao:address": "truffle exec scripts/current-address.js",
"lint:contracts": "solhint \"contracts/**/*.sol\" \"apps/*/contracts/**/*.sol\"",
"lint:contract-tests": "eslint apps/*/test",
"lint:wrapper": "eslint lib/",
"test": "npm run test:token && npm run test:contributor && npm run test:contribution && npm run test:proposal",
"test:token": "cd apps/token && npm run test",
"test:contributor": "cd apps/contributor && npm run test",
"test:contribution": "cd apps/contribution && npm run test",
"test:proposal": "cd apps/proposal && npm run test",
"setup-git-hooks": "sh scripts/git-hooks/install"
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
@@ -42,26 +31,20 @@
},
"homepage": "https://github.com/67P/truffle-kredits#readme",
"devDependencies": {
"@aragon/cli": "^5.5.0",
"@aragon/kits-base": "^1.0.0",
"@aragon/os": "^4.2.0",
"@aragon/os": "^4.1.0",
"async-each-series": "^1.1.0",
"cli-table": "^0.3.1",
"eslint": "^5.16.0",
"eslint-plugin-import": "^2.17.3",
"eslint-plugin-node": "^8.0.1",
"eslint-plugin-promise": "^4.1.1",
"eth-provider": "^0.2.2",
"openzeppelin-solidity": "^2.2.0",
"promptly": "^3.0.3",
"solc": "^0.4.26",
"solhint": "^2.1.0",
"yargs": "^12.0.0"
"solc": "^0.4.25"
},
"dependencies": {
"ethers": "^4.0.29",
"ipfs-http-client": "^30.1.3",
"kosmos-schemas": "^2.1.0",
"node-fetch": "^2.6.0",
"tv4": "^1.3.0"
"eth-ens-namehash": "^2.0.8",
"ethers": "^4.0.27",
"ipfs-api": "^19.0.0",
"rsvp": "^4.8.2"
},
"keywords": [
"kosmos",

View File

@@ -1,5 +1,4 @@
const promptly = require('promptly');
const { inspect } = require('util');
const initKredits = require('./helpers/init_kredits.js');
@@ -27,32 +26,23 @@ module.exports = async function(callback) {
console.log(`Creating a contribution for contributor account ${contributorAccount} ID: ${contributorId}`);
[ dateNow, timeNow ] = (new Date()).toISOString().split('T');
let contributionAttributes = {
contributorId,
date: dateNow,
time: timeNow,
contributorAccount,
amount: await promptly.prompt('Amount: '),
description: await promptly.prompt('Description: '),
kind: await promptly.prompt('Kind: ', { default: 'dev' }),
url: await promptly.prompt('URL: ', { default: '' })
}
const contributorData = await kredits.Contributor.getById(contributorId);
contributionAttributes.contributorIpfsHash = contributorData.ipfsHash;
console.log("\nAdding contribution:");
console.log(contributionAttributes);
kredits.Contribution.addContribution(contributionAttributes, { gasLimit: 300000 })
.then(result => {
console.log("\n\nResult:");
console.log(result);
callback();
})
.catch(error => {
console.log('Failed to create contribution');
callback(inspect(error));
});
kredits.Contribution.addContribution(contributionAttributes, { gasLimit: 300000 }).then((result) => {
console.log("\n\nResult:");
console.log(result);
callback();
}).catch((error) => {
console.log('Failed to create contribution');
callback(error);
});
}

View File

@@ -23,17 +23,18 @@ module.exports = async function(callback) {
let contributorAttributes = {
account: await prompt('Contributor address: ', {}),
name: await prompt('Name: '),
isCore: await prompt('core? y/n') === 'y',
kind: await prompt('Kind (default person): ', {default: 'person'}),
url: await prompt('URL: '),
github_username: await prompt('GitHub username: '),
github_uid: parseInt(await prompt('GitHub UID: ')),
github_uid: await prompt('GitHub UID: '),
wiki_username: await prompt('Wiki username: '),
};
console.log("\nAdding contributor:");
console.log(contributorAttributes);
kredits.Contributor.add(contributorAttributes, { gasLimit: 350000 }).then((result) => {
kredits.Contributor.add(contributorAttributes, { gasLimit: 250000 }).then((result) => {
console.log("\n\nResult:");
console.log(result);
callback();

View File

@@ -1,5 +1,4 @@
const promptly = require('promptly');
const { inspect } = require('util');
const initKredits = require('./helpers/init_kredits.js');
@@ -26,31 +25,23 @@ module.exports = async function(callback) {
}
console.log(`Creating a proposal for contributor ID #${contributorId} account: ${contributorAccount}`);
[ dateNow, timeNow ] = (new Date()).toISOString().split('T');
let contributionAttributes = {
contributorId,
date: dateNow,
time: timeNow,
amount: await promptly.prompt('Amount: '),
description: await promptly.prompt('Description: '),
kind: await promptly.prompt('Kind: ', { default: 'dev' }),
url: await promptly.prompt('URL: ', { default: '' })
}
const contributorData = await kredits.Contributor.getById(contributorId);
contributionAttributes.contributorIpfsHash = contributorData.ipfsHash;
console.log("\nAdding proposal:");
console.log(contributionAttributes);
kredits.Proposal.addProposal(contributionAttributes, { gasLimit: 300000 })
.then((result) => {
console.log("\n\nResult:");
console.log(result);
callback();
}).catch((error) => {
console.log('Failed to create proposal');
callback(inspect(error));
});
kredits.Proposal.addProposal(contributionAttributes, { gasLimit: 300000 }).then((result) => {
console.log("\n\nResult:");
console.log(result);
callback();
}).catch((error) => {
console.log('Failed to create proposal');
callback(error);
});
}

View File

@@ -1,53 +0,0 @@
const promptly = require('promptly');
const Table = require('cli-table');
const initKredits = require('./helpers/init_kredits.js');
module.exports = async function(callback) {
let kredits;
try {
kredits = await initKredits(web3);
} catch(e) {
callback(e);
return;
}
console.log(`Using Contribution at: ${kredits.Contribution.contract.address}`);
let recipient = await promptly.prompt('Contributor ID: ');
recipient = parseInt(recipient);
const table = new Table({
head: ['ID', 'Description', 'Amount', 'Claim Transaction'],
});
try {
let blockNumber = await kredits.provider.getBlockNumber();
let contributions = await kredits.Contribution.all({page: {size: 200}});
console.log(`Current block number: ${blockNumber}`);
let claimPromises = contributions.map(async (c) => {
const confirmed = c.confirmedAtBlock <= blockNumber;
if (c.contributorId === recipient && confirmed && !c.vetoed && !c.claimed) {
console.log(`Claiming contribution ID=${c.id}`);
return kredits.Contribution.functions.claim(c.id, { gasLimit: 500000 }).then(tx => {
table.push([
c.id.toString(),
`${c.description}`,
c.amount.toString(),
tx.hash,
]);
});
}
});
Promise.all(claimPromises).then(_ => {
console.log(table.toString());
callback();
});
} catch (err) {
console.log(err);
callback();
}
};

View File

@@ -10,7 +10,13 @@ echo "Setting up each aragon app in ./apps"
echo "a new app version will be deployed"
echo "----"
./scripts/every-app.sh "npm install"
./scripts/every-app.sh "aragon apm publish major"
for dir in ./apps/*/; do
set -x
cd $dir
npm install
aragon apm publish major
cd $rootDir
set +x
done
echo "Done, new versions of all apps deployed"

View File

@@ -3,7 +3,7 @@ const deployDAOFactory = require('@aragon/os/scripts/deploy-daofactory.js')
const fs = require('fs');
const path = require('path');
const argv = require('yargs').argv
const namehash = require('ethers').utils.namehash;
const namehash = require('eth-ens-namehash').hash
const fileInject = require('./helpers/file_inject.js')
const getNetworkId = require('./helpers/networkid.js')
@@ -35,9 +35,7 @@ module.exports = async function(callback) {
}
console.log(`Using DAOFactory at: ${daoFactory.address}`)
const apps = fs.readdirSync('./apps', { withFileTypes: true })
.filter(e => e.isDirectory())
.map(e => e.name);
const apps = fs.readdirSync('./apps')
console.log(`Found apps: [${apps}].${apm}`)
let appIds = {}
apps.sort().forEach((app) => {

View File

@@ -1,14 +0,0 @@
#!/bin/bash
set -e
rootDir=`pwd`
for dir in ./apps/*/; do
set -x
cd $dir
eval $1
cd $rootDir
set +x
done

View File

@@ -1,47 +0,0 @@
#!/usr/bin/env node
// whatever npm does?! and for whatever this is needed..
// https://github.com/aragon/aragon-cli/blob/master/packages/aragon-cli/scripts/fix-lockfile
// https://github.com/aragon/aragon-cli/blob/master/docs-internal/Dependencies.md#regenerate-the-lockfiles
const fs = require('fs')
const path = require('path')
function replaceAll(string, mapObject) {
const regex = new RegExp(Object.keys(mapObject).join('|'), 'gi')
let occurrences = 0
const result = string.replace(regex, matched => {
occurrences++
return mapObject[matched]
})
console.log(`[fix-lockfile] Replaced ${occurrences} occurrences.`)
return result
}
async function fixLockfile(path, replacementMap) {
const originalJson = require(path)
const originalText = JSON.stringify(originalJson, null, 2)
const fixedText = replaceAll(originalText, replacementMap)
const fixedJson = JSON.parse(fixedText)
console.log('writing file', path);
await fs.writeFileSync(path, JSON.stringify(fixedJson, null, 2))
}
//
const LOCKFILE_PATH = path.join(__dirname, '..', 'package-lock.json')
const replacementMap = {
//
'"version": "github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c"':
'"version": "0.2.3"',
//
'"from": "github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c"':
'"resolved": "github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c"',
//
'"from": "async-eventemitter@github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c"':
'"resolved": "github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c"',
//
'"async-eventemitter": "async-eventemitter@github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c"':
'"async-eventemitter": "github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c"',
}
fixLockfile(LOCKFILE_PATH, replacementMap)

View File

@@ -1,2 +0,0 @@
#!/bin/sh
cp -f scripts/git-hooks/pre-commit .git/hooks

View File

@@ -1,17 +0,0 @@
#!/bin/sh
#
# Run appropriate linter against staged files
#
if [ $(git diff --name-only --cached lib/ | wc -l) != 0 ]; then
./node_modules/.bin/eslint lib/
if [ $? != 0 ]; then
exit 1
fi
fi
# TODO master not linted yet, uncomment this when ready
# if [ $(git diff --name-only --cached contracts/ | wc -l) != 0 ]; then
# solhint contracts/**/*.sol && apps/*/contracts/**/*.sol
# if [ $? != 0 ]; then
# exit 1
# fi
# fi

View File

@@ -20,7 +20,7 @@ module.exports = async function(web3) {
});
}).catch(e => {
console.log(`Signer account not available; readonly connection (${e.message}`);
new Kredits(provider, null, { apm }).init().then(kredits => {
new Kredits(provider, null).init().then(kredits => {
resolve(kredits);
}).catch(e => {
reject(e);

View File

@@ -14,39 +14,23 @@ module.exports = async function(callback) {
console.log(`Using Contribution at: ${kredits.Contribution.contract.address}`);
const table = new Table({
head: ['ID', 'Contributor ID', 'Description', 'Amount', 'Confirmed?', 'Vetoed?', 'Claimed?', 'IPFS']
head: ['ID', 'Contributor account', 'Amount', 'Claimed?', 'Vetoed?', 'Description']
})
try {
let blockNumber = await kredits.provider.getBlockNumber();
let contributions = await kredits.Contribution.all({page: {size: 1000}});
let contributions = await kredits.Contribution.all()
let kreditsSum = 0;
console.log(`Current block number: ${blockNumber}`);
contributions.forEach((c) => {
const confirmed = c.confirmedAtBlock <= blockNumber;
table.push([
c.id.toString(),
c.contributorId,
`${c.description}`,
c.amount.toString(),
`${confirmed} (${c.confirmedAtBlock})`,
c.vetoed,
c.claimed,
c.ipfsHash
])
});
console.log(table.toString());
let totalKreditsEarnedUnConfirmed = await kredits.Contribution.functions.totalKreditsEarned(false);
let totalKreditsEarnedConfirmed = await kredits.Contribution.functions.totalKreditsEarned(true);
console.log(`Total Kredits: ${totalKreditsEarnedConfirmed} (confirmed) | ${totalKreditsEarnedUnConfirmed} (including unconfirmed)`);
} catch (err) {
console.log(err);
}
callback();
contributions.forEach((c) => {
table.push([
c.id.toString(),
c.contributor,
c.amount.toString(),
c.claimed,
c.vetoed,
`${c.description}`
])
})
console.log(table.toString())
callback()
}

View File

@@ -1,6 +1,5 @@
const promptly = require('promptly');
const Table = require('cli-table');
const ethers = require('ethers');
const initKredits = require('./helpers/init_kredits.js');
@@ -15,31 +14,22 @@ module.exports = async function(callback) {
console.log(`Using Contributor at: ${kredits.Contributor.contract.address}`);
const table = new Table({
head: ['ID', 'Account', 'Name', 'Core?', 'Balance', 'Kredits earned', 'Contributions count', 'IPFS']
head: ['ID', 'Account', 'Core?', 'Name']
})
try {
const contributors = await kredits.Contributor.all()
contributors.forEach((c) => {
table.push([
c.id.toString(),
c.account,
`${c.name}`,
c.isCore,
c.balanceInt.toString(),
c.totalKreditsEarned.toString(),
c.contributionsCount.toString(),
c.ipfsHash
])
})
console.log(table.toString())
} catch(e) {
callback(e);
return;
}
let contributors = await kredits.Contributor.all()
contributors.forEach((c) => {
table.push([
c.id.toString(),
c.account,
c.isCore,
`${c.name}`
])
})
console.log(table.toString())
callback()
}

View File

@@ -14,6 +14,7 @@ module.exports = async function(callback) {
console.log(`Using Proposal at: ${kredits.Proposal.contract.address}`);
const table = new Table({
head: ['ID', 'Contributor ID', 'Amount', 'Votes', 'Executed?', 'Description']
})

View File

@@ -1,13 +1,13 @@
const fs = require('fs');
const path = require('path');
const ethers = require('ethers');
const fileInject = require('./helpers/file_inject.js');
const getNetworkId = require('./helpers/networkid.js');
const KreditsKit = require('../lib/kreditskit');
const addressesPath = path.join(__dirname, '..', 'lib/addresses');
const KreditsKit = artifacts.require('KreditsKit')
module.exports = async function(callback) {
const networkId = await getNetworkId(web3)
console.log(`Deploying to networkId: ${networkId}`)
@@ -20,21 +20,25 @@ module.exports = async function(callback) {
}
console.log(`Using KreditsKit at: ${kreditsKitAddress}`);
const provider = new ethers.providers.Web3Provider(web3.currentProvider);
let signer = provider.getSigner();
let kreditsKit = KreditsKit.at(kreditsKitAddress)
let kit = await new KreditsKit(provider, signer).init()
kreditsKit.newInstance().then((ret) => {
console.log(ret.logs);
const installedEvents = ret.logs.filter(log => log.event === 'InstalledApp').map(log => log.args)
const deployEvents = ret.logs.filter(log => log.event === 'DeployInstance').map(log => log.args)
// TODO: get rid of the hard coded gas limit
kit.newDAO({ gasLimit: 10000000 }).then(result => {
console.log(result);
fileInject(path.join(addressesPath, 'dao.json'), networkId, result.daoAddress)
if (deployEvents.length > 1) {
callback(new Error("More than one DAO was deployed. Something is wrong"))
}
const daoAddress = deployEvents[0].dao;
console.log(`\n\nCreated new DAO at: ${result.daoAddress}`)
fileInject(path.join(addressesPath, 'dao.json'), networkId, daoAddress)
console.log(`\n\nCreated new DAO at: ${daoAddress}`)
callback();
}).catch((err) => {
console.log('failed to create a new DAO')
console.log('failed to create a new instance')
callback(err)
})
}

View File

@@ -17,18 +17,13 @@ module.exports = async function(callback) {
let fundingAmount = 2;
each(seeds.funds, (address, next) => {
console.log(`funding ${address} with 2 ETH`);
try {
web3.eth.personal.getAccounts().then(accounts => {
web3.eth.personal.sendTransaction({
to: address,
from: accounts[0],
value: web3.utils.toWei(new web3.utils.BN(fundingAmount))
});
});
} catch(e) {
console.log('FAILED:', e);
}
next();
web3.eth.sendTransaction({
to: address,
value: web3.toWei(fundingAmount),
from: web3.eth.accounts[0]
},
(result) => { next(); }
)
});
each(seeds.contractCalls, (call, next) => {
@@ -44,9 +39,8 @@ module.exports = async function(callback) {
console.log(`[OK] kredits.${contractName}.${method}(${JSON.stringify(args)}) => ${result.hash}`);
next();
}).catch((error) => {
console.log(`[FAILED] kredits.${contractName}.${method}(${JSON.stringify(args)})`);
console.log(`Error: ${error.message}`);
next();
console.log(`[FAILD] kredits.${contractName}.${method}(${JSON.stringify(args)})`);
callback(error)
});
}, () => { console.log("\nDone!") });

View File

@@ -5,8 +5,7 @@ module.exports = async function(callback) {
let amount = await promptly.prompt('Amount: ', {default: '1'});
amount = parseInt(amount);
const accounts = await web3.eth.personal.getAccounts();
let fromAccount = accounts[0];
let fromAccount = web3.eth.accounts[0];
let fromBalance = await web3.eth.getBalance(fromAccount);
let recipientBalance = await web3.eth.getBalance(recipient);
@@ -14,11 +13,11 @@ module.exports = async function(callback) {
console.log(`sender account balance ${fromAccount}: ${fromBalance}`);
console.log(`recipient account balance ${recipient}: ${recipientBalance}`);
console.log(`\nsending ${amount} ETH from ${accounts[0]} to ${recipient}`);
console.log(`\nsending ${amount} ETH from ${web3.eth.accounts[0]} to ${recipient}`);
let transaction = await web3.eth.sendTransaction({to: recipient, value: web3.utils.toWei(new web3.utils.BN(amount)), from: accounts[0]});
let transaction = await web3.eth.sendTransaction({to: recipient, value: web3.toWei(amount), from: web3.eth.accounts[0]});
console.log(`transaction id: ${transaction.transactionHash}`);
console.log(`transaction id: ${transaction}`);
recipientBalance = await web3.eth.getBalance(recipient);
console.log(`\nnew recipient account balance ${recipient}: ${recipientBalance}`);

View File

@@ -1,23 +0,0 @@
#!/bin/bash
set -e
rootDir=`pwd`
app=$1
daoAddress=$2
if [ -z "$2" ]; then
daoAddress=$KREDITS_DAO_ADDRESS
fi
echo "## Deploying $app for $daoAddress"
set -x
cd "apps/$app"
aragon apm publish major
cd $rootDir
aragon dao upgrade $daoAddress kredits-$app
npm run build-json
set +x
echo "Done"

View File

@@ -1,32 +0,0 @@
const promptly = require('promptly');
const { inspect } = require('util');
const initKredits = require('./helpers/init_kredits.js');
module.exports = async function(callback) {
let kredits;
try { kredits = await initKredits(web3);
} catch(e) { callback(e); return; }
console.log(`Using Contributions at: ${kredits.Contribution.contract.address}\n`);
let contributionId = await promptly.prompt('Contribution ID: ');
console.log(`Recording a veto for contribution #${contributionId}`);
try {
kredits.Contribution.functions.veto(contributionId, { gasLimit: 300000 })
.then(result => {
console.log("\n\nResult:");
console.log(result);
callback();
})
.catch(error => {
console.log('Failed to veto contribution');
callback(inspect(error));
});
} catch(err) {
console.log('Failed to veto contribution');
callback(inspect(err));
}
}

View File

@@ -28,7 +28,7 @@ const providerForNetwork = (network) => (
let [first, ...rest] = network;
preset = `infura${first.toUpperCase()}${rest.join('')}`;
}
return provider(['frame', 'local', preset]);
return provider('frame', 'local', preset);
}
}
)

8836
yarn.lock

File diff suppressed because it is too large Load Diff