Compare commits

..

131 Commits

Author SHA1 Message Date
87a468bc91 5.4.0 2019-07-02 16:27:26 +02:00
0734acbcbe Merge pull request #149 from 67P/feature/support-ipfs-gateway
Add support to use IPFS gateway to read ipfs data
2019-07-01 15:41:07 +00:00
a45c8d14be Downgrade IPFS package
The new version uses generators which breaks the build on kredits-web.
2019-07-01 16:12:22 +02:00
61fe9add1b Add support to use IPFS gateway to read ipfs data
This makes caching of IPFS documents easier. Browsers should cache those
by default
2019-07-01 15:53:18 +02:00
16141ed482 Merge pull request #136 from 67P/feature/ipfs-pinner
Add IPFS pinning script
2019-07-01 13:06:45 +02:00
01ef37fd39 Merge pull request #147 from 67P/bugfix/compile_all
Fix compile command
2019-07-01 10:30:41 +00:00
03b9f69d22 Fix compile command
The Aragon one is failing now, but just using the underlying Truffle
command works. So it appears to be the passing of the argument in aragon
that's broken.

Co-authored-by: Michael Bumann <hello@michaelbumann.com>
2019-07-01 12:24:56 +02:00
dd76ebdc8d Merge pull request #141 from haythem96/setup-tests
Solidity test setup
2019-06-26 15:05:23 +00:00
e485bd90c0 install packages 2019-06-19 14:34:14 +01:00
1d89759c49 merge 2019-06-19 13:47:58 +01:00
d653b3715f Update package-lock 2019-06-19 14:10:44 +02:00
a05e2c75f3 Merge pull request #142 from 67P/chore/update-dependencies-2
Chore/update aragon-cli dependencies
2019-06-19 08:39:06 +00:00
124287b977 Updated latest package-json 2019-06-19 01:00:53 +02:00
9906fde7ef Use postshrinkwrap instead of postinstall
`postinstall` is executed before package-lock.json is written to disk.

https://github.com/npm/npm/issues/18798
2019-06-19 00:52:48 +02:00
c7e1f118a6 Add postinstall lockfile fix
https://github.com/67P/kredits-contracts/pull/140
2019-06-19 00:52:47 +02:00
1702c22084 Make sure fix lockfile uses the correct file path 2019-06-19 00:52:47 +02:00
32a123a825 Default to open.aragonpm.eth
it is now also available on the devchain
2019-06-19 00:52:47 +02:00
ac83573438 I don't know why this is needed but it fixes an npm issue
if install fails node scripts/fix-package-lock.js might help
2019-06-19 00:52:47 +02:00
77c6c11800 Remove npm cache and use LTS node on travis 2019-06-19 00:52:05 +02:00
ca0a6f6ef9 user 7545 port for tests 2019-06-17 23:35:53 +01:00
4614c454a5 test setup 2019-06-17 12:13:35 +01:00
86bbe49c0b Fix seeds script to support new web3 version 2019-06-13 18:39:25 +02:00
d5322530c3 Update aragon CLI to latest version 2019-06-13 18:37:33 +02:00
011d85447f Merge pull request #138 from 67P/dev/seeds
Various small seed/script improvements
2019-06-13 16:36:38 +00:00
9fd9dbc1b5 Handle errors in IPFS pin script 2019-06-13 18:31:47 +02:00
b7d87cd8f2 Ignore scripts folder in eslint
Lots of undef etc. due to the scripts being piped through truffle
2019-06-13 17:39:03 +02:00
6c8491e67b Handle errors in seed funding 2019-06-13 17:38:34 +02:00
2623e71055 Update deprecated functions in seeds 2019-06-13 17:38:00 +02:00
ed0d420fd5 Fix linter issues in seeds 2019-06-13 17:37:36 +02:00
36666fd417 Update package lockfile 2019-06-12 16:07:38 +02:00
231d6e2477 Merge pull request #135 from 67P/feature/expose-available-networks
Expose available network IDs
2019-06-12 07:57:31 +00:00
6d6c6056f0 Refactor IPFS pinner 2019-06-12 01:08:07 +02:00
b09c2830c8 Make IPFS node configurable 2019-06-11 23:45:27 +02:00
fc5a41123a Add IPFS pinning script
This script loads the IPFS hashes for contributors and contributions and
pins them on the connected IPFS node.

usage:

    $ node script/ipfs-pinner.js

    $ ETH_RPC_URL=http://localhost:7547 APM_DOMAIN=aragonpm.eth node scripts/ipfs.pinner.js
2019-06-11 21:48:50 +02:00
107654ecca Expose available network IDs
This exposes the network IDs on which kredits is deployed. Helpful to
check if the used network is supported.
2019-06-11 15:45:14 +02:00
810a2eb5fd Merge pull request #134 from 67P/chore/update-dependencies
Update dependencies
2019-06-10 13:19:29 +02:00
06a4e2173a Try npm instead of yarn on travis
There is some strange error with yarn: https://travis-ci.org/67P/kredits-contracts/builds/543186831
2019-06-10 12:59:02 +02:00
e55a209343 Merge pull request #133 from 67P/feature/list-totals
Print total unconfirmed and confirmed contribution kredits
2019-06-09 13:58:42 +02:00
17bc4e7d8f Merge pull request #121 from 67P/refactor/amounts_vs_counts
Improve totalKreditsEarned
2019-06-09 13:57:44 +02:00
4919605664 Merge pull request #130 from 67P/feature/kredits-kit-wrapper
Add JS wrapper for Kit contract
2019-06-09 13:56:32 +02:00
f639e3aa19 Add pre-commit hook and setup script (#129)
Add pre-commit hook and setup script

Runs the appropriate linter on staged files before committing.
2019-06-09 13:53:40 +02:00
84b4461ba6 Update dependencies 2019-06-08 19:05:35 +02:00
c4f8e9278e Use consitently uint32 for amounts 2019-06-08 17:56:50 +02:00
00905eb269 Update ABIs 2019-06-08 17:53:26 +02:00
6a64842415 Only count not-vetoed contributions for total balances 2019-06-08 17:49:24 +02:00
055d8bc0ef Print total unconfirmed and confirmed contribution kredits 2019-06-08 17:42:20 +02:00
91779ccd03 Merge pull request #131 from 67P/feature/claim-contribution-script
Add script to claim contributions for one contributor
2019-05-21 11:29:56 +00:00
516b8b31ac Add script to claim contributions for one contributor 2019-05-20 13:48:15 +02:00
ccd52f6ee5 Cleanup 2019-05-17 18:24:55 +02:00
ffff09ab23 Add contract address accessor 2019-05-17 15:56:24 +02:00
3df0831d9b Cleanup whitespace 2019-05-17 15:56:02 +02:00
27a746261c Add JS wrapper for kredits kit
This makes it easier to deploy new DAOs and the deploy script is less
dependent on truffle
2019-05-17 15:54:52 +02:00
345b6bde82 Merge pull request #127 from 67P/feature/allow-setting-contributor-accounts
Allow setting any contributor accounts
2019-05-17 08:42:43 +00:00
a86ea08bdf Merge pull request #128 from 67P/feature/simple-init
Add helper to initialize a new instance with provider
2019-05-17 07:37:10 +00:00
e20bda73fb Remove not needed network check
We default to localhost anyway
2019-05-17 09:17:10 +02:00
4c64aa7c2a linting 2019-05-14 12:03:29 +02:00
cf43bf9487 Linting 2019-05-14 11:32:42 +02:00
1a227ba67c Linting 2019-05-14 11:31:33 +02:00
df7536589d Add helper to initialize a new instance with provider
So far we always had to initialze a provider and signer and pass those
to the kredits constructor.
This helper makes it easier to initialize a default ethers provider and
a default signer.
2019-05-14 11:25:21 +02:00
f0fe62f6d7 Apply suggestions from code review
Co-Authored-By: Sebastian Kippe <sebastian@kip.pe>
2019-05-14 09:18:23 +00:00
66fc992291 Allow setting any contributor accounts
This allows to pass in an account object when creating or updating a
contribtor.
2019-05-14 11:06:49 +02:00
2a675c9417 Merge pull request #126 from 67P/chore/update_schemas
Update kosmos-schemas
2019-05-10 16:08:30 +02:00
a3590d7c16 Update kosmos-schemas
Includes the new "special" contribution kind, as discussed in our last
call.
2019-05-10 13:14:56 +02:00
07a5ca847d Merge pull request #125 from 67P/bugfix/pagination
Fix page number calculation for zero records
2019-05-07 17:12:16 +02:00
37ce2fddde Merge pull request #124 from 67P/bugfix/fix-me-1
Fix util import
2019-05-04 08:40:58 +00:00
3a43284557 Fix page number calculation for zero records
So far it returned 0 because of those in range checks which both
applied because numberOfPages is 0 if we have no record.
2019-05-04 01:34:05 +02:00
572848c04a Fix util import 2019-05-04 01:09:22 +02:00
b6ce9dcfc8 Merge pull request #118 from 67P/deprecate/old-add-methods
Deprecate old add* methods
2019-05-01 18:12:18 +00:00
e7e8744ad2 Update lib/contracts/proposal.js
Co-Authored-By: bumi <hello@michaelbumann.com>
2019-05-01 18:06:55 +00:00
d2198dca61 Update lib/contracts/contribution.js
Co-Authored-By: bumi <hello@michaelbumann.com>
2019-05-01 18:06:43 +00:00
f9f2ef234e Merge pull request #122 from 67P/feature/veto_script
Add script for vetoing via console
2019-05-01 18:05:37 +00:00
a1f51428d0 Add script for vetoing via console 2019-04-28 15:09:18 +01:00
555cb53c78 Improve totalKreditsEarned
* Use a more reasonable size of integer
* Use better variable names (count != amount, balance != count)
2019-04-27 21:01:51 +01:00
ad74d30aa7 Contributor canPerform fix deployment 2019-04-25 22:25:38 +02:00
3568d3c141 Merge pull request #120 from 67P/bugfix/fix-core-permissions
Fix canPerform method
2019-04-25 21:17:37 +01:00
db566a581f Merge pull request #119 from 67P/bugfix/fix-kit-deployment
Fix apps lookup
2019-04-25 21:10:04 +01:00
c69ebd0a62 Fix canPerform method
Even if the variable is not used and the linter might complain we have
to have that parameter in the signature. otherwise the method is not
found and can not be called.
2019-04-25 22:06:40 +02:00
5c375f764f Update scripts/deploy-kit.js
Co-Authored-By: bumi <hello@michaelbumann.com>
2019-04-25 20:05:03 +00:00
b04d572b9a Fix apps lookup
We use the directories in ./apps to load available app names.
For this we need to make sure that we only use directories.
2019-04-25 16:45:39 +02:00
1199b053fe Add missing returns for the deprecated methods 2019-04-24 21:55:10 +02:00
f06b21dfee Deprecate old add* methods 2019-04-24 21:49:27 +02:00
d643e5842c Merge pull request #97 from 67P/chore/linter
Add linter and travis.yml
2019-04-24 18:28:32 +00:00
a87d5326bf Oh my package-lock 2019-04-24 20:17:03 +02:00
c9fed46054 Add contract test linting 2019-04-24 19:56:37 +02:00
6f2097ed46 Fix regex 2019-04-24 19:39:30 +02:00
94d342ce63 Replace console.log by deprecate function 2019-04-24 19:31:26 +02:00
f984dec95a Style fixes 2019-04-24 19:31:04 +02:00
145b3ea766 Merge branch 'master' into chore/linter
# Conflicts:
#	lib/contracts/contribution.js
#	lib/contracts/contributor.js
#	lib/contracts/proposal.js
#	lib/kredits.js
#	lib/serializers/contributor.js
#	yarn.lock
2019-04-24 19:12:18 +02:00
a80dfee5c5 Merge pull request #116 from 67P/deployments/balance-update
Release/deployment notes for latest contract updates on Rinkeby
2019-04-24 17:03:48 +01:00
4649de24c8 Deploy notes for latest contributor and contribution deployment 2019-04-24 17:32:58 +02:00
8539b56a48 5.3.0 2019-04-24 14:22:53 +01:00
f59c37827a Merge pull request #108 from 67P/feature/kredits-formatter
Add kredits-formatter and additionally format balance
2019-04-24 14:20:44 +01:00
58db57ee45 Merge branch 'master' into feature/kredits-formatter 2019-04-24 13:10:48 +00:00
b64a7ca299 Merge pull request #113 from 67P/feature/gitea_site
Add Gitea site/username to serializer and seeds
2019-04-24 12:58:44 +00:00
6f09ca8d13 Merge pull request #114 from 67P/chore/scripts
Improve/fix scripts
2019-04-24 12:57:43 +00:00
6f53c8097e Wrap anything that could break in the try/catch block 2019-04-24 14:55:35 +02:00
6b2ac15f56 Improve error handling for list-contributors script 2019-04-24 13:46:27 +01:00
359989f235 Fix function name for total kredits issued/earned 2019-04-24 13:45:56 +01:00
5894f6323b Add Gitea username to contributor seeds 2019-04-24 13:43:46 +01:00
09b78e1e8f Add Gitea site/username to contributor serializer 2019-04-23 20:28:56 +01:00
48c8f6b9b3 Merge branch 'master' into feature/kredits-formatter 2019-04-23 12:56:55 +00:00
542ebaf3f3 5.2.0 2019-04-23 13:52:46 +01:00
d82ffba75e Update ABI 2019-04-23 13:52:37 +01:00
bdb2cee0c4 Merge pull request #110 from 67P/feature/contribution-balances
Add contract calls for total contribution balances
2019-04-23 13:47:50 +01:00
cd45ce260f Merge pull request #112 from 67P/feature/update-contributor-profile
Update contributor profile
2019-04-20 09:22:31 +00:00
3a97983540 Merge pull request #111 from 67P/feature/contributor-schema-validation
Add Contributor validation against JSON schema
2019-04-20 08:47:49 +01:00
9714926e11 Add function to update contributor profile to wrapper
The function updates the contributor profile,
adds the new profile to IPFS and does the contract call to update
the profile hash
2019-04-20 02:17:38 +02:00
59614201b5 Ups... 2019-04-20 02:15:53 +02:00
e591742e40 Add Contributor validation against JSON schema
based on the Contribution validation.
2019-04-20 01:10:46 +02:00
791190f5e7 Cleanup promise call 2019-04-20 00:30:20 +02:00
24b66daf2a Update ABI 2019-04-19 14:16:40 +02:00
ad034d7712 Naming is hard 2019-04-19 14:15:30 +02:00
375d8f3275 Cleanup 2019-04-19 13:34:39 +02:00
80dc787971 Merge pull request #109 from 67P/dev/list_contributors_error_handling
Handle errors in contributor list script
2019-04-19 11:22:38 +00:00
7967dc26f2 Update ABIs 2019-04-19 13:20:25 +02:00
c248725cc1 Add balances to getContributorById call 2019-04-19 13:19:55 +02:00
59135bf312 Add contract calls for total contribution balances
Those methods return the total kredits amounts of contribtions.
2019-04-19 13:06:36 +02:00
1d771c43e8 Handle errors in contributor list script 2019-04-19 12:02:28 +01:00
3cb94fb660 Add kredits-formatter and additionally format balance
This adds a `balanceInt` value to the contributor data that has the
token balance formatted as full Kredits. (similar to Ether)
2019-04-19 11:57:35 +02:00
5820d71b2c Merge pull request #96 from 67P/feature/pagination
Add pagination for .all methods
2019-04-17 13:53:17 +00:00
4771c0c8a0 Merge branch 'master' into feature/pagination 2019-04-17 13:52:53 +00:00
6f97c905d6 Add count property and DRY 2019-04-13 13:05:47 +02:00
b5b12e22ce Readd solhint:default 2019-04-13 01:53:52 +02:00
fe811cdb12 Use solhint:recommended 2019-04-13 01:17:38 +02:00
42df6fe310 Fix solhint invocation 2019-04-13 01:10:40 +02:00
c8805be054 Switch to yarn 2019-04-13 00:19:52 +02:00
764f63fc8c Trigger build 2019-04-13 00:14:23 +02:00
f73ccedf42 Activate linting on travis 2019-04-13 00:10:21 +02:00
a9093c1c40 Fix linter errors
# Conflicts:
#	lib/contracts/contribution.js
#	lib/contracts/contributor.js
#	lib/contracts/proposal.js
#	lib/kredits.js
#	lib/utils/pagination.js
2019-04-13 00:08:12 +02:00
e405644b1d Add eslint and solhint 2019-04-13 00:03:16 +02:00
f6189bf910 Add pagination for .all methods
I removed rsvp as a dependency as we only use Promise.all
2019-04-12 21:21:19 +02:00
68 changed files with 130045 additions and 53240 deletions

1
.eslintignore Normal file
View File

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

32
.eslintrc.js Normal file
View File

@@ -0,0 +1,32 @@
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',
}],
}
}

9
.solhint.json Normal file
View File

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

19
.travis.yml Normal file
View File

@@ -0,0 +1,19 @@
---
language: node_js
node_js:
- "lts/*"
sudo: false
dist: xenial
install:
- npm install
script:
- npm run lint:wrapper
- npm run lint:contract-tests
# - yarn lint:contracts
branches:
only:
- master

12
apps/.eslintrc.js Normal file
View File

@@ -0,0 +1,12 @@
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.aragonpm.eth"
"appName": "kredits-contribution.open.aragonpm.eth"
},
"rinkeby": {
"registry": "0x98df287b6c145399aaa709692c8d308357bc085d",

View File

@@ -0,0 +1,912 @@
{
"roles": [
{
"name": "Add contributions",
"id": "ADD_CONTRIBUTION_ROLE",
"params": [],
"bytes": "0x493d28cd0d82bcb20db66e4f6390a00122ef772717e282b436ba3240af18bfb1"
},
{
"name": "Manage token contract",
"id": "MANAGE_TOKEN_CONTRACT_ROLE",
"params": [],
"bytes": "0xdd275187bc43df45ce7b34f6716e572716c69ad44e5e496175008950f032854b"
},
{
"name": "Veto contributions",
"id": "VETO_CONTRIBUTION_ROLE",
"params": [],
"bytes": "0x495a36de1ed34d5c1b9f8704e7d8bc8badb027221b09c79691d430bc54c4c88f"
}
],
"environments": {
"default": {
"network": "development",
"appName": "kredits-contribution.open.aragonpm.eth"
}
},
"path": "contracts/Contribution.sol",
"appName": "kredits-contribution.open.aragonpm.eth",
"appId": "0x09f5274cba299b46c5be722ef672d10eef7a2ef980b612aef529d74fb9da7643",
"abi": [
{
"constant": true,
"inputs": [],
"name": "hasInitialized",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "ADD_CONTRIBUTION_ROLE",
"outputs": [
{
"name": "",
"type": "bytes32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "_script",
"type": "bytes"
}
],
"name": "getEVMScriptExecutor",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "getRecoveryVault",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "contributionsCount",
"outputs": [
{
"name": "",
"type": "uint32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "token",
"type": "address"
}
],
"name": "allowRecoverability",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "appId",
"outputs": [
{
"name": "",
"type": "bytes32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "getInitializationBlock",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "KERNEL_APP_ADDR_NAMESPACE",
"outputs": [
{
"name": "",
"type": "bytes32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_token",
"type": "address"
}
],
"name": "transferToVault",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "_sender",
"type": "address"
},
{
"name": "_role",
"type": "bytes32"
},
{
"name": "_params",
"type": "uint256[]"
}
],
"name": "canPerform",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "getEVMScriptRegistry",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "",
"type": "uint32"
}
],
"name": "contributionOwner",
"outputs": [
{
"name": "",
"type": "uint32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "",
"type": "uint32"
}
],
"name": "contributions",
"outputs": [
{
"name": "contributorId",
"type": "uint32"
},
{
"name": "amount",
"type": "uint32"
},
{
"name": "claimed",
"type": "bool"
},
{
"name": "hashDigest",
"type": "bytes32"
},
{
"name": "hashFunction",
"type": "uint8"
},
{
"name": "hashSize",
"type": "uint8"
},
{
"name": "tokenMetadataURL",
"type": "string"
},
{
"name": "confirmedAtBlock",
"type": "uint256"
},
{
"name": "vetoed",
"type": "bool"
},
{
"name": "exists",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "",
"type": "uint32"
},
{
"name": "",
"type": "uint256"
}
],
"name": "ownedContributions",
"outputs": [
{
"name": "",
"type": "uint32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "kernel",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "blocksToWait",
"outputs": [
{
"name": "",
"type": "uint32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "isPetrified",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "",
"type": "uint256"
}
],
"name": "appIds",
"outputs": [
{
"name": "",
"type": "bytes32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "VETO_CONTRIBUTION_ROLE",
"outputs": [
{
"name": "",
"type": "bytes32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"name": "id",
"type": "uint32"
},
{
"indexed": true,
"name": "contributorId",
"type": "uint32"
},
{
"indexed": false,
"name": "amount",
"type": "uint32"
}
],
"name": "ContributionAdded",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"name": "id",
"type": "uint32"
},
{
"indexed": true,
"name": "contributorId",
"type": "uint32"
},
{
"indexed": false,
"name": "amount",
"type": "uint32"
}
],
"name": "ContributionClaimed",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"name": "id",
"type": "uint32"
},
{
"indexed": false,
"name": "vetoedByAccount",
"type": "address"
}
],
"name": "ContributionVetoed",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "executor",
"type": "address"
},
{
"indexed": false,
"name": "script",
"type": "bytes"
},
{
"indexed": false,
"name": "input",
"type": "bytes"
},
{
"indexed": false,
"name": "returnData",
"type": "bytes"
}
],
"name": "ScriptResult",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "vault",
"type": "address"
},
{
"indexed": true,
"name": "token",
"type": "address"
},
{
"indexed": false,
"name": "amount",
"type": "uint256"
}
],
"name": "RecoverToVault",
"type": "event"
},
{
"constant": false,
"inputs": [
{
"name": "_appIds",
"type": "bytes32[4]"
}
],
"name": "initialize",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "getTokenContract",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "getContributorContract",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "contributorAccount",
"type": "address"
}
],
"name": "getContributorIdByAddress",
"outputs": [
{
"name": "",
"type": "uint32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "contributorId",
"type": "uint32"
}
],
"name": "getContributorAddressById",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "name",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "symbol",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "owner",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "contributionId",
"type": "uint32"
}
],
"name": "ownerOf",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "owner",
"type": "address"
},
{
"name": "index",
"type": "uint32"
}
],
"name": "tokenOfOwnerByIndex",
"outputs": [
{
"name": "",
"type": "uint32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "contributionId",
"type": "uint32"
}
],
"name": "tokenMetadata",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "confirmedOnly",
"type": "bool"
}
],
"name": "totalKreditsEarned",
"outputs": [
{
"name": "amount",
"type": "uint32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "contributorId",
"type": "uint32"
},
{
"name": "confirmedOnly",
"type": "bool"
}
],
"name": "totalKreditsEarnedByContributor",
"outputs": [
{
"name": "amount",
"type": "uint32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "contributionId",
"type": "uint32"
}
],
"name": "getContribution",
"outputs": [
{
"name": "id",
"type": "uint32"
},
{
"name": "contributorId",
"type": "uint32"
},
{
"name": "amount",
"type": "uint32"
},
{
"name": "claimed",
"type": "bool"
},
{
"name": "hashDigest",
"type": "bytes32"
},
{
"name": "hashFunction",
"type": "uint8"
},
{
"name": "hashSize",
"type": "uint8"
},
{
"name": "confirmedAtBlock",
"type": "uint256"
},
{
"name": "exists",
"type": "bool"
},
{
"name": "vetoed",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "amount",
"type": "uint32"
},
{
"name": "contributorId",
"type": "uint32"
},
{
"name": "hashDigest",
"type": "bytes32"
},
{
"name": "hashFunction",
"type": "uint8"
},
{
"name": "hashSize",
"type": "uint8"
}
],
"name": "add",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "contributionId",
"type": "uint32"
}
],
"name": "veto",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "contributionId",
"type": "uint32"
}
],
"name": "claim",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "contributionId",
"type": "uint32"
}
],
"name": "exists",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
],
"deployment": {
"contractName": "Contribution",
"compiledAt": "2019-06-13T12:39:07.659Z",
"compiler": {
"name": "solc",
"version": "0.4.24+commit.e67f0147.Emscripten.clang",
"optimizer": {
"enabled": false
}
},
"flattenedCode": "./code.sol",
"transactionHash": "0x073057bb616243e415823fa9a8c8cc096b573fbd0bbf11c2f59bb75a84291689"
},
"functions": [
{
"sig": "mintFor(address,uint256,uint32)",
"roles": [],
"notice": null
},
{
"sig": "initialize(bytes32[4])",
"roles": [],
"notice": null
},
{
"sig": "add(uint32,uint32,bytes32,uint8,uint8)",
"roles": [
"ADD_CONTRIBUTION_ROLE"
],
"notice": null
},
{
"sig": "veto(uint32)",
"roles": [
"VETO_CONTRIBUTION_ROLE"
],
"notice": null
},
{
"sig": "claim(uint32)",
"roles": [],
"notice": null
}
]
}

1252
apps/contribution/code.sol Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -118,6 +118,26 @@ contract Contribution is AragonApp {
// Custom functions
//
function totalKreditsEarned(bool confirmedOnly) public view returns (uint32 amount) {
for (uint32 i = 1; i <= contributionsCount; i++) {
ContributionData memory c = contributions[i];
if (!c.vetoed && (block.number >= c.confirmedAtBlock || !confirmedOnly)) {
amount += c.amount; // should use safemath
}
}
}
function totalKreditsEarnedByContributor(uint32 contributorId, bool confirmedOnly) public view returns (uint32 amount) {
uint256 tokenCount = ownedContributions[contributorId].length;
for (uint256 i = 0; i < tokenCount; i++) {
uint32 cId = ownedContributions[contributorId][i];
ContributionData memory c = contributions[cId];
if (!c.vetoed && (block.number >= c.confirmedAtBlock || !confirmedOnly)) {
amount += c.amount; // should use safemath
}
}
}
function getContribution(uint32 contributionId) public view returns (uint32 id, uint32 contributorId, uint32 amount, bool claimed, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint256 confirmedAtBlock, bool exists, bool vetoed) {
id = contributionId;
ContributionData storage c = contributions[id];

File diff suppressed because it is too large Load Diff

View File

@@ -3,16 +3,20 @@
"version": "1.0.0",
"description": "",
"dependencies": {
"@aragon/os": "^4.1.0",
"@aragon/cli": "^5.5.0"
"@aragon/os": "^4.2.0",
"@aragon/cli": "^5.9.6"
},
"devDependencies": {
"@aragon/test-helpers": "^1.1.0",
"eth-gas-reporter": "^0.2.0",
"ganache-cli": "^6.4.3",
"solidity-coverage": "^0.5.11"
},
"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": "",
@@ -21,7 +25,11 @@
"publish:patch": "aragon apm publish patch",
"publish:minor": "aragon apm publish minor",
"publish:major": "aragon apm publish major",
"versions": "aragon apm versions"
"versions": "aragon apm versions",
"test": "TRUFFLE_TEST=true PORT=7545 npm run ganache-cli:test",
"test:gas": "GAS_REPORTER=true npm test",
"coverage": "SOLIDITY_COVERAGE=true npm run ganache-cli:test",
"ganache-cli:test": "./node_modules/@aragon/test-helpers/ganache-cli.sh"
},
"keywords": []
}

View File

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

View File

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

View File

@@ -6,6 +6,10 @@ 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;
@@ -43,6 +47,12 @@ contract Contributor is AragonApp {
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[uint8(Apps.Token)]);
}
function getContributionContract() public view returns (address) {
IKernel k = IKernel(kernel());
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[uint8(Apps.Contribution)]);
}
function coreContributorsCount() view public returns (uint32) {
uint32 count = 0;
for (uint32 i = 1; i <= contributorsCount; i++) {
@@ -118,7 +128,7 @@ contract Contributor is AragonApp {
return contributors[id];
}
function getContributorById(uint32 _id) public view returns (uint32 id, address account, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, bool isCore, uint256 balance, bool exists ) {
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 ) {
id = _id;
Contributor storage c = contributors[_id];
account = c.account;
@@ -128,10 +138,13 @@ contract Contributor is AragonApp {
isCore = isCoreTeam(id);
address token = getTokenContract();
balance = ITokenBalance(token).balanceOf(c.account);
address contribution = getContributionContract();
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) {
function canPerform(address _who, address _where, bytes32 _what, uint256[] memory _how) public returns (bool) {
address sender = _who;
if (sender == address(-1)) {
sender = tx.origin;

File diff suppressed because it is too large Load Diff

View File

@@ -3,16 +3,20 @@
"version": "1.0.0",
"description": "",
"dependencies": {
"@aragon/os": "^4.1.0",
"@aragon/cli": "^5.5.0"
"@aragon/os": "^4.2.0",
"@aragon/cli": "^5.9.6"
},
"devDependencies": {
"@aragon/test-helpers": "^1.1.0",
"eth-gas-reporter": "^0.2.0",
"ganache-cli": "^6.4.3",
"solidity-coverage": "^0.5.11"
},
"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": "",
@@ -21,7 +25,11 @@
"publish:patch": "aragon apm publish patch",
"publish:minor": "aragon apm publish minor",
"publish:major": "aragon apm publish major",
"versions": "aragon apm versions"
"versions": "aragon apm versions",
"test": "TRUFFLE_TEST=true PORT=7545 npm run ganache-cli:test",
"test:gas": "GAS_REPORTER=true npm test",
"coverage": "SOLIDITY_COVERAGE=true npm run ganache-cli:test",
"ganache-cli:test": "./node_modules/@aragon/test-helpers/ganache-cli.sh"
},
"keywords": []
}

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -3,16 +3,20 @@
"version": "1.0.0",
"description": "",
"dependencies": {
"@aragon/os": "^4.1.0",
"@aragon/cli": "^5.5.0"
"@aragon/os": "^4.2.0",
"@aragon/cli": "^5.9.6"
},
"devDependencies": {
"@aragon/test-helpers": "^1.1.0",
"eth-gas-reporter": "^0.2.0",
"ganache-cli": "^6.4.3",
"solidity-coverage": "^0.5.11"
},
"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": "",
@@ -21,7 +25,11 @@
"publish:patch": "aragon apm publish patch",
"publish:minor": "aragon apm publish minor",
"publish:major": "aragon apm publish major",
"versions": "aragon apm versions"
"versions": "aragon apm versions",
"test": "TRUFFLE_TEST=true PORT=7545 npm run ganache-cli:test",
"test:gas": "GAS_REPORTER=true npm test",
"coverage": "SOLIDITY_COVERAGE=true npm run ganache-cli:test",
"ganache-cli:test": "./node_modules/@aragon/test-helpers/ganache-cli.sh"
},
"keywords": []
}

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -3,16 +3,20 @@
"version": "1.0.0",
"description": "",
"dependencies": {
"@aragon/os": "^4.1.0",
"@aragon/cli": "^5.5.0"
"@aragon/os": "^4.2.0",
"@aragon/cli": "^5.9.6"
},
"devDependencies": {
"@aragon/test-helpers": "^1.1.0",
"eth-gas-reporter": "^0.2.0",
"ganache-cli": "^6.4.3",
"solidity-coverage": "^0.5.11"
},
"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": "",
@@ -21,7 +25,11 @@
"publish:patch": "aragon apm publish patch",
"publish:minor": "aragon apm publish minor",
"publish:major": "aragon apm publish major",
"versions": "aragon apm versions"
"versions": "aragon apm versions",
"test": "TRUFFLE_TEST=true PORT=7545 npm run ganache-cli:test",
"test:gas": "GAS_REPORTER=true npm test",
"coverage": "SOLIDITY_COVERAGE=true npm run ganache-cli:test",
"ganache-cli:test": "./node_modules/@aragon/test-helpers/ganache-cli.sh"
},
"keywords": []
}

View File

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

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": "aragonpm.eth",
"apm": "open.aragonpm.eth",
"registry": "0x5f6f7e8cc7346a11ca2def8f827b7a0b612c56a1",
"appName": "dummy.aragonpm.eth"
"appName": "dummy.open.aragonpm.eth"
},
"rinkeby": {
"network": "rinkeby",
"registry": "0x98Df287B6C145399Aaa709692c8D308357bC085D",
"wsRPC": "wss://rinkeby.eth.aragon.network/ws",
"daoFactory": "0x2298d27a9b847c681d2b2c2828ab9d79013f5f1d",
"daoFactory": "0x2298d27a9b847c681d2b2c2828ab9d79013f5f1d",
"appName": "dummy.open.aragonpm.eth",
"apm": "open.aragonpm.eth"
},

View File

@@ -1,19 +1,48 @@
const contractCalls = [
['Contributor', 'add', [{ account: '0x7e8f313c56f809188313aa274fa67ee58c31515d', name: 'bumi', kind: 'person', url: '', github_username: 'bumi', github_uid: 318, wiki_username: 'Bumi' }, { gasLimit: 200000 }]],
['Contributor', 'add', [{ account: '0x49575f3DD9a0d60aE661BC992f72D837A77f05Bc', name: 'raucao', kind: 'person', url: '', github_username: 'skddc', github_uid: 842, 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', 'addProposal', [{ contributorId: 1, contributorIpfsHash: 'QmWKCYGr2rSf6abUPaTYqf98urvoZxGrb7dbspFZA6oyVF', date: '2019-04-09', amount: 500, kind: 'dev', description: '[67P/kredits-contracts] Ran the seeds', url: '' }, { gasLimit: 350000 }]],
['Proposal', 'addProposal', [{ contributorId: 2, contributorIpfsHash: 'QmcHzEeAM26HV2zHTf5HnZrCtCtGdEccL5kUtDakAB7ozB', date: '2019-04-10', amount: 500, kind: 'dev', description: '[67P/kredits-contracts] Ran the seeds', url: '' }, { gasLimit: 350000 }]],
['Proposal', 'addProposal', [{ contributorId: 2, contributorIpfsHash: 'QmcHzEeAM26HV2zHTf5HnZrCtCtGdEccL5kUtDakAB7ozB', date: '2019-04-11', amount: 500, kind: 'dev', description: '[67P/kredits-contracts] Hacked on kredits', url: '' }, { gasLimit: 350000 }]],
['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', 'addContribution', [{ contributorId: 1, contributorIpfsHash: 'QmWKCYGr2rSf6abUPaTYqf98urvoZxGrb7dbspFZA6oyVF', date: '2019-04-11', amount: 5000, kind: 'dev', description: '[67P/kredits-contracts] Introduce contribution token', url: '' }, { gasLimit: 350000 }]],
['Contribution', 'addContribution', [{ 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 }]]
['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 }]],
];
const funds = [
'0x7e8f313c56f809188313aa274fa67ee58c31515d',
'0xa502eb4021f3b9ab62f75b57a94e1cfbf81fd827'
'0xa502eb4021f3b9ab62f75b57a94e1cfbf81fd827',
];
module.exports = { contractCalls, funds };

View File

@@ -34,20 +34,20 @@ 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);
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);
@@ -57,7 +57,7 @@ contract KreditsKit is KitBase {
//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.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());
@@ -67,9 +67,9 @@ contract KreditsKit is KitBase {
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,5 +1,14 @@
# 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:

View File

@@ -1,5 +1,22 @@
# 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:

View File

@@ -1,5 +1,25 @@
# 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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
lib/abis/KreditsKit.json Normal file
View File

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

View File

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

View File

@@ -1,22 +1,26 @@
class Base {
constructor(contract) {
constructor (contract) {
this.contract = contract;
}
get functions() {
get functions () {
return this.contract.functions;
}
get ipfs() {
get address () {
return this.contract.address;
}
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);
}
}

View File

@@ -1,37 +1,29 @@
const ethers = require('ethers');
const Record = require('./record');
const ContributionSerializer = require('../serializers/contribution');
const Base = require('./base');
const deprecate = require('../utils/deprecate');
class Contribution extends Base {
all() {
return this.functions.contributionsCount()
.then(async (count) => {
let contributions = [];
for (let id = 1; id <= count; id++) {
const contribution = await this.getById(id)
contributions.push(contribution);
}
return contributions;
});
class Contribution extends Record {
get count () {
return this.functions.contributionsCount();
}
getById(id) {
getById (id) {
return this.functions.getContribution(id)
.then(data => {
return this.ipfs.catAndMerge(data, ContributionSerializer.deserialize);
});
}
getByContributorId(contributorId) {
getData (id) {
return this.functions.getContribution(id);
}
getByContributorId (contributorId) {
return this.functions.getContributorAddressById(contributorId)
.then(address => this.getByContributorAddress(address));
}
getByContributorAddress(address) {
getByContributorAddress (address) {
return this.functions.balanceOf(address)
.then(async (balance) => {
const count = balance.toNumber();
@@ -47,7 +39,7 @@ class Contribution extends Base {
});
}
async addContribution(contributionAttr, callOptions = {}) {
async add (contributionAttr, callOptions = {}) {
const contribution = new ContributionSerializer(contributionAttr);
try { await contribution.validate(); }
@@ -69,6 +61,12 @@ class Contribution extends Base {
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,38 +1,33 @@
const RSVP = require('rsvp');
const Record = require('./record');
const ContributorSerializer = require('../serializers/contributor');
const Base = require('./base');
const formatKredits = require('../utils/format-kredits');
class Contributor extends Base {
all() {
return this.functions.contributorsCount()
.then(count => {
let contributors = [];
for (let id = 1; id <= count; id++) {
contributors.push(this.getById(id));
}
return RSVP.all(contributors);
});
class Contributor extends Record {
get count () {
return this.functions.contributorsCount();
}
getById(id) {
getById (id) {
return this.functions.getContributorById(id)
.then((data) => {
.then(data => {
data.balanceInt = formatKredits(data.balance);
return this.ipfs.catAndMerge(data, ContributorSerializer.deserialize);
});
}
filterByAccount(search) {
getData (id) {
return this.functions.getContributorById(id);
}
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);
@@ -48,12 +43,16 @@ class Contributor extends Base {
});
}
add(contributorAttr, callOptions = {}) {
let json = ContributorSerializer.serialize(contributorAttr);
// TODO: validate against schema
async add (contributorAttr, callOptions = {}) {
let contributor = new ContributorSerializer(contributorAttr);
try { await contributor.validate(); }
catch (error) { return Promise.reject(error); }
const jsonStr = contributor.serialize();
return this.ipfs
.add(json)
.add(jsonStr)
.then((ipfsHashAttr) => {
let contributor = [
contributorAttr.account,
@@ -65,6 +64,31 @@ class Contributor extends Base {
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

@@ -4,19 +4,19 @@ const Base = require('./base');
const KERNEL_APP_ADDR_NAMESPACE = '0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb';
class Kernel extends Base {
constructor(contract) {
constructor (contract) {
super(contract);
this.apm = 'aragonpm.eth'; // can be overwritten if needed
this.apm = 'open.aragonpm.eth'; // can be overwritten if needed
}
getApp(appName) {
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,30 +1,20 @@
const RSVP = require('rsvp');
const Record = require('./record');
const ContributionSerializer = require('../serializers/contribution');
const Base = require('./base');
const deprecate = require('../utils/deprecate');
class Proposal extends Base {
all() {
return this.functions.proposalsCount()
.then(count => {
let proposals = [];
for (let id = 1; id <= count; id++) {
proposals.push(this.getById(id));
}
return RSVP.all(proposals);
});
class Proposal extends Record {
get count () {
return this.functions.proposalsCount();
}
getById(id) {
getById (id) {
return this.functions.getProposal(id)
.then(data => {
return this.ipfs.catAndMerge(data, ContributionSerializer.deserialize);
});
}
async addProposal(proposalAttr, callOptions = {}) {
async add (proposalAttr, callOptions = {}) {
const contribution = new ContributionSerializer(proposalAttr);
try { await contribution.validate(); }
@@ -46,6 +36,11 @@ class Proposal extends Base {
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;

26
lib/contracts/record.js Normal file
View File

@@ -0,0 +1,26 @@
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);
});
}
pinIpfsHashes () {
return this.count.then(count => {
let promises = [...Array(count).keys()].map(i => {
let id = i + 1; // 0 => 1 - ids start with 1 and not with 0
return this.getData(id).then(data => {
return this.ipfs.pin(data);
});
});
return Promise.all(promises);
});
}
}
module.exports = Record;

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,22 +51,43 @@ 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 RSVP.all(addressPromises).then(() => { return this });
return Promise.all(addressPromises).then(() => { return this; });
});
}
static setup(provider, signer, ipfsConfig = null) {
console.log('Kredits.setup() is deprecated use new Kredits().init() instead');
static setup (provider, signer, ipfsConfig = null) {
deprecate('Kredits.setup() is deprecated use new Kredits().init() instead');
return new Kredits(provider, signer, { ipfsConfig: ipfsConfig }).init();
}
get Kernel() {
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 () {
let k = this.contractFor('Kernel');
// in case we want to use a special apm (e.g. development vs. production)
if (this.options.apm) {
@@ -75,37 +96,37 @@ class Kredits {
return k;
}
get Contributor() {
get Contributor () {
return this.contractFor('Contributor');
}
get Contributors() {
console.log('Contributors is deprecated use Contributor instead');
get Contributors () {
deprecate('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];
}
@@ -125,7 +146,7 @@ class Kredits {
return this.contracts[name];
}
preflightChecks() {
preflightChecks () {
return new Preflight(this).check();
}
}

49
lib/kreditskit.js Normal file
View File

@@ -0,0 +1,49 @@
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

@@ -9,7 +9,7 @@ const validator = require('../utils/validator');
*/
class Contribution {
constructor(attrs) {
constructor (attrs) {
Object.keys(attrs).forEach(a => this[a] = attrs[a]);
}
@@ -26,24 +26,24 @@ class Contribution {
kind,
description,
url,
details
details,
} = this;
let data = {
"@context": "https://schema.kosmos.org",
"@type": "Contribution",
"contributor": {
"ipfs": contributorIpfsHash
'@context': 'https://schema.kosmos.org',
'@type': 'Contribution',
'contributor': {
'ipfs': contributorIpfsHash,
},
date,
time,
kind,
description,
"details": details || {}
'details': details || {},
};
if (url) {
data["url"] = url;
data['url'] = url;
}
// Write it pretty to ipfs

View File

@@ -1,3 +1,5 @@
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
@@ -6,13 +8,88 @@
* @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,
@@ -20,13 +97,17 @@ class Contributor {
accounts,
} = JSON.parse(serialized.toString('utf8'));
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');
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');
if (github) {
(({ username: github_username, uid: github_uid} = github));
}
if (gitea) {
(({ username: gitea_username } = gitea));
}
if (wiki) {
(({ username: wiki_username } = wiki));
}
@@ -38,59 +119,12 @@ 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;

5
lib/utils/deprecate.js Normal file
View File

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

View File

@@ -0,0 +1,10 @@
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);
}
};

32
lib/utils/ipfs-pinner.js Normal file
View File

@@ -0,0 +1,32 @@
class IpfsPinner {
constructor (kredits) {
this.kredits = kredits;
}
pinAll () {
return Promise.all([
this.kredits.Contributor.pinIpfsHashes(),
this.kredits.Contribution.pinIpfsHashes(),
]);
}
monitor (callback) {
this.kredits.Contribution.on('ContributionAdded', (id) => {
this.kredits.Contribution.getData(id)
.then(data => { return this.kredits.ipfs.pin(data); })
.then(callback);
});
this.kredits.Contributor.on('ContributorAdded', (id) => {
this.kredits.Contribution.getData(id)
.then(data => { return this.kredits.ipfs.pin(data); })
.then(callback);
});
this.kredits.Contributor.on('ContributorProfileUpdated', (id) => {
this.kredits.Contributor.getData(id)
.then(data => { return this.kredits.ipfs.pin(data); })
.then(callback);
});
}
}
module.exports = IpfsPinner;

View File

@@ -1,17 +1,17 @@
const ipfsClient = require('ipfs-http-client');
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 = ipfsClient(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,7 +26,7 @@ class IPFS {
});
}
add(data) {
add (data) {
return this._ipfsAPI
.add(ipfsClient.Buffer.from(data))
.then((res) => {
@@ -34,29 +34,40 @@ class IPFS {
});
}
cat(hashData) {
cat (hashData) {
let ipfsHash = hashData; // default - if it is a string
if (hashData.hasOwnProperty('hashSize')) {
ipfsHash = this.encodeHash(hashData);
}
return this._ipfsAPI.cat(ipfsHash);
if (this._config['gatewayUrl']) {
return fetch(`${this._config['gatewayUrl']}/${ipfsHash}`).then(r => r.text());
} else {
return this._ipfsAPI.cat(ipfsHash);
}
}
decodeHash(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) {
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) {
encodeHash (hashData) {
let digest = ipfsClient.Buffer.from(hashData.hashDigest.slice(2), 'hex');
return multihashes.encode(digest, hashData.hashFunction, hashData.hashSize);
}
}
module.exports = IPFS;

46
lib/utils/pagination.js Normal file
View File

@@ -0,0 +1,46 @@
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

@@ -4,12 +4,12 @@ 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";
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";
}
})
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;

37386
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "kredits-contracts",
"version": "5.1.1",
"version": "5.4.0",
"description": "Ethereum contracts and npm wrapper for Kredits",
"main": "./lib/kredits.js",
"directories": {
@@ -8,10 +8,11 @@
},
"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": "aragon contracts compile --all",
"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",
@@ -20,7 +21,14 @@
"deploy:apps": "./scripts/every-app.sh \"aragon apm publish major\"",
"devchain": "aragon devchain --port 7545",
"dao:address": "truffle exec scripts/current-address.js",
"test": "echo \"Error: no test specified\" && exit 1"
"lint:contracts": "solhint \"contracts/**/*.sol\" \"apps/*/contracts/**/*.sol\"",
"lint:contract-tests": "eslint apps/*/test",
"lint:wrapper": "eslint lib/",
"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"
},
"repository": {
"type": "git",
@@ -33,20 +41,26 @@
},
"homepage": "https://github.com/67P/truffle-kredits#readme",
"devDependencies": {
"@aragon/cli": "^5.5.0",
"@aragon/cli": "^5.9.6",
"@aragon/kits-base": "^1.0.0",
"@aragon/os": "^4.1.0",
"@aragon/os": "^4.2.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.25"
"solc": "^0.4.26",
"solhint": "^2.1.0",
"yargs": "^12.0.0"
},
"dependencies": {
"ethers": "^4.0.27",
"ipfs-http-client": "^30.1.1",
"kosmos-schemas": "^2.0.0",
"rsvp": "^4.8.2",
"ethers": "^4.0.29",
"ipfs-http-client": "^30.1.3",
"kosmos-schemas": "^2.1.0",
"node-fetch": "^2.6.0",
"tv4": "^1.3.0"
},
"keywords": [

View File

@@ -26,7 +26,7 @@ module.exports = async function(callback) {
kind: await prompt('Kind (default person): ', {default: 'person'}),
url: await prompt('URL: '),
github_username: await prompt('GitHub username: '),
github_uid: await prompt('GitHub UID: '),
github_uid: parseInt(await prompt('GitHub UID: ')),
wiki_username: await prompt('Wiki username: '),
};

View File

@@ -0,0 +1,53 @@
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

@@ -35,7 +35,9 @@ module.exports = async function(callback) {
}
console.log(`Using DAOFactory at: ${daoFactory.address}`)
const apps = fs.readdirSync('./apps')
const apps = fs.readdirSync('./apps', { withFileTypes: true })
.filter(e => e.isDirectory())
.map(e => e.name);
console.log(`Found apps: [${apps}].${apm}`)
let appIds = {}
apps.sort().forEach((app) => {

View File

@@ -0,0 +1,47 @@
#!/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

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

17
scripts/git-hooks/pre-commit Executable file
View File

@@ -0,0 +1,17 @@
#!/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

31
scripts/ipfs-pinner.js Normal file
View File

@@ -0,0 +1,31 @@
const Kredits = require('../lib/kredits');
const IpfsPinner = require('../lib/utils/ipfs-pinner');
const network = process.env.ETH_NETWORK || 'rinkeby';
const rpcUrl = process.env.ETH_RPC_URL;
const apm = process.env.APM_DOMAIN || 'open.aragonpm.eth';
const ipfsConfig = {
host: process.env.IPFS_HOST || 'localhost',
port: process.env.IPFS_PORT || '5001',
protocol: process.env.IPFS_PROTOCOL || 'http',
};
console.log(`Using IPFS:`, ipfsConfig);
(async () => {
try {
const kredits = await Kredits.for({ network, rpcUrl }, { apm, ipfsConfig }).init();
const ipfsPinner = new IpfsPinner(kredits);
ipfsPinner.pinAll().then(pins => {
console.log('Pinned', JSON.stringify(pins, null, 2));
});
ipfsPinner.monitor((pin) => {
console.log('Pinned', JSON.stringify(pin));
});
console.log(`Subscribed to DAO: ${kredits.Kernel.contract.address}`);
} catch(e) {
console.log(e);
process.exit(1);
}
})();

View File

@@ -20,8 +20,10 @@ module.exports = async function(callback) {
try {
let blockNumber = await kredits.provider.getBlockNumber();
let contributions = await kredits.Contribution.all();
let contributions = await kredits.Contribution.all({page: {size: 1000}});
let kreditsSum = 0;
console.log(`Current block number: ${blockNumber}`);
contributions.forEach((c) => {
const confirmed = c.confirmedAtBlock <= blockNumber;
@@ -30,7 +32,7 @@ module.exports = async function(callback) {
c.contributorId,
`${c.description}`,
c.amount.toString(),
confirmed,
`${confirmed} (${c.confirmedAtBlock})`,
c.vetoed,
c.claimed,
c.ipfsHash
@@ -38,6 +40,10 @@ module.exports = async function(callback) {
});
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);
}

View File

@@ -15,24 +15,31 @@ module.exports = async function(callback) {
console.log(`Using Contributor at: ${kredits.Contributor.contract.address}`);
const table = new Table({
head: ['ID', 'Account', 'Name', 'Core?', 'Balance', 'IPFS']
head: ['ID', 'Account', 'Name', 'Core?', 'Balance', 'Kredits earned', 'Contributions count', 'IPFS']
})
let contributors = await kredits.Contributor.all()
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;
}
contributors.forEach((c) => {
table.push([
c.id.toString(),
c.account,
`${c.name}`,
c.isCore,
ethers.utils.formatEther(c.balance),
c.ipfsHash
])
})
console.log(table.toString())
callback()
}

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,25 +20,21 @@ module.exports = async function(callback) {
}
console.log(`Using KreditsKit at: ${kreditsKitAddress}`);
let kreditsKit = KreditsKit.at(kreditsKitAddress)
const provider = new ethers.providers.Web3Provider(web3.currentProvider);
let signer = provider.getSigner();
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)
let kit = await new KreditsKit(provider, signer).init()
if (deployEvents.length > 1) {
callback(new Error("More than one DAO was deployed. Something is wrong"))
}
const daoAddress = deployEvents[0].dao;
// 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)
fileInject(path.join(addressesPath, 'dao.json'), networkId, daoAddress)
console.log(`\n\nCreated new DAO at: ${daoAddress}`)
console.log(`\n\nCreated new DAO at: ${result.daoAddress}`)
callback();
}).catch((err) => {
console.log('failed to create a new instance')
console.log('failed to create a new DAO')
callback(err)
})
}

View File

@@ -17,13 +17,18 @@ module.exports = async function(callback) {
let fundingAmount = 2;
each(seeds.funds, (address, next) => {
console.log(`funding ${address} with 2 ETH`);
web3.eth.sendTransaction({
to: address,
value: web3.toWei(fundingAmount),
from: web3.eth.accounts[0]
},
(result) => { next(); }
)
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();
});
each(seeds.contractCalls, (call, next) => {

View File

@@ -0,0 +1,32 @@
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));
}
}

638
yarn.lock

File diff suppressed because it is too large Load Diff