Compare commits

...

146 Commits

Author SHA1 Message Date
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
f40cc1d8ff Merge pull request #62 from 67P/aragonos
Build on aragonOS
2019-04-02 21:36:36 +02:00
91135dbc82 Fix deploy-kit helper script
We no longer need the app ids as json. Those are dynamically calculated
now
2019-04-02 18:06:33 +02:00
adb7122a28 Add helper script for updating local address file
Can be run after starting the devchain, so that all local truffle
scripts work with the new network ID.
2019-04-02 17:35:28 +02:00
ce446e530d Set Kreits apm from arapp.json file in helper scripts
different networks might require different apm domains. We set those in
the arapp.json file which we can reuise to init the kredits instance in
the helper scripts.
2019-04-02 09:58:44 +02:00
fedb10c5da Dynamically get app ids from the namehash
This dynamically hashes the app id which makes it easier to manually set
contract addresses. This is useful in development environments with no
default network ids.
The used apm (which is part of the app id hash) is configurable.

usage:

new Kredits(provider, signer, { addresses: { Kernel: '0x...' }, apm: 'aragonpm.eth' })
2019-04-01 22:41:00 +02:00
a24f80d44f Change addresses 2019-04-01 14:04:11 +02:00
61fa26da7a Add more information output to the send funds helper 2019-04-01 11:53:50 +02:00
9512ba4334 Update contract addresses 2019-04-01 11:19:30 +02:00
65c129eafd Update package locks 2019-04-01 11:17:26 +02:00
c2f763bec7 Add missing variable assignment 2019-04-01 11:16:55 +02:00
c93a81808a Update package lock 2019-04-01 10:34:48 +02:00
51e5da414f Update ethers.js to latest version v4
The main change is how ethers loads the networkId which is now async.
Thus the init process had to change a bit
2019-03-29 18:13:51 +01:00
3662f1ae24 Add Operator getter for backwards compatibility 2019-03-29 15:03:37 +01:00
c568263fea update eth-provider to support different providers
Ideally eth-provider checks which connection is available and uses the
available one.
frame => local node => infura

But it seems there is still a bug and it raises an error if frame is not
available.
But at least we can use frame now for testnet/mainnet deployments
2019-03-29 12:08:07 +01:00
38079d07db remove debug console 2019-03-29 12:07:52 +01:00
0cc67c2fad Support getting the network version from different web3
version/providers
2019-03-29 12:07:09 +01:00
a45be0021e Fix old registry reference 2019-03-29 12:06:43 +01:00
4bd1aed197 Refactor kredits init in helper scripts
This now also supports readonly providers like infura
2019-03-29 12:05:51 +01:00
94832d4d07 Add ACL contract wrapper
this mainly allows us to check if an account has a certain role and thus
if the account can call specific contract functions.

At some point we might want to extend that to support the check if an
account can call the function. For that we would need to have a mapping
between function names and roles, which we have not right now.
2019-03-28 12:39:04 +01:00
4f5ae01c5a Add helper funtion to list contract entries 2019-03-28 11:16:20 +01:00
d6f99f57b7 Auto resolve promises in repl 2019-03-28 11:12:32 +01:00
901566d1ff Update network specific addresses and app ids 2019-03-28 11:11:49 +01:00
5d4be8f176 Update ABIs used by wrapper 2019-03-28 11:09:49 +01:00
f9ab8b225a Fix proposal wrapper to support the latest contracts 2019-03-28 11:08:34 +01:00
dfa55516ec Add contribution veto function
Allows anybody with the VETO_CONTRIBUTION_ROLE to veto a contribution
and thus prevent that the contribution can be claimed.
So far like the ADD_CONTRIBUTION_ROLE any contributor is allowed to
veto.
2019-03-28 11:06:19 +01:00
b6f34ac9a5 Pretty print JSON for contract addresses
This makes it easier to read the file and a git diff is more useful
2019-03-27 21:58:19 +01:00
258c6729b6 Cleanup scripts and use helper to get the networkid 2019-03-27 17:06:52 +01:00
b201642485 remove obsolete comment 2019-03-27 16:56:52 +01:00
0686c79998 Print dao address and kreditskit address 2019-03-27 16:56:24 +01:00
7e68dbf8c2 rinkeby deployment 2019-03-27 00:39:00 +01:00
f5973756c8 Dynamically set AppIds
AppIds are used to lookup the actual contract addresses of each app.
Because of different registry names (open.aragonpm.eth vs. aragonpm.eth)
we have to use different ids in the local dev chain and in the
testnet/mainnet. To allow this we need to set the appids dynamically.

There is an open aragon issue to solve this and also allow to use
open.aragonpm.eth in the devchain by default.
https://github.com/aragon/aragen/issues/10
2019-03-27 00:21:06 +01:00
9cc237fbf4 Support web3 providers that do not support sync calls
This is currently the non default web3 provider used for rinkeby etc.
2019-03-26 00:35:22 +01:00
3584a73c08 Set kredits-<name>.open.aragonpm.eth as appid hashes 2019-03-26 00:34:46 +01:00
3c2cdf4376 Deployment hacking while trying to deploy on rinkeby
This currently breaks the local usage because of the different appIds:
<name>.open.aragonpm.eth vs. <name>.aragonpm.eth (local)
2019-03-26 00:18:59 +01:00
fbda45376e cleanup contract debugging and make sure contributions are only once claimable 2019-03-24 22:33:16 +01:00
cd7df3893e readme 2019-03-24 22:18:19 +01:00
e1fea4ed97 refactor DAO deployment 2019-03-24 22:11:24 +01:00
12341d1526 Add script to print current DAO address 2019-03-24 16:09:00 +01:00
14b23ecdf1 move app bootstrap script to scripts 2019-03-24 15:45:48 +01:00
1679afb9b4 formatting 2019-03-24 14:43:43 +00:00
b03095e149 Cleanup and readme
simplify bootstrap
2019-03-24 15:40:46 +01:00
0d9b2d7d58 readme 2019-03-24 11:01:59 +01:00
e7affdb531 updated npm scripts 2019-03-24 00:58:56 +01:00
1594bf0e17 update seeds 2019-03-24 00:39:46 +01:00
203199c268 cleanup 2019-03-24 00:21:36 +01:00
116f69cb12 cleanup 2019-03-24 00:20:48 +01:00
6c569239de refactor contrats with aragonos 2019-03-23 19:15:23 +01:00
d687ff604e Merge branch 'master' into contribution-contract
* master:
  Update truffle and ganache-cli
2018-09-29 19:54:28 +02:00
c9a4b1decc Update truffle and ganache-cli 2018-09-29 19:52:14 +02:00
dae44cac70 Continue Contribution contract and JS wrapper 2018-09-29 19:46:37 +02:00
6b8f718051 Create contribution when proposal gets executed 2018-06-18 18:43:35 +02:00
6d9a54b71c Remove whitespace 2018-06-18 18:42:32 +02:00
75d426f0cc Add Contribution contract
The contribution contract implements an ERC721 interface which
represents any contribution.
The contributions are non-fungible (as opposed to the Kredits tokens)
and can be not be transferred. The contract stores the history of any
contribution.
Contributions can be claimed which will issue the Kredits tokens to the
contributor.

This is an early implementation and misses some access control and
probably more things.
2018-06-18 15:28:50 +02:00
7ce100e819 3.0.2 2018-06-15 09:44:11 +02:00
7216522d83 Update Operator ABI 2018-06-15 09:42:29 +02:00
956f858620 Rename healthcheck to preflight 2018-06-07 16:39:07 +02:00
8a7abba486 Add keywords to package config 2018-05-03 13:31:49 +02:00
c6c48f49d2 Add npm version badge 2018-05-03 13:24:40 +02:00
db2e12c750 3.0.1 2018-05-03 13:20:06 +02:00
7dc75a19b5 Clean your room, kiddo. 2018-05-03 12:40:24 +02:00
926913da50 Update description 2018-05-03 12:40:16 +02:00
a045702937 Use normal Markdown extension
Hardly anyone uses `.mdown`, so let's use what everyone knows and looks
for.
2018-05-03 12:32:36 +02:00
b13b93122e 3.0.0 2018-05-03 12:04:10 +02:00
c23e7f2df8 Start with old npm package version 2018-05-03 12:03:19 +02:00
7260544838 Merge pull request #41 from 67P/feature/improve-scripts
Improve helper scripts
2018-04-29 09:43:22 +02:00
da2f951bdb Merge pull request #42 from 67P/feature/filter-contributors-by-account
Add filter and find by account function to contributors
2018-04-26 14:27:26 +00:00
017073018f Use more readable Array.every method instead of reduce 2018-04-26 15:35:32 +02:00
fe1fa2e881 Allow filter and find for contributors by account data 2018-04-26 14:51:18 +02:00
c367c9cf6b Merge pull request #44 from 67P/add-contributors-alias
Add Contributors alias for Contributor
2018-04-26 11:52:23 +00:00
52643da096 typo 2018-04-26 13:52:09 +02:00
ad5fe3ae77 Add Contributors alias for Contributor
Because the contract is named `Contributors` we alias to `Contributor` which will become the new contract name if we manage to change it.
2018-04-26 12:41:51 +02:00
418785059c Merge pull request #40 from 67P/feature/constructor-options
Constructor configuration options
2018-04-26 10:32:12 +00:00
89261d039a Fix contributor id lookup for proposal creation script 2018-04-26 02:42:31 +02:00
6949a5940a Merge pull request #26 from 67P/features/batch-voting
Add batch voting for proposals
2018-04-24 12:40:15 +00:00
ce5f5fb8d2 Merge branch 'master' into features/batch-voting 2018-04-24 12:39:16 +00:00
2e8d00bc2c Add a filter by account function to contributors
This allows to filter contributors by the account entries.
For example:

```js

Contributor.filterByAccount({site: 'github.com'}); // returns all
contributors with github account
Contributor.filterByAccount({site: 'github.com', username: 'bumi'});
// returns bumi

```
2018-04-23 16:29:44 +02:00
b1345b53f3 Add repl.js helper script
This is similar to the cli.js helper but only provides an initialized
`kredits` instance.
The cli.js is for executing contract functions
2018-04-23 16:24:40 +02:00
847f5a251c Allow funding accounts with the seeds script 2018-04-23 13:43:35 +02:00
62a5cefd1a Update readme to reflect helper script changes 2018-04-23 13:20:57 +02:00
6378df7075 cleanup 2018-04-23 12:11:26 +02:00
bd2af6ed72 Smarter cli script
It now allows you to list available functions and allows to call
functions on the wrapper or on the contract directly.
2018-04-23 12:10:20 +02:00
85032353ca Use kredits library in add-proposal script 2018-04-23 12:06:04 +02:00
e2e9cd5c3b Add batch voting for proposals
The batchVote function accepts an array of proposal ids and votes for
every one.
Normally arrays without fix length are problematic and gas usage can not be
estimated really well. So we need to see how that works or what other
pattern could be used.
2018-04-15 18:15:21 +02:00
128 changed files with 71804 additions and 4166 deletions

5
.gitignore vendored
View File

@@ -1,3 +1,6 @@
build
flattened_contracts
node_modules
.ganache-db
**/node_modules
.ganache-db
.tm_properties

201
README.md Normal file
View File

@@ -0,0 +1,201 @@
[![npm](https://img.shields.io/npm/v/kredits-contracts.svg)](https://www.npmjs.com/package/kredits-contracts)
# 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).
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
All requirements are defined in `package.json`.
$ npm install
Each of the aragon apps are separate packages:
$ cd apps/[app]
$ npm install
You can use `npm run install-all` to install all app dependencies at once.
### 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.
We use the default aragon-cli devchain command to confgure and run a local
development ganache.
$ npm run devchain (or aragon devchain --port 7545)
To clear/reset the chain use:
$ 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
Ethereum RPC port.
### Bootstrap
1. Run an Ethereum node and ipfs
$ npm run devchain
$ ipfs daemon
2. Deploy each app to the devchain
(make sure you've run `npm install` for every app - see installation)
$ npm run deploy:apps
3. Deploy a new KreditsKit and create a new DAO with the latest app versions
$ npm run deploy:kit
$ npm run deploy:dao
4. Execute seeds to create demo contributors, contributons, etc. (optional)
$ npm run seeds
**Step 2-4 is also summarized in `npm run bootstrap`**
If you want to reset your local setup:
$ npm run reset // deploys a new kit and a new DAO
$ npm run reset:hard // deploys all apps and does reset
## Contract architecture
Contracts are organized in independent apps (see `/apps`) and are developed and
deployed independently. Each app has a version and can be "installed" on the
Kredits DAO independently.
![](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.
See each app in `/apps/*` for details.
## Helper scripts
`scripts/` contains some helper scripts to interact with the contracts from the
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:
$ truffle exec scripts/cli.js
### repl.js
Similar to cli.js but only provides a REPL with an initialized `kredits`
instance.
$ truffle exec scripts/repl.js
### add-{contributor, contribution, proposal}.js
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 contract entries
$ truffle exec scripts/list-{contributor, contribution, proposal}.js
### send-funds.js
Sends funds to an address. Helpful in development mode to for example fund a
metamask account.
$ truffle exec scripts/send-funds.js
### seeds.js
Run seeds defined in `config/seeds.js`.
$ truffle exec scripts/seeds.js
or
$ npm run seeds
### current-address.js
Prints all known DAO addresses and the DAO address for the current network
$ truffle exec scripts/current-address.js
or
$ npm run dao:address
### deploy-kit.js
Deploys a new KreditsKit that allows to create a new DAO
$ truffle exec script/deploy-kit.js
or
$ npm run deploy:kit
`ENS` address is required as environment variable.
`DAO_FACTORY` can optionally be set as environment variable. (see aragon)
### new-dao.js
Creates and configures a new DAO instance.
$ truffle exec script/new-dao.js
or
$ npm run deploy:dao
KreditsKit address is load from `lib/addresses/KreditsKit.json` or can be
configured through the `KREDITS_KIT` environment variable.
### deploy-apps.sh
Runs `npm install` for each app and publishes a new version.
$ ./scripts/deploy-apps.sh
or
$ npm run deploy:apps
## 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)
for more details.
### Example
1. Setup (see #Bootstrap)
1. Deploy each contract/apps (see `/apps/*`)
2. Create a new DAO (see scripts/deploy-kit.js)
2. Update
1. Deploy a new Version of the contract/app (see `/apps/*`)
2. Use the `aragon dao upgrade` command to "install" the new version for the DAO
(`aragon dao upgrade <DAO address> <app name>`)
## Known Issues
When resetting ganache Metamask might have an invalid transaction nonce and
transactions get rejected. Nonces in Ethereum must be incrementing and have no
gap.
To solve this reset the metamask account (Account -> Settings -> Reset Account)

View File

@@ -1,153 +0,0 @@
# Kredits
This repository contains all the contracts for Kredits as a [truffle framework](http://truffleframework.com/) project.
## Development
### Installation
$ npm install
### Requirements
All requirements are defined in `package.json`.
Those can be installed globally for convenience:
* [truffle framework](http://truffleframework.com): `npm install -g truffle`
* [ganache](http://truffleframework.com/ganache): `npm install -g ganache-cli`
We use following solidity contract libraries:
* [Open Zeppelin](https://github.com/OpenZeppelin/zeppelin-solidity)
For local development it is recommended to use [ganache-cli](https://github.com/trufflesuite/ganache-cli) (or the [ganache GUI](http://truffleframework.com/ganache/) to run a local development chain.
Using the ganache simulator no full Ethereum node is required.
We default to:
* port 7545 for development to not get in conflict with the default Ethereum RPC port.
* network ID 100 to stay on the same network id
* store ganache data in .ganache-db to presist the chain data across restarts
* use a fixed Mnemonic code to get the same accounts across restarts
Have a look at `ganache-cli` for more configuration options.
Run your ganache simulator before using Kredits locally:
$ npm run ganache (which is: ganache-cli -p 7545 -i 100 --db=./.ganache-db -m kredits)
### Truffle console
Truffle comes with a simple REPL to interact with the Smart Contracts. Have a look at the [documentation here](http://truffleframework.com/docs/getting_started/console)
NOTE: There are promisses, have a look at the examples:
```javascript
Token.deployed().then(function(token) {
token.totalSupply.call().then(function(value) {
console.log(value.toString());
})
});
```
Also please be aware of the differences between web3.js 0.2x.x and [1.x.x](https://web3js.readthedocs.io/en/1.0/) - [web3 repo](https://github.com/ethereum/web3.js/)
## Contract Deployment
Truffle uses migration scripts to deploy contract to various networks. Have a look at the `migrations` folder for those.
The Ethereum nodes for the different networks need to be configured in `truffle.js`.
Run the truffle migration scripts:
$ truffle deploy
$ truffle deploy --network=<network config from truffle.js>
Truffle keeps track of already executed migration scripts. To reset the migration use the `--reset` option
$ truffle migrate --reset
Migration scripts can also be run from within `truffle console` or `truffle develop`
To initially bootstrap a local development chain in ganache you can use the bootstrap script:
$ npm run bootstrap (= `truffle migrate --reset && truffle exec scripts/seeds.js && npm run build-json`)
## Helper scripts
`scripts/` contains some helper scripts to interact with the contracts from the 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`
### cli.js
Call any function on any contract:
$ truffle exec scripts/cli.js <Contract Name> <Function> [<optional> <arguments>]
For example:
$ truffle exec scripts/cli.js Operator proposalsCount
Please note that the contract name and the function are case sensitive.
### add-contributor.js
Adds a new core contributor, creates a proposal for the new contributor and votes for that one.
$ truffle exec scripts/add-contributor.js <ethereum address> [<profile IPFS hash>]
### add-proposal.js
Adds a new proposal for an existing contributor
$ truffle exec scripts/add-proposal.js <ethereum address> [<proposal IPFS hash>]
### send-funds.js
Sends funds to an address. Helpful in development mode to for example fund a metamask account.
$ truffle exec scripts/send-funds.js <ethereum address>
### seeds.js
Run seeds defined in `config/seeds.js`.
$ truffle exec scripts/seeds.js
or
$ npm run seeds
## Upgradeable contracts
Some of the contracts use upgradability ideas from [zeppelinos](https://github.com/zeppelinos/labs) (see `contracts/upgradable`).
The basic idea is to have a Registry contract that knows about the current implementations and a Proxy contract that uses `delegatecall` to call the current implementation.
That means the Proxy contract holds the storage and the address of that one does not change but the actuall implemenation is managed through the Registry.
To deploy a new version a new contract is deployed then the version is registered (`addVersion()`) in the Registry and on the Proxy contract is "upgraded" (`upgrade()`) to the new version.
The Registry knows about all the different contracts and implementations. Versions are stored as uint and automatically incremented for every added implementation.
### Example:
Deployment is best done using the [truffle deployer]()
1. Setup
1. deploy the Registry
2. deploy the contract
3. register the contract at the Registry:
`registry.addVersion('Token', Token.address)`
4. create the Proxy:
`registry.createProxy('Token', 1)`
2. Update
1. deploy a new Version of the contract
2. register the new version at the Registry:
`registry.addVersion('Token', NewToken.address)`
3. set the new implementation address on the Proxy contract:
`registry.upgrade('Token', 2)`
## Known Issues
When resetting ganache Metamask might have an invalid transaction nonce and transactions get rejected.
Nonces in Ethereum must be incrementing and have no gap.
To solve this reset the metamask account (Account -> Settings -> Reset Account)

4
apps/contribution/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
node_modules
build
.cache
dist

View File

@@ -0,0 +1,14 @@
# Git files
.gitignore
# Build files
.cache
node_modules
build
# Lock files
package-lock.json
yarn.lock
# Others
test

View File

@@ -0,0 +1 @@
# Kredits Contribution

View File

@@ -0,0 +1,38 @@
{
"roles": [
{
"name": "Add contributions",
"id": "ADD_CONTRIBUTION_ROLE",
"params": []
},
{
"name": "Manage token contract",
"id": "MANAGE_TOKEN_CONTRACT_ROLE",
"params": []
},
{
"name": "Veto contributions",
"id": "VETO_CONTRIBUTION_ROLE",
"params": []
}
],
"environments": {
"default": {
"network": "development",
"appName": "kredits-contribution.aragonpm.eth"
},
"rinkeby": {
"registry": "0x98df287b6c145399aaa709692c8d308357bc085d",
"appName": "kredits-contribution.open.aragonpm.eth",
"wsRPC": "wss://rinkeby.eth.aragon.network/ws",
"network": "rinkeby"
},
"production": {
"registry": "0x314159265dd8dbb310642f98f50c066173c1259b",
"appName": "contribution.open.aragonpm.eth",
"wsRPC": "wss://mainnet.eth.aragon.network/ws",
"network": "mainnet"
}
},
"path": "contracts/Contribution.sol"
}

View File

@@ -0,0 +1,191 @@
pragma solidity ^0.4.24;
import "@aragon/os/contracts/apps/AragonApp.sol";
import "@aragon/os/contracts/kernel/IKernel.sol";
interface IToken {
function mintFor(address contributorAccount, uint256 amount, uint32 contributionId) public;
}
interface ContributorInterface {
function getContributorAddressById(uint32 contributorId) public view returns (address);
function getContributorIdByAddress(address contributorAccount) public view returns (uint32);
// TODO Maybe use for validation
// function exists(uint32 contributorId) public view returns (bool);
}
contract Contribution is AragonApp {
bytes32 public constant ADD_CONTRIBUTION_ROLE = keccak256("ADD_CONTRIBUTION_ROLE");
bytes32 public constant VETO_CONTRIBUTION_ROLE = keccak256("VETO_CONTRIBUTION_ROLE");
bytes32 public constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb;
// ensure alphabetic order
enum Apps { Contribution, Contributor, Proposal, Token }
bytes32[4] public appIds;
struct ContributionData {
uint32 contributorId;
uint32 amount;
bool claimed;
bytes32 hashDigest;
uint8 hashFunction;
uint8 hashSize;
string tokenMetadataURL;
uint256 confirmedAtBlock;
bool vetoed;
bool exists;
}
string internal name_;
string internal symbol_;
// map contribution ID to contributor
mapping(uint32 => uint32) public contributionOwner;
// map contributor to contribution IDs
mapping(uint32 => uint32[]) public ownedContributions;
mapping(uint32 => ContributionData) public contributions;
uint32 public contributionsCount;
uint32 public blocksToWait;
event ContributionAdded(uint32 id, uint32 indexed contributorId, uint32 amount);
event ContributionClaimed(uint32 id, uint32 indexed contributorId, uint32 amount);
event ContributionVetoed(uint32 id, address vetoedByAccount);
function initialize(bytes32[4] _appIds) public onlyInit {
appIds = _appIds;
blocksToWait = 40320; // 7 days; 15 seconds block time
initialized();
}
// TODO refactor into a single function
function getTokenContract() public view returns (address) {
IKernel k = IKernel(kernel());
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[uint8(Apps.Token)]);
}
function getContributorContract() public view returns (address) {
IKernel k = IKernel(kernel());
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[uint8(Apps.Contributor)]);
}
function getContributorIdByAddress(address contributorAccount) public view returns (uint32) {
address contributor = getContributorContract();
return ContributorInterface(contributor).getContributorIdByAddress(contributorAccount);
}
function getContributorAddressById(uint32 contributorId) public view returns (address) {
address contributor = getContributorContract();
return ContributorInterface(contributor).getContributorAddressById(contributorId);
}
//
// Token standard functions (ERC 721)
//
function name() external view returns (string) {
return name_;
}
function symbol() external view returns (string) {
return symbol_;
}
// Balance is amount of ERC271 tokens, not amount of kredits
function balanceOf(address owner) public view returns (uint256) {
require(owner != address(0));
uint32 contributorId = getContributorIdByAddress(owner);
return ownedContributions[contributorId].length;
}
function ownerOf(uint32 contributionId) public view returns (address) {
require(exists(contributionId));
uint32 contributorId = contributions[contributionId].contributorId;
return getContributorAddressById(contributorId);
}
function tokenOfOwnerByIndex(address owner, uint32 index) public view returns (uint32) {
uint32 contributorId = getContributorIdByAddress(owner);
return ownedContributions[contributorId][index];
}
function tokenMetadata(uint32 contributionId) public view returns (string) {
return contributions[contributionId].tokenMetadataURL;
}
//
// Custom functions
//
function getContribution(uint32 contributionId) public view returns (uint32 id, uint32 contributorId, uint32 amount, bool claimed, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint256 confirmedAtBlock, bool exists, bool vetoed) {
id = contributionId;
ContributionData storage c = contributions[id];
return (
id,
c.contributorId,
c.amount,
c.claimed,
c.hashDigest,
c.hashFunction,
c.hashSize,
c.confirmedAtBlock,
c.exists,
c.vetoed
);
}
function add(uint32 amount, uint32 contributorId, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(ADD_CONTRIBUTION_ROLE) {
//require(canPerform(msg.sender, ADD_CONTRIBUTION_ROLE, new uint32[](0)), 'nope');
uint32 contributionId = contributionsCount + 1;
ContributionData storage c = contributions[contributionId];
c.exists = true;
c.amount = amount;
c.claimed = false;
c.contributorId = contributorId;
c.hashDigest = hashDigest;
c.hashFunction = hashFunction;
c.hashSize = hashSize;
if (contributionId < 10) {
c.confirmedAtBlock = block.number;
} else {
c.confirmedAtBlock = block.number + blocksToWait;
}
contributionsCount++;
contributionOwner[contributionId] = contributorId;
ownedContributions[contributorId].push(contributionId);
emit ContributionAdded(contributionId, contributorId, amount);
}
function veto(uint32 contributionId) public isInitialized auth(VETO_CONTRIBUTION_ROLE) {
ContributionData storage c = contributions[contributionId];
require(c.exists, 'NOT_FOUND');
require(!c.claimed, 'ALREADY_CLAIMED');
require(block.number < c.confirmedAtBlock, 'VETO_PERIOD_ENDED');
c.vetoed = true;
emit ContributionVetoed(contributionId, msg.sender);
}
function claim(uint32 contributionId) public isInitialized {
ContributionData storage c = contributions[contributionId];
require(c.exists, 'NOT_FOUND');
require(!c.claimed, 'ALREADY_CLAIMED');
require(!c.vetoed, 'VETOED');
require(block.number >= c.confirmedAtBlock, 'NOT_CLAIMABLE');
c.claimed = true;
address token = getTokenContract();
address contributorAccount = getContributorAddressById(c.contributorId);
uint256 amount = uint256(c.amount);
IToken(token).mintFor(contributorAccount, amount, contributionId);
emit ContributionClaimed(contributionId, c.contributorId, c.amount);
}
function exists(uint32 contributionId) view public returns (bool) {
return contributions[contributionId].exists;
}
}

View File

@@ -1,4 +1,4 @@
pragma solidity ^0.4.17;
pragma solidity ^0.4.24;
contract Migrations {
address public owner;
@@ -8,7 +8,7 @@ contract Migrations {
if (msg.sender == owner) _;
}
function Migrations() public {
constructor() public {
owner = msg.sender;
}

View File

@@ -0,0 +1,4 @@
{
"name": "Contribution",
"description": "Kredits contribution app"
}

View File

@@ -0,0 +1,5 @@
var Migrations = artifacts.require('./Migrations.sol')
module.exports = function (deployer) {
deployer.deploy(Migrations)
}

View File

@@ -0,0 +1,5 @@
var Contribution = artifacts.require('Contribution.sol')
module.exports = function (deployer) {
deployer.deploy(Contribution)
}

13977
apps/contribution/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
{
"name": "kredits-contribution",
"version": "1.0.0",
"description": "",
"dependencies": {
"@aragon/os": "^4.1.0",
"@aragon/cli": "^5.5.0"
},
"devDependencies": {},
"scripts": {
"start": "npm run start:aragon:ipfs",
"start:aragon:ipfs": "aragon run",
"start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist",
"start:app": "npm run sync-assets && npm run build:script -- --no-minify && parcel serve app/index.html -p 8001 --out-dir dist/ --no-cache",
"test": "aragon contracts test",
"compile": "aragon contracts compile",
"sync-assets": "copy-aragon-ui-assets -n aragon-ui ./dist",
"build:app": "",
"build:script": "",
"build": "",
"publish:patch": "aragon apm publish patch",
"publish:minor": "aragon apm publish minor",
"publish:major": "aragon apm publish major",
"versions": "aragon apm versions"
},
"keywords": []
}

View File

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

View File

@@ -0,0 +1 @@
module.exports = require("../../truffle.js");

4
apps/contributor/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
node_modules
build
.cache
dist

View File

@@ -0,0 +1,14 @@
# Git files
.gitignore
# Build files
.cache
node_modules
build
# Lock files
package-lock.json
yarn.lock
# Others
test

View File

@@ -0,0 +1 @@
# Kredits Contributor

View File

@@ -0,0 +1,28 @@
{
"roles": [
{
"name": "Manage contributors",
"id": "MANAGE_CONTRIBUTORS_ROLE",
"params": []
}
],
"environments": {
"default": {
"network": "development",
"appName": "kredits-contributor.aragonpm.eth"
},
"rinkeby": {
"registry": "0x98df287b6c145399aaa709692c8d308357bc085d",
"appName": "kredits-contributor.open.aragonpm.eth",
"wsRPC": "wss://rinkeby.eth.aragon.network/ws",
"network": "rinkeby"
},
"production": {
"registry": "0x314159265dd8dbb310642f98f50c066173c1259b",
"appName": "contributor.open.aragonpm.eth",
"wsRPC": "wss://mainnet.eth.aragon.network/ws",
"network": "mainnet"
}
},
"path": "contracts/Contributor.sol"
}

View File

@@ -0,0 +1,146 @@
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);
}
contract Contributor is AragonApp {
bytes32 public constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb;
bytes32 public constant MANAGE_CONTRIBUTORS_ROLE = keccak256("MANAGE_CONTRIBUTORS_ROLE");
struct Contributor {
address account;
bytes32 hashDigest;
uint8 hashFunction;
uint8 hashSize;
bool exists;
}
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(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 {
appIds = _appIds;
initialized();
}
function getTokenContract() public view returns (address) {
IKernel k = IKernel(kernel());
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[uint8(Apps.Token)]);
}
function coreContributorsCount() view public returns (uint32) {
uint32 count = 0;
for (uint32 i = 1; i <= contributorsCount; i++) {
if (isCoreTeam(i)) {
count += 1;
}
}
return count;
}
function updateContributorAccount(uint32 id, address oldAccount, address newAccount) public auth(MANAGE_CONTRIBUTORS_ROLE) {
contributorIds[oldAccount] = 0;
contributorIds[newAccount] = id;
contributors[id].account = newAccount;
ContributorAccountUpdated(id, oldAccount, newAccount);
}
function updateContributorProfileHash(uint32 id, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(MANAGE_CONTRIBUTORS_ROLE) {
Contributor storage c = contributors[id];
bytes32 oldHashDigest = c.hashDigest;
c.hashDigest = hashDigest;
c.hashFunction = hashFunction;
c.hashSize = hashSize;
ContributorProfileUpdated(id, oldHashDigest, c.hashDigest);
}
function addContributor(address account, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(MANAGE_CONTRIBUTORS_ROLE) {
require(!addressExists(account));
uint32 _id = contributorsCount + 1;
assert(!contributors[_id].exists); // this can not be acually
Contributor storage c = contributors[_id];
c.exists = true;
c.hashDigest = hashDigest;
c.hashFunction = hashFunction;
c.hashSize = hashSize;
c.account = account;
contributorIds[account] = _id;
contributorsCount += 1;
emit ContributorAdded(_id, account);
}
function isCoreTeam(uint32 id) view public returns (bool) {
// TODO: for simplicity we simply define the first contributors as core
// later this needs to be changed to something more dynamic
return id < 7;
}
function exists(uint32 id) view public returns (bool) {
return contributors[id].exists;
}
function addressIsCore(address account) view public returns (bool) {
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 (uint32) {
return contributorIds[account];
}
function getContributorAddressById(uint32 id) view public returns (address) {
return contributors[id].account;
}
function getContributorByAddress(address account) internal view returns (Contributor) {
uint32 id = contributorIds[account];
return contributors[id];
}
function getContributorById(uint32 _id) public view returns (uint32 id, address account, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, bool isCore, uint256 balance, bool exists ) {
id = _id;
Contributor storage c = contributors[_id];
account = c.account;
hashDigest = c.hashDigest;
hashFunction = c.hashFunction;
hashSize = c.hashSize;
isCore = isCoreTeam(id);
address token = getTokenContract();
balance = ITokenBalance(token).balanceOf(c.account);
exists = c.exists;
}
function canPerform(address _who, address _where, bytes32 _what/*, uint256[] memory _how*/) public returns (bool) {
address sender = _who;
if (sender == address(-1)) {
sender = tx.origin;
}
// _what == keccak256('VOTE_PROPOSAL_ROLE')
if (_what == 0xd61216798314d2fc33e42ff2021d66707b1e38517d3f7166798a9d3a196a9c96) {
return contributorIds[sender] != uint256(0);
}
return addressIsCore(sender);
}
}

View File

@@ -1,4 +1,4 @@
pragma solidity ^0.4.17;
pragma solidity ^0.4.24;
contract Migrations {
address public owner;
@@ -8,7 +8,7 @@ contract Migrations {
if (msg.sender == owner) _;
}
function Migrations() public {
constructor() public {
owner = msg.sender;
}

View File

@@ -0,0 +1,4 @@
{
"name": "Contributor",
"description": "Kredits Contributor app"
}

View File

@@ -0,0 +1,5 @@
var Migrations = artifacts.require('./Migrations.sol')
module.exports = function (deployer) {
deployer.deploy(Migrations)
}

View File

@@ -0,0 +1,5 @@
var Contributor = artifacts.require('Contributor.sol')
module.exports = function (deployer) {
deployer.deploy(Contributor)
}

13977
apps/contributor/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
{
"name": "kredits-contributor",
"version": "1.0.0",
"description": "",
"dependencies": {
"@aragon/os": "^4.1.0",
"@aragon/cli": "^5.5.0"
},
"devDependencies": {},
"scripts": {
"start": "npm run start:aragon:ipfs",
"start:aragon:ipfs": "aragon run",
"start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist",
"start:app": "",
"test": "aragon contracts test",
"compile": "aragon contracts compile",
"sync-assets": "",
"build:app": "",
"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"
},
"keywords": []
}

View File

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

View File

@@ -0,0 +1 @@
module.exports = require("../../truffle.js");

1
apps/proposal/README.md Normal file
View File

@@ -0,0 +1 @@
# Kredits Proposal

33
apps/proposal/arapp.json Normal file
View File

@@ -0,0 +1,33 @@
{
"roles": [
{
"name": "Add proposal",
"id": "ADD_PROPOSAL_ROLE",
"params": []
},
{
"name": "Vote proposals",
"id": "VOTE_PROPOSAL_ROLE",
"params": []
}
],
"environments": {
"default": {
"network": "development",
"appName": "kredits-proposal.aragonpm.eth"
},
"rinkeby": {
"registry": "0x98df287b6c145399aaa709692c8d308357bc085d",
"appName": "kredits-proposal.open.aragonpm.eth",
"wsRPC": "wss://rinkeby.eth.aragon.network/ws",
"network": "rinkeby"
},
"production": {
"registry": "0x314159265dd8dbb310642f98f50c066173c1259b",
"appName": "proposal.open.aragonpm.eth",
"wsRPC": "wss://mainnet.eth.aragon.network/ws",
"network": "mainnet"
}
},
"path": "contracts/Proposal.sol"
}

View File

@@ -0,0 +1,133 @@
pragma solidity ^0.4.24;
import "@aragon/os/contracts/apps/AragonApp.sol";
import "@aragon/os/contracts/kernel/IKernel.sol";
interface IContributor {
function getContributorAddressById(uint32 contributorId) public view returns (address);
function getContributorIdByAddress(address contributorAccount) public view returns (uint32);
function exists(uint32 contributorId) public view returns (bool);
}
interface IContribution {
function add(uint32 amount, uint32 contributorId, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public;
}
contract Proposal is AragonApp {
bytes32 public constant ADD_PROPOSAL_ROLE = keccak256("ADD_PROPOSAL_ROLE");
bytes32 public constant VOTE_PROPOSAL_ROLE = keccak256("VOTE_PROPOSAL_ROLE");
bytes32 public constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb;
// ensure alphabetic order
enum Apps { Contribution, Contributor, Proposal, Token }
bytes32[4] public appIds;
struct Proposal {
address creatorAccount;
uint32 contributorId;
uint16 votesCount;
uint16 votesNeeded;
uint32 amount;
bool executed;
bytes32 hashDigest;
uint8 hashFunction;
uint8 hashSize;
uint32[] voterIds;
mapping (uint32 => bool) votes;
bool exists;
}
mapping(uint32 => Proposal) public proposals;
uint32 public proposalsCount;
event ProposalCreated(uint32 id, address creatorAccount, uint32 contributorId, uint32 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 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(getContributorContract()).exists(contributorId), 'CONTRIBUTOR_NOT_FOUND');
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.hashFunction = hashFunction;
p.hashSize = hashSize;
p.votesCount = 0;
p.votesNeeded = _votesNeeded;
p.exists = true;
proposalsCount++;
emit ProposalCreated(proposalId, msg.sender, p.contributorId, p.amount);
}
function getProposal(uint32 proposalId) public view returns (uint32 id, address creatorAccount, uint32 contributorId, uint16 votesCount, uint16 votesNeeded, uint32 amount, bool executed, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint32[] voterIds, bool exists) {
id = proposalId;
Proposal storage p = proposals[id];
return (
id,
p.creatorAccount,
p.contributorId,
p.votesCount,
p.votesNeeded,
p.amount,
p.executed,
p.hashDigest,
p.hashFunction,
p.hashSize,
p.voterIds,
p.exists
);
}
function vote(uint32 proposalId) public isInitialized auth(VOTE_PROPOSAL_ROLE) {
Proposal storage p = proposals[proposalId];
require(!p.executed, 'ALREADY_EXECUTED');
uint32 voterId = IContributor(getContributorContract()).getContributorIdByAddress(msg.sender);
require(p.votes[voterId] != true, 'ALREADY_VOTED');
p.voterIds.push(voterId);
p.votes[voterId] = true;
p.votesCount++;
if (p.votesCount >= p.votesNeeded) {
executeProposal(proposalId);
}
emit ProposalVoted(proposalId, voterId, p.votesCount);
}
function batchVote(uint32[] _proposalIds) public isInitialized auth(VOTE_PROPOSAL_ROLE) {
for (uint32 i = 0; i < _proposalIds.length; i++) {
vote(_proposalIds[i]);
}
}
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;
IContribution(getContributionContract()).add(p.amount, p.contributorId, p.hashDigest, p.hashFunction, p.hashSize);
emit ProposalExecuted(proposalId, p.contributorId, p.amount);
}
}

View File

@@ -0,0 +1,23 @@
pragma solidity ^0.4.24;
contract Migrations {
address public owner;
uint public last_completed_migration;
modifier restricted() {
if (msg.sender == owner) _;
}
constructor() public {
owner = msg.sender;
}
function setCompleted(uint completed) public restricted {
last_completed_migration = completed;
}
function upgrade(address new_address) public restricted {
Migrations upgraded = Migrations(new_address);
upgraded.setCompleted(last_completed_migration);
}
}

View File

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

View File

@@ -0,0 +1,5 @@
var Migrations = artifacts.require('./Migrations.sol')
module.exports = function (deployer) {
deployer.deploy(Migrations)
}

13977
apps/proposal/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
{
"name": "kredits-proposal",
"version": "1.0.0",
"description": "",
"dependencies": {
"@aragon/os": "^4.1.0",
"@aragon/cli": "^5.5.0"
},
"devDependencies": {},
"scripts": {
"start": "npm run start:aragon:ipfs",
"start:aragon:ipfs": "aragon run",
"start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist",
"start:app": "",
"test": "aragon contracts test",
"compile": "aragon contracts compile",
"sync-assets": "",
"build:app": "",
"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"
},
"keywords": []
}

1
apps/proposal/truffle.js Normal file
View File

@@ -0,0 +1 @@
module.exports = require("../../truffle.js");

4
apps/token/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
node_modules
build
.cache
dist

14
apps/token/.ipfsignore Normal file
View File

@@ -0,0 +1,14 @@
# Git files
.gitignore
# Build files
.cache
node_modules
build
# Lock files
package-lock.json
yarn.lock
# Others
test

1
apps/token/README.md Normal file
View File

@@ -0,0 +1 @@
# Kredits Token

28
apps/token/arapp.json Normal file
View File

@@ -0,0 +1,28 @@
{
"roles": [
{
"name": "Mint token",
"id": "MINT_TOKEN_ROLE",
"params": []
}
],
"environments": {
"default": {
"network": "development",
"appName": "kredits-token.aragonpm.eth"
},
"rinkeby": {
"registry": "0x98df287b6c145399aaa709692c8d308357bc085d",
"appName": "kredits-token.open.aragonpm.eth",
"wsRPC": "wss://rinkeby.eth.aragon.network/ws",
"network": "rinkeby"
},
"mainnet": {
"registry": "0x314159265dd8dbb310642f98f50c066173c1259b",
"appName": "kredits-token.open.aragonpm.eth",
"wsRPC": "wss://mainnet.eth.aragon.network/ws",
"network": "mainnet"
}
},
"path": "contracts/Token.sol"
}

View File

@@ -0,0 +1,173 @@
pragma solidity ^0.4.24;
import "@aragon/os/contracts/lib/math/SafeMath.sol";
/**
* beause ERC20.sol conflicts with the aragon ERC20.sol this is copied and modified from:
* https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/token/ERC20/ERC20.sol
* @title Standard ERC20 token
*
* @dev Implementation of the basic standard token.
* https://eips.ethereum.org/EIPS/eip-20
* Originally based on code by FirstBlood:
* https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
*
* This implementation emits additional Approval events, allowing applications to reconstruct the allowance status for
* all accounts just by listening to said events. Note that this isn't required by the specification, and other
* compliant implementations may not do it.
*/
contract ERC20Token {
using SafeMath for uint256;
mapping (address => uint256) public _balances;
mapping (address => mapping (address => uint256)) private _allowed;
uint256 public _totalSupply;
string public name;
string public symbol;
uint8 public decimals;
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Total number of tokens in existence
*/
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
/**
* @dev Gets the balance of the specified address.
* @param owner The address to query the balance of.
* @return A uint256 representing the amount owned by the passed address.
*/
function balanceOf(address owner) public view returns (uint256) {
return _balances[owner];
}
/**
* @dev Function to check the amount of tokens that an owner allowed to a spender.
* @param owner address The address which owns the funds.
* @param spender address The address which will spend the funds.
* @return A uint256 specifying the amount of tokens still available for the spender.
*/
function allowance(address owner, address spender) public view returns (uint256) {
return _allowed[owner][spender];
}
/**
* @dev Transfer token to a specified address
* @param to The address to transfer to.
* @param value The amount to be transferred.
*/
function transfer(address to, uint256 value) public returns (bool) {
_transfer(msg.sender, to, value);
return true;
}
/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
* Beware that changing an allowance with this method brings the risk that someone may use both the old
* and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
* race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
*/
function approve(address spender, uint256 value) public returns (bool) {
_approve(msg.sender, spender, value);
return true;
}
/**
* @dev Transfer tokens from one address to another.
* Note that while this function emits an Approval event, this is not required as per the specification,
* and other compliant implementations may not emit the event.
* @param from address The address which you want to send tokens from
* @param to address The address which you want to transfer to
* @param value uint256 the amount of tokens to be transferred
*/
function transferFrom(address from, address to, uint256 value) public returns (bool) {
_transfer(from, to, value);
_approve(from, msg.sender, _allowed[from][msg.sender].sub(value));
return true;
}
/**
* @dev Increase the amount of tokens that an owner allowed to a spender.
* approve should be called when _allowed[msg.sender][spender] == 0. To increment
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* Emits an Approval event.
* @param spender The address which will spend the funds.
* @param addedValue The amount of tokens to increase the allowance by.
*/
function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
_approve(msg.sender, spender, _allowed[msg.sender][spender].add(addedValue));
return true;
}
/**
* @dev Decrease the amount of tokens that an owner allowed to a spender.
* approve should be called when _allowed[msg.sender][spender] == 0. To decrement
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* Emits an Approval event.
* @param spender The address which will spend the funds.
* @param subtractedValue The amount of tokens to decrease the allowance by.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
_approve(msg.sender, spender, _allowed[msg.sender][spender].sub(subtractedValue));
return true;
}
/**
* @dev Transfer token for a specified addresses
* @param from The address to transfer from.
* @param to The address to transfer to.
* @param value The amount to be transferred.
*/
function _transfer(address from, address to, uint256 value) internal {
require(to != address(0));
_balances[from] = _balances[from].sub(value);
_balances[to] = _balances[to].add(value);
emit Transfer(from, to, value);
}
/**
* @dev Internal function that mints an amount of the token and assigns it to
* an account. This encapsulates the modification of balances such that the
* proper events are emitted.
* @param account The account that will receive the created tokens.
* @param value The amount that will be created.
*/
function _mint(address account, uint256 value) internal {
require(account != address(0), 'invalid address');
_totalSupply = _totalSupply.add(value);
_balances[account] = _balances[account].add(value);
emit Transfer(address(0), account, value);
}
/**
* @dev Approve an address to spend another addresses' tokens.
* @param owner The address that owns the tokens.
* @param spender The address that will spend the tokens.
* @param value The number of tokens that can be spent.
*/
function _approve(address owner, address spender, uint256 value) internal {
require(spender != address(0));
require(owner != address(0));
_allowed[owner][spender] = value;
emit Approval(owner, spender, value);
}
}

View File

@@ -0,0 +1,25 @@
pragma solidity ^0.4.24;
import "@aragon/os/contracts/apps/AragonApp.sol";
import "./ERC20Token.sol";
contract Token is ERC20Token, AragonApp {
bytes32 public constant MINT_TOKEN_ROLE = keccak256("MINT_TOKEN_ROLE");
// ensure alphabetic order
enum Apps { Contribution, Contributor, Proposal, Token }
bytes32[4] public appIds;
event LogMint(address indexed recipient, uint256 amount, uint32 contributionId);
function initialize(bytes32[4] _appIds) public onlyInit {
appIds = _appIds;
initialized();
}
function mintFor(address contributorAccount, uint256 amount, uint32 contributionId) public isInitialized auth(MINT_TOKEN_ROLE) {
_mint(contributorAccount, amount);
emit LogMint(contributorAccount, amount, contributionId);
}
}

View File

@@ -0,0 +1,23 @@
pragma solidity ^0.4.24;
contract Migrations {
address public owner;
uint public last_completed_migration;
modifier restricted() {
if (msg.sender == owner) _;
}
constructor() public {
owner = msg.sender;
}
function setCompleted(uint completed) public restricted {
last_completed_migration = completed;
}
function upgrade(address new_address) public restricted {
Migrations upgraded = Migrations(new_address);
upgraded.setCompleted(last_completed_migration);
}
}

4
apps/token/manifest.json Normal file
View File

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

View File

@@ -0,0 +1,5 @@
var Migrations = artifacts.require('./Migrations.sol')
module.exports = function (deployer) {
deployer.deploy(Migrations)
}

View File

@@ -0,0 +1,5 @@
var Token = artifacts.require('Token.sol')
module.exports = function (deployer) {
deployer.deploy(Token)
}

13977
apps/token/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

27
apps/token/package.json Normal file
View File

@@ -0,0 +1,27 @@
{
"name": "kredits-token",
"version": "1.0.0",
"description": "",
"dependencies": {
"@aragon/os": "^4.1.0",
"@aragon/cli": "^5.5.0"
},
"devDependencies": {},
"scripts": {
"start": "npm run start:aragon:ipfs",
"start:aragon:ipfs": "aragon run",
"start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist",
"start:app": "",
"test": "aragon contracts test",
"compile": "aragon contracts compile",
"sync-assets": "",
"build:app": "",
"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"
},
"keywords": []
}

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

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

1
apps/token/truffle.js Normal file
View File

@@ -0,0 +1 @@
module.exports = require("../../truffle.js");

60
arapp.json Normal file
View File

@@ -0,0 +1,60 @@
{
"roles": [
{
"name": "Add contributions",
"id": "ADD_CONTRIBUTION_ROLE",
"params": []
},
{
"name": "Veto contributions",
"id": "VETO_CONTRIBUTION_ROLE",
"params": []
},
{
"name": "Manage contributors",
"id": "MANAGE_CONTRIBUTORS_ROLE",
"params": []
},
{
"name": "Mint token",
"id": "MINT_TOKEN_ROLE",
"params": []
},
{
"name": "Add proposal",
"id": "ADD_PROPOSAL_ROLE",
"params": []
},
{
"name": "Vote proposal",
"id": "VOTE_PROPOSAL_ROLE",
"params": []
}
],
"environments": {
"development": {
"network": "development",
"apm": "aragonpm.eth",
"registry": "0x5f6f7e8cc7346a11ca2def8f827b7a0b612c56a1",
"appName": "dummy.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"
},
"kovan": {
"network": "kovan",
"appName": "dummy.aragonpm.eth"
},
"default": {
"network": "development",
"appName": "dummy.aragonpm.eth",
"apm": "open.aragonpm.eth"
}
},
"path": "contracts/misc/DummyApp.sol"
}

View File

@@ -1,9 +1,16 @@
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}]],
['Operator', 'addProposal', [{ contributorId: 2, amount: 42, kind: 'code', description: 'runs the seeds', url: '' }, {gasLimit: 350000}]],
['Operator', 'addProposal', [{ contributorId: 3, amount: 23, kind: 'code', description: 'runs the seeds', url: '' }, {gasLimit: 350000}]],
['Operator', 'addProposal', [{contributorId: 3, amount: 100, kind: 'code', description: 'hacks on kredits', url: '' }, {gasLimit: 350000}]],
['Operator', 'vote', ['1', {gasLimit: 250000}]]
const contractCalls = [
['Contributor', 'add', [{ account: '0x7e8f313c56f809188313aa274fa67ee58c31515d', name: 'bumi', isCore: true, kind: 'person', url: '', github_username: 'bumi', github_uid: 318, wiki_username: 'bumi' }, { gasLimit: 200000 }]],
['Contributor', 'add', [{ account: '0x49575f3DD9a0d60aE661BC992f72D837A77f05Bc', name: 'raucao', isCore: true, kind: 'person', url: '', github_username: 'skddc', github_uid: 842, wiki_username: 'raucau' }, { gasLimit: 200000 }]],
['Proposal', 'addProposal', [{ contributorId: 1, amount: 500, kind: 'code', description: '[67P/kredits-contracts] Ran the seeds', url: '' }, { gasLimit: 350000 }]],
['Proposal', 'addProposal', [{ contributorId: 2, amount: 500, kind: 'code', description: '[67P/kredits-contracts] Ran the seeds', url: '' }, { gasLimit: 350000 }]],
['Proposal', 'addProposal', [{ contributorId: 2, amount: 500, kind: 'code', description: '[67P/kredits-contracts] Hacked on kredits', url: '' }, { gasLimit: 350000 }]],
['Proposal', 'vote', [1, { gasLimit: 550000 }]],
['Contribution', 'addContribution', [{ contributorId: 1, amount: 5000, kind: 'dev', description: '[67P/kredits-contracts] Introduce contribution token', url: '' }, { gasLimit: 350000 }]],
['Contribution', 'addContribution', [{ contributorId: 2, amount: 1500, kind: 'dev', description: '[67P/kredits-web] Reviewed stuff', url: '' }, { gasLimit: 350000 }]],
['Contribution', 'claim', [1, { gasLimit: 300000 }]]
];
module.exports = { contractCalls };
const funds = [
'0x7e8f313c56f809188313aa274fa67ee58c31515d',
'0xa502eb4021f3b9ab62f75b57a94e1cfbf81fd827'
];
module.exports = { contractCalls, funds };

View File

@@ -1,128 +0,0 @@
pragma solidity ^0.4.18;
// import basic ERC20 details to be able to call balanceOf
import 'zeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol';
import './upgradeable/Upgradeable.sol';
contract Contributors is Upgradeable {
struct Contributor {
address account;
bytes32 ipfsHash;
uint8 hashFunction;
uint8 hashSize;
bool isCore;
bool exists;
}
mapping (address => uint) public contributorIds;
mapping (uint => Contributor) public contributors;
uint256 public contributorsCount;
event ContributorProfileUpdated(uint id, bytes32 oldIpfsHash, bytes32 newIpfsHash);
event ContributorAccountUpdated(uint id, address oldAccount, address newAccount);
event ContributorAdded(uint id, address account);
modifier onlyCoreOrOperator() {
require(msg.sender == registry.getProxyFor('Operator') || addressIsCore(msg.sender));
_;
}
function initialize(address sender) public payable {
require(msg.sender == address(registry));
uint _id = 1;
Contributor storage c = contributors[_id];
c.exists = true;
c.isCore = true;
c.account = sender;
contributorIds[sender] = _id;
contributorsCount += 1;
}
function coreContributorsCount() view public returns (uint) {
uint count = 0;
for (uint256 i = 1; i <= contributorsCount; i++) {
if (contributors[i].isCore) {
count += 1;
}
}
return count;
}
function updateContributorAccount(uint id, address oldAccount, address newAccount) public onlyCoreOrOperator {
contributorIds[oldAccount] = 0;
contributorIds[newAccount] = id;
contributors[id].account = newAccount;
ContributorAccountUpdated(id, oldAccount, newAccount);
}
function updateContributorIpfsHash(uint id, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize) public onlyCoreOrOperator {
Contributor storage c = contributors[id];
bytes32 oldIpfsHash = c.ipfsHash;
c.ipfsHash = ipfsHash;
c.hashFunction = hashFunction;
c.hashSize = hashSize;
ContributorProfileUpdated(id, oldIpfsHash, c.ipfsHash);
}
function addContributor(address account, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize, bool isCore) public onlyCoreOrOperator {
require(!addressExists(account));
uint _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.hashFunction = hashFunction;
c.hashSize = hashSize;
c.account = account;
contributorIds[account] = _id;
contributorsCount += 1;
ContributorAdded(_id, account);
}
function isCore(uint id) view public returns (bool) {
return contributors[id].isCore;
}
function exists(uint id) view public returns (bool) {
return contributors[id].exists;
}
function addressIsCore(address account) view public returns (bool) {
return getContributorByAddress(account).isCore;
}
function addressExists(address account) view public returns (bool) {
return getContributorByAddress(account).exists;
}
function getContributorIdByAddress(address account) view public returns (uint) {
return contributorIds[account];
}
function getContributorAddressById(uint id) view public returns (address) {
return contributors[id].account;
}
function getContributorByAddress(address account) internal view returns (Contributor) {
uint 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, uint balance, bool exists ) {
id = _id;
Contributor storage c = contributors[_id];
account = c.account;
ipfsHash = c.ipfsHash;
hashFunction = c.hashFunction;
hashSize = c.hashSize;
isCore = c.isCore;
exists = c.exists;
ERC20Basic token = ERC20Basic(registry.getProxyFor('Token'));
balance = token.balanceOf(account);
}
}

90
contracts/KreditsKit.sol Normal file
View File

@@ -0,0 +1,90 @@
pragma solidity 0.4.24;
import "@aragon/os/contracts/apps/AragonApp.sol";
import "@aragon/os/contracts/kernel/Kernel.sol";
import "@aragon/os/contracts/acl/ACL.sol";
import "@aragon/kits-base/contracts/KitBase.sol";
import "../apps/contribution/contracts/Contribution.sol";
import "../apps/contributor/contracts/Contributor.sol";
import "../apps/token/contracts/Token.sol";
import "../apps/proposal/contracts/Proposal.sol";
contract KreditsKit is KitBase {
// ensure alphabetic order
enum Apps { Contribution, Contributor, Proposal, Token }
bytes32[4] public appIds;
event DeployInstance(address dao);
event InstalledApp(address dao, address appProxy, bytes32 appId);
constructor (DAOFactory _fac, ENS _ens, bytes32[4] _appIds) public KitBase(_fac, _ens) {
appIds = _appIds;
}
function newInstance() public returns (Kernel dao) {
address root = msg.sender;
dao = fac.newDAO(this);
ACL acl = ACL(dao.acl());
acl.createPermission(this, dao, dao.APP_MANAGER_ROLE(), this);
Contributor contributor = Contributor(_installApp(dao, appIds[uint8(Apps.Contributor)]));
contributor.initialize(root, appIds);
acl.createPermission(root, contributor, contributor.MANAGE_CONTRIBUTORS_ROLE(), this);
Token token = Token(_installApp(dao, appIds[uint8(Apps.Token)]));
token.initialize(appIds);
Contribution contribution = Contribution(_installApp(dao, appIds[uint8(Apps.Contribution)]));
contribution.initialize(appIds);
Proposal proposal = Proposal(_installApp(dao, appIds[uint8(Apps.Proposal)]));
proposal.initialize(appIds);
acl.createPermission(root, contribution, contribution.ADD_CONTRIBUTION_ROLE(), this);
acl.createPermission(root, contribution, contribution.VETO_CONTRIBUTION_ROLE(), this);
acl.grantPermission(proposal, contribution, contribution.ADD_CONTRIBUTION_ROLE());
uint256[] memory params = new uint256[](1);
params[0] = uint256(203) << 248 | uint256(1) << 240 | uint240(contributor);
acl.grantPermissionP(acl.ANY_ENTITY(), contribution, contribution.ADD_CONTRIBUTION_ROLE(), params);
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(acl.ANY_ENTITY(), proposal, proposal.VOTE_PROPOSAL_ROLE(), params);
acl.createPermission(root, proposal, proposal.ADD_PROPOSAL_ROLE(), this);
//acl.grantPermissionP(address(-1), proposal, proposal.ADD_PROPOSAL_ROLE(), params);
acl.grantPermission(acl.ANY_ENTITY(), proposal, proposal.ADD_PROPOSAL_ROLE());
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.setPermissionManager(root, token, token.MINT_TOKEN_ROLE());
cleanupDAOPermissions(dao, acl, root);
emit DeployInstance(dao);
return dao;
}
function _installApp(Kernel _dao, bytes32 _appId) internal returns (AragonApp) {
address baseAppAddress = latestVersionAppBase(_appId);
require(baseAppAddress != address(0), "App should be deployed");
AragonApp appProxy = AragonApp(_dao.newAppInstance(_appId, baseAppAddress, new bytes(0), true));
emit InstalledApp(_dao, appProxy, _appId);
return appProxy;
}
}

View File

@@ -1,123 +0,0 @@
pragma solidity ^0.4.18;
// ToDo: only load interfaces
import './Token.sol';
import './Contributors.sol';
contract Operator is Upgradeable {
struct Proposal {
address creatorAccount;
uint contributorId;
uint votesCount;
uint votesNeeded;
uint256 amount;
bool executed;
bytes32 ipfsHash;
uint8 hashFunction;
uint8 hashSize;
uint256[] voterIds;
mapping (uint256 => bool) votes;
bool exists;
}
mapping(uint256 => Proposal) public proposals;
uint256 public proposalsCount;
event ProposalCreated(uint256 id, address creatorAccount, uint256 contributorId, uint256 amount);
event ProposalVoted(uint256 id, uint256 voterId, uint256 totalVotes);
event ProposalExecuted(uint256 id, uint256 contributorId, uint256 amount);
modifier coreOnly() {
require(contributorsContract().addressIsCore(msg.sender));
_;
}
modifier contributorOnly() {
require(contributorsContract().addressExists(msg.sender));
_;
}
modifier noEther() {
require(msg.value == 0);
_;
}
function contributorsContract() view public returns (Contributors) {
return Contributors(registry.getProxyFor('Contributors'));
}
function tokenContract() view public returns (Token) {
return Token(registry.getProxyFor('Token'));
}
function contributorsCount() view public returns (uint) {
return contributorsContract().contributorsCount();
}
function coreContributorsCount() view public returns (uint) {
return contributorsContract().coreContributorsCount();
}
function addProposal(uint contributorId, uint256 amount, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize) public {
require(contributorsContract().exists(contributorId));
uint256 proposalId = proposalsCount + 1;
uint256 _votesNeeded = contributorsContract().coreContributorsCount() / 100 * 75;
var p = proposals[proposalId];
p.creatorAccount = msg.sender;
p.contributorId = contributorId;
p.amount = amount;
p.ipfsHash = ipfsHash;
p.hashFunction = hashFunction;
p.hashSize = hashSize;
p.votesCount = 0;
p.votesNeeded = _votesNeeded;
p.exists = true;
proposalsCount++;
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 ipfsHash, uint8 hashFunction, uint8 hashSize, uint256[] voterIds, bool exists) {
id = proposalId;
Proposal storage p = proposals[id];
return (
id,
p.creatorAccount,
p.contributorId,
p.votesCount,
p.votesNeeded,
p.amount,
p.executed,
p.ipfsHash,
p.hashFunction,
p.hashSize,
p.voterIds,
p.exists
);
}
function vote(uint256 proposalId) public coreOnly {
var p = proposals[proposalId];
require(!p.executed);
uint256 voterId = contributorsContract().getContributorIdByAddress(msg.sender);
require(p.votes[voterId] != true);
p.voterIds.push(voterId);
p.votes[voterId] = true;
p.votesCount++;
if (p.votesCount >= p.votesNeeded) {
executeProposal(proposalId);
}
ProposalVoted(proposalId, voterId, p.votesCount);
}
function executeProposal(uint proposalId) private {
var p = proposals[proposalId];
require(!p.executed);
require(p.votesCount >= p.votesNeeded);
address recipientAddress = contributorsContract().getContributorAddressById(p.contributorId);
tokenContract().mintFor(recipientAddress, p.amount, proposalId);
p.executed = true;
ProposalExecuted(proposalId, p.contributorId, p.amount);
}
}

View File

@@ -1,27 +0,0 @@
pragma solidity ^0.4.18;
import 'zeppelin-solidity/contracts/token/ERC20/BasicToken.sol';
import './upgradeable/Upgradeable.sol';
contract Token is Upgradeable, BasicToken {
string public name;
string public symbol;
uint8 public decimals;
event LogMint(address indexed recipient, uint256 amount, uint256 proposalId);
function initialize(address sender) public payable {
require(msg.sender == address(registry));
name = 'Kredits';
symbol = 'K';
decimals = 18;
}
function mintFor(address contributorAccount, uint256 amount, uint proposalId) onlyRegistryContractFor('Operator') public {
totalSupply_ = totalSupply_.add(amount);
balances[contributorAccount] = balances[contributorAccount].add(amount);
LogMint(contributorAccount, amount, proposalId);
}
}

View File

@@ -0,0 +1,13 @@
pragma solidity 0.4.24;
import "@aragon/os/contracts/apm/APMNamehash.sol";
contract APMNamehashOpen is APMNamehash {
bytes32 public constant OPEN_TITLE = keccak256("open");
bytes32 public constant OPEN_APM_NODE = keccak256(abi.encodePacked(APM_NODE, OPEN_TITLE));
function apmNamehashOpen(string name) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(OPEN_APM_NODE, keccak256(name)));
}
}

View File

@@ -0,0 +1,16 @@
pragma solidity 0.4.24;
import "@aragon/os/contracts/apps/AragonApp.sol";
// This is a "Dummy" app which's only purpose to exist is because
// Aragon's CLI still doesn't support running a Kit inside a project
// which isn't considered to be a "valid" Aragon project.
// It requires us to have an arrap.json file pointing to the contract
// and a manifest.json file which describes the front-end structure.
contract DummyApp is AragonApp {
function initialize() public onlyInit {
initialized();
}
}

View File

@@ -0,0 +1,23 @@
pragma solidity ^0.4.4;
contract Migrations {
address public owner;
uint public last_completed_migration;
modifier restricted() {
if (msg.sender == owner) _;
}
function constructor() public {
owner = msg.sender;
}
function setCompleted(uint completed) public restricted {
last_completed_migration = completed;
}
function upgrade(address new_address) public restricted {
Migrations upgraded = Migrations(new_address);
upgraded.setCompleted(last_completed_migration);
}
}

View File

@@ -1,53 +0,0 @@
pragma solidity ^0.4.18;
/**
* @title IRegistry
* @dev This contract represents the interface of a registry contract
*/
interface IRegistry {
/**
* @dev This event will be emitted every time a new proxy is created
* @param name of the contract, as specified in the registry
* @param proxy representing the address of the proxy created
*/
event ProxyCreated(string name, address proxy);
/**
* @dev This event will be emitted every time a new implementation is registered
* @param name of the contract, as specified in the registry
* @param version representing the version name of the registered implementation
* @param implementation representing the address of the registered implementation
*/
event VersionAdded(string name, uint version, address implementation);
/**
* @dev This event will be emitted every time a proxy is upgraded to a new version
* @param name of the contract, as specified in the registry
* @param version representing the version name of the registered implementation
*/
event ProxyImplementationUpgraded(string name, uint version);
/**
* @dev Registers a new version with its implementation address
* @param name of the contract, as specified in the registry
* @param implementation representing the address of the new implementation to be registered
*/
function addVersion(string name, address implementation) public;
/**
* @dev Tells the address of the implementation for a given version
* @param name of the contract, as specified in the registry
* @param version to query the implementation of
* @return address of the implementation registered for the given version
*/
function getVersion(string name, uint version) public view returns (address);
/**
* @dev Tells the latest address of the implementation
* @param name of the contract, as specified in the registry
* @return address of the implementation registered for the latest version
*/
function getLatestVersion(string name) public view returns (address);
function getProxyFor(string name) public view returns (address);
}

View File

@@ -1,36 +0,0 @@
pragma solidity ^0.4.18;
/**
* @title Proxy
* @dev Gives the possibility to delegate any call to a foreign implementation.
*/
contract Proxy {
/**
* @dev Tells the address of the implementation where every call will be delegated.
* @return address of the implementation to which it will be delegated
*/
function implementation() public view returns (address);
/**
* @dev Fallback function allowing to perform a delegatecall to the given implementation.
* This function will return whatever the implementation call returns
*/
function () payable public {
address _impl = implementation();
require(_impl != address(0));
bytes memory data = msg.data;
assembly {
let result := delegatecall(gas, _impl, add(data, 0x20), mload(data), 0, 0)
let size := returndatasize
let ptr := mload(0x40)
returndatacopy(ptr, 0, size)
switch result
case 0 { revert(ptr, size) }
default { return(ptr, size) }
}
}
}

View File

@@ -1,86 +0,0 @@
pragma solidity ^0.4.18;
import './IRegistry.sol';
import './Upgradeable.sol';
import './UpgradeabilityProxy.sol';
/**
* @title Registry
* @dev This contract works as a registry of versions, it holds the implementations for the registered versions.
*/
contract Registry is IRegistry {
// mapping of contract names to versions to implementation
// "Token" => "1.0.0" => "0x123"
mapping(bytes32 => mapping(uint => address)) public versions;
// current version for a certain contract
mapping(bytes32 => uint) public currentVersions;
// mapping of the contract names to the proxy addresses
mapping(bytes32 => address) public proxies;
/**
* @dev Registers a new version with its implementation address
* @param name of the contract
* @param implementation representing the address of the new implementation to be registered
*/
function addVersion(string name, address implementation) public {
bytes32 key = keccak256(name);
currentVersions[key] = currentVersions[key] + 1;
uint version = currentVersions[key];
require(versions[key][version] == 0x0);
versions[key][version] = implementation;
VersionAdded(name, version, implementation);
}
/**
* @dev Tells the address of the implementation for a given version
* @param name of the contract
* @param version to query the implementation of
* @return address of the implementation registered for the given version
*/
function getVersion(string name, uint version) public view returns (address) {
bytes32 key = keccak256(name);
return versions[key][version];
}
function getLatestVersion(string name) public view returns (address) {
bytes32 key = keccak256(name);
uint current = currentVersions[key];
return getVersion(name, current);
}
function getProxyFor(string name) public view returns (address) {
bytes32 key = keccak256(name);
return proxies[key];
}
function upgrade(string name, uint version) public {
bytes32 key = keccak256(name);
UpgradeabilityProxy(proxies[key]).upgradeTo(version);
ProxyImplementationUpgraded(name, version);
}
function upgradeToLatest(string name) public {
bytes32 key = keccak256(name);
uint current = currentVersions[key];
upgrade(name, current);
}
/**
* @dev Creates an upgradeable proxy
* @param name of the contract
* @param version representing the first version to be set for the proxy
* @return address of the new proxy created
*/
function createProxy(string name, uint version) public payable returns (UpgradeabilityProxy) {
bytes32 key = keccak256(name);
require(proxies[key] == 0x0);
UpgradeabilityProxy proxy = new UpgradeabilityProxy(name, version);
proxies[key] = address(proxy);
Upgradeable(proxy).initialize.value(msg.value)(msg.sender);
ProxyCreated(name, proxy);
return proxy;
}
}

View File

@@ -1,28 +0,0 @@
pragma solidity ^0.4.18;
import './Proxy.sol';
import './IRegistry.sol';
import './UpgradeabilityStorage.sol';
/**
* @title UpgradeabilityProxy
* @dev This contract represents a proxy where the implementation address to which it will delegate can be upgraded
*/
contract UpgradeabilityProxy is Proxy, UpgradeabilityStorage {
function UpgradeabilityProxy(string _name, uint _version) public {
_proxiedContractName = _name;
registry = IRegistry(msg.sender);
upgradeTo(_version);
}
/**
* @dev Upgrades the implementation to the requested version
* @param _version representing the version name of the new implementation to be set
*/
function upgradeTo(uint _version) public {
require(msg.sender == address(registry));
_implementation = registry.getVersion(_proxiedContractName, _version);
}
}

View File

@@ -1,37 +0,0 @@
pragma solidity ^0.4.18;
import './IRegistry.sol';
/**
* @title UpgradeabilityStorage
* @dev This contract holds all the necessary state variables to support the upgrade functionality
*/
contract UpgradeabilityStorage {
// Versions registry
IRegistry internal registry;
// Address of the current implementation
address internal _implementation;
// contract name
string public _proxiedContractName;
modifier requireRegistry() {
require(address(registry) != 0x0);
_;
}
modifier onlyRegistryContractFor(string name) {
require(address(registry) != 0x0);
require(msg.sender == registry.getProxyFor(name));
_;
}
/**
* @dev Tells the address of the current implementation
* @return address of the current implementation
*/
function implementation() public view returns (address) {
return _implementation;
}
}

View File

@@ -1,19 +0,0 @@
pragma solidity ^0.4.18;
import './UpgradeabilityStorage.sol';
/**
* @title Upgradeable
* @dev This contract holds all the minimum required functionality for a behavior to be upgradeable.
* This means, required state variables for owned upgradeability purpose and simple initialization validation.
*/
contract Upgradeable is UpgradeabilityStorage {
/**
* @dev Validates the caller is the versions registry.
* THIS FUNCTION SHOULD BE OVERRIDDEN CALLING SUPER
* @param sender representing the address deploying the initial behavior of the contract
*/
function initialize(address sender) public payable {
require(msg.sender == address(registry));
}
}

View File

@@ -0,0 +1,36 @@
# Contribution deployments
## 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
from account: 0x18f6d06de7e6d556b1bbb5875f8cfafb5eaef9c5
✔ Successfully published kredits-contribution.open.aragonpm.eth v3.0.0:
Contract address: 0x9b4954834f070380faf9B6CB3794C7eA92d3B506
Content (ipfs): Qma3yvT5AmBbJHL8WkpXHs7aCFqpGd9ZaMpbiz3cT6Un59
Transaction hash: 0xc2b2d19e1b6d68ba50ed051a35c22eac74c69bd50665aed65488f9209171d382
## 2019-03-25
### v2.0.0 updated appid hashes
from account: 0x18f6d06de7e6d556b1bbb5875f8cfafb5eaef9c5
✔ Successfully published kredits-contribution.open.aragonpm.eth v2.0.0:
Contract address: 0x2765F13d82B83C0C9d770715e1fB376ab01C0361
Content (ipfs): QmYiv1cMh6cj8pcbmaTUAWxf93k4UhN8mPJWeqEo7BEkMx
Transaction hash: 0xe4841a6552527c8fbeb6aa44769f95ce60d99413958c6289f67e8ec83823ee96
### v1.0.0
from account: 0x18f6d06de7e6d556b1bbb5875f8cfafb5eaef9c5
✔ Successfully published kredits-contribution.open.aragonpm.eth v1.0.0:
Contract address: 0x1F460107361047064d982f61002D1BdEb02Ce705
Content (ipfs): QmQ6rTWCGcseCmGb6thYG2kwijHk6W84qaFPeftZ4bUY2k
Transaction hash: 0xeee90c5d18a8cb8ac845f52a4202cd62b27c23b4251b69c3681a854786b6e336

View File

@@ -0,0 +1,26 @@
# Contributor deployments
## 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
from account: 0x18f6d06de7e6d556b1bbb5875f8cfafb5eaef9c5
✔ Successfully published kredits-contributor.open.aragonpm.eth v2.0.0:
Contract address: 0xf8e7B45b3c5A98dbE3D69C6828052dfD9fe2dc69
Content (ipfs): QmTDduMJVUVcqz5SgVEns1xwY9LqYrMoAyYcx3pMrPxn6i
Transaction hash: 0x48b38f32f5dcb52a0a8603743b66a7ca490fec04ccb1888dcfd37106bbd3b886
## 2019-03-25
from account: 0x18f6d06de7e6d556b1bbb5875f8cfafb5eaef9c5
✔ Successfully published kredits-contributor.open.aragonpm.eth v1.0.0:
Contract address: 0x6D80EFEE6F9A40AA86Ef7c4C95c8b2e453d260fb
Content (ipfs): QmbGSQPwi3AXHjDFG2fkMpJrBJLtyuaA6DVPMkFTfMpksn
Transaction hash: 0x26376c59dfdb617c35b740a0f110bf3040cdad0103593cdc788267a84d9847b9

View File

@@ -0,0 +1,32 @@
# Kredits deployment
## 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
Using network 'rinkeby'.
Deploying to networkId: 4
Using KreditsKit at: 0x1d6a9c2146a330575ee860eef9a012b5ff7caa68
Created new DAO at: 0x95a7ce185efc2d1f13efd2a00ee9247c51ea7009"
### 2019-03-25
kredits/truffle-kredits@aragonos » aragon contracts exec scripts/new-dao.js --network=rinkeby
Use of `--network` is deprecated and has been replaced with `--environment`. You may need to update your arapp.json
Passing the command to Truffle
Using network 'rinkeby'.
Deploying to networkId: 4
Using KreditsKit at: 0xf4f3963718e5c2b426dd5c3ef0ab4b31ffb7a318
Created new DAO at: 0x8b7c0bec9476ce08d9769a87d272b03b350712e2
from account: 0x18f6d06de7e6d556b1bbb5875f8cfafb5eaef9c5
https://rinkeby.etherscan.io/tx/0x4deb02b3740b3baa7735097d57c1456a5cb329ee741d123478fdb5114e2c305b

View File

@@ -0,0 +1,62 @@
# 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'.
Deploying to networkId: 4
Using ENS at: 0x98Df287B6C145399Aaa709692c8D308357bC085D
Using DAOFactory at: 0x2298d27a9b847c681d2b2c2828ab9d79013f5f1d
Found apps: [contribution,contributor,proposal,token].open.aragonpm.eth
Deployed KreditsKit at: 0x1d6a9c2146a330575ee860eef9a012b5ff7caa68
### fails
a few deployments to fix the deploy script
## 2019-03-25
### fixed kit with correct appids
kredits/truffle-kredits@aragonos » aragon contracts exec scripts/deploy-kit.js --debug --network=rinkeby
Use of `--network` is deprecated and has been replaced with `--environment`. You may need to update your arapp.json
Passing the command to Truffle
Using network 'rinkeby'.
Deploying to networkId: 4
Using ENS at: 0x98Df287B6C145399Aaa709692c8D308357bC085D
Using DAOFactory at: 0x2298d27a9b847c681d2b2c2828ab9d79013f5f1d
Found apps: [contribution,contributor,proposal,token].open.aragonpm.eth
Deployed KreditsKit at: 0xf4f3963718e5c2b426dd5c3ef0ab4b31ffb7a318
from account: 0x18f6d06de7e6d556b1bbb5875f8cfafb5eaef9c5
### test with fixed deploy script
kredits/truffle-kredits@aragonos » ENS=0x98df287b6c145399aaa709692c8d308357bc085d DAO_FACTORY=0x2298d27a9b847c681d2b2c2828ab9d79013f5f1d truffle exec scripts/deploy-kit.js --network=rinkeby
Using network 'rinkeby'.
Deploying to networkId: 4
Using ENS at: 0x98df287b6c145399aaa709692c8d308357bc085d
Using DAOFactory at: 0x2298d27a9b847c681d2b2c2828ab9d79013f5f1d
Deployed KreditsKit at: 0x83afd3c99563fc467aec69e0187ffd53fc8faa76
### success
kredits/truffle-kredits@aragonos » ENS=0x98df287b6c145399aaa709692c8d308357bc085d DAO_FACTORY=0x2298d27a9b847c681d2b2c2828ab9d79013f5f1d truffle exec scripts/deploy-kit.js --network=rinkeby
Using network 'rinkeby'.
Using ENS at: 0x98df287b6c145399aaa709692c8d308357bc085d
Using DAOFactory at: 0x2298d27a9b847c681d2b2c2828ab9d79013f5f1d
Deployed KreditsKit at: 0x1fd2f9206addaf86f3ef921a3b7c84400374ba68
### deployment script error:
deployment script failure at: https://rinkeby.etherscan.io/tx/0x3571b889b6b9b2b3f26dd0ee7fb82c7ece90b28d910078f2e06753d878832af4"

View File

@@ -0,0 +1,35 @@
# Proposal deployments
## 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
from account: 0x18f6d06de7e6d556b1bbb5875f8cfafb5eaef9c5
✔ Successfully published kredits-proposal.open.aragonpm.eth v3.0.0:
Contract address: 0x9f47c31aBB6F53ff2fec09C0bF79c3337C20f5AA
Content (ipfs): Qmdxg33FxAQSH7U8oTApaM5SgUUC9btiA6tu5Hystzp5tV
Transaction hash: 0xceb572f3fff368b9f07fc835577529a113e2de8ff7972a51ffaf4c63acbda9eb
## 2019-03-25
### v2.0.0 updated appid hashes
from account: 0x18f6d06de7e6d556b1bbb5875f8cfafb5eaef9c5
✔ Successfully published kredits-proposal.open.aragonpm.eth v2.0.0:
Contract address: 0x0a48dc5415f4d7A3B2B4a9aaD4A7a8775f923ce3
Content (ipfs): QmZej9xPJN7aTiQ7SzWziRuzEjjfMzjPakcGYcoN8QQBMH
Transaction hash: 0xf14c308b9f92b43a719fab2602c624dc2e11d0f629a0be9a6ec8ac47719ee9ca
### v1.0.0
from account: 0x18f6d06de7e6d556b1bbb5875f8cfafb5eaef9c5
✔ Successfully published kredits-proposal.open.aragonpm.eth v1.0.0:
Contract address: 0xFc04eB3eF666507F96A22d330d8a852d8eC996EA
Content (ipfs): QmRTzoYr7B5f8gDGY8RmoodJkMm59D4jSJLZNRnhAd4wCg
Transaction hash: 0xce419d9d555551eefc624a7ac87c19b5d660eb9c4fe0da611cd5e28e3ee6844f

View File

@@ -0,0 +1,26 @@
# Token deployments
## 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
from account: 0x18f6d06de7e6d556b1bbb5875f8cfafb5eaef9c5
✔ Successfully published kredits-token.open.aragonpm.eth v2.0.0:
Contract address: 0xc126919B3b30F14b848c2cc66Ec21A1e85a7c621
Content (ipfs): QmWB9JrWa93vtdv5VG2yohiWTbDiuzbz9P58apJdP3FnNP
Transaction hash: 0xa5ccd532a91d67b0616ee5505144988be4360440daf70dc2f5108dd2a59f3d68
## 2019-03-25
from account: 0x18f6d06de7e6d556b1bbb5875f8cfafb5eaef9c5
✔ Successfully published kredits-token.open.aragonpm.eth v1.0.0:
Contract address: 0xB4147Be6d4bcC790c6CbD3d508354DDfd3EbC8e6
Content (ipfs): QmQPue7WzQhaXbTLJwyZXSHaRY1u5ezWxyVmBP8bEgGoTE
Transaction hash: 0xa7866d81e236adfeaff6bf2d5dbc9f9757fbc1b0ce689357901194913fe93a3b"

BIN
docs/kredits-diagram.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

1
lib/abis/ACL.json Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"contributors","outputs":[{"name":"account","type":"address"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"isCore","type":"bool"},{"name":"exists","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"implementation","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_proxiedContractName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"contributorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"contributorIds","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"oldIpfsHash","type":"bytes32"},{"indexed":false,"name":"newIpfsHash","type":"bytes32"}],"name":"ContributorProfileUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"oldAccount","type":"address"},{"indexed":false,"name":"newAccount","type":"address"}],"name":"ContributorAccountUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"account","type":"address"}],"name":"ContributorAdded","type":"event"},{"constant":false,"inputs":[{"name":"sender","type":"address"}],"name":"initialize","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"coreContributorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"id","type":"uint256"},{"name":"oldAccount","type":"address"},{"name":"newAccount","type":"address"}],"name":"updateContributorAccount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"id","type":"uint256"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"}],"name":"updateContributorIpfsHash","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"account","type":"address"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"isCore","type":"bool"}],"name":"addContributor","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"id","type":"uint256"}],"name":"isCore","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"id","type":"uint256"}],"name":"exists","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"addressIsCore","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"addressExists","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"getContributorIdByAddress","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"id","type":"uint256"}],"name":"getContributorAddressById","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_id","type":"uint256"}],"name":"getContributorById","outputs":[{"name":"id","type":"uint256"},{"name":"account","type":"address"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"isCore","type":"bool"},{"name":"balance","type":"uint256"},{"name":"exists","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}]

1
lib/abis/Kernel.json Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"proposals","outputs":[{"name":"creatorAccount","type":"address"},{"name":"contributorId","type":"uint256"},{"name":"votesCount","type":"uint256"},{"name":"votesNeeded","type":"uint256"},{"name":"amount","type":"uint256"},{"name":"executed","type":"bool"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"exists","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proposalsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"implementation","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_proxiedContractName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"sender","type":"address"}],"name":"initialize","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"creatorAccount","type":"address"},{"indexed":false,"name":"contributorId","type":"uint256"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"ProposalCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"voterId","type":"uint256"},{"indexed":false,"name":"totalVotes","type":"uint256"}],"name":"ProposalVoted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"contributorId","type":"uint256"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"ProposalExecuted","type":"event"},{"constant":true,"inputs":[],"name":"contributorsContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"contributorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"coreContributorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"contributorId","type":"uint256"},{"name":"amount","type":"uint256"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"}],"name":"addProposal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"proposalId","type":"uint256"}],"name":"getProposal","outputs":[{"name":"id","type":"uint256"},{"name":"creatorAccount","type":"address"},{"name":"contributorId","type":"uint256"},{"name":"votesCount","type":"uint256"},{"name":"votesNeeded","type":"uint256"},{"name":"amount","type":"uint256"},{"name":"executed","type":"bool"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"voterIds","type":"uint256[]"},{"name":"exists","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"proposalId","type":"uint256"}],"name":"vote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]

1
lib/abis/Proposal.json Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
[{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"currentVersions","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"proxies","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"},{"name":"","type":"uint256"}],"name":"versions","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"name","type":"string"},{"indexed":false,"name":"proxy","type":"address"}],"name":"ProxyCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"name","type":"string"},{"indexed":false,"name":"version","type":"uint256"},{"indexed":false,"name":"implementation","type":"address"}],"name":"VersionAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"name","type":"string"},{"indexed":false,"name":"version","type":"uint256"}],"name":"ProxyImplementationUpgraded","type":"event"},{"constant":false,"inputs":[{"name":"name","type":"string"},{"name":"implementation","type":"address"}],"name":"addVersion","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"name","type":"string"},{"name":"version","type":"uint256"}],"name":"getVersion","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"name","type":"string"}],"name":"getLatestVersion","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"name","type":"string"}],"name":"getProxyFor","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"name","type":"string"},{"name":"version","type":"uint256"}],"name":"upgrade","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"name","type":"string"}],"name":"upgradeToLatest","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"name","type":"string"},{"name":"version","type":"uint256"}],"name":"createProxy","outputs":[{"name":"","type":"address"}],"payable":true,"stateMutability":"payable","type":"function"}]

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
{"42":"0x205fe1b3dac678b594c5f0535e7d158e38591f93","100":"0xa7fc9b1f678c41396b53904f94f50a42ff44d826"}

View File

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

View File

@@ -1 +0,0 @@
{"42":"0x9fd66ee78a5ebe86006f12b37ff59c63f9caa15b","100":"0x95d3bd7d136bb0b7ac9988097e964236f8a9976e"}

View File

@@ -1 +0,0 @@
{"42":"0xc270e6ea4fe303df9f1a3d4a132ac425264082e7","100":"0x7458dea485d9d8301e3ce43e8a1ec1456be5ba83"}

View File

@@ -1 +0,0 @@
{"42":"0xf71ccf7ab48044ef9ae0b5e6983dbd3266b78b36","100":"0x3fc29fbe40c2d0ca78c7e81342f00226650fe2ad"}

4
lib/addresses/dao.json Normal file
View File

@@ -0,0 +1,4 @@
{
"4": "0xcd75458fbc4aa2231252d5b21f1391fd031e5cb2",
"41787949": "0x183af3950364390a266edff2a0e7c4c2f95c0691"
}

13
lib/contracts/acl.js Normal file
View File

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

View File

@@ -17,11 +17,7 @@ class Base {
}
on(type, callback) {
let eventMethod = `on${type.toLowerCase()}`;
// Don't use this.contract.events here. Seems to be a bug in ethers.js
this.contract[eventMethod] = callback;
return this;
return this.contract.on(type, callback);
}
}
module.exports = Base;

View File

@@ -0,0 +1,70 @@
const ethers = require('ethers');
const ContributionSerializer = require('../serializers/contribution');
const Base = require('./base');
class Contribution extends Base {
all() {
return this.functions.contributionsCount()
.then(async (count) => {
let contributions = [];
for (let id = 1; id <= count; id++) {
const contribution = await this.getById(id)
contributions.push(contribution);
}
return contributions;
});
}
getById(id) {
return this.functions.getContribution(id)
.then(data => {
return this.ipfs.catAndMerge(data, ContributionSerializer.deserialize);
});
}
getByContributorId(contributorId) {
return this.functions.getContributorAddressById(contributorId)
.then(address => this.getByContributorAddress(address));
}
getByContributorAddress(address) {
return this.functions.balanceOf(address)
.then(async (balance) => {
const count = balance.toNumber();
const contributions = [];
for (let index = 0; index < count; index++) {
const id = await this.functions.tokenOfOwnerByIndex(address, index);
const contribution = await this.getById(id);
contributions.push(contribution);
}
return contributions;
});
}
addContribution(contributionAttr, callOptions = {}) {
let json = ContributionSerializer.serialize(contributionAttr);
// TODO: validate against schema
return this.ipfs
.add(json)
.then((ipfsHashAttr) => {
let contribution = [
contributionAttr.amount,
contributionAttr.contributorId,
ipfsHashAttr.hashDigest,
ipfsHashAttr.hashFunction,
ipfsHashAttr.hashSize,
];
return this.functions.add(...contribution, callOptions);
});
}
}
module.exports = Contribution;

View File

@@ -7,8 +7,7 @@ const Base = require('./base');
class Contributor extends Base {
all() {
return this.functions.contributorsCount()
.then((count) => {
count = count.toNumber();
.then(count => {
let contributors = [];
for (let id = 1; id <= count; id++) {
@@ -20,20 +19,37 @@ class Contributor extends Base {
}
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) => {
return this.ipfs.catAndMerge(data, ContributorSerializer.deserialize);
});
}
filterByAccount(search) {
return this._byAccount(search, 'filter');
}
findByAccount(search) {
return this._byAccount(search, 'find');
}
_byAccount(search, method = 'filter') {
return this.all().then((contributors) => {
const searchEntries = Object.entries(search);
return contributors[method]((contributor) => {
if (!contributor.accounts) { return false; }
return contributor.accounts.find((account) => {
return searchEntries.every((item) => {
let [ key, value ] = item;
return account[key] === value;
});
});
});
});
}
add(contributorAttr, callOptions = {}) {
let json = ContributorSerializer.serialize(contributorAttr);
// TODO: validate against schema
@@ -46,7 +62,6 @@ class Contributor extends Base {
ipfsHashAttr.hashDigest,
ipfsHashAttr.hashFunction,
ipfsHashAttr.hashSize,
contributorAttr.isCore,
];
return this.functions.addContributor(...contributor, callOptions);

View File

@@ -1,6 +1,8 @@
module.exports = {
Contributors: require('./contributor'),
Operator: require('./operator'),
Contributor: require('./contributor'),
Contribution: require('./contribution'),
Proposal: require('./proposal'),
Token: require('./token'),
Registry: require('./registry')
Kernel: require('./kernel'),
Acl: require('./acl')
};

24
lib/contracts/kernel.js Normal file
View File

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

View File

@@ -4,11 +4,10 @@ const RSVP = require('rsvp');
const ContributionSerializer = require('../serializers/contribution');
const Base = require('./base');
class Operator extends Base {
class Proposal extends Base {
all() {
return this.functions.proposalsCount()
.then((count) => {
count = count.toNumber();
.then(count => {
let proposals = [];
for (let id = 1; id <= count; id++) {
@@ -20,16 +19,8 @@ class Operator extends Base {
}
getById(id) {
id = ethers.utils.bigNumberify(id);
return this.functions.getProposal(id)
.then((data) => {
// TODO: remove when naming updated on the contract
data.hashDigest = data.ipfsHash;
return data;
})
// Fetch IPFS data if available
.then((data) => {
.then(data => {
return this.ipfs.catAndMerge(data, ContributionSerializer.deserialize);
});
}
@@ -54,4 +45,4 @@ class Operator extends Base {
}
}
module.exports = Operator;
module.exports = Proposal

View File

@@ -1,6 +0,0 @@
const Base = require('./base');
class Registry extends Base {
}
module.exports = Registry;

View File

@@ -1,15 +1,24 @@
const ethers = require('ethers');
const RSVP = require('rsvp');
const Healthcheck = require('./utils/healthcheck');
const Preflight = require('./utils/preflight');
const ABIS = {
Contributors: require('./abis/Contributors.json'),
Operator: require('./abis/Operator.json'),
Registry: require('./abis/Registry.json'),
Token: require('./abis/Token.json')
Contributor: require('./abis/Contributor.json'),
Contribution: require('./abis/Contribution.json'),
Token: require('./abis/Token.json'),
Proposal: require('./abis/Proposal.json'),
Kernel: require('./abis/Kernel.json'),
Acl: require('./abis/ACL.json')
};
const RegistryAddress = require('./addresses/Registry.json');
const APP_CONTRACTS = [
'Contributor',
'Contribution',
'Token',
'Proposal',
'Acl'
];
const DaoAddresses = require('./addresses/dao.json');
const Contracts = require('./contracts');
const IPFS = require('./utils/ipfs')
@@ -27,26 +36,29 @@ class Kredits {
this.provider = provider;
this.signer = signer;
// by default we only need the registry address.
// the rest is loaded from there in the init() function
this.addresses = addresses || { Registry: RegistryAddress[this.provider.chainId.toString()] }; // chaiID must be a string
this.options = options;
this.addresses = addresses || {};
this.abis = abis || ABIS;
this.ipfs = new IPFS(ipfsConfig);
this.contracts = {};
}
init(names) {
let contractsToLoad = names || Object.keys(ABIS);
let addressPromises = contractsToLoad.map((contractName) => {
return this.Registry.functions.getProxyFor(contractName).then((address) => {
this.addresses[contractName] = address;
}).catch((error) => {
throw new Error(`Failed to get address for ${contractName} from registry at ${this.Registry.contract.address}
- correct registry? does it have version entry? - ${error.message}`
);
let contractsToLoad = names || APP_CONTRACTS;
return this.provider.getNetwork().then(network => {
this.addresses['Kernel'] = this.addresses['Kernel'] || DaoAddresses[network.chainId.toString()];
let addressPromises = contractsToLoad.map((contractName) => {
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 RSVP.all(addressPromises).then(() => { return this });
}
static setup(provider, signer, ipfsConfig = null) {
@@ -54,21 +66,42 @@ class Kredits {
return new Kredits(provider, signer, { ipfsConfig: ipfsConfig }).init();
}
get Registry() {
return this.contractFor('registry');
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) {
k.apm = this.options.apm;
}
return k;
}
get Contributor() {
// TODO: rename to contributor
return this.contractFor('contributors');
return this.contractFor('Contributor');
}
get Contributors() {
console.log('Contributors is deprecated use Contributor instead');
return this.Contributor;
}
get Proposal() {
return this.contractFor('Proposal');
}
get Operator() {
return this.contractFor('operator');
return this.Proposal;
}
get Token() {
return this.contractFor('token');
return this.contractFor('Token');
}
get Contribution() {
return this.contractFor('Contribution');
}
get Acl() {
return this.contractFor('Acl');
}
// Should be private
@@ -92,8 +125,8 @@ class Kredits {
return this.contracts[name];
}
healthcheck() {
return new Healthcheck(this).check();
preflightChecks() {
return new Preflight(this).check();
}
}

View File

@@ -1,4 +1,4 @@
const ipfsAPI = require('ipfs-api');
const ipfsClient = require('ipfs-http-client');
const multihashes = require('multihashes');
class IPFS {
@@ -7,7 +7,7 @@ class IPFS {
if (!config) {
config = { host: 'localhost', port: '5001', protocol: 'http' };
}
this._ipfsAPI = ipfsAPI(config);
this._ipfsAPI = ipfsClient(config);
this._config = config;
}
@@ -28,7 +28,7 @@ class IPFS {
add(data) {
return this._ipfsAPI
.add(new this._ipfsAPI.Buffer(data))
.add(ipfsClient.Buffer.from(data))
.then((res) => {
return this.decodeHash(res[0].hash);
});
@@ -53,7 +53,7 @@ class IPFS {
}
encodeHash(hashData) {
let digest = this._ipfsAPI.Buffer.from(hashData.hashDigest.slice(2), 'hex');
let digest = ipfsClient.Buffer.from(hashData.hashDigest.slice(2), 'hex');
return multihashes.encode(digest, hashData.hashFunction, hashData.hashSize);
}

View File

@@ -1,4 +1,4 @@
class Healthcheck {
class Preflight {
constructor(kredits) {
this.kredits = kredits;
}
@@ -25,4 +25,4 @@ class Healthcheck {
}
}
module.exports = Healthcheck;
module.exports = Preflight;

Some files were not shown because too many files have changed in this diff Show More