Compare commits

...

281 Commits

Author SHA1 Message Date
8f0d7e5196 6.0.0 2020-07-17 13:22:59 +02:00
4add0c7d96 package-lock 2020-07-17 13:22:24 +02:00
fbd82a442e Merge pull request #200 from 67P/ethers5
Update to ethers 5
2020-07-07 16:59:40 +02:00
c39fe406d0 Fix deprecation 2020-06-29 23:49:46 +02:00
a609d921aa Remove accidentally added local DAO addresses 2020-06-29 23:49:03 +02:00
23d3dd3a80 Adjust API for new ethers v5 API
see issue for details: https://github.com/ethers-io/ethers.js/issues/920#issuecomment-650836642
2020-06-29 17:23:08 +02:00
bc38dcb136 Copy contract data into new object to avoid object is not extensible error
It seems the new ethers.js version returns contract data as a non-
extensible object. We have to clone it before adding the IPFS data.
2020-06-27 20:39:23 +02:00
c2dcedfe58 Update to ethers 5 2020-06-24 01:20:04 +02:00
2b99593699 Merge pull request #196 from 67P/chore/dependency-updates
Chore/dependency updates
2020-05-27 16:22:40 +02:00
606350eb5e Update solc
our contracts still use an older version, but I guess it is good to have
the latest solc package
2020-05-27 10:53:31 +02:00
a330a8eedf npm upgrade 2020-05-27 10:46:41 +02:00
3c72fa3a8b run npm upgrade in every app
are those package-lock.json there actually used?
2020-05-27 10:31:12 +02:00
a4ef2398be update dependencies 2020-05-27 10:06:06 +02:00
31c29ab6d0 Merge pull request #194 from 67P/updates
Some required dependency updates
2020-05-07 10:08:26 +02:00
ef0c0c0a39 (ci) Disable all tests 2020-05-07 09:59:27 +02:00
844bdbd681 (ci) Disable token tests
They are failing in different ways both locally and on Travis.
2020-05-07 09:51:26 +02:00
9d96824fe9 (ci) Wait a little for the devchain to start up 2020-05-07 09:41:11 +02:00
d246531cfa Update package lockfile 2020-05-07 09:40:58 +02:00
6be06b2e57 Merge branch 'master' into updates 2020-05-07 09:30:29 +02:00
c2220c3654 Fix ESLint error 2020-05-07 09:27:15 +02:00
5044d8fe41 fix syntax error 2020-04-15 20:44:13 +02:00
3c49c688d2 Add missing dev dependencies and update dependencies 2020-04-15 20:43:46 +02:00
47f59126a7 5.5.0 2020-03-01 12:46:22 +01:00
2ce5d925d0 Merge pull request #182 from 67P/chore/update_dependencies
WIP: Fix Aragon breaking changes; update dependencies
2020-02-20 11:42:04 +00:00
3db89b0fa3 Refactor kredits related configuration in arapp.json
we use an environment specific entry for the daoAddress.
Locally we deploy our own, but for rinkeby and other public networks
we use the official entries.
To make deployments easy we store the address in the arapp.json
2020-02-10 15:55:16 +01:00
190d3b77d5 Update tests
getContract now seems to return a Promise
2020-02-10 14:59:22 +01:00
b7eac4202c Adjust deploy script and readme for updates 2020-02-10 14:30:03 +01:00
93aeea69c6 Fix test commands
Aragon CLI does not come with the contracts command anymore.
2020-02-08 17:37:26 -05:00
e7700d5ec7 Remove @aragon/cli from app package configs
We're assuming a global install now.
2020-02-08 17:36:48 -05:00
c3eced5c1d Remove apm app config keys 2020-02-08 17:19:39 -05:00
4c6ed12167 Re-add fixed package lock 2020-02-08 16:41:08 -05:00
9431bc22a6 Re-add package lock fix script
m(
2020-02-08 16:36:29 -05:00
78c8052629 Update dependencies
Dance the update dance with me.
2020-02-08 16:28:13 -05:00
a318fe8374 Add release-drafter config 2019-09-18 12:49:00 +02:00
eded4a811f Merge pull request #175 from 67P/feature/zoom-profile
Add support for zoom profiles
2019-09-18 09:28:19 +02:00
66a37a1e74 Improve property name 2019-09-18 08:44:13 +02:00
aba4a23104 Fixes & linting 2019-09-17 18:30:19 +02:00
13ed02e134 Add support for zoom profils
This adds an accessor for the zoom_name to the contributor profile.
Doing this also removes general support for preserviing the contributor
accounts array as this was buggy and caused accounts to be added
multiple times.
2019-09-17 17:24:35 +02:00
48ff304861 Merge pull request #166 from 67P/dev/remove_yarn_lockfile
Remove yarn lockfiles
2019-08-27 12:34:29 +02:00
1c1a318ae6 Merge pull request #165 from 67P/docs/blocktime
Document how to configure block time for Ganache
2019-08-09 22:10:06 +00:00
580d6e8f51 Remove yarn lockfiles 2019-08-09 19:03:29 +02:00
a572f02b2e Merge pull request #164 from 67P/chore/remove-cached-artifacts
Remove cached artifacts
2019-08-09 19:00:21 +02:00
abd4caa336 Document how to configure block time for Ganache 2019-08-09 18:58:20 +02:00
f1941eb6b8 Remove cached artifacts
Thos should be generated on every contract app deployment and thus don't need to
be in the repo.
We also don't have those for the other contract apps.
2019-08-09 18:46:36 +02:00
ddeeb5ffd7 Merge pull request #162 from 67P/dev/aragon_truffle
Remove Aragon CLI, document system deps
2019-08-09 18:38:40 +02:00
ae71691c5b Install Aragon CLI and Truffle on Travis 2019-08-09 18:26:38 +02:00
819fbc547a Add contract publish options
This removes most of the features we don't need/want and minimizes
prompts during the bootstrap process.
2019-08-09 18:18:36 +02:00
7236b04b4b Remove Aragon CLI, document system deps
In order to prevent regular issues with the notoriously npm-issue-ridden
Aragon CLI module, this removes it as a direct dependency and requires
it to be installed as a system dependency.

Also adds Truffle as a system dependency to the docs (which was missing
before).
2019-08-09 18:04:21 +02:00
f746470cf6 Merge pull request #161 from 67P/fix_typos
Fix a few typos in the README
2019-08-07 16:42:01 +02:00
Greg Karékinian
5452cf2dad Fix a few typos 2019-08-07 15:55:11 +02:00
3c08e3ecfc Merge pull request #160 from 67P/bugfix/packages-install-scripts
Fix package script command deploy:kit
2019-08-04 19:42:42 +02:00
6e8c994a0a Fix package script command deploy:kit 2019-08-04 19:37:31 +02:00
Haythem Sellami
fd844a00fd Test token app (#157)
* test token app
* update travis config
2019-07-31 15:17:38 +00:00
Haythem Sellami
eb4e06edf1 Contributor app tests (#143)
Adds tests for the contributor contract
2019-07-22 06:55:22 +00:00
4ae17aa36f Merge pull request #156 from haythem96/refactor-get-contract
Refactor get apps contract functions
2019-07-22 06:54:05 +00:00
e14cb0a77d refactor get apps contract functions 2019-07-20 15:06:17 +01:00
d92ad83379 Merge pull request #155 from 67P/bugfix/send-for-new-web3
Fix send-funds script to use new web3 API
2019-07-19 09:11:55 +00:00
fa3e99d404 Fix send-funds script to use new web3 API
The API changed with the recent aragon/truffle update.
2019-07-18 16:25:06 +02:00
4aa5f3aa89 Merge pull request #151 from 67P/chore/remove-ipfs-pinner
Remove IPFS pinner
2019-07-03 12:50:59 +02:00
2316e8f15a Remove IPFS pinner
The pinner now lives in its own npm package
2019-07-02 19:09:26 +02:00
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
2a1ae117e8 5.1.1 2019-04-16 12:50:07 +01:00
8abc7ba77f Merge pull request #104 from 67P/chore/update-kosmos-schema
Update kosmos-schema dependency
2019-04-16 11:19:03 +00:00
f696721918 Readme: add note about locally running out of funds 2019-04-16 13:17:54 +02:00
00b58dec66 Update kosmos-schema dependency 2019-04-16 13:16:05 +02:00
560315cbca Merge pull request #102 from 67P/chore/add-yarn-support
Add yarn lock files
2019-04-15 15:54:38 +00:00
24933f31a7 Add yarn lock files 2019-04-15 17:34:48 +02:00
b13bf6e8b0 Readme: add note about locally running out of funds 2019-04-15 15:59:54 +02:00
8dcad88372 Merge pull request #99 from 67P/update-seeds
Update seeds
2019-04-15 10:39:10 +00:00
dc6d2716aa Update seeds 2019-04-15 12:33:51 +02:00
80ad9db630 5.1.0 2019-04-13 12:41:49 +01:00
6f97c905d6 Add count property and DRY 2019-04-13 13:05:47 +02:00
1b5f7ff95d Merge pull request #93 from 67P/deployments/weltempfaenger-release
Production release to rinkeby
2019-04-13 11:07:58 +01: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
b6c06c289c Merge pull request #95 from 67P/cosmetics
Cosmetic cleanups
2019-04-12 18:55:59 +00:00
f405e39c04 Merge pull request #94 from 67P/bugfix/schema_changes
Refactor contribution serializer and validation
2019-04-12 18:54:56 +00:00
1dbf3b5742 Cosmetic cleanups 2019-04-12 20:16:24 +02:00
952b5153fd Validate proposed contributions 2019-04-11 13:46:42 +02:00
d953141f52 Refactor contribution serializer and validation 2019-04-11 13:05:40 +02:00
aa57d7c70b Adjust seeds for new requirements 2019-04-11 11:02:15 +02:00
4c0bb879e8 Show IPFS hash in list script output 2019-04-11 11:01:45 +02:00
9c5a517fb9 5.0.0 2019-04-11 08:45:49 +02:00
b00ddda312 Merge pull request #92 from 67P/feature/contribution_schema
Validate contribution docs against schema
2019-04-11 08:40:38 +02:00
130e2a7797 Validate contribution docs against schema
Currently requires an open PR branch for the schemas, which is adding
date and time for contributions.

refs #30
2019-04-10 18:37:02 +02:00
27e5190e29 Production release to rinkeby
Deployed all apps and created a new Kredits DAO
2019-04-10 18:28:24 +02:00
b9c171884b Merge pull request #91 from 67P/feature/store-balance-as-wei
Store kredits tokens as wei with 18 decimals
2019-04-10 15:51:36 +02:00
4d2e0ea84b Update Token symbol 2019-04-10 15:50:33 +02:00
26d12ba239 Store kredits tokens as wei with 18 decimals
This stores the tokens with 18 decimals as most other tokens and as
assumed by a potential future token standard EIP 777.
2019-04-10 13:59:00 +02:00
1aae62e139 Merge pull request #88 from 67P/bugfix/confirmed-contributions
Fix confirmed contribution check
2019-04-09 22:36:37 +02:00
f21c4cf9de Fix confirmed contribution check
If the blocknumber is higher or equal the contribution.confirmedAt block
number then the contribution is confirmed and can be claimed.
2019-04-09 22:30:17 +02:00
6778b9060f 4.0.2 2019-04-09 12:47:59 +02:00
4d36f78824 4.0.1 2019-04-09 12:47:37 +02:00
98ecf28262 Merge pull request #87 from 67P/dev/dependency_install
Rename postinstall script
2019-04-09 12:45:41 +02:00
f5564fa923 Formatting, whitespace 2019-04-09 12:45:20 +02:00
096a9f3cea Update README for install-all script 2019-04-09 12:45:09 +02:00
3d24835908 Merge pull request #86 from 67P/dev/bootstrap
Don't run `npm install` on bootstrap
2019-04-09 09:50:51 +02:00
8cbef8458d Rename postinstall script
Prevents it being run when contracts are installed as dependency.
2019-04-09 09:48:00 +02:00
9ca0580db6 Merge pull request #85 from 67P/dev/fix_seeds
Fix contributor ID in seeds
2019-04-09 07:33:42 +00:00
dfe38e7d21 Don't run npm install on bootstrap
When one wants to install, they can install. When one wants to
bootstrap, they should be able to only bootstrap.
2019-04-09 08:44:56 +02:00
377560805f Fix contributor ID in seeds
We have one contributor less, after having removed the initial dummy
account recently.
2019-04-09 08:43:13 +02:00
65d9cf2a06 Fix bootstrap
This runs npm install for every app through a postinstall hook.
2019-04-08 18:10:13 +02:00
b35dc2049b Merge pull request #82 from 67P/dev/list_contributions
Improve list-contributions script
2019-04-06 19:40:24 +00:00
26c2710149 Improve list-contributions script
* Improve sort order of table columns
* Print if the contribution is confirmed
* Log error if something goes wrong
2019-04-06 17:56:48 +02:00
cc24c27444 Merge pull request #80 from 67P/chore/rename-ipfshash-1
Rename ipfsHash to hashDigest in Contributor
2019-04-06 13:55:19 +00:00
f717968402 Better function naming to update contributor profiles 2019-04-06 15:42:16 +02:00
14d7fbd75e Rename ipfsHash to hashDigest in Contributor
naming should be consistent with the other contracts and digest is more
correct.
2019-04-06 15:24:44 +02:00
7eb1fedb42 Merge pull request #76 from 67P/features/dynamic-core-flag
Dynamic function to test for core contributor flag
2019-04-06 15:19:01 +02:00
cbfe39f804 Merge pull request #78 from 67P/feature/veto-period
Set contribution veto period to 1 week
2019-04-06 00:30:30 +02:00
a0e4f5410d Set veto period to 1 week
This makes a contribution claimable after roughly 7 days
2019-04-05 20:30:02 +02:00
766463b57b Dynamic function to test for core contributor flag
This removes the isCore flag and allows us to dynamically calculate the
core flag for contributors.

For now this is just us (the first 6 contirbutors)

Also we do not need the default contributor anymore because the deploy
user has the role to manage contributors and can create the first real
contributors.
2019-04-05 18:55:21 +02:00
93e0b30b35 4.0.0 2019-04-05 15:44:51 +02:00
6510dca4b1 Fix provider fallbacks for non dev networks
This now checks if frame or a local node is running and else uses infura
to connect to the ethereum network.
Now you can use all the helpers for the testnets/mainnet without running
anything (in readonly mode)
2019-04-05 13:51:23 +02:00
551edcfa72 Fix init kredits for readonly providers
The apm option was not passed on to kredits
2019-04-05 01:05:16 +02:00
d58e2d560a deploy to rinkeby 2019-04-05 00:38:51 +02:00
99394e7f14 Merge pull request #73 from 67P/feature/contribution_owner
Convert contribution owner to ID, use smaller number formats
2019-04-04 21:40:16 +00:00
8ceab08bfc ups 2019-04-04 23:23:02 +02:00
896db5e047 Update abi 2019-04-04 23:16:23 +02:00
d507d7b562 Enable all seeds again 2019-04-04 23:16:01 +02:00
40c5d2a275 Merge branch 'master' into feature/contribution_owner 2019-04-04 23:12:16 +02:00
a049f2eedb Adjust proposals for format changes 2019-04-04 23:11:43 +02:00
b68b037000 Merge pull request #74 from 67P/chore/npm-scripts
Add some helpers to reset a fresh DAO and upgrade an app
2019-04-04 23:11:12 +02:00
181e6f3c23 Fix/refactor contribution wrapper 2019-04-04 22:56:06 +02:00
19556349f6 Fix syntax error 2019-04-04 22:30:27 +02:00
a1a1c5ef9f Get contributor by ID
Co-Authored-By: skddc <sebastian@kip.pe>
2019-04-04 22:29:37 +02:00
afbd114a36 Add app upgrade helper script
This script deploys a new app version, upgrades the given DAO to the
latest version of that app and builds the ABI files for the wrapper.

usage:

    scripts/upgrade.sh <app name> <dao address>

e.g.

    scripts/upgrade.sh token 0xfoobar

The DAO address can also be set as environment variable
$KREDITS_DAO_ADDRESS
2019-04-04 19:51:38 +02:00
db312dafbf Add some helpers to reset a fresh DAO 2019-04-04 19:27:57 +02:00
e4c3d9a468 Update abi 2019-04-04 16:28:34 +02:00
5b49f82544 Adjust wrapper and scripts for contract changes 2019-04-04 16:24:37 +02:00
6088f30cd8 Use smaller numbers for contributor ID
2.2 billion contributors should be enough.
2019-04-04 16:01:08 +02:00
f1e2c65b9e Use smaller numbers where appropriate 2019-04-04 15:28:18 +02:00
786f38cfb7 Print errors in seed contract calls 2019-04-04 15:25:31 +02:00
197a31b9e1 Remove whitespace 2019-04-04 14:47:25 +02:00
20acfc70e3 Use ID instead of address for contribution owner 2019-04-04 14:43:38 +02:00
732dfe7b29 Merge pull request #71 from 67P/chore/update_seeds
Update seeds
2019-04-04 11:32:58 +00:00
3ce8f41d32 Merge pull request #72 from 67P/chore/whitespace
Remove unnecessary space chars
2019-04-04 11:17:35 +00:00
e0ff29d1c9 Remove unnecessary space chars 2019-04-04 11:38:18 +02:00
0ce62839e9 Update seeds
Use more realistic data, add more contributions
2019-04-04 11:32:46 +02:00
cc2c9a7368 Merge pull request #70 from 67P/feature/claim-contribution-in-seeds
Update seeds
2019-04-03 21:50:43 +02:00
1abfc5a265 Merge pull request #69 from 67P/fix/add-contributor-permissions
Fix permissions to manage contributors
2019-04-03 21:50:19 +02:00
6b9466c348 Update seeds
Claim contribution which issues Kredits to the contributor
2019-04-03 21:40:37 +02:00
9fffdbfb58 Fix permissions to manage contributors
Uses the contributor oracle to decide if somebody has the
MANAGE_CONTRIBUTOR_ROLE
2019-04-03 21:22:53 +02:00
350dadec4d Merge pull request #68 from 67P/update-ipfs-package
Update ipfs package
2019-04-03 21:16:22 +02:00
70b9edbcac Update ipfs package 2019-04-03 21:01:51 +02:00
92da07dca2 Merge pull request #67 from 67P/feature/contributor-balance-1
Add balance when loading contributor
2019-04-03 20:22:50 +02:00
6fed81c0ed Merge pull request #66 from 67P/chore/seeds
Allow seed tx to fail
2019-04-03 17:03:22 +00:00
74e61e1393 Show balance in list-contributors helper script 2019-04-03 14:05:16 +02:00
6351db3057 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:56:52 +02:00
ed3e5dd4c4 Allow seed tx to fail
This allows for running seeds multiple times, e.g. after adding new
ones.
2019-04-03 11:41:55 +02:00
bd39d0f126 Use new address for raucao seed 2019-04-03 11:41:45 +02:00
4c8ee69664 Merge pull request #65 from 67P/fix/ethersjs-namehash
Use namehash from ethers.utils
2019-04-03 11:39:35 +02:00
eae8967322 Merge pull request #63 from 67P/fix/contriutor-permissions
Fix contributor oracle permission auth
2019-04-03 11:37:10 +02:00
9984ca66ba Use namehash from ethers.utils
no need for the additional dependency
2019-04-03 10:24:11 +02:00
1b09a30646 Fix contributor oracle permission auth
It seems that the entity is either defined by the permission or if we
want to use the oracle the permission must be defined for any_entity.
In that case the oracle does not get the msg.sender as who/entity thus
we will use tx.origin in that case.
2019-04-03 10:06:00 +02:00
95 changed files with 20517 additions and 49551 deletions

1
.eslintignore Normal file
View File

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

33
.eslintrc.js Normal file
View File

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

4
.github/release-drafter.yml vendored Normal file
View File

@@ -0,0 +1,4 @@
template: |
## Changes
$CHANGES

2
.gitignore vendored
View File

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

9
.solhint.json Normal file
View File

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

38
.travis.yml Normal file
View File

@@ -0,0 +1,38 @@
---
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 &
- sleep 10
script:
- npm run lint:wrapper
- npm run lint:contract-tests
# FIXME Fix tests
# - npm run test:token
# - npm run test:contributor
# - npm run test:contribution
# - npm run test:proposal
branches:
only:
- master

113
README.md
View File

@@ -2,17 +2,20 @@
# 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
@@ -22,59 +25,81 @@ Each of the aragon apps are separate packages:
$ cd apps/[app]
$ npm install
or use the bootstrap command (see below)
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
### Local development chain
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.
For local development it is recommended to use
[ganache](http://truffleframework.com/ganache/) to run a local development
chain. When using the ganache simulator, no full Ethereum node is required.
We use the default aragon-cli devchain command to confgure and run a local
We use the default aragon-cli devchain command to configure and run a local
development ganache.
$ npm run devchain (or aragon devchain --port 7545)
To clear/reset the chain use:
To clear/reset the chain use (e.g. if you run out of funds on your devchain)
$ 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.
You can also set certain ganache options to configure the devchain, for example
if you want to increase the block time to 10 seconds you can add
`--block-time=10`.
### Bootstrap
1. Run an Ethereum node and ipfs
$ npm run devchain
$ ipfs daemon
2. Deploy each app to the devchain
2. Compile contracts
(compiled contracts will be in `/build`)
$ npm run compile-contracts
3. 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
4. Deploy a new KreditsKit and create a new DAO with the latest app versions
$ npm run deploy:kit
$ npm run deploy:dao
4. Execute seeds to create demo contributors, contributons, etc. (optional)
5. Execute seeds to create demo contributors, contributions, etc. (optional)
$ npm run seeds
**Step 2-4 is also summarized in `npm run bootstrap`**
**Step 2-5 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.
@@ -87,6 +112,8 @@ 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:
@@ -106,11 +133,11 @@ Script to add a new entries to the contracts using the JS wrapper
$ truffle exec scripts/add-{contributor, contribution, proposal}.js
### list-{contributor, contribution, proposal}.js
### list-{contributors, contributions, proposals}.js
List contract entries
$ truffle exec scripts/list-{contributor, contribution, proposal}.js
$ truffle exec scripts/list-{contributors, contributions, proposals}.js
### send-funds.js
@@ -143,8 +170,14 @@ Deploys a new KreditsKit that allows to create a new DAO
or
$ npm run deploy:kit
`ENS` address is required as environment variable.
`DAO_FACTORY` can optionally be set as environment variable. (see aragon)
#### Kredits configuration options:
Configuration options can be set in an environment specific `kredits` object in the `arapp.json` or using a CLI parameter.
* daoFactory: Ethereum address of the used DAO Factory. On public networks we use [official aragon factories](https://github.com/aragon/deployments/tree/master/environments/)
* apmDomain: the ENS domain of the aragonPM (normally `open.aragonpm.eth`)
(please also see the [arapp.json related configuration options](https://hack.aragon.org/docs/cli-global-confg#the-arappjson-file))
### new-dao.js
@@ -154,7 +187,7 @@ Creates and configures a new DAO instance.
or
$ npm run deploy:dao
KreditsKit address is load from `lib/addresses/KreditsKit.json` or can be
KreditsKit address is loaded from `lib/addresses/KreditsKit.json` or can be
configured through the `KREDITS_KIT` environment variable.
### deploy-apps.sh
@@ -165,14 +198,32 @@ 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 upgradeablity of the different contracts.
Refer to the [aragonOS upgradeablity documentation](https://hack.aragon.org/docs/upgradeability-intro)
We use aragonOS for upgradeability of the different contracts. Refer to the
[aragonOS upgradeablity documentation](https://hack.aragon.org/docs/upgradeability-intro)
for more details.
### Example

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

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

File diff suppressed because it is too large Load Diff

View File

@@ -3,25 +3,29 @@
"version": "1.0.0",
"description": "",
"dependencies": {
"@aragon/os": "^4.1.0",
"@aragon/cli": "^5.5.0"
"@aragon/os": "^4.4.0"
},
"devDependencies": {
"@aragon/test-helpers": "^2.1.0",
"eth-gas-reporter": "^0.2.17",
"ganache-cli": "^6.9.1",
"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": "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",
"start:app": "",
"compile": "aragon contracts compile",
"sync-assets": "copy-aragon-ui-assets -n aragon-ui ./dist",
"sync-assets": "",
"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"
"versions": "aragon apm versions",
"test": "truffle test"
},
"keywords": []
}

View File

@@ -1,5 +1,5 @@
const CounterApp = 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

@@ -1,80 +1,88 @@
pragma solidity ^0.4.24;
import "@aragon/os/contracts/apps/AragonApp.sol";
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 ipfsHash;
bytes32 hashDigest;
uint8 hashFunction;
uint8 hashSize;
bool isCore;
bool exists;
}
mapping (address => uint) public contributorIds;
mapping (uint => Contributor) public contributors;
uint256 public contributorsCount;
mapping (address => uint32) public contributorIds;
mapping (uint32 => Contributor) public contributors;
uint32 public contributorsCount;
// ensure alphabetic order
enum Apps { Contribution, Contributor, Proposal, Token }
bytes32[4] public appIds;
event ContributorProfileUpdated(uint id, bytes32 oldIpfsHash, bytes32 newIpfsHash);
event ContributorAccountUpdated(uint id, address oldAccount, address newAccount);
event ContributorAdded(uint id, address account);
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);
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 coreContributorsCount() view public returns (uint) {
uint count = 0;
for (uint256 i = 1; i <= contributorsCount; i++) {
if (contributors[i].isCore) {
function getContract(uint8 appId) public view returns (address) {
IKernel k = IKernel(kernel());
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[appId]);
}
function coreContributorsCount() public view returns (uint32) {
uint32 count = 0;
for (uint32 i = 1; i <= contributorsCount; i++) {
if (isCoreTeam(i)) {
count += 1;
}
}
return count;
}
function updateContributorAccount(uint id, address oldAccount, address newAccount) public auth(MANAGE_CONTRIBUTORS_ROLE) {
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");
contributorIds[oldAccount] = 0;
contributorIds[newAccount] = id;
contributors[id].account = newAccount;
ContributorAccountUpdated(id, oldAccount, newAccount);
emit ContributorAccountUpdated(id, oldAccount, newAccount);
}
function updateContributorIpfsHash(uint id, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize) public isInitialized auth(MANAGE_CONTRIBUTORS_ROLE) {
function updateContributorProfileHash(uint32 id, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(MANAGE_CONTRIBUTORS_ROLE) {
Contributor storage c = contributors[id];
bytes32 oldIpfsHash = c.ipfsHash;
c.ipfsHash = ipfsHash;
bytes32 oldHashDigest = c.hashDigest;
c.hashDigest = hashDigest;
c.hashFunction = hashFunction;
c.hashSize = hashSize;
ContributorProfileUpdated(id, oldIpfsHash, c.ipfsHash);
ContributorProfileUpdated(id, oldHashDigest, c.hashDigest);
}
function addContributor(address account, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize, bool isCore) public isInitialized auth(MANAGE_CONTRIBUTORS_ROLE) {
function addContributor(address account, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(MANAGE_CONTRIBUTORS_ROLE) {
require(!addressExists(account));
uint _id = contributorsCount + 1;
uint32 _id = contributorsCount + 1;
assert(!contributors[_id].exists); // this can not be acually
Contributor storage c = contributors[_id];
c.exists = true;
c.isCore = isCore;
c.ipfsHash = ipfsHash;
c.hashDigest = hashDigest;
c.hashFunction = hashFunction;
c.hashSize = hashSize;
c.account = account;
@@ -84,47 +92,64 @@ contract Contributor is AragonApp {
emit ContributorAdded(_id, account);
}
function isCore(uint id) view public returns (bool) {
return contributors[id].isCore;
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 exists(uint id) view public returns (bool) {
function exists(uint32 id) view public returns (bool) {
return contributors[id].exists;
}
function addressIsCore(address account) view public returns (bool) {
return getContributorByAddress(account).isCore;
uint32 id = getContributorIdByAddress(account);
return isCoreTeam(id);
}
function addressExists(address account) view public returns (bool) {
return getContributorByAddress(account).exists;
}
function getContributorIdByAddress(address account) view public returns (uint) {
function getContributorIdByAddress(address account) view public returns (uint32) {
return contributorIds[account];
}
function getContributorAddressById(uint id) view public returns (address) {
function getContributorAddressById(uint32 id) view public returns (address) {
return contributors[id].account;
}
function getContributorByAddress(address account) internal view returns (Contributor) {
uint id = contributorIds[account];
uint32 id = contributorIds[account];
return contributors[id];
}
function getContributorById(uint _id) public view returns (uint id, address account, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize, bool isCore, 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;
ipfsHash = c.ipfsHash;
hashDigest = c.hashDigest;
hashFunction = c.hashFunction;
hashSize = c.hashSize;
isCore = c.isCore;
isCore = isCoreTeam(id);
address token = getContract(uint8(Apps.Token));
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[] _how) public view returns (bool) {
return addressExists(_who);
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);
}
}

View File

@@ -0,0 +1,16 @@
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,16 +3,19 @@
"version": "1.0.0",
"description": "",
"dependencies": {
"@aragon/os": "^4.1.0",
"@aragon/cli": "^5.5.0"
"@aragon/os": "^4.4.0"
},
"devDependencies": {
"@aragon/test-helpers": "^2.1.0",
"eth-gas-reporter": "^0.2.17",
"ganache-cli": "^6.9.1",
"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 +24,8 @@
"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"
},
"keywords": []
}

View File

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

View File

@@ -0,0 +1,170 @@
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 = await getContract('Kernel').at(r.logs.filter(l => l.event == 'DeployDAO')[0].args.dao);
acl = await 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';
});
});
});
});

View File

@@ -14,7 +14,7 @@
"environments": {
"default": {
"network": "development",
"appName": "kredits-proposal.aragonpm.eth"
"appName": "kredits-proposal.open.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(uint256 contributorId) public view returns (address);
function getContributorIdByAddress(address contributorAccount) public view returns (uint256);
function exists(uint256 contributorId) public view returns (bool);
function getContributorAddressById(uint32 contributorId) public view returns (address);
function getContributorIdByAddress(address contributorAccount) public view returns (uint32);
function exists(uint32 contributorId) public view returns (bool);
}
interface IContribution {
function add(uint256 amount, address contributor, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public;
function add(uint32 amount, uint32 contributorId, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public;
}
contract Proposal is AragonApp {
@@ -25,73 +25,70 @@ contract Proposal is AragonApp {
struct Proposal {
address creatorAccount;
uint contributorId;
uint votesCount;
uint votesNeeded;
uint256 amount;
uint32 contributorId;
uint16 votesCount;
uint16 votesNeeded;
uint32 amount;
bool executed;
bytes32 hashDigest;
uint8 hashFunction;
uint8 hashSize;
uint256[] voterIds;
mapping (uint256 => bool) votes;
uint32[] voterIds;
mapping (uint32 => bool) votes;
bool exists;
}
mapping(uint256 => Proposal) public proposals;
uint256 public proposalsCount;
mapping(uint32 => Proposal) public proposals;
uint32 public proposalsCount;
event ProposalCreated(uint256 id, address creatorAccount, uint256 contributorId, uint256 amount);
event ProposalCreated(uint32 id, address creatorAccount, uint32 contributorId, uint32 amount);
event ProposalVoted(uint256 id, uint256 voterId, uint256 totalVotes);
event ProposalExecuted(uint256 id, uint256 contributorId, uint256 amount);
event ProposalVoted(uint32 id, uint32 voterId, uint16 totalVotes);
event ProposalExecuted(uint32 id, uint32 contributorId, uint32 amount);
function initialize(bytes32[4] _appIds) public onlyInit {
appIds = _appIds;
initialized();
}
function getContributorContract() public view returns (address) {
return IKernel(kernel()).getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[uint8(Apps.Contributor)]);
function getContract(uint8 appId) public view returns (address) {
IKernel k = IKernel(kernel());
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[appId]);
}
function getContributionContract() public view returns (address) {
return IKernel(kernel()).getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[uint8(Apps.Contribution)]);
}
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 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;
uint32 proposalId = proposalsCount + 1;
uint16 _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(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) {
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) {
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,
@@ -99,10 +96,10 @@ contract Proposal is AragonApp {
);
}
function vote(uint256 proposalId) public isInitialized auth(VOTE_PROPOSAL_ROLE) {
function vote(uint32 proposalId) public isInitialized auth(VOTE_PROPOSAL_ROLE) {
Proposal storage p = proposals[proposalId];
require(!p.executed, 'ALREADY_EXECUTED');
uint256 voterId = IContributor(getContributorContract()).getContributorIdByAddress(msg.sender);
uint32 voterId = IContributor(getContract(uint8(Apps.Contributor))).getContributorIdByAddress(msg.sender);
require(p.votes[voterId] != true, 'ALREADY_VOTED');
p.voterIds.push(voterId);
p.votes[voterId] = true;
@@ -114,20 +111,19 @@ contract Proposal is AragonApp {
emit ProposalVoted(proposalId, voterId, p.votesCount);
}
function batchVote(uint256[] _proposalIds) public isInitialized auth(VOTE_PROPOSAL_ROLE) {
for (uint256 i = 0; i < _proposalIds.length; i++) {
function batchVote(uint32[] _proposalIds) public isInitialized auth(VOTE_PROPOSAL_ROLE) {
for (uint32 i = 0; i < _proposalIds.length; i++) {
vote(_proposalIds[i]);
}
}
function executeProposal(uint proposalId) private {
function executeProposal(uint32 proposalId) private {
Proposal storage p = proposals[proposalId];
require(!p.executed, 'ALREADY_EXECUTED');
require(p.votesCount >= p.votesNeeded, 'MISSING_VOTES');
p.executed = true;
address contributorAccount = IContributor(getContributorContract()).getContributorAddressById(p.contributorId);
IContribution(getContributionContract()).add(p.amount, contributorAccount, p.hashDigest, p.hashFunction, p.hashSize);
IContribution(getContract(uint8(Apps.Contribution))).add(p.amount, p.contributorId, 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,16 +3,19 @@
"version": "1.0.0",
"description": "",
"dependencies": {
"@aragon/os": "^4.1.0",
"@aragon/cli": "^5.5.0"
"@aragon/os": "^4.4.0"
},
"devDependencies": {
"@aragon/test-helpers": "^2.1.0",
"eth-gas-reporter": "^0.2.17",
"ganache-cli": "^6.9.1",
"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 +24,8 @@
"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"
},
"keywords": []
}

View File

@@ -0,0 +1,5 @@
// const Proposal = artifacts.require('Proposal.sol');
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",

View File

@@ -9,16 +9,22 @@ 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, uint256 contributionId);
event LogMint(address indexed recipient, uint256 amount, uint32 contributionId);
function initialize(bytes32[4] _appIds) public onlyInit {
appIds = _appIds;
name = 'Kredits';
symbol = '₭S';
decimals = 18;
initialized();
}
function mintFor(address contributorAccount, uint256 amount, uint256 contributionId) public isInitialized auth(MINT_TOKEN_ROLE) {
_mint(contributorAccount, amount);
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);
emit LogMint(contributorAccount, amount, contributionId);
}

View File

@@ -0,0 +1,16 @@
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,16 +3,19 @@
"version": "1.0.0",
"description": "",
"dependencies": {
"@aragon/os": "^4.1.0",
"@aragon/cli": "^5.5.0"
"@aragon/os": "^4.4.0"
},
"devDependencies": {
"@aragon/test-helpers": "^2.1.0",
"eth-gas-reporter": "^0.2.17",
"ganache-cli": "^6.9.1",
"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 +24,8 @@
"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"
},
"keywords": []
}

View File

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

123
apps/token/test/token.js Normal file
View File

@@ -0,0 +1,123 @@
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 = await getContract('Kernel').at(r.logs.filter(l => l.event == 'DeployDAO')[0].args.dao);
acl = await 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
});
});
});

View File

@@ -21,7 +21,7 @@
"params": []
},
{
"name": "Add proposal",
"name": "Add proposal",
"id": "ADD_PROPOSAL_ROLE",
"params": []
},
@@ -34,17 +34,17 @@
"environments": {
"development": {
"network": "development",
"apm": "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",
"appName": "dummy.open.aragonpm.eth",
"apm": "open.aragonpm.eth"
"kredits": {
"daoFactory": "0x2298d27a9b847c681d2b2c2828ab9d79013f5f1d"
}
},
"kovan": {
"network": "kovan",
@@ -52,8 +52,7 @@
},
"default": {
"network": "development",
"appName": "dummy.aragonpm.eth",
"apm": "open.aragonpm.eth"
"appName": "dummy.aragonpm.eth"
}
},
"path": "contracts/misc/DummyApp.sol"

View File

@@ -1,14 +1,48 @@
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 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 funds = [
const funds = [
'0x7e8f313c56f809188313aa274fa67ee58c31515d',
'0xa502eb4021f3b9ab62f75b57a94e1cfbf81fd827'
'0xa502eb4021f3b9ab62f75b57a94e1cfbf81fd827',
];
module.exports = { contractCalls, funds };

View File

@@ -33,40 +33,43 @@ 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(), root);
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(root, contribution, contribution.ADD_CONTRIBUTION_ROLE(), params);
acl.grantPermissionP(root, contribution, contribution.VETO_CONTRIBUTION_ROLE(), params);
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.setPermissionManager(this, proposal, proposal.VOTE_PROPOSAL_ROLE();
acl.createPermission(root, proposal, proposal.VOTE_PROPOSAL_ROLE(), this);
acl.grantPermissionP(root, proposal, proposal.VOTE_PROPOSAL_ROLE(), params);
acl.grantPermissionP(acl.ANY_ENTITY(), proposal, proposal.VOTE_PROPOSAL_ROLE(), params);
acl.createPermission(root, proposal, proposal.ADD_PROPOSAL_ROLE(), this);
acl.grantPermissionP(root, proposal, proposal.ADD_PROPOSAL_ROLE(), params);
//acl.grantPermissionP(address(-1), proposal, proposal.ADD_PROPOSAL_ROLE(), params);
acl.grantPermission(acl.ANY_ENTITY(), proposal, proposal.ADD_PROPOSAL_ROLE());
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,5 +1,30 @@
# 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,5 +1,37 @@
# 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,5 +1,35 @@
# 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,5 +1,13 @@
# 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,5 +1,21 @@
# 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,5 +1,21 @@
# 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

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"}]

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,4 +1,3 @@
{
"4": "0x1d6a9c2146a330575ee860eef9a012b5ff7caa68",
"41787949": "0xa35aacdfccac54d3d96e0d29050c773b251c2c83"
}
"4": "0x76e069b47b79442657eaf0555a32c6b16fa1b8b4"
}

View File

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

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.hasPermission(
fromAddress,
contractAddress,
roleHash,
params
);
}
}

View File

@@ -1,23 +1,31 @@
const deprecate = require('../utils/deprecate');
class Base {
constructor(contract) {
constructor (contract) {
this.contract = contract;
}
get functions() {
return this.contract.functions;
get functions () {
deprecate('The property `functions` is deprecated. contract functions are now directly defined on the ethers contract object. https://github.com/ethers-io/ethers.js/issues/920#issuecomment-650836642');
return this.contract;
}
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);
}
}
module.exports = Base;

View File

@@ -1,70 +1,72 @@
const ethers = require('ethers');
const RSVP = require('rsvp');
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((count) => {
count = count.toNumber();
let contributions = [];
for (let id = 1; id <= count; id++) {
contributions.push(this.getById(id));
}
return RSVP.all(contributions);
});
class Contribution extends Record {
get count () {
return this.contract.contributionsCount();
}
getById(id) {
id = ethers.utils.bigNumberify(id);
return this.functions.getContribution(id)
.then((data) => {
getById (id) {
return this.contract.getContribution(id)
.then(data => {
return this.ipfs.catAndMerge(data, ContributionSerializer.deserialize);
});
}
getByContributor(contributor) {
return this.functions.balanceOf(contributor)
then((balance) => {
count = balance.toNumber();
getData (id) {
return this.contract.getContribution(id);
}
let contributions = [];
getByContributorId (contributorId) {
return this.contract.getContributorAddressById(contributorId)
.then(address => this.getByContributorAddress(address));
}
for (let index = 0; index <= count; index++) {
this.functions.tokenOfOwnerByIndex(contributor, index)
.then((id) => {
contributions.push(this.getById(id));
});
getByContributorAddress (address) {
return this.contract.balanceOf(address)
.then(async (balance) => {
const count = balance.toNumber();
const contributions = [];
for (let index = 0; index < count; index++) {
const id = await this.contract.tokenOfOwnerByIndex(address, index);
const contribution = await this.getById(id);
contributions.push(contribution);
}
return RSVP.all(contributions);
return contributions;
});
}
addContribution(contributionAttr, callOptions = {}) {
let json = ContributionSerializer.serialize(contributionAttr);
// TODO: validate against schema
async add (contributionAttr, callOptions = {}) {
const contribution = new ContributionSerializer(contributionAttr);
try { await contribution.validate(); }
catch (error) { return Promise.reject(error); }
const jsonStr = contribution.serialize();
return this.ipfs
.add(json)
.then((ipfsHashAttr) => {
.add(jsonStr)
.then(ipfsHashAttr => {
let contribution = [
contributionAttr.amount,
contributionAttr.contributorAccount,
contributionAttr.contributorId,
ipfsHashAttr.hashDigest,
ipfsHashAttr.hashFunction,
ipfsHashAttr.hashSize,
];
return this.functions.add(...contribution, callOptions);
return this.contract.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,48 +1,34 @@
const ethers = require('ethers');
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) => {
count = count.toNumber();
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.contract.contributorsCount();
}
getById(id) {
id = ethers.utils.bigNumberify(id);
return this.functions.getContributorById(id)
.then((data) => {
// TODO: remove when naming updated on the contract
data.hashDigest = data.ipfsHash;
return data;
})
// Fetch IPFS data if available
.then((data) => {
getById (id) {
return this.contract.getContributorById(id)
.then(contractData => {
let data = {...contractData};
data.balanceInt = formatKredits(data.balance);
return this.ipfs.catAndMerge(data, ContributorSerializer.deserialize);
});
}
filterByAccount(search) {
getData (id) {
return this.contract.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);
@@ -58,24 +44,52 @@ 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,
ipfsHashAttr.hashDigest,
ipfsHashAttr.hashFunction,
ipfsHashAttr.hashSize,
contributorAttr.isCore,
];
return this.functions.addContributor(...contributor, callOptions);
return this.contract.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.contract.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('eth-ens-namehash').hash;
const namehash = require('ethers').utils.namehash;
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.contract.acl();
}
return this.functions.getApp(KERNEL_APP_ADDR_NAMESPACE, this.appNamehash(appName));
return this.contract.getApp(KERNEL_APP_ADDR_NAMESPACE, this.appNamehash(appName));
}
appNamehash(appName) {
appNamehash (appName) {
return namehash(`kredits-${appName.toLowerCase()}.${this.apm}`);
}
}

View File

@@ -1,39 +1,29 @@
const ethers = require('ethers');
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) => {
count = count.toNumber();
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.contract.proposalsCount();
}
getById(id) {
id = ethers.utils.bigNumberify(id);
return this.functions.getProposal(id)
.then((data) => {
getById (id) {
return this.contract.getProposal(id)
.then(data => {
return this.ipfs.catAndMerge(data, ContributionSerializer.deserialize);
});
}
addProposal(proposalAttr, callOptions = {}) {
let json = ContributionSerializer.serialize(proposalAttr);
// TODO: validate against schema
async add (proposalAttr, callOptions = {}) {
const contribution = new ContributionSerializer(proposalAttr);
try { await contribution.validate(); }
catch (error) { return Promise.reject(error); }
const jsonStr = contribution.serialize();
return this.ipfs
.add(json)
.add(jsonStr)
.then((ipfsHashAttr) => {
let proposal = [
proposalAttr.contributorId,
@@ -43,9 +33,14 @@ class Proposal extends Base {
ipfsHashAttr.hashSize,
];
return this.functions.addProposal(...proposal, callOptions);
return this.contract.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;

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

@@ -0,0 +1,14 @@
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,4 +4,3 @@ 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,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.appIds(knownContracts.indexOf(contractName));
}
newDAO (options = {}) {
return this.contract.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,19 +1,75 @@
const schemas = require('kosmos-schemas');
const validator = require('../utils/validator');
/**
* Handle serialization for JSON-LD object of the contribution, according to
* https://github.com/67P/kosmos-schemas/blob/master/schemas/contribution.json
* Serialization and validation for JSON-LD document of the contribution.
*
* @class
* @public
*/
class Contribution {
/**
* Deserialize JSON to object
*
* @method
* @public
*/
static deserialize(serialized) {
constructor (attrs) {
Object.keys(attrs).forEach(a => this[a] = attrs[a]);
}
/**
* Serialize object to JSON
*
* @public
*/
serialize () {
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,
@@ -21,6 +77,8 @@ class Contribution {
} = JSON.parse(serialized.toString('utf8'));
return {
date,
time,
kind,
description,
details,
@@ -29,39 +87,6 @@ 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,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,95 @@
* @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,
zoom_display_name,
} = this;
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 (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}`,
});
}
if (zoom_display_name) {
data.accounts.push({
'site': 'zoom.us',
'username': zoom_display_name,
});
}
// 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,16 +104,24 @@ 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, zoom_display_name;
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 zoom = accounts.find(a => a.site === 'zoom.us');
if (github) {
(({ username: github_username, uid: github_uid} = github));
}
if (gitea) {
(({ username: gitea_username } = gitea));
}
if (wiki) {
(({ username: wiki_username } = wiki));
}
if (zoom) {
(({ username: zoom_display_name } = zoom));
}
return {
name,
@@ -38,59 +130,13 @@ class Contributor {
accounts,
github_uid,
github_username,
gitea_username,
wiki_username,
zoom_display_name,
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);
}
};

View File

@@ -1,17 +1,18 @@
const ipfsAPI = require('ipfs-api');
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 = ipfsAPI(config);
this._config = config;
this._ipfsAPI = ipfsClient(config);
}
catAndMerge(data, deserialize) {
catAndMerge (contractData, deserialize) {
let data = {...contractData}; // data from ethers.js is not extensible. this copy the attributes in a new object
// if no hash details are found simply return the data; nothing to merge
if (!data.hashSize || data.hashSize === 0) {
return data;
@@ -26,37 +27,48 @@ class IPFS {
});
}
add(data) {
add (data) {
return this._ipfsAPI
.add(new this._ipfsAPI.Buffer(data))
.add(ipfsClient.Buffer.from(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')) {
if (Object.prototype.hasOwnProperty.call(hashData, '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 (Object.prototype.hasOwnProperty.call(hashData, '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) {
let digest = this._ipfsAPI.Buffer.from(hashData.hashDigest.slice(2), 'hex');
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}`);

15
lib/utils/validator.js Normal file
View File

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

15566
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,24 +1,35 @@
{
"name": "kredits-contracts",
"version": "3.0.2",
"version": "6.0.0",
"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": "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/deploy-apps.sh",
"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 compile-contracts && npm run deploy:apps && npm run reset",
"deploy:kit": "truffle exec scripts/deploy-kit.js",
"deploy:dao": "truffle exec scripts/new-dao.js",
"deploy:apps": "./scripts/every-app.sh \"aragon apm publish major --propagate-content=false --build=false --prepublish=false --skip-confirmation\"",
"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": "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"
},
"repository": {
"type": "git",
@@ -31,20 +42,29 @@
},
"homepage": "https://github.com/67P/truffle-kredits#readme",
"devDependencies": {
"@aragon/cli": "^5.5.0",
"@aragon/kits-base": "^1.0.0",
"@aragon/os": "^4.1.0",
"@aragon/os": "^4.4.0",
"async-each-series": "^1.1.0",
"eth-provider": "^0.2.2",
"openzeppelin-solidity": "^2.2.0",
"cli-table": "^0.3.1",
"eslint": "^7.1.0",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eth-provider": "^0.2.5",
"homedir": "^0.6.0",
"promptly": "^3.0.3",
"solc": "^0.4.25"
"solc": "^0.6.8",
"solhint": "^2.3.1",
"truffle-hdwallet-provider": "^1.0.17",
"truffle-hdwallet-provider-privkey": "^0.3.0",
"yargs": "^15.0.0"
},
"dependencies": {
"eth-ens-namehash": "^2.0.8",
"ethers": "^4.0.27",
"ipfs-api": "^19.0.0",
"rsvp": "^4.8.2"
"ethers": "^5.0.2",
"ipfs-http-client": "^41.0.1",
"kosmos-schemas": "^2.2.1",
"node-fetch": "^2.6.0",
"tv4": "^1.3.0"
},
"keywords": [
"kosmos",

View File

@@ -1,4 +1,5 @@
const promptly = require('promptly');
const { inspect } = require('util');
const initKredits = require('./helpers/init_kredits.js');
@@ -18,31 +19,40 @@ module.exports = async function(callback) {
let contributorAccount;
if (contributor.length < 5) {
contributorId = contributor;
contributorAccount = await kredits.Contributor.functions.getContributorAddressById(contributor);
contributorAccount = await kredits.Contributor.contract.getContributorAddressById(contributor);
} else {
contributorAccount = contributor;
contributorId = await kredits.Contributor.functions.getContributorIdByAddress(contributor);
contributorId = await kredits.Contributor.contract.getContributorIdByAddress(contributor);
}
console.log(`Creating a contribution for contributor account ${contributorAccount} ID: ${contributorId}`);
[ dateNow, timeNow ] = (new Date()).toISOString().split('T');
let contributionAttributes = {
contributorAccount,
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 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(error);
});
kredits.Contribution.add(contributionAttributes, { gasLimit: 300000 })
.then(result => {
console.log("\n\nResult:");
console.log(result);
callback();
})
.catch(error => {
console.log('Failed to create contribution');
callback(inspect(error));
});
}

View File

@@ -23,18 +23,17 @@ 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: await prompt('GitHub UID: '),
github_uid: parseInt(await prompt('GitHub UID: ')),
wiki_username: await prompt('Wiki username: '),
};
console.log("\nAdding contributor:");
console.log(contributorAttributes);
kredits.Contributor.add(contributorAttributes, { gasLimit: 250000 }).then((result) => {
kredits.Contributor.add(contributorAttributes, { gasLimit: 350000 }).then((result) => {
console.log("\n\nResult:");
console.log(result);
callback();

View File

@@ -1,4 +1,5 @@
const promptly = require('promptly');
const { inspect } = require('util');
const initKredits = require('./helpers/init_kredits.js');
@@ -18,30 +19,38 @@ module.exports = async function(callback) {
let contributorAccount;
if (contributor.length < 5) {
contributorId = contributor;
contributorAccount = await kredits.Contributor.functions.getContributorAddressById(contributor);
contributorAccount = await kredits.Contributor.contract.getContributorAddressById(contributor);
} else {
contributorAccount = contributor;
contributorId = await kredits.Contributor.functions.getContributorIdByAddress(contributor);
contributorId = await kredits.Contributor.contract.getContributorIdByAddress(contributor);
}
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(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(inspect(error));
});
}

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.contract.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

@@ -17,7 +17,7 @@ module.exports = async function(callback) {
method = await promptly.prompt('Function: ');
}
if (!contractWrapper[method] && !contractWrapper.functions[method]) {
if (!contractWrapper[method] && !contractWrapper.contract[method]) {
callback(new Error(`Method ${method} is not defined on ${contractName}`));
return;
}
@@ -33,7 +33,7 @@ module.exports = async function(callback) {
if (contractWrapper[method]) {
func = contractWrapper[method];
} else {
func = contractWrapper.functions[method];
func = contractWrapper.contract[method];
}
func.apply(contractWrapper, args).then((result) => {
console.log("\nResult:");

View File

@@ -1,9 +1,12 @@
const knownDAOAddresses = require('../lib/addresses/dao.json');
const knownKreditsKitAddresses = require('../lib/addresses/KreditsKit.json');
const getNetworkId = require('./helpers/networkid.js')
const ethers = require('ethers');
module.exports = async function(callback) {
const networkId = await getNetworkId(web3)
const provider = new ethers.providers.Web3Provider(web3.currentProvider);
let network = await provider.getNetwork();
let networkId = network.chainId;
console.log('# All known DAO addresses');
Object.keys(knownDAOAddresses).forEach((networkId) => {

View File

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

View File

@@ -3,23 +3,33 @@ 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('eth-ens-namehash').hash
const ethers = require('ethers');
const namehash = ethers.utils.namehash;
const fileInject = require('./helpers/file_inject.js')
const getNetworkId = require('./helpers/networkid.js')
const DAOFactory = artifacts.require('DAOFactory')
const KreditsKit = artifacts.require('KreditsKit')
const arapp = require('../arapp.json')
const environment = argv['network'] || argv['environment'] || 'development'
const apm = arapp.environments[environment].apm
const ensAddr = arapp.environments[environment].registry || process.env.ENS
const daoFactoryAddress = arapp.environments[environment].daoFactory || process.env.DAO_FACTORY
const kreditsArappConfig = arapp.environments[environment].kredits || {}
// typically we use the open.aragonpm.eth aragonpm.
const apm = kreditsArappConfig.apmDomain || argv['apmDomain'] || 'open.aragonpm.eth'
// daoFactory is environment specific.
// See https://github.com/aragon/deployments/tree/master/environments/ for the official daoFactory
// Locally we deploy our own daoFactory and no daoFactory is required (`daoFactoryAddress` is null).
const daoFactoryAddress = kreditsArappConfig.daoFactory || argv['daoFactory']
const ensAddr = arapp.environments[environment].registry || argv['ensAddress']
module.exports = async function(callback) {
const networkId = await getNetworkId(web3)
const provider = new ethers.providers.Web3Provider(web3.currentProvider);
const network = await provider.getNetwork();
const networkId = network.chainId;
console.log(`Deploying to networkId: ${networkId}`)
if (!ensAddr) {
@@ -35,7 +45,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) => {

14
scripts/every-app.sh Executable file
View File

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

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

View File

@@ -1,6 +1,5 @@
const argv = require('yargs').argv;
const ethers = require('ethers');
const getNetworkId = require('./networkid.js');
const Kredits = require('../../lib/kredits');
const arapp = require('../../arapp.json');
@@ -10,7 +9,7 @@ const apm = arapp.environments[environment].apm;
module.exports = async function(web3) {
return new Promise((resolve, reject) => {
const provider = new ethers.providers.Web3Provider(web3.currentProvider);
let signer = provider.getSigner();
const signer = provider.getSigner();
// checking if siner supports signing transactions
signer.getAddress().then(_ => {
new Kredits(provider, signer, { apm }).init().then(kredits => {
@@ -20,7 +19,7 @@ module.exports = async function(web3) {
});
}).catch(e => {
console.log(`Signer account not available; readonly connection (${e.message}`);
new Kredits(provider, null).init().then(kredits => {
new Kredits(provider, null, { apm }).init().then(kredits => {
resolve(kredits);
}).catch(e => {
reject(e);

View File

@@ -1,17 +0,0 @@
module.exports = function(web3) {
return new Promise((resolve, reject) => {
let func;
if (web3.version.getNetwork) {
func = web3.version.getNetwork;
} else {
func = web3.eth.net.getId;
}
func((err, network) => {
if (err) {
reject(err);
} else {
resolve(network);
}
})
})
}

View File

@@ -1,12 +1,14 @@
const fs = require('fs');
const getNetworkId = require('./networkid.js');
const ethers = require('ethers');
module.exports = async function(callback) {
const daoAddressPath = 'lib/addresses/dao.json';
// TODO maybe do the same for KreditsKit address file
try {
const networkId = await getNetworkId(web3);
const provider = new ethers.providers.Web3Provider(web3.currentProvider);
const network = await provider.getNetwork();
const networkId = network.chainId;
const daoAddresses = JSON.parse(fs.readFileSync(daoAddressPath));
const oldNetworkId = Math.max(...Object.keys(daoAddresses).map(a => parseInt(a)));
const localDaoAddress = daoAddresses[oldNetworkId];

View File

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

View File

@@ -1,5 +1,6 @@
const promptly = require('promptly');
const Table = require('cli-table');
const ethers = require('ethers');
const initKredits = require('./helpers/init_kredits.js');
@@ -14,22 +15,31 @@ module.exports = async function(callback) {
console.log(`Using Contributor at: ${kredits.Contributor.contract.address}`);
const table = new Table({
head: ['ID', 'Account', 'Core?', 'Name']
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.isCore,
`${c.name}`
])
})
console.log(table.toString())
callback()
}

View File

@@ -14,7 +14,6 @@ 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,15 +1,17 @@
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)
const provider = new ethers.providers.Web3Provider(web3.currentProvider);
const signer = provider.getSigner();
const network = await provider.getNetwork();
const networkId = network.chainId;
console.log(`Deploying to networkId: ${networkId}`)
let kitAddresseFile = path.join(addressesPath, 'KreditsKit.json');
@@ -20,25 +22,18 @@ module.exports = async function(callback) {
}
console.log(`Using KreditsKit at: ${kreditsKitAddress}`);
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;
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) => {
@@ -33,14 +38,15 @@ module.exports = async function(callback) {
if (contractWrapper[method]) {
func = contractWrapper[method];
} else {
func = contractWrapper.functions[method];
func = contractWrapper.contract[method];
}
func.apply(contractWrapper, args).then((result) => {
console.log(`[OK] kredits.${contractName}.${method}(${JSON.stringify(args)}) => ${result.hash}`);
next();
}).catch((error) => {
console.log(`[FAILD] kredits.${contractName}.${method}(${JSON.stringify(args)})`);
callback(error)
console.log(`[FAILED] kredits.${contractName}.${method}(${JSON.stringify(args)})`);
console.log(`Error: ${error.message}`);
next();
});
}, () => { console.log("\nDone!") });

View File

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

23
scripts/upgrade.sh Executable file
View File

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

@@ -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.contract.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]);
}
}
)