Compare commits
555 Commits
feature/co
...
master
Author | SHA1 | Date | |
---|---|---|---|
0f872c8f1e | |||
484a9ffcaf | |||
6e9f565587 | |||
f4634fe692 | |||
68968e1fbd | |||
|
a2ebc609ea | ||
beca6afc4f | |||
|
90223dd22e | ||
ea3a591a0c | |||
|
e0f20d363c | ||
|
5e33381f2a | ||
1f91a13f06 | |||
7d2a492fc9 | |||
|
30490ce393 | ||
|
f0a71ca8f1 | ||
|
117918e66f | ||
d0d456b357 | |||
cda84fa2ce | |||
97e2db93be | |||
17c582f6df | |||
|
914d4d9585 | ||
|
0deeba14fc | ||
|
c61632d949 | ||
|
0b593ec795 | ||
|
401c528e95 | ||
ed2033980c | |||
17c7a36cb4 | |||
|
1cb777aa44 | ||
|
21522a86a0 | ||
|
269a2cf87a | ||
3112ef0e95 | |||
|
d63f7ca743 | ||
f10af14fae | |||
|
2692613b9a | ||
|
b49d1130a9 | ||
|
657cdc8afa | ||
|
ae9aa7aaaf | ||
|
708515ba4b | ||
7eceea5d57 | |||
|
089ffd42fe | ||
|
43d9bc0a07 | ||
|
6113e456af | ||
8ef6fc06ce | |||
|
093273f15b | ||
|
1dc54eccea | ||
|
bb662e377c | ||
|
d6bbc441f8 | ||
|
46090b3740 | ||
|
828f831c52 | ||
|
500180c6da | ||
|
b63c68cd1c | ||
c875e775b6 | |||
|
6e0ec8741e | ||
|
a1a68092f6 | ||
c6168e59e8 | |||
e810424163 | |||
f71ff4ce9a | |||
1c097f37a6 | |||
|
d3fb1010d5 | ||
f390b5dff5 | |||
|
1e4f7be5cf | ||
|
258c1cc755 | ||
|
f29054bc0b | ||
fd012d5359 | |||
|
5da710cc14 | ||
|
e99184b83f | ||
|
2b3fd1241d | ||
|
dc2c8130f3 | ||
|
796ccebd84 | ||
|
d72413eb66 | ||
|
9dd9d298cc | ||
|
67add71a22 | ||
|
cd07313679 | ||
|
90172071fa | ||
|
de1574155c | ||
|
0fc4eed09a | ||
|
55877897be | ||
|
59bda71f97 | ||
|
1521e272f9 | ||
|
990e2a9649 | ||
|
883f9adb96 | ||
|
550bc2b9f4 | ||
2fca436fa8 | |||
2b05be1897 | |||
|
46b1bbfbf2 | ||
|
98348dc544 | ||
|
fd93993a1b | ||
|
0d6702fd2b | ||
|
51e50e7c46 | ||
793642c238 | |||
|
c55593d46f | ||
|
2b314556ad | ||
53955126ff | |||
1d1f2bb4ed | |||
42a2945692 | |||
12326ce73f | |||
18da1306b9 | |||
f6c302dd21 | |||
e3a0f0e8d3 | |||
534f68c5a9 | |||
d0fd1a3971 | |||
a8597bba8e | |||
683ec5b2e9 | |||
d93d736370 | |||
bcee608920 | |||
643bd01a78 | |||
e21fb40661 | |||
fa4583158f | |||
d46b084e2c | |||
687040791c | |||
936115afb9 | |||
ba52e61d50 | |||
9d87636fb0 | |||
cc6b6de827 | |||
0d6dc78a58 | |||
195cd42313 | |||
1721c33620 | |||
96940716bd | |||
ab8f632a30 | |||
9e92fbfa8f | |||
75bcde4f49 | |||
53fafc1c78 | |||
16d5704173 | |||
23b30b7f8e | |||
50b10dd46c | |||
b80273b308 | |||
0fde17a4e0 | |||
ad8daa8649 | |||
d1d92c6644 | |||
53dbc1e4ad | |||
24990de42a | |||
d52f502fd5 | |||
04fff0934d | |||
22c3a82167 | |||
f0edfa038f | |||
8bb3f234c7 | |||
15a08fdaec | |||
0756569dc7 | |||
8a7858a0bb | |||
5e6d066e40 | |||
c3bd2b88d4 | |||
ffd4a5aeaf | |||
623cf6d6da | |||
cc9cc53d0b | |||
9bfe131cd5 | |||
8a962aea62 | |||
b81fc98740 | |||
5d4c9ba403 | |||
f5ca44d779 | |||
dd70bf77d5 | |||
63e8ce1e3f | |||
a626409221 | |||
1425c3664a | |||
c865c154a4 | |||
a63a37c5d6 | |||
20879c4c08 | |||
7ab8e4e52d | |||
f11c4f7764 | |||
44cad2d26e | |||
357698db02 | |||
6c80179a3d | |||
c471060cfd | |||
7418d33e63 | |||
1cfbc09e4a | |||
8984ba3802 | |||
2238ccf40e | |||
0df7930e06 | |||
eebc0db02b | |||
b22d16e61e | |||
6bc6bcb7f6 | |||
9df58b7f9a | |||
c83a560e3b | |||
eadca6904a | |||
89829ee81f | |||
32b212f9cf | |||
e3c03bf4a0 | |||
7dba0e4501 | |||
e6349f0594 | |||
15b47dbc42 | |||
8f0d7e5196 | |||
4add0c7d96 | |||
7ad2515b67 | |||
fbd82a442e | |||
c39fe406d0 | |||
a609d921aa | |||
23d3dd3a80 | |||
c63fcc002b | |||
bc38dcb136 | |||
c4e7e1259e | |||
17cc44cb98 | |||
c2dcedfe58 | |||
2c567fa71a | |||
9b4a95f375 | |||
687f87f43b | |||
7fdeb78617 | |||
19f212f283 | |||
1f248812a7 | |||
3f8407fa02 | |||
a0b0183beb | |||
2b99593699 | |||
606350eb5e | |||
a330a8eedf | |||
3c72fa3a8b | |||
a4ef2398be | |||
31c29ab6d0 | |||
ef0c0c0a39 | |||
844bdbd681 | |||
9d96824fe9 | |||
d246531cfa | |||
6be06b2e57 | |||
c2220c3654 | |||
5044d8fe41 | |||
3c49c688d2 | |||
47f59126a7 | |||
2ce5d925d0 | |||
3db89b0fa3 | |||
190d3b77d5 | |||
b7eac4202c | |||
93aeea69c6 | |||
e7700d5ec7 | |||
c3eced5c1d | |||
4c6ed12167 | |||
9431bc22a6 | |||
78c8052629 | |||
a318fe8374 | |||
eded4a811f | |||
66a37a1e74 | |||
aba4a23104 | |||
13ed02e134 | |||
48ff304861 | |||
1c1a318ae6 | |||
580d6e8f51 | |||
a572f02b2e | |||
abd4caa336 | |||
f1941eb6b8 | |||
ddeeb5ffd7 | |||
ae71691c5b | |||
819fbc547a | |||
7236b04b4b | |||
f746470cf6 | |||
|
5452cf2dad | ||
3c08e3ecfc | |||
6e8c994a0a | |||
|
fd844a00fd | ||
|
eb4e06edf1 | ||
4ae17aa36f | |||
e14cb0a77d | |||
d92ad83379 | |||
fa3e99d404 | |||
4aa5f3aa89 | |||
2316e8f15a | |||
87a468bc91 | |||
0734acbcbe | |||
a45c8d14be | |||
61fe9add1b | |||
16141ed482 | |||
01ef37fd39 | |||
03b9f69d22 | |||
dd76ebdc8d | |||
e485bd90c0 | |||
1d89759c49 | |||
d653b3715f | |||
a05e2c75f3 | |||
124287b977 | |||
9906fde7ef | |||
c7e1f118a6 | |||
1702c22084 | |||
32a123a825 | |||
ac83573438 | |||
77c6c11800 | |||
ca0a6f6ef9 | |||
4614c454a5 | |||
86bbe49c0b | |||
d5322530c3 | |||
011d85447f | |||
9fd9dbc1b5 | |||
b7d87cd8f2 | |||
6c8491e67b | |||
2623e71055 | |||
ed0d420fd5 | |||
36666fd417 | |||
231d6e2477 | |||
6d6c6056f0 | |||
b09c2830c8 | |||
fc5a41123a | |||
107654ecca | |||
810a2eb5fd | |||
06a4e2173a | |||
e55a209343 | |||
17bc4e7d8f | |||
4919605664 | |||
f639e3aa19 | |||
84b4461ba6 | |||
c4f8e9278e | |||
00905eb269 | |||
6a64842415 | |||
055d8bc0ef | |||
91779ccd03 | |||
516b8b31ac | |||
ccd52f6ee5 | |||
ffff09ab23 | |||
3df0831d9b | |||
27a746261c | |||
345b6bde82 | |||
a86ea08bdf | |||
e20bda73fb | |||
4c64aa7c2a | |||
cf43bf9487 | |||
1a227ba67c | |||
df7536589d | |||
f0fe62f6d7 | |||
66fc992291 | |||
2a675c9417 | |||
a3590d7c16 | |||
07a5ca847d | |||
37ce2fddde | |||
3a43284557 | |||
572848c04a | |||
b6ce9dcfc8 | |||
e7e8744ad2 | |||
d2198dca61 | |||
f9f2ef234e | |||
a1f51428d0 | |||
555cb53c78 | |||
ad74d30aa7 | |||
3568d3c141 | |||
db566a581f | |||
c69ebd0a62 | |||
5c375f764f | |||
b04d572b9a | |||
1199b053fe | |||
f06b21dfee | |||
d643e5842c | |||
a87d5326bf | |||
c9fed46054 | |||
6f2097ed46 | |||
94d342ce63 | |||
f984dec95a | |||
145b3ea766 | |||
a80dfee5c5 | |||
4649de24c8 | |||
8539b56a48 | |||
f59c37827a | |||
58db57ee45 | |||
b64a7ca299 | |||
6f09ca8d13 | |||
6f53c8097e | |||
6b2ac15f56 | |||
359989f235 | |||
5894f6323b | |||
09b78e1e8f | |||
48c8f6b9b3 | |||
542ebaf3f3 | |||
d82ffba75e | |||
bdb2cee0c4 | |||
cd45ce260f | |||
3a97983540 | |||
9714926e11 | |||
59614201b5 | |||
e591742e40 | |||
791190f5e7 | |||
24b66daf2a | |||
ad034d7712 | |||
375d8f3275 | |||
80dc787971 | |||
7967dc26f2 | |||
c248725cc1 | |||
59135bf312 | |||
1d771c43e8 | |||
3cb94fb660 | |||
5820d71b2c | |||
4771c0c8a0 | |||
2a1ae117e8 | |||
8abc7ba77f | |||
f696721918 | |||
00b58dec66 | |||
560315cbca | |||
24933f31a7 | |||
b13bf6e8b0 | |||
8dcad88372 | |||
dc6d2716aa | |||
80ad9db630 | |||
6f97c905d6 | |||
1b5f7ff95d | |||
b5b12e22ce | |||
fe811cdb12 | |||
42df6fe310 | |||
c8805be054 | |||
764f63fc8c | |||
f73ccedf42 | |||
a9093c1c40 | |||
e405644b1d | |||
f6189bf910 | |||
b6c06c289c | |||
f405e39c04 | |||
1dbf3b5742 | |||
952b5153fd | |||
d953141f52 | |||
aa57d7c70b | |||
4c0bb879e8 | |||
9c5a517fb9 | |||
b00ddda312 | |||
130e2a7797 | |||
27e5190e29 | |||
b9c171884b | |||
4d2e0ea84b | |||
26d12ba239 | |||
1aae62e139 | |||
f21c4cf9de | |||
6778b9060f | |||
4d36f78824 | |||
98ecf28262 | |||
f5564fa923 | |||
096a9f3cea | |||
3d24835908 | |||
8cbef8458d | |||
9ca0580db6 | |||
dfe38e7d21 | |||
377560805f | |||
65d9cf2a06 | |||
b35dc2049b | |||
26c2710149 | |||
cc24c27444 | |||
f717968402 | |||
14d7fbd75e | |||
7eb1fedb42 | |||
cbfe39f804 | |||
a0e4f5410d | |||
766463b57b | |||
93e0b30b35 | |||
6510dca4b1 | |||
551edcfa72 | |||
d58e2d560a | |||
99394e7f14 | |||
8ceab08bfc | |||
896db5e047 | |||
d507d7b562 | |||
40c5d2a275 | |||
a049f2eedb | |||
b68b037000 | |||
181e6f3c23 | |||
19556349f6 | |||
a1a1c5ef9f | |||
afbd114a36 | |||
db312dafbf | |||
e4c3d9a468 | |||
5b49f82544 | |||
6088f30cd8 | |||
f1e2c65b9e | |||
786f38cfb7 | |||
197a31b9e1 | |||
20acfc70e3 | |||
732dfe7b29 | |||
3ce8f41d32 | |||
e0ff29d1c9 | |||
0ce62839e9 | |||
cc2c9a7368 | |||
1abfc5a265 | |||
6b9466c348 | |||
9fffdbfb58 | |||
350dadec4d | |||
70b9edbcac | |||
92da07dca2 | |||
6fed81c0ed | |||
74e61e1393 | |||
6351db3057 | |||
ed3e5dd4c4 | |||
bd39d0f126 | |||
4c8ee69664 | |||
eae8967322 | |||
9984ca66ba | |||
1b09a30646 | |||
f40cc1d8ff | |||
91135dbc82 | |||
adb7122a28 | |||
ce446e530d | |||
fedb10c5da | |||
a24f80d44f | |||
61fa26da7a | |||
9512ba4334 | |||
65c129eafd | |||
c2f763bec7 | |||
c93a81808a | |||
51e5da414f | |||
3662f1ae24 | |||
c568263fea | |||
38079d07db | |||
0cc67c2fad | |||
a45be0021e | |||
4bd1aed197 | |||
94832d4d07 | |||
4f5ae01c5a | |||
d6f99f57b7 | |||
901566d1ff | |||
5d4be8f176 | |||
f9ab8b225a | |||
dfa55516ec | |||
b6f34ac9a5 | |||
258c6729b6 | |||
b201642485 | |||
0686c79998 | |||
7e68dbf8c2 | |||
f5973756c8 | |||
9cc237fbf4 | |||
3584a73c08 | |||
3c2cdf4376 | |||
fbda45376e | |||
cd7df3893e | |||
e1fea4ed97 | |||
12341d1526 | |||
14b23ecdf1 | |||
1679afb9b4 | |||
b03095e149 | |||
0d9b2d7d58 | |||
e7affdb531 | |||
1594bf0e17 | |||
203199c268 | |||
116f69cb12 | |||
6c569239de | |||
d687ff604e | |||
c9a4b1decc | |||
dae44cac70 | |||
6b8f718051 | |||
6d9a54b71c | |||
75d426f0cc | |||
7ce100e819 | |||
7216522d83 | |||
956f858620 | |||
8a7abba486 | |||
c6c48f49d2 | |||
db2e12c750 | |||
7dc75a19b5 | |||
926913da50 | |||
a045702937 | |||
b13b93122e | |||
c23e7f2df8 | |||
7260544838 | |||
da2f951bdb | |||
017073018f | |||
fe1fa2e881 | |||
c367c9cf6b | |||
52643da096 | |||
ad5fe3ae77 | |||
418785059c | |||
89261d039a | |||
6949a5940a | |||
ce5f5fb8d2 | |||
2e8d00bc2c | |||
b1345b53f3 | |||
847f5a251c | |||
62a5cefd1a | |||
6378df7075 | |||
bd2af6ed72 | |||
85032353ca | |||
e2e9cd5c3b |
6
.dockerignore
Normal file
6
.dockerignore
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
.openzeppelin
|
||||||
|
artifacts
|
||||||
|
cache
|
||||||
|
deployments
|
||||||
|
gitno
|
||||||
|
node_modules
|
40
.drone.yml
Normal file
40
.drone.yml
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: default
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: setup
|
||||||
|
image: gitea.kosmos.org/kredits/docker-ci:latest
|
||||||
|
commands:
|
||||||
|
- cp -r /app/node_modules /drone/src/node_modules
|
||||||
|
- chown -R drone:drone /drone/src
|
||||||
|
- su drone -c 'npm install'
|
||||||
|
- name: lint js
|
||||||
|
image: gitea.kosmos.org/kredits/docker-ci:latest
|
||||||
|
commands:
|
||||||
|
- su drone -c 'npm run lint:wrapper'
|
||||||
|
depends_on:
|
||||||
|
- setup
|
||||||
|
# - name: lint contracts
|
||||||
|
# image: gitea.kosmos.org/kredits/docker-ci:latest
|
||||||
|
# commands:
|
||||||
|
# - su drone -c 'npm run lint:contracts'
|
||||||
|
# depends_on:
|
||||||
|
# - setup
|
||||||
|
- name: build contracts
|
||||||
|
image: gitea.kosmos.org/kredits/docker-ci:latest
|
||||||
|
commands:
|
||||||
|
- su drone -c 'npm run devchain -- --silent' &
|
||||||
|
- sleep 5
|
||||||
|
- su drone -c 'npm run build'
|
||||||
|
depends_on:
|
||||||
|
- setup
|
||||||
|
- name: test
|
||||||
|
image: gitea.kosmos.org/kredits/docker-ci:latest
|
||||||
|
commands:
|
||||||
|
- su drone -c 'npm run devchain -- --silent' &
|
||||||
|
- sleep 5
|
||||||
|
- su drone -c 'npm test'
|
||||||
|
depends_on:
|
||||||
|
- setup
|
||||||
|
- build contracts
|
2
.eslintignore
Normal file
2
.eslintignore
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/scripts/
|
||||||
|
/test/
|
33
.eslintrc.js
Normal file
33
.eslintrc.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
module.exports = {
|
||||||
|
'env': {
|
||||||
|
'browser': true,
|
||||||
|
'es6': true,
|
||||||
|
'node': true
|
||||||
|
},
|
||||||
|
'extends': 'eslint:recommended',
|
||||||
|
'globals': {
|
||||||
|
'Atomics': 'readonly',
|
||||||
|
'SharedArrayBuffer': 'readonly'
|
||||||
|
},
|
||||||
|
'parserOptions': {
|
||||||
|
'ecmaVersion': 2018,
|
||||||
|
'sourceType': 'module'
|
||||||
|
},
|
||||||
|
'rules': {
|
||||||
|
'comma-dangle': ['error', {
|
||||||
|
arrays: 'always-multiline',
|
||||||
|
objects: 'always-multiline',
|
||||||
|
imports: 'never',
|
||||||
|
exports: 'never',
|
||||||
|
functions: 'ignore',
|
||||||
|
}],
|
||||||
|
'eol-last': ['error', 'always'],
|
||||||
|
semi: ['error', 'always'],
|
||||||
|
'space-before-function-paren': ['error', {
|
||||||
|
anonymous: 'never',
|
||||||
|
named: 'always',
|
||||||
|
asyncArrow: 'always',
|
||||||
|
}],
|
||||||
|
'indent': ['error', 2]
|
||||||
|
}
|
||||||
|
}
|
14
.gitea/release-drafter.yml
Normal file
14
.gitea/release-drafter.yml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
name-template: 'v$RESOLVED_VERSION'
|
||||||
|
tag-template: 'v$RESOLVED_VERSION'
|
||||||
|
version-resolver:
|
||||||
|
major:
|
||||||
|
labels:
|
||||||
|
- release/major
|
||||||
|
minor:
|
||||||
|
labels:
|
||||||
|
- release/minor
|
||||||
|
- feature
|
||||||
|
patch:
|
||||||
|
labels:
|
||||||
|
- release/patch
|
||||||
|
default: patch
|
11
.gitea/workflows/release_drafter.yml
Normal file
11
.gitea/workflows/release_drafter.yml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
name: Release Drafter
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [closed]
|
||||||
|
jobs:
|
||||||
|
release_drafter_job:
|
||||||
|
name: Update release notes draft
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Release Drafter
|
||||||
|
uses: https://github.com/raucao/gitea-release-drafter@dev
|
4
.github/release-drafter.yml
vendored
Normal file
4
.github/release-drafter.yml
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
template: |
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
$CHANGES
|
14
.gitignore
vendored
14
.gitignore
vendored
@ -1,3 +1,13 @@
|
|||||||
build
|
|
||||||
node_modules
|
node_modules
|
||||||
.ganache-db
|
**/node_modules
|
||||||
|
.ganache-db
|
||||||
|
.tm_properties
|
||||||
|
yarn-error.log
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
data
|
||||||
|
|
||||||
|
cache
|
||||||
|
artifacts
|
||||||
|
.openzeppelin/unknown-1337.json
|
||||||
|
.env
|
||||||
|
0
.openzeppelin/.keep
Normal file
0
.openzeppelin/.keep
Normal file
937
.openzeppelin/unknown-31.json
Normal file
937
.openzeppelin/unknown-31.json
Normal file
@ -0,0 +1,937 @@
|
|||||||
|
{
|
||||||
|
"manifestVersion": "3.2",
|
||||||
|
"admin": {
|
||||||
|
"address": "0xcf844786B3E8b23f6d6D8CA845c0DE07047D54a7",
|
||||||
|
"txHash": "0xafcf40769c999bae279f48e7d81d0f698c82286dd4178029021f82ecb549e645"
|
||||||
|
},
|
||||||
|
"proxies": [
|
||||||
|
{
|
||||||
|
"address": "0x0E4b2A1b7655266Ba9A029B50eEB42DBCd83090B",
|
||||||
|
"txHash": "0xfd629e11999d0448404c81e33ea79cbd18f8a978cf93afcf5c45e97eb63991f8",
|
||||||
|
"kind": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "0x242fab10d4F395B9DCC59b8Adc6629722c2E65d2",
|
||||||
|
"txHash": "0xcd6633873db24736e9f40a4c401b658b469b290176e0d03e640ccfb7945a5426",
|
||||||
|
"kind": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "0x4fe4311a91F90ff98f0978002D84AE4771fe7cF1",
|
||||||
|
"txHash": "0x8b6e5088ad7f39918bf9e3e67cdc45bd9bf2a62b13e4f7a1441ad3bca27b4c04",
|
||||||
|
"kind": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "0x252b3Ab7913d6Ed8f303a7e5452F0BFf8bE6b64d",
|
||||||
|
"txHash": "0x138a863f21ab0ef2400434280bc8c9e389e6dead125b5622deb9533eb2506a53",
|
||||||
|
"kind": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "0x2C5cf87C0dE8e4E3642de900Fa7B4b630163E2aA",
|
||||||
|
"txHash": "0x0e4310cd5faba8ce7cae60b3de1412050b4315957e58bc8415680eb5e9de0314",
|
||||||
|
"kind": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "0x95DC31665D193E377f54b70C535fcDb205525291",
|
||||||
|
"txHash": "0xb03a4d57e80f73d65f29f75fc419fd6197148e979ed9c330353c8574d952c544",
|
||||||
|
"kind": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "0x049bA8E70FEbFfd6d03C71211bDA37B4ff064115",
|
||||||
|
"txHash": "0x5f561059950c1e06fc62cfc7cac4c4618a0e066704fb680b659ae9a08b6e1d27",
|
||||||
|
"kind": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "0x7ab26A0f00eF0D6e05e5BDE047505a4eD53aF809",
|
||||||
|
"txHash": "0xfa73dbe843ea1151f925f531365fb919b6138c8963e62332c8e00cf5141e457c",
|
||||||
|
"kind": "transparent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "0x99EC72b34295b62f4bC1527Da461262c615a0b2c",
|
||||||
|
"txHash": "0x1ca664ffdf50a6519b900c84e715d43a59ae39b35d5914ecb964f5440d486098",
|
||||||
|
"kind": "transparent"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"impls": {
|
||||||
|
"4ef0a0d41e92c95e53ad65fb957441f9b9443f4fe2d1c2221dfde7d1341db071": {
|
||||||
|
"address": "0x73069A23A5252E9c0D0b5F0493918277074ED987",
|
||||||
|
"txHash": "0x4c139a7a70dea23d9fdbcb7d8d381be7593fc3d80d59f29aec0fd6f16698136b",
|
||||||
|
"layout": {
|
||||||
|
"storage": [
|
||||||
|
{
|
||||||
|
"label": "_initialized",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "0",
|
||||||
|
"type": "t_uint8",
|
||||||
|
"contract": "Initializable",
|
||||||
|
"src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62",
|
||||||
|
"retypedFrom": "bool"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "_initializing",
|
||||||
|
"offset": 1,
|
||||||
|
"slot": "0",
|
||||||
|
"type": "t_bool",
|
||||||
|
"contract": "Initializable",
|
||||||
|
"src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "deployer",
|
||||||
|
"offset": 2,
|
||||||
|
"slot": "0",
|
||||||
|
"type": "t_address",
|
||||||
|
"contract": "Contributor",
|
||||||
|
"src": "contracts/Contributor.sol:15"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "contributionContract",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "1",
|
||||||
|
"type": "t_contract(IContributionBalance)2238",
|
||||||
|
"contract": "Contributor",
|
||||||
|
"src": "contracts/Contributor.sol:16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "tokenContract",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "2",
|
||||||
|
"type": "t_contract(IToken)2221",
|
||||||
|
"contract": "Contributor",
|
||||||
|
"src": "contracts/Contributor.sol:17"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "contributorIds",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "3",
|
||||||
|
"type": "t_mapping(t_address,t_uint32)",
|
||||||
|
"contract": "Contributor",
|
||||||
|
"src": "contracts/Contributor.sol:28"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "contributors",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "4",
|
||||||
|
"type": "t_mapping(t_uint32,t_struct(Contributor)2261_storage)",
|
||||||
|
"contract": "Contributor",
|
||||||
|
"src": "contracts/Contributor.sol:29"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "contributorsCount",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "5",
|
||||||
|
"type": "t_uint32",
|
||||||
|
"contract": "Contributor",
|
||||||
|
"src": "contracts/Contributor.sol:30"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "profileManager",
|
||||||
|
"offset": 4,
|
||||||
|
"slot": "5",
|
||||||
|
"type": "t_address",
|
||||||
|
"contract": "Contributor",
|
||||||
|
"src": "contracts/Contributor.sol:32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"types": {
|
||||||
|
"t_address": {
|
||||||
|
"label": "address",
|
||||||
|
"numberOfBytes": "20"
|
||||||
|
},
|
||||||
|
"t_bool": {
|
||||||
|
"label": "bool",
|
||||||
|
"numberOfBytes": "1"
|
||||||
|
},
|
||||||
|
"t_bytes32": {
|
||||||
|
"label": "bytes32",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_contract(IContributionBalance)2238": {
|
||||||
|
"label": "contract IContributionBalance",
|
||||||
|
"numberOfBytes": "20"
|
||||||
|
},
|
||||||
|
"t_contract(IToken)2221": {
|
||||||
|
"label": "contract IToken",
|
||||||
|
"numberOfBytes": "20"
|
||||||
|
},
|
||||||
|
"t_mapping(t_address,t_uint32)": {
|
||||||
|
"label": "mapping(address => uint32)",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_mapping(t_uint32,t_struct(Contributor)2261_storage)": {
|
||||||
|
"label": "mapping(uint32 => struct Contributor.Contributor)",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_struct(Contributor)2261_storage": {
|
||||||
|
"label": "struct Contributor.Contributor",
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"label": "account",
|
||||||
|
"type": "t_address",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "hashDigest",
|
||||||
|
"type": "t_bytes32",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "hashFunction",
|
||||||
|
"type": "t_uint8",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "hashSize",
|
||||||
|
"type": "t_uint8",
|
||||||
|
"offset": 1,
|
||||||
|
"slot": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "exists",
|
||||||
|
"type": "t_bool",
|
||||||
|
"offset": 2,
|
||||||
|
"slot": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "kreditsWithdrawn",
|
||||||
|
"type": "t_uint32",
|
||||||
|
"offset": 3,
|
||||||
|
"slot": "2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"numberOfBytes": "96"
|
||||||
|
},
|
||||||
|
"t_uint32": {
|
||||||
|
"label": "uint32",
|
||||||
|
"numberOfBytes": "4"
|
||||||
|
},
|
||||||
|
"t_uint8": {
|
||||||
|
"label": "uint8",
|
||||||
|
"numberOfBytes": "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"960de0fa0d50c2ede5b2a868a9475e44622a753d4ff7829422c7eb342572b189": {
|
||||||
|
"address": "0x872fa1C9d207b5ce3CE786f2099c3EeA621dFD70",
|
||||||
|
"txHash": "0xb338b0878d66e466425c9032ef0f9019f91cda395fae4f83c999534249cd228d",
|
||||||
|
"layout": {
|
||||||
|
"storage": [
|
||||||
|
{
|
||||||
|
"label": "_initialized",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "0",
|
||||||
|
"type": "t_uint8",
|
||||||
|
"contract": "Initializable",
|
||||||
|
"src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62",
|
||||||
|
"retypedFrom": "bool"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "_initializing",
|
||||||
|
"offset": 1,
|
||||||
|
"slot": "0",
|
||||||
|
"type": "t_bool",
|
||||||
|
"contract": "Initializable",
|
||||||
|
"src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "contributorContract",
|
||||||
|
"offset": 2,
|
||||||
|
"slot": "0",
|
||||||
|
"type": "t_contract(ContributorInterface)1546",
|
||||||
|
"contract": "Contribution",
|
||||||
|
"src": "contracts/Contribution.sol:14"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "name_",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "1",
|
||||||
|
"type": "t_string_storage",
|
||||||
|
"contract": "Contribution",
|
||||||
|
"src": "contracts/Contribution.sol:28"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "symbol_",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "2",
|
||||||
|
"type": "t_string_storage",
|
||||||
|
"contract": "Contribution",
|
||||||
|
"src": "contracts/Contribution.sol:29"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "contributionOwner",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "3",
|
||||||
|
"type": "t_mapping(t_uint32,t_uint32)",
|
||||||
|
"contract": "Contribution",
|
||||||
|
"src": "contracts/Contribution.sol:32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "ownedContributions",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "4",
|
||||||
|
"type": "t_mapping(t_uint32,t_array(t_uint32)dyn_storage)",
|
||||||
|
"contract": "Contribution",
|
||||||
|
"src": "contracts/Contribution.sol:34"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "contributions",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "5",
|
||||||
|
"type": "t_mapping(t_uint32,t_struct(ContributionData)1570_storage)",
|
||||||
|
"contract": "Contribution",
|
||||||
|
"src": "contracts/Contribution.sol:36"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "contributionsCount",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "6",
|
||||||
|
"type": "t_uint32",
|
||||||
|
"contract": "Contribution",
|
||||||
|
"src": "contracts/Contribution.sol:37"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "blocksToWait",
|
||||||
|
"offset": 4,
|
||||||
|
"slot": "6",
|
||||||
|
"type": "t_uint32",
|
||||||
|
"contract": "Contribution",
|
||||||
|
"src": "contracts/Contribution.sol:40"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "deployer",
|
||||||
|
"offset": 8,
|
||||||
|
"slot": "6",
|
||||||
|
"type": "t_address",
|
||||||
|
"contract": "Contribution",
|
||||||
|
"src": "contracts/Contribution.sol:43"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "migrationDone",
|
||||||
|
"offset": 28,
|
||||||
|
"slot": "6",
|
||||||
|
"type": "t_bool",
|
||||||
|
"contract": "Contribution",
|
||||||
|
"src": "contracts/Contribution.sol:46"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"types": {
|
||||||
|
"t_address": {
|
||||||
|
"label": "address",
|
||||||
|
"numberOfBytes": "20"
|
||||||
|
},
|
||||||
|
"t_array(t_uint32)dyn_storage": {
|
||||||
|
"label": "uint32[]",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_bool": {
|
||||||
|
"label": "bool",
|
||||||
|
"numberOfBytes": "1"
|
||||||
|
},
|
||||||
|
"t_bytes32": {
|
||||||
|
"label": "bytes32",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_contract(ContributorInterface)1546": {
|
||||||
|
"label": "contract ContributorInterface",
|
||||||
|
"numberOfBytes": "20"
|
||||||
|
},
|
||||||
|
"t_mapping(t_uint32,t_array(t_uint32)dyn_storage)": {
|
||||||
|
"label": "mapping(uint32 => uint32[])",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_mapping(t_uint32,t_struct(ContributionData)1570_storage)": {
|
||||||
|
"label": "mapping(uint32 => struct Contribution.ContributionData)",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_mapping(t_uint32,t_uint32)": {
|
||||||
|
"label": "mapping(uint32 => uint32)",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_string_storage": {
|
||||||
|
"label": "string",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_struct(ContributionData)1570_storage": {
|
||||||
|
"label": "struct Contribution.ContributionData",
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"label": "contributorId",
|
||||||
|
"type": "t_uint32",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "amount",
|
||||||
|
"type": "t_uint32",
|
||||||
|
"offset": 4,
|
||||||
|
"slot": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "hashDigest",
|
||||||
|
"type": "t_bytes32",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "hashFunction",
|
||||||
|
"type": "t_uint8",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "hashSize",
|
||||||
|
"type": "t_uint8",
|
||||||
|
"offset": 1,
|
||||||
|
"slot": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "tokenMetadataURL",
|
||||||
|
"type": "t_string_storage",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "confirmedAtBlock",
|
||||||
|
"type": "t_uint256",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "vetoed",
|
||||||
|
"type": "t_bool",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "exists",
|
||||||
|
"type": "t_bool",
|
||||||
|
"offset": 1,
|
||||||
|
"slot": "5"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"numberOfBytes": "192"
|
||||||
|
},
|
||||||
|
"t_uint256": {
|
||||||
|
"label": "uint256",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_uint32": {
|
||||||
|
"label": "uint32",
|
||||||
|
"numberOfBytes": "4"
|
||||||
|
},
|
||||||
|
"t_uint8": {
|
||||||
|
"label": "uint8",
|
||||||
|
"numberOfBytes": "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"75d216a88a04f139c0c69c1369ddd6ded974ebf995ccce44546b6ace2dabb597": {
|
||||||
|
"address": "0x2D7848AF9Af0d2d8D67bbaAbf84D58Bd19F17eFa",
|
||||||
|
"txHash": "0x8a6621bfc3db4c2bc2d294d69e6d25d499c7667b8da1910b3b0f2bc43968adba",
|
||||||
|
"layout": {
|
||||||
|
"storage": [
|
||||||
|
{
|
||||||
|
"label": "_initialized",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "0",
|
||||||
|
"type": "t_uint8",
|
||||||
|
"contract": "Initializable",
|
||||||
|
"src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62",
|
||||||
|
"retypedFrom": "bool"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "_initializing",
|
||||||
|
"offset": 1,
|
||||||
|
"slot": "0",
|
||||||
|
"type": "t_bool",
|
||||||
|
"contract": "Initializable",
|
||||||
|
"src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "__gap",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "1",
|
||||||
|
"type": "t_array(t_uint256)50_storage",
|
||||||
|
"contract": "ContextUpgradeable",
|
||||||
|
"src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "_balances",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "51",
|
||||||
|
"type": "t_mapping(t_address,t_uint256)",
|
||||||
|
"contract": "ERC20Upgradeable",
|
||||||
|
"src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:37"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "_allowances",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "52",
|
||||||
|
"type": "t_mapping(t_address,t_mapping(t_address,t_uint256))",
|
||||||
|
"contract": "ERC20Upgradeable",
|
||||||
|
"src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:39"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "_totalSupply",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "53",
|
||||||
|
"type": "t_uint256",
|
||||||
|
"contract": "ERC20Upgradeable",
|
||||||
|
"src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:41"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "_name",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "54",
|
||||||
|
"type": "t_string_storage",
|
||||||
|
"contract": "ERC20Upgradeable",
|
||||||
|
"src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:43"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "_symbol",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "55",
|
||||||
|
"type": "t_string_storage",
|
||||||
|
"contract": "ERC20Upgradeable",
|
||||||
|
"src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:44"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "__gap",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "56",
|
||||||
|
"type": "t_array(t_uint256)45_storage",
|
||||||
|
"contract": "ERC20Upgradeable",
|
||||||
|
"src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:400"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "contributorContract",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "101",
|
||||||
|
"type": "t_contract(ContributorInterface)3364",
|
||||||
|
"contract": "Token",
|
||||||
|
"src": "contracts/Token.sol:13"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "contributorContractAddress",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "102",
|
||||||
|
"type": "t_address",
|
||||||
|
"contract": "Token",
|
||||||
|
"src": "contracts/Token.sol:16"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"types": {
|
||||||
|
"t_address": {
|
||||||
|
"label": "address",
|
||||||
|
"numberOfBytes": "20"
|
||||||
|
},
|
||||||
|
"t_array(t_uint256)45_storage": {
|
||||||
|
"label": "uint256[45]",
|
||||||
|
"numberOfBytes": "1440"
|
||||||
|
},
|
||||||
|
"t_array(t_uint256)50_storage": {
|
||||||
|
"label": "uint256[50]",
|
||||||
|
"numberOfBytes": "1600"
|
||||||
|
},
|
||||||
|
"t_bool": {
|
||||||
|
"label": "bool",
|
||||||
|
"numberOfBytes": "1"
|
||||||
|
},
|
||||||
|
"t_contract(ContributorInterface)3364": {
|
||||||
|
"label": "contract ContributorInterface",
|
||||||
|
"numberOfBytes": "20"
|
||||||
|
},
|
||||||
|
"t_mapping(t_address,t_mapping(t_address,t_uint256))": {
|
||||||
|
"label": "mapping(address => mapping(address => uint256))",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_mapping(t_address,t_uint256)": {
|
||||||
|
"label": "mapping(address => uint256)",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_string_storage": {
|
||||||
|
"label": "string",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_uint256": {
|
||||||
|
"label": "uint256",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_uint8": {
|
||||||
|
"label": "uint8",
|
||||||
|
"numberOfBytes": "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"d06fbc00ef593e162a054b1f961d39eaf32e0734f8ba230d3519837b8548e33b": {
|
||||||
|
"address": "0x5b8AF16247593465cC82b68A7Af49338E141A207",
|
||||||
|
"txHash": "0x2e052dd42231aa43a484c5a4c376e73b70fd14026611be0b585c826d37fe829c",
|
||||||
|
"layout": {
|
||||||
|
"storage": [
|
||||||
|
{
|
||||||
|
"label": "_initialized",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "0",
|
||||||
|
"type": "t_uint8",
|
||||||
|
"contract": "Initializable",
|
||||||
|
"src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62",
|
||||||
|
"retypedFrom": "bool"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "_initializing",
|
||||||
|
"offset": 1,
|
||||||
|
"slot": "0",
|
||||||
|
"type": "t_bool",
|
||||||
|
"contract": "Initializable",
|
||||||
|
"src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "contributorContract",
|
||||||
|
"offset": 2,
|
||||||
|
"slot": "0",
|
||||||
|
"type": "t_contract(ContributorInterface)2958",
|
||||||
|
"contract": "Reimbursement",
|
||||||
|
"src": "contracts/Reimbursement.sol:14"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "reimbursements",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "1",
|
||||||
|
"type": "t_mapping(t_uint32,t_struct(ReimbursementData)2982_storage)",
|
||||||
|
"contract": "Reimbursement",
|
||||||
|
"src": "contracts/Reimbursement.sol:29"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "reimbursementsCount",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "2",
|
||||||
|
"type": "t_uint32",
|
||||||
|
"contract": "Reimbursement",
|
||||||
|
"src": "contracts/Reimbursement.sol:30"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "blocksToWait",
|
||||||
|
"offset": 4,
|
||||||
|
"slot": "2",
|
||||||
|
"type": "t_uint32",
|
||||||
|
"contract": "Reimbursement",
|
||||||
|
"src": "contracts/Reimbursement.sol:32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"types": {
|
||||||
|
"t_address": {
|
||||||
|
"label": "address",
|
||||||
|
"numberOfBytes": "20"
|
||||||
|
},
|
||||||
|
"t_bool": {
|
||||||
|
"label": "bool",
|
||||||
|
"numberOfBytes": "1"
|
||||||
|
},
|
||||||
|
"t_bytes32": {
|
||||||
|
"label": "bytes32",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_contract(ContributorInterface)2958": {
|
||||||
|
"label": "contract ContributorInterface",
|
||||||
|
"numberOfBytes": "20"
|
||||||
|
},
|
||||||
|
"t_mapping(t_uint32,t_struct(ReimbursementData)2982_storage)": {
|
||||||
|
"label": "mapping(uint32 => struct Reimbursement.ReimbursementData)",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_struct(ReimbursementData)2982_storage": {
|
||||||
|
"label": "struct Reimbursement.ReimbursementData",
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"label": "recipientId",
|
||||||
|
"type": "t_uint32",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "amount",
|
||||||
|
"type": "t_uint256",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "token",
|
||||||
|
"type": "t_address",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "hashDigest",
|
||||||
|
"type": "t_bytes32",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "hashFunction",
|
||||||
|
"type": "t_uint8",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "hashSize",
|
||||||
|
"type": "t_uint8",
|
||||||
|
"offset": 1,
|
||||||
|
"slot": "4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "confirmedAtBlock",
|
||||||
|
"type": "t_uint256",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "vetoed",
|
||||||
|
"type": "t_bool",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "exists",
|
||||||
|
"type": "t_bool",
|
||||||
|
"offset": 1,
|
||||||
|
"slot": "6"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"numberOfBytes": "224"
|
||||||
|
},
|
||||||
|
"t_uint256": {
|
||||||
|
"label": "uint256",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_uint32": {
|
||||||
|
"label": "uint32",
|
||||||
|
"numberOfBytes": "4"
|
||||||
|
},
|
||||||
|
"t_uint8": {
|
||||||
|
"label": "uint8",
|
||||||
|
"numberOfBytes": "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"27e6fdeedf4e7e81a2c2ecc839d0c13414aa8f5527d4d3573f469358c8aad652": {
|
||||||
|
"address": "0xfaeA425E6eDd4e9B31dAa3614c7c7862751D6782",
|
||||||
|
"txHash": "0xe7768586dbfb5d528db3c9d0c2a608a4fcb5d7f04eb9f89913ac21b8d20a72df",
|
||||||
|
"layout": {
|
||||||
|
"storage": [
|
||||||
|
{
|
||||||
|
"label": "_initialized",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "0",
|
||||||
|
"type": "t_uint8",
|
||||||
|
"contract": "Initializable",
|
||||||
|
"src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62",
|
||||||
|
"retypedFrom": "bool"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "_initializing",
|
||||||
|
"offset": 1,
|
||||||
|
"slot": "0",
|
||||||
|
"type": "t_bool",
|
||||||
|
"contract": "Initializable",
|
||||||
|
"src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "contributorContract",
|
||||||
|
"offset": 2,
|
||||||
|
"slot": "0",
|
||||||
|
"type": "t_contract(ContributorInterface)1546",
|
||||||
|
"contract": "Contribution",
|
||||||
|
"src": "contracts/Contribution.sol:14"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "name_",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "1",
|
||||||
|
"type": "t_string_storage",
|
||||||
|
"contract": "Contribution",
|
||||||
|
"src": "contracts/Contribution.sol:28"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "symbol_",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "2",
|
||||||
|
"type": "t_string_storage",
|
||||||
|
"contract": "Contribution",
|
||||||
|
"src": "contracts/Contribution.sol:29"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "contributionOwner",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "3",
|
||||||
|
"type": "t_mapping(t_uint32,t_uint32)",
|
||||||
|
"contract": "Contribution",
|
||||||
|
"src": "contracts/Contribution.sol:32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "ownedContributions",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "4",
|
||||||
|
"type": "t_mapping(t_uint32,t_array(t_uint32)dyn_storage)",
|
||||||
|
"contract": "Contribution",
|
||||||
|
"src": "contracts/Contribution.sol:34"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "contributions",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "5",
|
||||||
|
"type": "t_mapping(t_uint32,t_struct(ContributionData)1570_storage)",
|
||||||
|
"contract": "Contribution",
|
||||||
|
"src": "contracts/Contribution.sol:36"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "contributionsCount",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "6",
|
||||||
|
"type": "t_uint32",
|
||||||
|
"contract": "Contribution",
|
||||||
|
"src": "contracts/Contribution.sol:37"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "blocksToWait",
|
||||||
|
"offset": 4,
|
||||||
|
"slot": "6",
|
||||||
|
"type": "t_uint32",
|
||||||
|
"contract": "Contribution",
|
||||||
|
"src": "contracts/Contribution.sol:40"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "deployer",
|
||||||
|
"offset": 8,
|
||||||
|
"slot": "6",
|
||||||
|
"type": "t_address",
|
||||||
|
"contract": "Contribution",
|
||||||
|
"src": "contracts/Contribution.sol:43"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "migrationDone",
|
||||||
|
"offset": 28,
|
||||||
|
"slot": "6",
|
||||||
|
"type": "t_bool",
|
||||||
|
"contract": "Contribution",
|
||||||
|
"src": "contracts/Contribution.sol:46"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"types": {
|
||||||
|
"t_address": {
|
||||||
|
"label": "address",
|
||||||
|
"numberOfBytes": "20"
|
||||||
|
},
|
||||||
|
"t_array(t_uint32)dyn_storage": {
|
||||||
|
"label": "uint32[]",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_bool": {
|
||||||
|
"label": "bool",
|
||||||
|
"numberOfBytes": "1"
|
||||||
|
},
|
||||||
|
"t_bytes32": {
|
||||||
|
"label": "bytes32",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_contract(ContributorInterface)1546": {
|
||||||
|
"label": "contract ContributorInterface",
|
||||||
|
"numberOfBytes": "20"
|
||||||
|
},
|
||||||
|
"t_mapping(t_uint32,t_array(t_uint32)dyn_storage)": {
|
||||||
|
"label": "mapping(uint32 => uint32[])",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_mapping(t_uint32,t_struct(ContributionData)1570_storage)": {
|
||||||
|
"label": "mapping(uint32 => struct Contribution.ContributionData)",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_mapping(t_uint32,t_uint32)": {
|
||||||
|
"label": "mapping(uint32 => uint32)",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_string_storage": {
|
||||||
|
"label": "string",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_struct(ContributionData)1570_storage": {
|
||||||
|
"label": "struct Contribution.ContributionData",
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"label": "contributorId",
|
||||||
|
"type": "t_uint32",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "amount",
|
||||||
|
"type": "t_uint32",
|
||||||
|
"offset": 4,
|
||||||
|
"slot": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "hashDigest",
|
||||||
|
"type": "t_bytes32",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "hashFunction",
|
||||||
|
"type": "t_uint8",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "hashSize",
|
||||||
|
"type": "t_uint8",
|
||||||
|
"offset": 1,
|
||||||
|
"slot": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "tokenMetadataURL",
|
||||||
|
"type": "t_string_storage",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "confirmedAtBlock",
|
||||||
|
"type": "t_uint256",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "vetoed",
|
||||||
|
"type": "t_bool",
|
||||||
|
"offset": 0,
|
||||||
|
"slot": "5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "exists",
|
||||||
|
"type": "t_bool",
|
||||||
|
"offset": 1,
|
||||||
|
"slot": "5"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"numberOfBytes": "192"
|
||||||
|
},
|
||||||
|
"t_uint256": {
|
||||||
|
"label": "uint256",
|
||||||
|
"numberOfBytes": "32"
|
||||||
|
},
|
||||||
|
"t_uint32": {
|
||||||
|
"label": "uint32",
|
||||||
|
"numberOfBytes": "4"
|
||||||
|
},
|
||||||
|
"t_uint8": {
|
||||||
|
"label": "uint8",
|
||||||
|
"numberOfBytes": "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
.solhint.json
Normal file
9
.solhint.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"solhint:default",
|
||||||
|
"solhint:recommended"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"max-line-length": "warn"
|
||||||
|
}
|
||||||
|
}
|
13
Dockerfile
Normal file
13
Dockerfile
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
FROM node:16.16
|
||||||
|
#ENV NODE_ENV=production
|
||||||
|
|
||||||
|
RUN useradd -ms /bin/bash drone
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY ["package.json", "package-lock.json*", "./"]
|
||||||
|
RUN chown -R drone:drone /app
|
||||||
|
USER drone
|
||||||
|
RUN npm install
|
||||||
|
USER root
|
158
README.md
Normal file
158
README.md
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
[](https://www.npmjs.com/package/@kredits/contracts)
|
||||||
|
[](https://drone.kosmos.org/kredits/contracts)
|
||||||
|
|
||||||
|
# Kredits Contracts
|
||||||
|
|
||||||
|
This repository contains the Solidity smart contracts and the JavaScript API
|
||||||
|
wrapper for [Kosmos Kredits](https://wiki.kosmos.org/Kredits).
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
#### App dependencies
|
||||||
|
|
||||||
|
All requirements are defined in `package.json`.
|
||||||
|
|
||||||
|
$ npm install
|
||||||
|
|
||||||
|
### Local development chain
|
||||||
|
|
||||||
|
We use [hardhat](https://hardhat.org/) as development environment for the
|
||||||
|
smart contracts.
|
||||||
|
|
||||||
|
To run a local development chain run:
|
||||||
|
|
||||||
|
$ npm run devchain # or: hardhat node --network hardhat
|
||||||
|
|
||||||
|
### Bootstrap
|
||||||
|
|
||||||
|
1. Run an EVM node and ipfs
|
||||||
|
|
||||||
|
$ npm run devchain
|
||||||
|
$ ipfs daemon
|
||||||
|
|
||||||
|
2. Compile contracts and build ABIs
|
||||||
|
|
||||||
|
(compiled artifacts will be in `/artifacts`)
|
||||||
|
$ npm run build
|
||||||
|
|
||||||
|
3. Deploy new upgradable contract proxies
|
||||||
|
|
||||||
|
$ npm run deploy:dao
|
||||||
|
|
||||||
|
4. Execute seeds to create demo contributors, contributions, etc. (optional)
|
||||||
|
|
||||||
|
$ npm run seeds
|
||||||
|
|
||||||
|
**Step 2-4 is also summarized in `npm run bootstrap`**
|
||||||
|
|
||||||
|
5. Show contract addresses
|
||||||
|
|
||||||
|
$ cat lib/addresses.json
|
||||||
|
|
||||||
|
## Fund a local development account
|
||||||
|
|
||||||
|
If you need to fund development accounts with devchain coins:
|
||||||
|
|
||||||
|
$ npm run fund # or hardhat fund --network localhost
|
||||||
|
|
||||||
|
## Specs / Testing
|
||||||
|
|
||||||
|
With a local development chain running:
|
||||||
|
|
||||||
|
$ hardhat test
|
||||||
|
|
||||||
|
If you add or change contract code, please make sure to add and/or adapt tests
|
||||||
|
accordingly. Don't worry, it's easy! You can use existing tests as a template
|
||||||
|
for new ones.
|
||||||
|
|
||||||
|
## Contract architecture
|
||||||
|
|
||||||
|
We use the [OpenZeppelin hardhat
|
||||||
|
proxy](https://www.npmjs.com/package/@openzeppelin/hardhat-upgrades) for
|
||||||
|
deploying and managing upgradeable contracts. (see `scripts/create-proxy.js`)
|
||||||
|
|
||||||
|
Each contract is independent and is connected to its dependencies by storing
|
||||||
|
the addresses of the other contracts.
|
||||||
|
|
||||||
|
## 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 `hardhat run`. For example: `hardhat run
|
||||||
|
scripts/list-contributors.js --network localhost`. (NOTE: add `--network
|
||||||
|
localhost` or the network you want to use)
|
||||||
|
|
||||||
|
Some scripts are also defined as npm script, see `package.json`.
|
||||||
|
|
||||||
|
### repl/console
|
||||||
|
|
||||||
|
Similar to cli.js but only provides a REPL with an initialized `kredits`
|
||||||
|
instance.
|
||||||
|
|
||||||
|
$ hardhat console --network localhost
|
||||||
|
|
||||||
|
### add-{contributor, contribution, proposal}.js
|
||||||
|
|
||||||
|
Script to add a new entries to the contracts using the JS wrapper
|
||||||
|
|
||||||
|
$ hardhat run scripts/add-{contributor, contribution, proposal}.js --network localhost
|
||||||
|
|
||||||
|
### list-{contributors, contributions, proposals}.js
|
||||||
|
|
||||||
|
List contract entries
|
||||||
|
|
||||||
|
$ hardhat run scripts/list-{contributors, contributions, proposals}.js --network localhost
|
||||||
|
|
||||||
|
### seeds.js
|
||||||
|
|
||||||
|
Run seeds defined in `config/seeds.js`.
|
||||||
|
|
||||||
|
$ npm run seeds
|
||||||
|
|
||||||
|
### Get the contract addresses
|
||||||
|
|
||||||
|
All contract addresses are stored in `lib/addresses.json`
|
||||||
|
|
||||||
|
$ cat lib/addresses.json
|
||||||
|
|
||||||
|
## Upgradeable contracts
|
||||||
|
|
||||||
|
We use OpenZeppelin for an upgradeable contracts:
|
||||||
|
[https://www.npmjs.com/package/@openzeppelin/hardhat-upgrades](https://www.npmjs.com/package/@openzeppelin/hardhat-upgrades)
|
||||||
|
|
||||||
|
Refer to the OpenZeppelin README and `scripts/create-proxy.js`
|
||||||
|
|
||||||
|
[OpenZeppelin Step by Step guide](https://forum.openzeppelin.com/t/openzeppelin-upgrades-step-by-step-tutorial-for-hardhat/3580)
|
||||||
|
|
||||||
|
For an upgrade example checkout `scripts/upgrade-example.js`
|
||||||
|
|
||||||
|
|
||||||
|
## Deployment to other networks
|
||||||
|
|
||||||
|
Deployable networks are configured in the `hardhat.config.js`.
|
||||||
|
|
||||||
|
To deploy to those networks provide the `--network` argument to the hardhat
|
||||||
|
commands, e.g. `--network rsk`.
|
||||||
|
|
||||||
|
Please note that some npm scripts combine multiple hardhat commands. In those
|
||||||
|
cases the hardhat commands needs to be run manually with the `--network`
|
||||||
|
argument. (=> don't use `npm run bootstrap`)
|
||||||
|
|
||||||
|
Set a `DEPLOY_KEY` environment variable with the private key (HEX) which will
|
||||||
|
be used as a root/deploy account
|
||||||
|
|
||||||
|
Typical deployment flow:
|
||||||
|
|
||||||
|
$ npm run build
|
||||||
|
$ hardhat run scripts/create-proxy.js --network rsk
|
||||||
|
# OR with deploy key:
|
||||||
|
$ DEPLOY_KEY=0xsomething hardhat run scripts/create-proxy.js --network rsk
|
||||||
|
$ # commit the new addresses in the addresses.json file if needed
|
||||||
|
|
||||||
|
To run the console on one of the non localhost networks you can also just pass
|
||||||
|
on the --network argument.
|
||||||
|
|
||||||
|
$ hardhat console --network rsk
|
153
README.mdown
153
README.mdown
@ -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)
|
|
@ -1,9 +1,91 @@
|
|||||||
let contractCalls = [
|
const 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', [{
|
||||||
['Contributor', 'add', [{ account: '0xa502eb4021f3b9ab62f75b57a94e1cfbf81fd827', name: 'raucau', isCore: true, kind: 'person', url: '', github_username: 'skddc', github_uid: 842, wiki_username: 'raucau' }, {gasLimit: 200000}]],
|
account: '0x7e8f313c56f809188313aa274fa67ee58c31515d',
|
||||||
['Operator', 'addProposal', [{ contributorId: 2, amount: 42, kind: 'code', description: 'runs the seeds', url: '' }, {gasLimit: 350000}]],
|
name: 'bumi',
|
||||||
['Operator', 'addProposal', [{ contributorId: 3, amount: 23, kind: 'code', description: 'runs the seeds', url: '' }, {gasLimit: 350000}]],
|
kind: 'person',
|
||||||
['Operator', 'addProposal', [{contributorId: 3, amount: 100, kind: 'code', description: 'hacks on kredits', url: '' }, {gasLimit: 350000}]],
|
url: '',
|
||||||
['Operator', 'vote', ['1', {gasLimit: 250000}]]
|
github_username: 'bumi',
|
||||||
|
github_uid: 318,
|
||||||
|
gitea_username: 'bumi',
|
||||||
|
wiki_username: 'Bumi',
|
||||||
|
}, { gasLimit: 200000 }]],
|
||||||
|
|
||||||
|
['Contributor', 'add', [{
|
||||||
|
account: '0x49575f3DD9a0d60aE661BC992f72D837A77f05Bc',
|
||||||
|
name: 'raucao',
|
||||||
|
kind: 'person',
|
||||||
|
url: '',
|
||||||
|
github_username: 'raucao',
|
||||||
|
github_uid: 842,
|
||||||
|
gitea_username: 'raucao',
|
||||||
|
wiki_username: 'Basti',
|
||||||
|
}, { gasLimit: 200000 }]],
|
||||||
|
|
||||||
|
['Contributor', 'add', [{
|
||||||
|
account: '0xF722709ECC3B05c19d02E82a2a4A4021B8F48C62',
|
||||||
|
name: 'Manuel',
|
||||||
|
kind: 'person',
|
||||||
|
url: '',
|
||||||
|
github_username: 'fsmanuel',
|
||||||
|
github_uid: 54812,
|
||||||
|
wiki_username: 'Manuel',
|
||||||
|
}, { gasLimit: 200000 }]],
|
||||||
|
|
||||||
|
['Contribution', 'add', [{
|
||||||
|
contributorId: 1, contributorIpfsHash: 'QmWKCYGr2rSf6abUPaTYqf98urvoZxGrb7dbspFZA6oyVF',
|
||||||
|
date: '2019-04-11', amount: 500, kind: 'dev',
|
||||||
|
description: '[67P/kredits-contracts] Test this thing',
|
||||||
|
url: '',
|
||||||
|
confirmedAtBlock: 1,
|
||||||
|
}, { gasLimit: 350000 }]],
|
||||||
|
|
||||||
|
['Contribution', 'add', [{
|
||||||
|
contributorId: 2, contributorIpfsHash: 'QmcHzEeAM26HV2zHTf5HnZrCtCtGdEccL5kUtDakAB7ozB',
|
||||||
|
date: '2019-04-11', amount: 1500, kind: 'dev',
|
||||||
|
description: '[67P/kredits-web] Reviewed stuff',
|
||||||
|
url: '',
|
||||||
|
confirmedAtBlock: 1,
|
||||||
|
}, { gasLimit: 350000 }]],
|
||||||
|
|
||||||
|
['Contribution', 'add', [{
|
||||||
|
contributorId: 1, contributorIpfsHash: 'QmWKCYGr2rSf6abUPaTYqf98urvoZxGrb7dbspFZA6oyVF',
|
||||||
|
date: '2019-04-11', amount: 5000, kind: 'dev',
|
||||||
|
description: '[67P/kredits-contracts] Add tests',
|
||||||
|
url: '',
|
||||||
|
confirmedAtBlock: 1,
|
||||||
|
}, { gasLimit: 350000 }]],
|
||||||
|
|
||||||
|
['Contribution', 'add', [{
|
||||||
|
contributorId: 1, contributorIpfsHash: 'QmWKCYGr2rSf6abUPaTYqf98urvoZxGrb7dbspFZA6oyVF',
|
||||||
|
date: '2019-04-11', amount: 1500, kind: 'dev',
|
||||||
|
description: '[67P/kredits-contracts] Introduce contribution token',
|
||||||
|
url: '',
|
||||||
|
}, { gasLimit: 350000 }]],
|
||||||
|
|
||||||
|
['Contribution', 'add', [{
|
||||||
|
contributorId: 2, contributorIpfsHash: 'QmcHzEeAM26HV2zHTf5HnZrCtCtGdEccL5kUtDakAB7ozB',
|
||||||
|
date: '2019-04-11', amount: 1500, kind: 'design',
|
||||||
|
description: '[67P/kredits-web] Expense UI, first draft',
|
||||||
|
url: '',
|
||||||
|
}, { gasLimit: 350000 }]],
|
||||||
|
|
||||||
|
['Reimbursement', 'add', [{ amount: 346800, recipientId: 2, token: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', expenses: [
|
||||||
|
{ title: 'Domain kosmos.social', description: 'Yearly registration fee for domain kosmos.social', amount: 69.00, currency: 'EUR', amountSats: 69216, date: '2020-04-30' },
|
||||||
|
], confirmedAtBlock: 1 }, { gasLimit: 300000 }]],
|
||||||
|
|
||||||
|
['Reimbursement', 'add', [{ amount: 1116000, recipientId: 1, token: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', expenses: [
|
||||||
|
{ title: 'Server rent', description: 'Dedicated server: andromeda.kosmos.org, April 2020', amount: 61, currency: 'EUR', amountSats: 61191, date: '2020-05-28' },
|
||||||
|
{ title: 'Server rent', description: 'Dedicated server: centaurus.kosmos.org, April 2020', amount: 32, currency: 'EUR', amountSats: 32201, date: '2020-05-28' },
|
||||||
|
], confirmedAtBlock: 1 }, { gasLimit: 300000 }]],
|
||||||
|
|
||||||
|
['Reimbursement', 'add', [{ amount: 166800, recipientId: 2, token: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', expenses: [
|
||||||
|
{ title: 'Domain kosmos.chat', description: 'Yearly registration fee for domain kosmos.chat', amount: 13.90, currency: 'EUR', amountSats: 13944, date: '2020-05-30' },
|
||||||
|
]}, { gasLimit: 300000 }]],
|
||||||
];
|
];
|
||||||
module.exports = { contractCalls };
|
|
||||||
|
const funds = [
|
||||||
|
'0x7e8f313c56f809188313aa274fa67ee58c31515d',
|
||||||
|
'0xa502eb4021f3b9ab62f75b57a94e1cfbf81fd827',
|
||||||
|
];
|
||||||
|
|
||||||
|
module.exports = { contractCalls, funds };
|
||||||
|
199
contracts/Contribution.sol
Normal file
199
contracts/Contribution.sol
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
|
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
|
||||||
|
|
||||||
|
interface ContributorInterface {
|
||||||
|
function getContributorAddressById(uint32 contributorId) external view returns (address);
|
||||||
|
function getContributorIdByAddress(address contributorAccount) external view returns (uint32);
|
||||||
|
function addressIsCore(address sender) external view returns (bool);
|
||||||
|
// TODO Maybe use for validation
|
||||||
|
// function exists(uint32 contributorId) public view returns (bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Contribution is Initializable {
|
||||||
|
ContributorInterface public contributorContract;
|
||||||
|
|
||||||
|
struct ContributionData {
|
||||||
|
uint32 contributorId;
|
||||||
|
uint32 amount;
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Confirmation veto period
|
||||||
|
uint32 public blocksToWait;
|
||||||
|
|
||||||
|
// The address that deployed the contract
|
||||||
|
address public deployer;
|
||||||
|
|
||||||
|
// Data migration flag
|
||||||
|
bool public migrationDone;
|
||||||
|
|
||||||
|
event ContributionAdded(uint32 id, uint32 indexed contributorId, uint32 amount);
|
||||||
|
event ContributionVetoed(uint32 id, address vetoedByAccount);
|
||||||
|
|
||||||
|
modifier onlyCore {
|
||||||
|
require(contributorContract.addressIsCore(tx.origin), "Core only");
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
modifier onlyDeployer {
|
||||||
|
require(msg.sender == deployer, "Deployer only");
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initialize(uint32 blocksToWait_) public initializer {
|
||||||
|
deployer = msg.sender;
|
||||||
|
migrationDone = false;
|
||||||
|
blocksToWait = blocksToWait_;
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishMigration() public onlyDeployer {
|
||||||
|
migrationDone = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setContributorContract(address contributor) public {
|
||||||
|
require(address(contributorContract) == address(0) || contributorContract.addressIsCore(msg.sender), "Core only");
|
||||||
|
contributorContract = ContributorInterface(contributor);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContributorIdByAddress(address contributorAccount) public view returns (uint32) {
|
||||||
|
return contributorContract.getContributorIdByAddress(contributorAccount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContributorAddressById(uint32 contributorId) public view returns (address) {
|
||||||
|
return contributorContract.getContributorAddressById(contributorId);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Token standard functions (ERC 721)
|
||||||
|
//
|
||||||
|
|
||||||
|
function name() external view returns (string memory) {
|
||||||
|
return name_;
|
||||||
|
}
|
||||||
|
|
||||||
|
function symbol() external view returns (string memory) {
|
||||||
|
return symbol_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Balance is amount of ERC271 tokens, not amount of kredits
|
||||||
|
function balanceOf(address owner) public view returns (uint256) {
|
||||||
|
require(owner != address(0), "Address invalid");
|
||||||
|
uint32 contributorId = getContributorIdByAddress(owner);
|
||||||
|
return ownedContributions[contributorId].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ownerOf(uint32 contributionId) public view returns (address) {
|
||||||
|
require(exists(contributionId), "Contribution does not exist");
|
||||||
|
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 memory) {
|
||||||
|
return contributions[contributionId].tokenMetadataURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Custom functions
|
||||||
|
//
|
||||||
|
|
||||||
|
function totalKreditsEarned(bool confirmedOnly) public view returns (uint32 amount) {
|
||||||
|
for (uint32 i = 1; i <= contributionsCount; i++) {
|
||||||
|
ContributionData memory c = contributions[i];
|
||||||
|
if (!c.vetoed && (block.number >= c.confirmedAtBlock || !confirmedOnly)) {
|
||||||
|
amount += c.amount; // should use safemath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function totalKreditsEarnedByContributor(uint32 contributorId, bool confirmedOnly) public view returns (uint32 amount) {
|
||||||
|
uint256 tokenCount = ownedContributions[contributorId].length;
|
||||||
|
for (uint256 i = 0; i < tokenCount; i++) {
|
||||||
|
uint32 cId = ownedContributions[contributorId][i];
|
||||||
|
ContributionData memory c = contributions[cId];
|
||||||
|
if (!c.vetoed && (block.number >= c.confirmedAtBlock || !confirmedOnly)) {
|
||||||
|
amount += c.amount; // should use safemath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContribution(uint32 contributionId) public view returns (uint32 id, uint32 contributorId, uint32 amount, 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.hashDigest,
|
||||||
|
c.hashFunction,
|
||||||
|
c.hashSize,
|
||||||
|
c.confirmedAtBlock,
|
||||||
|
c.exists,
|
||||||
|
c.vetoed
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function add(uint32 amount, uint32 contributorId, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint256 confirmedAtBlock, bool vetoed) public {
|
||||||
|
require((confirmedAtBlock == 0 && vetoed == false) || migrationDone == false, "Extra arguments not allowed");
|
||||||
|
require(balanceOf(msg.sender) > 0 || contributorContract.addressIsCore(msg.sender), "Requires kredits or core status");
|
||||||
|
|
||||||
|
uint32 contributionId = contributionsCount + 1;
|
||||||
|
ContributionData storage c = contributions[contributionId];
|
||||||
|
c.exists = true;
|
||||||
|
c.amount = amount;
|
||||||
|
c.contributorId = contributorId;
|
||||||
|
c.hashDigest = hashDigest;
|
||||||
|
c.hashFunction = hashFunction;
|
||||||
|
c.hashSize = hashSize;
|
||||||
|
|
||||||
|
if (confirmedAtBlock > 0) {
|
||||||
|
c.confirmedAtBlock = confirmedAtBlock;
|
||||||
|
} else {
|
||||||
|
c.confirmedAtBlock = block.number + 1 + blocksToWait;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vetoed) { c.vetoed = true; }
|
||||||
|
|
||||||
|
contributionsCount++;
|
||||||
|
|
||||||
|
contributionOwner[contributionId] = contributorId;
|
||||||
|
ownedContributions[contributorId].push(contributionId);
|
||||||
|
|
||||||
|
emit ContributionAdded(contributionId, contributorId, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function veto(uint32 contributionId) public onlyCore {
|
||||||
|
ContributionData storage c = contributions[contributionId];
|
||||||
|
require(c.exists, "NOT_FOUND");
|
||||||
|
require(block.number < c.confirmedAtBlock, "VETO_PERIOD_ENDED");
|
||||||
|
c.vetoed = true;
|
||||||
|
|
||||||
|
emit ContributionVetoed(contributionId, msg.sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exists(uint32 contributionId) public view returns (bool) {
|
||||||
|
return contributions[contributionId].exists;
|
||||||
|
}
|
||||||
|
}
|
178
contracts/Contributor.sol
Normal file
178
contracts/Contributor.sol
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
|
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
|
||||||
|
|
||||||
|
interface IToken {
|
||||||
|
function mintFor(address contributorAccount, uint256 amount) external;
|
||||||
|
function balanceOf(address contributorAccount) external view returns (uint256);
|
||||||
|
}
|
||||||
|
interface IContributionBalance {
|
||||||
|
function totalKreditsEarnedByContributor(uint32 contributorId, bool confirmedOnly) external view returns (uint32 amount);
|
||||||
|
function balanceOf(address owner) external view returns (uint256);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Contributor is Initializable {
|
||||||
|
address public deployer;
|
||||||
|
IContributionBalance public contributionContract;
|
||||||
|
IToken public tokenContract;
|
||||||
|
|
||||||
|
struct Contributor {
|
||||||
|
address account;
|
||||||
|
bytes32 hashDigest;
|
||||||
|
uint8 hashFunction;
|
||||||
|
uint8 hashSize;
|
||||||
|
bool exists;
|
||||||
|
uint32 kreditsWithdrawn;
|
||||||
|
}
|
||||||
|
|
||||||
|
mapping (address => uint32) public contributorIds;
|
||||||
|
mapping (uint32 => Contributor) public contributors;
|
||||||
|
uint32 public contributorsCount;
|
||||||
|
|
||||||
|
address public profileManager;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
modifier onlyCore {
|
||||||
|
require(addressIsCore(tx.origin), "Core only");
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
modifier onlyContributors {
|
||||||
|
require(addressExists(msg.sender) && contributionContract.balanceOf(msg.sender) > 0, "Contributors only");
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initialize(address profileManagerAddress) public initializer {
|
||||||
|
deployer = msg.sender;
|
||||||
|
profileManager = profileManagerAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
function reinitialize(address profileManagerAddress) public reinitializer(2) {
|
||||||
|
profileManager = profileManagerAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setContributionContract(address contribution) public onlyCore {
|
||||||
|
require(address(contributionContract) == address(0) || addressIsCore(msg.sender), "Core only");
|
||||||
|
contributionContract = IContributionBalance(contribution);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTokenContract(address token) public onlyCore {
|
||||||
|
require(address(tokenContract) == address(0) || addressIsCore(msg.sender), "Core only");
|
||||||
|
tokenContract = IToken(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
function coreContributorsCount() public view returns (uint32) {
|
||||||
|
uint32 count = 0;
|
||||||
|
for (uint32 i = 1; i <= contributorsCount; i++) {
|
||||||
|
if (isCoreTeam(i)) {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateContributorAccount(uint32 id, address oldAccount, address newAccount) public onlyCore {
|
||||||
|
require(newAccount != address(0), "invalid new account address");
|
||||||
|
require(getContributorAddressById(id) == oldAccount, "contributor does not exist");
|
||||||
|
|
||||||
|
contributorIds[oldAccount] = 0;
|
||||||
|
contributorIds[newAccount] = id;
|
||||||
|
contributors[id].account = newAccount;
|
||||||
|
emit ContributorAccountUpdated(id, oldAccount, newAccount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateContributorProfileHash(uint32 id, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public onlyCore {
|
||||||
|
Contributor storage c = contributors[id];
|
||||||
|
bytes32 oldHashDigest = c.hashDigest;
|
||||||
|
c.hashDigest = hashDigest;
|
||||||
|
c.hashFunction = hashFunction;
|
||||||
|
c.hashSize = hashSize;
|
||||||
|
|
||||||
|
emit ContributorProfileUpdated(id, oldHashDigest, c.hashDigest);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addContributor(address account, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public {
|
||||||
|
require(!addressExists(account), "Address already in use");
|
||||||
|
require((msg.sender == profileManager) || addressIsCore(msg.sender), "Only core and profile manager");
|
||||||
|
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;
|
||||||
|
c.kreditsWithdrawn = 0;
|
||||||
|
contributorIds[account] = _id;
|
||||||
|
|
||||||
|
contributorsCount += 1;
|
||||||
|
emit ContributorAdded(_id, account);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCoreTeam(uint32 id) public view 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 > 0 && id < 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
function exists(uint32 id) public view returns (bool) {
|
||||||
|
return contributors[id].exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addressIsCore(address account) public view returns (bool) {
|
||||||
|
// the deployer is always core
|
||||||
|
if(account == deployer) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
uint32 id = getContributorIdByAddress(account);
|
||||||
|
return isCoreTeam(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addressExists(address account) public view returns (bool) {
|
||||||
|
return getContributorByAddress(account).exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContributorIdByAddress(address account) public view returns (uint32) {
|
||||||
|
return contributorIds[account];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContributorAddressById(uint32 id) public view returns (address) {
|
||||||
|
return contributors[id].account;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContributorByAddress(address account) internal view returns (Contributor memory) {
|
||||||
|
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, uint32 totalKreditsEarned, uint256 contributionsCount, bool exists, uint256 kreditsWithdrawn) {
|
||||||
|
id = _id;
|
||||||
|
Contributor storage c = contributors[_id];
|
||||||
|
account = c.account;
|
||||||
|
hashDigest = c.hashDigest;
|
||||||
|
hashFunction = c.hashFunction;
|
||||||
|
hashSize = c.hashSize;
|
||||||
|
isCore = isCoreTeam(id);
|
||||||
|
balance = tokenContract.balanceOf(c.account);
|
||||||
|
totalKreditsEarned = contributionContract.totalKreditsEarnedByContributor(_id, true);
|
||||||
|
contributionsCount = contributionContract.balanceOf(c.account);
|
||||||
|
exists = c.exists;
|
||||||
|
kreditsWithdrawn = c.kreditsWithdrawn;
|
||||||
|
}
|
||||||
|
|
||||||
|
function withdraw() public onlyContributors {
|
||||||
|
uint32 id = getContributorIdByAddress(msg.sender);
|
||||||
|
Contributor storage c = contributors[id];
|
||||||
|
|
||||||
|
// TODO check if we need a failsafe for unconfirmed or malicious txs
|
||||||
|
uint32 confirmedKredits = contributionContract.totalKreditsEarnedByContributor(id, true);
|
||||||
|
uint32 amountWithdrawable = confirmedKredits - c.kreditsWithdrawn;
|
||||||
|
require (amountWithdrawable > 0, "No kredits available");
|
||||||
|
|
||||||
|
c.kreditsWithdrawn += amountWithdrawable;
|
||||||
|
tokenContract.mintFor(msg.sender, amountWithdrawable);
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
pragma solidity ^0.4.17;
|
|
||||||
|
|
||||||
contract Migrations {
|
|
||||||
address public owner;
|
|
||||||
uint public last_completed_migration;
|
|
||||||
|
|
||||||
modifier restricted() {
|
|
||||||
if (msg.sender == owner) _;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Migrations() 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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
139
contracts/Reimbursement.sol
Normal file
139
contracts/Reimbursement.sol
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
|
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
|
||||||
|
|
||||||
|
interface ContributorInterface {
|
||||||
|
function getContributorAddressById(uint32 contributorId) external view returns (address);
|
||||||
|
function getContributorIdByAddress(address contributorAccount) external view returns (uint32);
|
||||||
|
function addressIsCore(address sender) external view returns (bool);
|
||||||
|
// TODO Maybe use for validation
|
||||||
|
// function exists(uint32 contributorId) public view returns (bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Reimbursement is Initializable {
|
||||||
|
ContributorInterface public contributorContract;
|
||||||
|
|
||||||
|
struct ReimbursementData {
|
||||||
|
uint32 recipientId;
|
||||||
|
uint256 amount;
|
||||||
|
// TODO remove token entirely
|
||||||
|
address token;
|
||||||
|
bytes32 hashDigest;
|
||||||
|
uint8 hashFunction;
|
||||||
|
uint8 hashSize;
|
||||||
|
uint256 confirmedAtBlock;
|
||||||
|
bool vetoed;
|
||||||
|
bool exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
mapping(uint32 => ReimbursementData) public reimbursements;
|
||||||
|
uint32 public reimbursementsCount;
|
||||||
|
|
||||||
|
uint32 public blocksToWait;
|
||||||
|
|
||||||
|
// The address that deployed the contract
|
||||||
|
address public deployer;
|
||||||
|
|
||||||
|
// Data migration flag
|
||||||
|
bool public migrationDone;
|
||||||
|
|
||||||
|
event ReimbursementAdded(uint32 id, address indexed addedByAccount, uint256 amount);
|
||||||
|
event ReimbursementVetoed(uint32 id, address vetoedByAccount);
|
||||||
|
|
||||||
|
modifier onlyCore {
|
||||||
|
require(contributorContract.addressIsCore(tx.origin), "Core only");
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
modifier onlyDeployer {
|
||||||
|
require(msg.sender == deployer, "Deployer only");
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initialize() public initializer {
|
||||||
|
deployer = msg.sender;
|
||||||
|
migrationDone = false;
|
||||||
|
blocksToWait = 40320; // 7 days; 15 seconds block time
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishMigration() public onlyDeployer {
|
||||||
|
migrationDone = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setContributorContract(address contributor) public {
|
||||||
|
require(address(contributorContract) == address(0) || contributorContract.addressIsCore(msg.sender), "Core only");
|
||||||
|
contributorContract = ContributorInterface(contributor);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContributorIdByAddress(address contributorAccount) public view returns (uint32) {
|
||||||
|
return contributorContract.getContributorIdByAddress(contributorAccount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContributorAddressById(uint32 contributorId) public view returns (address) {
|
||||||
|
return contributorContract.getContributorAddressById(contributorId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function totalAmount(bool confirmedOnly) public view returns (uint256 amount) {
|
||||||
|
for (uint32 i = 1; i <= reimbursementsCount; i++) {
|
||||||
|
ReimbursementData memory r = reimbursements[i];
|
||||||
|
if (!r.vetoed && (block.number >= r.confirmedAtBlock || !confirmedOnly)) {
|
||||||
|
amount += r.amount; // should use safemath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function get(uint32 reimbursementId) public view returns (uint32 id, uint32 recipientId, uint256 amount, address token, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint256 confirmedAtBlock, bool exists, bool vetoed) {
|
||||||
|
id = reimbursementId;
|
||||||
|
ReimbursementData storage r = reimbursements[id];
|
||||||
|
return (
|
||||||
|
id,
|
||||||
|
r.recipientId,
|
||||||
|
r.amount,
|
||||||
|
r.token,
|
||||||
|
r.hashDigest,
|
||||||
|
r.hashFunction,
|
||||||
|
r.hashSize,
|
||||||
|
r.confirmedAtBlock,
|
||||||
|
r.exists,
|
||||||
|
r.vetoed
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function add(uint256 amount, address token, uint32 recipientId, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint256 confirmedAtBlock, bool vetoed) public onlyCore {
|
||||||
|
require((confirmedAtBlock == 0 && vetoed == false) || migrationDone == false, "Extra arguments not allowed");
|
||||||
|
uint32 reimbursementId = reimbursementsCount + 1;
|
||||||
|
ReimbursementData storage r = reimbursements[reimbursementId];
|
||||||
|
r.exists = true;
|
||||||
|
r.amount = amount;
|
||||||
|
r.token = token;
|
||||||
|
r.recipientId = recipientId;
|
||||||
|
r.hashDigest = hashDigest;
|
||||||
|
r.hashFunction = hashFunction;
|
||||||
|
r.hashSize = hashSize;
|
||||||
|
|
||||||
|
if (confirmedAtBlock > 0) {
|
||||||
|
r.confirmedAtBlock = confirmedAtBlock;
|
||||||
|
} else {
|
||||||
|
r.confirmedAtBlock = block.number + 1 + blocksToWait;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vetoed) { r.vetoed = true; }
|
||||||
|
|
||||||
|
reimbursementsCount++;
|
||||||
|
|
||||||
|
emit ReimbursementAdded(reimbursementId, msg.sender, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function veto(uint32 reimbursementId) public onlyCore {
|
||||||
|
ReimbursementData storage r = reimbursements[reimbursementId];
|
||||||
|
require(r.exists, "NOT_FOUND");
|
||||||
|
require(block.number < r.confirmedAtBlock, "VETO_PERIOD_ENDED");
|
||||||
|
r.vetoed = true;
|
||||||
|
|
||||||
|
emit ReimbursementVetoed(reimbursementId, msg.sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exists(uint32 reimbursementId) public view returns (bool) {
|
||||||
|
return reimbursements[reimbursementId].exists;
|
||||||
|
}
|
||||||
|
}
|
@ -1,27 +1,41 @@
|
|||||||
pragma solidity ^0.4.18;
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
import 'zeppelin-solidity/contracts/token/ERC20/BasicToken.sol';
|
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
|
||||||
import './upgradeable/Upgradeable.sol';
|
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
interface ContributorInterface {
|
||||||
|
function getContributorAddressById(uint32 contributorId) external view returns (address);
|
||||||
|
function getContributorIdByAddress(address contributorAccount) external view returns (uint32);
|
||||||
|
function addressIsCore(address sender) external view returns (bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Token is Initializable, ERC20Upgradeable {
|
||||||
|
ContributorInterface public contributorContract;
|
||||||
|
using SafeMathUpgradeable for uint256;
|
||||||
|
|
||||||
|
address public contributorContractAddress;
|
||||||
|
|
||||||
|
event KreditsMinted(address indexed recipient, uint256 amount);
|
||||||
|
|
||||||
|
function initialize() public virtual initializer {
|
||||||
|
__ERC20_init("Kredits", "KS");
|
||||||
|
}
|
||||||
|
|
||||||
|
function decimals() public view virtual override returns (uint8) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setContributorContract(address contributor) public {
|
||||||
|
require(address(contributorContract) == address(0) || contributorContract.addressIsCore(msg.sender), "Core only");
|
||||||
|
contributorContract = ContributorInterface(contributor);
|
||||||
|
contributorContractAddress = contributor;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mintFor(address contributorAccount, uint256 amount) public {
|
||||||
|
require(contributorContractAddress == msg.sender, "Only Contributor");
|
||||||
|
require(amount > 0, "INVALID_AMOUNT");
|
||||||
|
|
||||||
|
_mint(contributorAccount, amount);
|
||||||
|
emit KreditsMinted(contributorAccount, amount);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
pragma solidity ^0.4.17;
|
|
||||||
|
|
||||||
contract Migrations {
|
|
||||||
address public owner;
|
|
||||||
uint public last_completed_migration;
|
|
||||||
|
|
||||||
modifier restricted() {
|
|
||||||
if (msg.sender == owner) _;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Migrations() 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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
83
deployments/rinkeby/contribution.md
Normal file
83
deployments/rinkeby/contribution.md
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
# Contribution deployments
|
||||||
|
|
||||||
|
aragon apm publish major --environment=rinkeby"
|
||||||
|
|
||||||
|
## 20212-01-14
|
||||||
|
|
||||||
|
apps/contribution@master » aragon apm publish major --environment=rinkeby
|
||||||
|
eth-provider | Invalid provider preset/location: "local"
|
||||||
|
✔ Start IPFS
|
||||||
|
✔ Applying version bump (major)
|
||||||
|
↓ Building frontend [skipped]
|
||||||
|
→ build script not defined in package.json
|
||||||
|
✔ Deploy contract
|
||||||
|
✔ Determine contract address for version
|
||||||
|
✔ Prepare files for publishing
|
||||||
|
✔ Generate application artifact
|
||||||
|
✔ Publish intent
|
||||||
|
|
||||||
|
⚠ Publishing files from the project's root folder is not recommended. Consider using the distribution folder of your project: "--files <folder>".
|
||||||
|
|
||||||
|
The following information will be published:
|
||||||
|
Contract address: 0x914Da982ef17B56D2e868E3a67E923EbED1aE017
|
||||||
|
Content (ipfs): QmdVrY2R48NFqwLopd8ix1anAK1d6WafDGauou3ZJrB9gf
|
||||||
|
|
||||||
|
? Publish to kredits-contribution.open.aragonpm.eth repo Yes
|
||||||
|
|
||||||
|
✔ Publish kredits-contribution.open.aragonpm.eth
|
||||||
|
|
||||||
|
Successfully published kredits-contribution.open.aragonpm.eth v7.0.0 :
|
||||||
|
|
||||||
|
Transaction hash: 0xb817b2e80e90a6be60b45dd39987498e3132c9962c0501feb7549ad30186c6d5
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 2019-04-24 update balances
|
||||||
|
|
||||||
|
✔ Successfully published kredits-contribution.open.aragonpm.eth v6.0.0:
|
||||||
|
ℹ Contract address: 0x2c083EEA83fd3a99C93759D97D0317A43261c758
|
||||||
|
ℹ Content (ipfs): QmULpSqz7BgTFmDu8AL7YZZEz525xkcEzf3dPKtbRdUtFs
|
||||||
|
ℹ Transaction hash: 0x8b01c4c00162e918659d267a2beaf33b578e2aaf9f427f1aa9a43029333c5cd7
|
||||||
|
|
||||||
|
## 2019-04-10 - Weltempfänger release
|
||||||
|
|
||||||
|
✔ Successfully published kredits-contribution.open.aragonpm.eth v5.0.0:
|
||||||
|
ℹ Contract address: 0xe0f7dB486321b917e3A986Bdb2F2b9d51BA98fa9
|
||||||
|
ℹ Content (ipfs): QmU3XEBb4f5jU8MFFEpwaa95C1mhc82UeYLRWLrKsvcQNw
|
||||||
|
ℹ Transaction hash: 0xd736ff5f79f8142be3fad1a50580fb40aa468838da397f8630285fd91a445af3
|
||||||
|
|
||||||
|
|
||||||
|
## 2019-04-04
|
||||||
|
|
||||||
|
✔ Successfully published kredits-contribution.open.aragonpm.eth v4.0.0:
|
||||||
|
ℹ Contract address: 0x7485e8fbde0112C53587079db450Eb002D359372
|
||||||
|
ℹ Content (ipfs): QmZKxYPm8wz4phgL4428Gh7MjmbyFFDMP2st7qhNL9qGDQ
|
||||||
|
ℹ Transaction hash: 0x7d9be7920db675be88e8f60ebf08dc62ee77c4e6e454c5fbf0f91f6ac97e4c26
|
||||||
|
|
||||||
|
|
||||||
|
## 2019-03-26
|
||||||
|
|
||||||
|
### v3.0.0 appids
|
||||||
|
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
|
||||||
|
|
51
deployments/rinkeby/contributor.md
Normal file
51
deployments/rinkeby/contributor.md
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# Contributor deployments
|
||||||
|
|
||||||
|
aragon apm publish major --environment=rinkeby
|
||||||
|
|
||||||
|
## 2019-04-25 canPerform fix
|
||||||
|
|
||||||
|
✔ Successfully published kredits-contributor.open.aragonpm.eth v6.0.0:
|
||||||
|
ℹ Contract address: 0xA5379D49C718845A1BD7720c6BE3872bA69906cc
|
||||||
|
ℹ Content (ipfs): QmdennNV6s2FNpe6QNYxrUsUXPVdnQGvh1vCi22Tqs8ojq
|
||||||
|
ℹ Transaction hash: 0x51077afeff70a24e87c78bb23ea13bdb9b4445bd43ea7a74a4178fadfeeb6c35
|
||||||
|
|
||||||
|
## 2019-04-24 update balances
|
||||||
|
|
||||||
|
✔ Successfully published kredits-contributor.open.aragonpm.eth v5.0.0:
|
||||||
|
ℹ Contract address: 0xadefa3b66b68a127Fe38bEa1813b844EE69CFD86
|
||||||
|
ℹ Content (ipfs): QmeygbQgoj2McLWzo9hJayLWuBZqFaK4HTpa5qLeQdkn5K
|
||||||
|
ℹ Transaction hash: 0x4237a9636f6e4a8190e0d5bcfa85a452da097bf654a173a88e0e1de3d078f08d
|
||||||
|
|
||||||
|
|
||||||
|
## 2019-04-10 - Weltempfänger release
|
||||||
|
|
||||||
|
✔ Successfully published kredits-contributor.open.aragonpm.eth v4.0.0:
|
||||||
|
ℹ Contract address: 0x08a6D4D915FCAA5524F05F5F715a6C17cB6eeA6B
|
||||||
|
ℹ Content (ipfs): QmR62PWwe1EzommfkhJDYcTvHoZjbXuv9dTG6vCn5dWCsb
|
||||||
|
ℹ Transaction hash: 0xd5317c9e207a413485c55ec3046b09d467d978443680304737a6d7d3db0c90e1
|
||||||
|
|
||||||
|
|
||||||
|
## 2019-04-04
|
||||||
|
|
||||||
|
✔ Successfully published kredits-contributor.open.aragonpm.eth v3.0.0:
|
||||||
|
ℹ Contract address: 0x95D8458E28C8de7216279512601A3B9Dc512D70B
|
||||||
|
ℹ Content (ipfs): QmSpHnmsf8FybvqfD49kwxvDixwiBUR7D5yG4BWSnnwCPS
|
||||||
|
ℹ Transaction hash: 0x736f9751b07a91178f453c279ecb57604b3b5686ae5d7115880f3b27fbeb50f2
|
||||||
|
|
||||||
|
## 2019-03-26
|
||||||
|
|
||||||
|
### v2.0.0 support for appids
|
||||||
|
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
|
||||||
|
|
68
deployments/rinkeby/dao.md
Normal file
68
deployments/rinkeby/dao.md
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
# Kredits deployment
|
||||||
|
|
||||||
|
|
||||||
|
## 2021-01-14
|
||||||
|
|
||||||
|
apps/contribution@master » aragon dao upgrade 0xc34edf7d11b7f8433d597f0bb0697acdff55ef14 kredits-contribution.open.aragonpm.eth --environment=rinkeby
|
||||||
|
eth-provider | Invalid provider preset/location: "local"
|
||||||
|
✔ Fetching kredits-contribution.open.aragonpm.eth@latest
|
||||||
|
✔ Fetching kredits-contribution.open.aragonpm.eth@latest
|
||||||
|
✔ Upgrading app
|
||||||
|
|
||||||
|
✔ Successfully executed: "Upgrade 'kredits-contribution.open.aragonpm.eth' app instances to v7.0.0"
|
||||||
|
|
||||||
|
## 2019-04-25 canPerfom fix
|
||||||
|
|
||||||
|
aragon dao upgrade 0xc34edf7d11b7f8433d597f0bb0697acdff55ef14 kredits-contributor.open.aragonpm.eth --environment=rinkeby
|
||||||
|
✔ Fetching kredits-contributor.open.aragonpm.eth@latest
|
||||||
|
✔ Upgrading app
|
||||||
|
✔ Successfully executed: "Set the resolving address of 'kredits-contributor.open.aragonpm.eth' in namespace 'App code' to 0xA5379D49C718845A1BD7720c6BE3872bA69906cc"
|
||||||
|
|
||||||
|
## 2019-04-24 upgrade contributor and contribution
|
||||||
|
|
||||||
|
aragon dao upgrade 0xc34edf7d11b7f8433d597f0bb0697acdff55ef14 kredits-contributor.open.aragonpm.eth --environment=rinkeby
|
||||||
|
eth-provider | Invalid provider preset/location: "local"
|
||||||
|
✔ Fetching kredits-contributor.open.aragonpm.eth@latest
|
||||||
|
✔ Upgrading app
|
||||||
|
✔ Successfully executed: "Set the resolving address of 'kredits-contributor.open.aragonpm.eth' in namespace 'App code' to 0xadefa3b66b68a127Fe38bEa1813b844EE69CFD86"
|
||||||
|
|
||||||
|
aragon dao upgrade 0xc34edf7d11b7f8433d597f0bb0697acdff55ef14 kredits-contribution.open.aragonpm.eth --environment=rinkeby
|
||||||
|
✔ Fetching kredits-contribution.open.aragonpm.eth@latest
|
||||||
|
✔ Upgrading app
|
||||||
|
✔ Successfully executed: "Set the resolving address of 'kredits-contribution.open.aragonpm.eth' in namespace 'App code' to 0x2c083EEA83fd3a99C93759D97D0317A43261c758"
|
||||||
|
|
||||||
|
## 2019-04-10 - Weltempfänger release
|
||||||
|
|
||||||
|
Using KreditsKit at: 0x76e069b47b79442657eaf0555a32c6b16fa1b8b4
|
||||||
|
Created new DAO at: 0xc34edf7d11b7f8433d597f0bb0697acdff55ef14
|
||||||
|
|
||||||
|
## 2019-04-04
|
||||||
|
|
||||||
|
Using KreditsKit at: 0x76e069b47b79442657eaf0555a32c6b16fa1b8b4
|
||||||
|
Created new DAO at: 0xcd75458fbc4aa2231252d5b21f1391fd031e5cb2
|
||||||
|
|
||||||
|
### 2019-03-26
|
||||||
|
|
||||||
|
kredits/truffle-kredits@aragonos » truffle exec scripts/new-dao.js --network=rinkeby
|
||||||
|
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
|
62
deployments/rinkeby/kreditskit.md
Normal file
62
deployments/rinkeby/kreditskit.md
Normal 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"
|
||||||
|
|
43
deployments/rinkeby/proposal.md
Normal file
43
deployments/rinkeby/proposal.md
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# Proposal deployments
|
||||||
|
|
||||||
|
## 2019-04-10 - Weltempfänger release
|
||||||
|
|
||||||
|
✔ Successfully published kredits-proposal.open.aragonpm.eth v5.0.0:
|
||||||
|
ℹ Contract address: 0x4ce5b0286483c66b861e5599a199054687434552
|
||||||
|
ℹ Content (ipfs): QmNYXEcmvKTGxYiob7WUf85oZhdmFDCuGiA6TsRrDE9TYb
|
||||||
|
ℹ Transaction hash: 0x0482b58a1ba87d494c6391026399d0ac41b45384330d916f3f99ba70e501584b
|
||||||
|
|
||||||
|
|
||||||
|
## 2019-04-04
|
||||||
|
|
||||||
|
✔ Successfully published kredits-proposal.open.aragonpm.eth v4.0.0:
|
||||||
|
ℹ Contract address: 0x4993275362Ba50D76f349A262B7b842e7FbD7490
|
||||||
|
ℹ Content (ipfs): QmesCPKLapASPTB3rnPpSdD7ULkg8BQwRcpdE1B8WwfSh7
|
||||||
|
ℹ Transaction hash: 0xe4be7b1a75b663eb345b957b216a8476b0f75e2bbfc5880943b05c82fab15dff
|
||||||
|
|
||||||
|
|
||||||
|
## 2019-03-26
|
||||||
|
|
||||||
|
### v3.0.0 appids
|
||||||
|
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
|
34
deployments/rinkeby/token.md
Normal file
34
deployments/rinkeby/token.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# Token deployments
|
||||||
|
|
||||||
|
## 2019-04-10 - Weltempfänger release
|
||||||
|
|
||||||
|
✔ Successfully published kredits-token.open.aragonpm.eth v4.0.0:
|
||||||
|
ℹ Contract address: 0x05E0C2bbdA8e5BeE22AC1E20C1457dA4de63aE26
|
||||||
|
ℹ Content (ipfs): QmUuYLRMRNZcundUk2pxVaSjMNvtB7hzdf3eyoaUNqDPow
|
||||||
|
ℹ Transaction hash: 0x98c28b5ca645904d56eb83c4783682f018c0fcee015b3a3d5fa8bd609223fb89
|
||||||
|
|
||||||
|
|
||||||
|
## 2019-04-04
|
||||||
|
|
||||||
|
✔ Successfully published kredits-token.open.aragonpm.eth v3.0.0:
|
||||||
|
ℹ Contract address: 0xd9913A96e087f50E71BF14c62cBCa3b9635392A9
|
||||||
|
ℹ Content (ipfs): QmfZ7LwjkQRuLsEnVjrEF3ryTaHsL6YTxffiiDS5KWmHQG
|
||||||
|
ℹ Transaction hash: 0x102b27e154d1e144ec084f5e8dc78e1626fda9f26c20db3e3b230fac381c317a
|
||||||
|
|
||||||
|
|
||||||
|
## 2019-03-26
|
||||||
|
|
||||||
|
### v2.0.0 support for appIds
|
||||||
|
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
BIN
docs/kredits-diagram.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
90
hardhat.config.js
Normal file
90
hardhat.config.js
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
require("@nomiclabs/hardhat-waffle");
|
||||||
|
require("hardhat-deploy");
|
||||||
|
require("hardhat-deploy-ethers");
|
||||||
|
require("@nomicfoundation/hardhat-chai-matchers");
|
||||||
|
require("@openzeppelin/hardhat-upgrades");
|
||||||
|
const Kredits = require("./lib/kredits");
|
||||||
|
|
||||||
|
const promptly = require("promptly");
|
||||||
|
|
||||||
|
extendEnvironment(async (hre) => {
|
||||||
|
hre.kredits = new Kredits(
|
||||||
|
hre.ethers.provider,
|
||||||
|
hre.ethers.provider.getSigner()
|
||||||
|
);
|
||||||
|
await hre.kredits.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
// This is a sample Hardhat task. To learn how to create your own go to
|
||||||
|
// https://hardhat.org/guides/create-task.html
|
||||||
|
task("accounts", "Prints the list of accounts", async () => {
|
||||||
|
const accounts = await ethers.getSigners();
|
||||||
|
|
||||||
|
for (const account of accounts) {
|
||||||
|
console.log(account.address);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
task("fund", "Send eth to an address", async () => {
|
||||||
|
const to = await promptly.prompt("Address:");
|
||||||
|
const value = await promptly.prompt("Value:");
|
||||||
|
|
||||||
|
const signer = await ethers.getSigners();
|
||||||
|
|
||||||
|
const fundTransaction = await signer[0].sendTransaction({
|
||||||
|
to: to,
|
||||||
|
value: ethers.utils.parseEther(value),
|
||||||
|
});
|
||||||
|
console.log(fundTransaction);
|
||||||
|
});
|
||||||
|
|
||||||
|
task("create-wallet", "Creates a new wallet json", async () => {
|
||||||
|
const wallet = ethers.Wallet.createRandom();
|
||||||
|
|
||||||
|
console.log("New wallet:");
|
||||||
|
console.log(`Address: ${wallet.address}`);
|
||||||
|
console.log(`Public key: ${wallet.publicKey}`);
|
||||||
|
console.log(`Private key: ${wallet.privateKey}`);
|
||||||
|
console.log(`Mnemonic: ${JSON.stringify(wallet.mnemonic)}`);
|
||||||
|
|
||||||
|
const password = await promptly.prompt("Encryption password: ");
|
||||||
|
const encryptedJSON = await wallet.encrypt(password);
|
||||||
|
|
||||||
|
console.log("Encrypted wallet JSON:");
|
||||||
|
console.log(encryptedJSON);
|
||||||
|
});
|
||||||
|
|
||||||
|
// You need to export an object to set up your config
|
||||||
|
// Go to https://hardhat.org/config/ to learn more
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type import('hardhat/config').HardhatUserConfig
|
||||||
|
*/
|
||||||
|
module.exports = {
|
||||||
|
solidity: "0.8.2",
|
||||||
|
defaultNetwork: "localhost",
|
||||||
|
networks: {
|
||||||
|
hardhat: {
|
||||||
|
chainId: 1337,
|
||||||
|
},
|
||||||
|
rinkeby: {
|
||||||
|
url: "https://rinkeby.infura.io/v3/2e73045db2e84711912f8d0e5968f309",
|
||||||
|
accounts: [
|
||||||
|
process.env.DEPLOY_KEY ||
|
||||||
|
"0xffb4230bdf9b1f1dd48f0bc54e4007436733f225a4f163d4f7e58e620ae329eb",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
rsk: {
|
||||||
|
url: "https://rsk-testnet.kosmos.org",
|
||||||
|
accounts: [
|
||||||
|
process.env.DEPLOY_KEY ||
|
||||||
|
"0xffb4230bdf9b1f1dd48f0bc54e4007436733f225a4f163d4f7e58e620ae329eb",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
namedAccounts: {
|
||||||
|
deployer: {
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
1
lib/abis/Contribution.json
Normal file
1
lib/abis/Contribution.json
Normal file
File diff suppressed because one or more lines are too long
1
lib/abis/Contributor.json
Normal file
1
lib/abis/Contributor.json
Normal file
File diff suppressed because one or more lines are too long
@ -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 +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 +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"}]
|
|
1
lib/abis/Reimbursement.json
Normal file
1
lib/abis/Reimbursement.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"id","type":"uint32"},{"indexed":true,"internalType":"address","name":"addedByAccount","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ReimbursementAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"id","type":"uint32"},{"indexed":false,"internalType":"address","name":"vetoedByAccount","type":"address"}],"name":"ReimbursementVetoed","type":"event"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint32","name":"recipientId","type":"uint32"},{"internalType":"bytes32","name":"hashDigest","type":"bytes32"},{"internalType":"uint8","name":"hashFunction","type":"uint8"},{"internalType":"uint8","name":"hashSize","type":"uint8"},{"internalType":"uint256","name":"confirmedAtBlock","type":"uint256"},{"internalType":"bool","name":"vetoed","type":"bool"}],"name":"add","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"blocksToWait","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"contributorContract","outputs":[{"internalType":"contract ContributorInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deployer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"reimbursementId","type":"uint32"}],"name":"exists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"finishMigration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"reimbursementId","type":"uint32"}],"name":"get","outputs":[{"internalType":"uint32","name":"id","type":"uint32"},{"internalType":"uint32","name":"recipientId","type":"uint32"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"bytes32","name":"hashDigest","type":"bytes32"},{"internalType":"uint8","name":"hashFunction","type":"uint8"},{"internalType":"uint8","name":"hashSize","type":"uint8"},{"internalType":"uint256","name":"confirmedAtBlock","type":"uint256"},{"internalType":"bool","name":"exists","type":"bool"},{"internalType":"bool","name":"vetoed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"contributorId","type":"uint32"}],"name":"getContributorAddressById","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"contributorAccount","type":"address"}],"name":"getContributorIdByAddress","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"migrationDone","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"reimbursements","outputs":[{"internalType":"uint32","name":"recipientId","type":"uint32"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"bytes32","name":"hashDigest","type":"bytes32"},{"internalType":"uint8","name":"hashFunction","type":"uint8"},{"internalType":"uint8","name":"hashSize","type":"uint8"},{"internalType":"uint256","name":"confirmedAtBlock","type":"uint256"},{"internalType":"bool","name":"vetoed","type":"bool"},{"internalType":"bool","name":"exists","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reimbursementsCount","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"contributor","type":"address"}],"name":"setContributorContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"confirmedOnly","type":"bool"}],"name":"totalAmount","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"reimbursementId","type":"uint32"}],"name":"veto","outputs":[],"stateMutability":"nonpayable","type":"function"}]
|
@ -1 +1 @@
|
|||||||
[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"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":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"recipient","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"proposalId","type":"uint256"}],"name":"LogMint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"constant":false,"inputs":[{"name":"sender","type":"address"}],"name":"initialize","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"contributorAccount","type":"address"},{"name":"amount","type":"uint256"},{"name":"proposalId","type":"uint256"}],"name":"mintFor","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]
|
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"KreditsMinted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"contributorContract","outputs":[{"internalType":"contract ContributorInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"contributorContractAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"contributorAccount","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mintFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"contributor","type":"address"}],"name":"setContributorContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]
|
14
lib/addresses.json
Normal file
14
lib/addresses.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"31": {
|
||||||
|
"Contributor": "0x95DC31665D193E377f54b70C535fcDb205525291",
|
||||||
|
"Contribution": "0x049bA8E70FEbFfd6d03C71211bDA37B4ff064115",
|
||||||
|
"Token": "0x7ab26A0f00eF0D6e05e5BDE047505a4eD53aF809",
|
||||||
|
"Reimbursement": "0x99EC72b34295b62f4bC1527Da461262c615a0b2c"
|
||||||
|
},
|
||||||
|
"1337": {
|
||||||
|
"Contributor": "0xCc66f9A3cA2670972938FAD91d0865c4a62DFB25",
|
||||||
|
"Contribution": "0x8999CaBc43E28202c5A2257f2a95A45b1F8A62BD",
|
||||||
|
"Token": "0xe082678eCF749982e33Ea6839852a8cd989aEDE2",
|
||||||
|
"Reimbursement": "0x984f797d26d3da2E9b9f8Ae4eeFEACC60fCAA90C"
|
||||||
|
}
|
||||||
|
}
|
@ -1 +0,0 @@
|
|||||||
{"42":"0x205fe1b3dac678b594c5f0535e7d158e38591f93","100":"0xa7fc9b1f678c41396b53904f94f50a42ff44d826"}
|
|
3
lib/addresses/KreditsKit.json
Normal file
3
lib/addresses/KreditsKit.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"4": "0x76e069b47b79442657eaf0555a32c6b16fa1b8b4"
|
||||||
|
}
|
@ -1 +0,0 @@
|
|||||||
{"42":"0x9fd66ee78a5ebe86006f12b37ff59c63f9caa15b","100":"0x95d3bd7d136bb0b7ac9988097e964236f8a9976e"}
|
|
@ -1 +0,0 @@
|
|||||||
{"42":"0xc270e6ea4fe303df9f1a3d4a132ac425264082e7","100":"0x7458dea485d9d8301e3ce43e8a1ec1456be5ba83"}
|
|
@ -1 +0,0 @@
|
|||||||
{"42":"0xf71ccf7ab48044ef9ae0b5e6983dbd3266b78b36","100":"0x3fc29fbe40c2d0ca78c7e81342f00226650fe2ad"}
|
|
3
lib/addresses/dao.json
Normal file
3
lib/addresses/dao.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"4": "0xc34edf7d11b7f8433d597f0bb0697acdff55ef14"
|
||||||
|
}
|
@ -1,27 +1,22 @@
|
|||||||
|
const deprecate = require('../utils/deprecate');
|
||||||
|
|
||||||
class Base {
|
class Base {
|
||||||
constructor(contract) {
|
constructor (contract) {
|
||||||
this.contract = contract;
|
this.contract = contract;
|
||||||
}
|
}
|
||||||
|
|
||||||
get functions() {
|
get functions () {
|
||||||
return this.contract.functions;
|
deprecate('The property `functions` is deprecated. contract functions are now directly defined on the ethers contract object. https://github.com/ethers-io/ethers.js/issues/920#issuecomment-650836642');
|
||||||
|
return this.contract;
|
||||||
}
|
}
|
||||||
|
|
||||||
get ipfs() {
|
get address () {
|
||||||
if (!this._ipfsAPI) { throw new Error('IPFS API not configured; please set an ipfs instance'); }
|
return this.contract.address;
|
||||||
return this._ipfsAPI;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set ipfs(ipfsAPI) {
|
on (type, callback) {
|
||||||
this._ipfsAPI = ipfsAPI;
|
return this.contract.on(type, callback);
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Base;
|
module.exports = Base;
|
||||||
|
76
lib/contracts/contribution.js
Normal file
76
lib/contracts/contribution.js
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
const Record = require('./record');
|
||||||
|
const ContributionSerializer = require('../serializers/contribution');
|
||||||
|
const deprecate = require('../utils/deprecate');
|
||||||
|
|
||||||
|
class Contribution extends Record {
|
||||||
|
get count () {
|
||||||
|
return this.contract.contributionsCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
getById (id) {
|
||||||
|
return this.contract.getContribution(id)
|
||||||
|
.then(data => {
|
||||||
|
return this.ipfs.catAndMerge(data, ContributionSerializer.deserialize);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getData (id) {
|
||||||
|
return this.contract.getContribution(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
getByContributorId (contributorId) {
|
||||||
|
return this.contract.getContributorAddressById(contributorId)
|
||||||
|
.then(address => this.getByContributorAddress(address));
|
||||||
|
}
|
||||||
|
|
||||||
|
getByContributorAddress (address) {
|
||||||
|
return this.contract.balanceOf(address)
|
||||||
|
.then(async (balance) => {
|
||||||
|
const count = balance.toNumber();
|
||||||
|
const contributions = [];
|
||||||
|
|
||||||
|
for (let index = 0; index < count; index++) {
|
||||||
|
const id = await this.contract.tokenOfOwnerByIndex(address, index);
|
||||||
|
const contribution = await this.getById(id);
|
||||||
|
contributions.push(contribution);
|
||||||
|
}
|
||||||
|
|
||||||
|
return contributions;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async add (contributionAttr, callOptions = {}) {
|
||||||
|
const contribution = new ContributionSerializer(contributionAttr);
|
||||||
|
const confirmedAtBlock = contributionAttr.confirmedAtBlock || 0;
|
||||||
|
const vetoed = contributionAttr.vetoed || false;
|
||||||
|
|
||||||
|
try { await contribution.validate(); }
|
||||||
|
catch (error) { return Promise.reject(error); }
|
||||||
|
|
||||||
|
const jsonStr = contribution.serialize();
|
||||||
|
|
||||||
|
return this.ipfs
|
||||||
|
.add(jsonStr)
|
||||||
|
.then(ipfsHashAttr => {
|
||||||
|
let contribution = [
|
||||||
|
contributionAttr.amount,
|
||||||
|
contributionAttr.contributorId,
|
||||||
|
ipfsHashAttr.hashDigest,
|
||||||
|
ipfsHashAttr.hashFunction,
|
||||||
|
ipfsHashAttr.hashSize,
|
||||||
|
confirmedAtBlock,
|
||||||
|
vetoed,
|
||||||
|
];
|
||||||
|
|
||||||
|
return this.contract.add(...contribution, callOptions);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addContribution () {
|
||||||
|
deprecate('The function `addContribution()` is deprecated and will be removed in the next major version. Use `add()` instead');
|
||||||
|
return this.add(...arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Contribution;
|
@ -1,57 +1,100 @@
|
|||||||
const ethers = require('ethers');
|
const Record = require('./record');
|
||||||
const RSVP = require('rsvp');
|
|
||||||
|
|
||||||
const ContributorSerializer = require('../serializers/contributor');
|
const ContributorSerializer = require('../serializers/contributor');
|
||||||
const Base = require('./base');
|
const formatKredits = require('../utils/format-kredits');
|
||||||
|
|
||||||
class Contributor extends Base {
|
class Contributor extends Record {
|
||||||
all() {
|
get count () {
|
||||||
return this.functions.contributorsCount()
|
return this.contract.contributorsCount();
|
||||||
.then((count) => {
|
|
||||||
count = count.toNumber();
|
|
||||||
let contributors = [];
|
|
||||||
|
|
||||||
for (let id = 1; id <= count; id++) {
|
|
||||||
contributors.push(this.getById(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
return RSVP.all(contributors);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getById(id) {
|
getById (id) {
|
||||||
id = ethers.utils.bigNumberify(id);
|
return this.contract.getContributorById(id)
|
||||||
|
.then(contractData => {
|
||||||
return this.functions.getContributorById(id)
|
let data = {...contractData};
|
||||||
.then((data) => {
|
data.balanceInt = formatKredits(data.balance);
|
||||||
// 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);
|
return this.ipfs.catAndMerge(data, ContributorSerializer.deserialize);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
add(contributorAttr, callOptions = {}) {
|
getData (id) {
|
||||||
let json = ContributorSerializer.serialize(contributorAttr);
|
return this.contract.getContributorById(id);
|
||||||
// TODO: validate against schema
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async add (contributorAttr, callOptions = {}) {
|
||||||
|
let contributor = new ContributorSerializer(contributorAttr);
|
||||||
|
|
||||||
|
try { await contributor.validate(); }
|
||||||
|
catch (error) { return Promise.reject(error); }
|
||||||
|
|
||||||
|
const jsonStr = contributor.serialize();
|
||||||
|
|
||||||
|
// console.log('Adding IPFS doc for', contributorAttr.account);
|
||||||
return this.ipfs
|
return this.ipfs
|
||||||
.add(json)
|
.add(jsonStr)
|
||||||
.then((ipfsHashAttr) => {
|
.then((ipfsHashAttr) => {
|
||||||
let contributor = [
|
let contributor = [
|
||||||
contributorAttr.account,
|
contributorAttr.account,
|
||||||
ipfsHashAttr.hashDigest,
|
ipfsHashAttr.hashDigest,
|
||||||
ipfsHashAttr.hashFunction,
|
ipfsHashAttr.hashFunction,
|
||||||
ipfsHashAttr.hashSize,
|
ipfsHashAttr.hashSize,
|
||||||
contributorAttr.isCore,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return this.functions.addContributor(...contributor, callOptions);
|
// console.log('Adding onchain record for', contributorAttr.account);
|
||||||
|
return this.contract.addContributor(...contributor, callOptions);
|
||||||
|
}).catch(err => {
|
||||||
|
console.log('Failed to add IPFS document:', err.message);
|
||||||
|
throw(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateProfile (contributorId, updateAttr, callOptions = {}) {
|
||||||
|
return this.getById(contributorId).then(async (contributor) => {
|
||||||
|
let updatedContributorAttr = Object.assign(contributor, updateAttr);
|
||||||
|
let updatedContributor = new ContributorSerializer(updatedContributorAttr);
|
||||||
|
|
||||||
|
try { await updatedContributor.validate(); }
|
||||||
|
catch (error) { return Promise.reject(error); }
|
||||||
|
|
||||||
|
const jsonStr = updatedContributor.serialize();
|
||||||
|
|
||||||
|
return this.ipfs
|
||||||
|
.add(jsonStr)
|
||||||
|
.then(ipfsHashAttr => {
|
||||||
|
return this.contract.updateContributorProfileHash(
|
||||||
|
contributorId,
|
||||||
|
ipfsHashAttr.hashDigest,
|
||||||
|
ipfsHashAttr.hashFunction,
|
||||||
|
ipfsHashAttr.hashSize,
|
||||||
|
callOptions
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Contributor;
|
module.exports = Contributor;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
Contributors: require('./contributor'),
|
Contributor: require('./contributor'),
|
||||||
Operator: require('./operator'),
|
Contribution: require('./contribution'),
|
||||||
|
Proposal: require('./proposal'),
|
||||||
Token: require('./token'),
|
Token: require('./token'),
|
||||||
Registry: require('./registry')
|
Reimbursement: require('./reimbursement'),
|
||||||
};
|
};
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
const ethers = require('ethers');
|
|
||||||
const RSVP = require('rsvp');
|
|
||||||
|
|
||||||
const ContributionSerializer = require('../serializers/contribution');
|
|
||||||
const Base = require('./base');
|
|
||||||
|
|
||||||
class Operator extends Base {
|
|
||||||
all() {
|
|
||||||
return this.functions.proposalsCount()
|
|
||||||
.then((count) => {
|
|
||||||
count = count.toNumber();
|
|
||||||
let proposals = [];
|
|
||||||
|
|
||||||
for (let id = 1; id <= count; id++) {
|
|
||||||
proposals.push(this.getById(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
return RSVP.all(proposals);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getById(id) {
|
|
||||||
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) => {
|
|
||||||
return this.ipfs.catAndMerge(data, ContributionSerializer.deserialize);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
addProposal(proposalAttr, callOptions = {}) {
|
|
||||||
let json = ContributionSerializer.serialize(proposalAttr);
|
|
||||||
// TODO: validate against schema
|
|
||||||
|
|
||||||
return this.ipfs
|
|
||||||
.add(json)
|
|
||||||
.then((ipfsHashAttr) => {
|
|
||||||
let proposal = [
|
|
||||||
proposalAttr.contributorId,
|
|
||||||
proposalAttr.amount,
|
|
||||||
ipfsHashAttr.hashDigest,
|
|
||||||
ipfsHashAttr.hashFunction,
|
|
||||||
ipfsHashAttr.hashSize,
|
|
||||||
];
|
|
||||||
|
|
||||||
return this.functions.addProposal(...proposal, callOptions);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Operator;
|
|
46
lib/contracts/proposal.js
Normal file
46
lib/contracts/proposal.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
const Record = require('./record');
|
||||||
|
const ContributionSerializer = require('../serializers/contribution');
|
||||||
|
const deprecate = require('../utils/deprecate');
|
||||||
|
|
||||||
|
class Proposal extends Record {
|
||||||
|
get count () {
|
||||||
|
return this.contract.proposalsCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
getById (id) {
|
||||||
|
return this.contract.getProposal(id)
|
||||||
|
.then(data => {
|
||||||
|
return this.ipfs.catAndMerge(data, ContributionSerializer.deserialize);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async add (proposalAttr, callOptions = {}) {
|
||||||
|
const contribution = new ContributionSerializer(proposalAttr);
|
||||||
|
|
||||||
|
try { await contribution.validate(); }
|
||||||
|
catch (error) { return Promise.reject(error); }
|
||||||
|
|
||||||
|
const jsonStr = contribution.serialize();
|
||||||
|
|
||||||
|
return this.ipfs
|
||||||
|
.add(jsonStr)
|
||||||
|
.then((ipfsHashAttr) => {
|
||||||
|
let proposal = [
|
||||||
|
proposalAttr.contributorId,
|
||||||
|
proposalAttr.amount,
|
||||||
|
ipfsHashAttr.hashDigest,
|
||||||
|
ipfsHashAttr.hashFunction,
|
||||||
|
ipfsHashAttr.hashSize,
|
||||||
|
];
|
||||||
|
|
||||||
|
return this.contract.addProposal(...proposal, callOptions);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addProposal () {
|
||||||
|
deprecate('The function `addProposal()` is deprecated and will be removed in the next major version. Use `add()` instead');
|
||||||
|
return this.add(...arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Proposal;
|
14
lib/contracts/record.js
Normal file
14
lib/contracts/record.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
const Base = require('./base');
|
||||||
|
const paged = require('../utils/pagination');
|
||||||
|
|
||||||
|
class Record extends Base {
|
||||||
|
all (options = {}) {
|
||||||
|
return this.count
|
||||||
|
.then((count) => {
|
||||||
|
let records = paged(count, options).map((id) => this.getById(id));
|
||||||
|
return Promise.all(records);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Record;
|
@ -1,6 +0,0 @@
|
|||||||
const Base = require('./base');
|
|
||||||
|
|
||||||
class Registry extends Base {
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Registry;
|
|
71
lib/contracts/reimbursement.js
Normal file
71
lib/contracts/reimbursement.js
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
const Record = require("./record");
|
||||||
|
const ExpenseSerializer = require("../serializers/expense");
|
||||||
|
|
||||||
|
class Reimbursement extends Record {
|
||||||
|
get count () {
|
||||||
|
return this.contract.reimbursementsCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
getById (id) {
|
||||||
|
return this.contract.get(id).then((data) => {
|
||||||
|
return this.ipfs.catAndMerge(data, (ipfsDocument) => {
|
||||||
|
const expenses = JSON.parse(ipfsDocument);
|
||||||
|
return { expenses };
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getData (id) {
|
||||||
|
return this.contract.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async add (attrs, callOptions = {}) {
|
||||||
|
const amount = parseInt(attrs.amount);
|
||||||
|
const token = attrs.token;
|
||||||
|
const recipientId = attrs.recipientId;
|
||||||
|
const confirmedAtBlock = attrs.confirmedAtBlock || 0;
|
||||||
|
const vetoed = attrs.vetoed || false;
|
||||||
|
const expenses = attrs.expenses.map((e) => new ExpenseSerializer(e));
|
||||||
|
let errorMessage;
|
||||||
|
|
||||||
|
if (typeof amount !== "number" || amount <= 0) {
|
||||||
|
errorMessage = "Invalid data: amount must be a positive number.";
|
||||||
|
}
|
||||||
|
if (!token || token === "") {
|
||||||
|
errorMessage = "Invalid data: token must be a token address.";
|
||||||
|
}
|
||||||
|
if (!recipientId || recipientId === "") {
|
||||||
|
errorMessage = "Invalid data: recipientId is required.";
|
||||||
|
}
|
||||||
|
if (expenses.length === 0) {
|
||||||
|
errorMessage = "Invalid data: at least one expense item is required.";
|
||||||
|
}
|
||||||
|
if (errorMessage) {
|
||||||
|
return Promise.reject(new Error(errorMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all(expenses.map((e) => e.validate())).then(() => {
|
||||||
|
const jsonStr = JSON.stringify(
|
||||||
|
expenses.map((e) => e.data),
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
return this.ipfs.add(jsonStr).then((ipfsHashAttr) => {
|
||||||
|
const reimbursement = [
|
||||||
|
amount,
|
||||||
|
token,
|
||||||
|
parseInt(recipientId),
|
||||||
|
ipfsHashAttr.hashDigest,
|
||||||
|
ipfsHashAttr.hashFunction,
|
||||||
|
ipfsHashAttr.hashSize,
|
||||||
|
confirmedAtBlock,
|
||||||
|
vetoed,
|
||||||
|
];
|
||||||
|
|
||||||
|
return this.contract.add(...reimbursement, callOptions);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Reimbursement;
|
@ -4,4 +4,3 @@ class Token extends Base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Token;
|
module.exports = Token;
|
||||||
|
|
||||||
|
117
lib/kredits.js
117
lib/kredits.js
@ -1,78 +1,113 @@
|
|||||||
const ethers = require('ethers');
|
const ethers = require('ethers');
|
||||||
const RSVP = require('rsvp');
|
|
||||||
|
|
||||||
const Healthcheck = require('./utils/healthcheck');
|
const Preflight = require('./utils/preflight');
|
||||||
|
const deprecate = require('./utils/deprecate');
|
||||||
|
|
||||||
const ABIS = {
|
const ABIS = {
|
||||||
Contributors: require('./abis/Contributors.json'),
|
Contributor: require('./abis/Contributor.json'),
|
||||||
Operator: require('./abis/Operator.json'),
|
Contribution: require('./abis/Contribution.json'),
|
||||||
Registry: require('./abis/Registry.json'),
|
Reimbursement: require('./abis/Reimbursement.json'),
|
||||||
Token: require('./abis/Token.json')
|
Token: require('./abis/Token.json'),
|
||||||
};
|
};
|
||||||
const RegistryAddress = require('./addresses/Registry.json');
|
// const APP_CONTRACTS = [
|
||||||
|
// 'Contributor',
|
||||||
|
// 'Contribution',
|
||||||
|
// 'Token',
|
||||||
|
// 'Reimbursement',
|
||||||
|
// ];
|
||||||
|
const Addresses = require('./addresses.json');
|
||||||
|
|
||||||
const Contracts = require('./contracts');
|
const Contracts = require('./contracts');
|
||||||
const IPFS = require('./utils/ipfs')
|
const IPFS = require('./utils/ipfs');
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
function capitalize(word) {
|
function capitalize (word) {
|
||||||
let [first, ...rest] = word;
|
let [first, ...rest] = word;
|
||||||
return `${first.toUpperCase()}${rest.join('')}`;
|
return `${first.toUpperCase()}${rest.join('')}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Kredits {
|
class Kredits {
|
||||||
|
|
||||||
constructor(provider, signer, options = {}) {
|
constructor (provider, signer, options = {}) {
|
||||||
let { addresses, abis, ipfsConfig } = options;
|
const { addresses, abis, ipfsConfig } = options;
|
||||||
|
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this.signer = signer;
|
this.signer = signer;
|
||||||
// by default we only need the registry address.
|
this.options = options;
|
||||||
// the rest is loaded from there in the init() function
|
this.addresses = addresses || {};
|
||||||
this.addresses = addresses || { Registry: RegistryAddress[this.provider.chainId.toString()] }; // chainID must be a string
|
|
||||||
this.abis = abis || ABIS;
|
this.abis = abis || ABIS;
|
||||||
this.ipfs = new IPFS(ipfsConfig);
|
this.ipfs = new IPFS(ipfsConfig);
|
||||||
this.contracts = {};
|
this.contracts = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
init(names) {
|
init (/* names */) {
|
||||||
let contractsToLoad = names || Object.keys(ABIS);
|
// TODO implement
|
||||||
let addressPromises = contractsToLoad.map((contractName) => {
|
// const contractsToLoad = names || APP_CONTRACTS;
|
||||||
return this.Registry.functions.getProxyFor(contractName).then((address) => {
|
|
||||||
this.addresses[contractName] = address;
|
return this.provider.getNetwork().then(network => {
|
||||||
}).catch((error) => {
|
if (Object.keys(this.addresses).length === 0) {
|
||||||
throw new Error(`Failed to get address for ${contractName} from registry at ${this.Registry.contract.address}
|
this.addresses = Addresses[network.chainId.toString()];
|
||||||
- correct registry? does it have version entry? - ${error.message}`
|
}
|
||||||
);
|
return this;
|
||||||
});
|
|
||||||
});
|
});
|
||||||
return RSVP.all(addressPromises).then(() => { return this });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static setup(provider, signer, ipfsConfig = null) {
|
static setup (provider, signer, ipfsConfig = null) {
|
||||||
console.log('Kredits.setup() is deprecated use new Kredits().init() instead');
|
deprecate('Kredits.setup() is deprecated use new Kredits().init() instead');
|
||||||
return new Kredits(provider, signer, { ipfsConfig: ipfsConfig }).init();
|
return new Kredits(provider, signer, { ipfsConfig: ipfsConfig }).init();
|
||||||
}
|
}
|
||||||
|
|
||||||
get Registry() {
|
static for (connectionOptions, kreditsOptions) {
|
||||||
return this.contractFor('registry');
|
let { network, rpcUrl, wallet } = connectionOptions;
|
||||||
|
if (!rpcUrl && network === 'local') { rpcUrl = 'http://localhost:8545'; }
|
||||||
|
let ethProvider, signer;
|
||||||
|
if (rpcUrl) {
|
||||||
|
ethProvider = new ethers.providers.JsonRpcProvider(rpcUrl);
|
||||||
|
} else {
|
||||||
|
ethProvider = new ethers.getDefaultProvider(network);
|
||||||
|
}
|
||||||
|
if (wallet) {
|
||||||
|
signer = wallet.connect(ethProvider);
|
||||||
|
} else if (ethProvider.getSigner) {
|
||||||
|
// Only useful for reading data, not writing. The (unused) address is
|
||||||
|
// necessary because without an address, ethers.js will try to look up
|
||||||
|
// the provider's account 0, which doesn't work on our public RSK nodes.
|
||||||
|
signer = ethProvider.getSigner('0xfa77675540E550b911a6AABF3805ac17C6641ec1');
|
||||||
|
}
|
||||||
|
return new Kredits(ethProvider, signer, kreditsOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
get Contributor() {
|
static availableNetworks () {
|
||||||
// TODO: rename to contributor
|
return Object.keys(Addresses);
|
||||||
return this.contractFor('contributors');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get Operator() {
|
get Contributor () {
|
||||||
return this.contractFor('operator');
|
return this.contractFor('Contributor');
|
||||||
}
|
}
|
||||||
|
|
||||||
get Token() {
|
get Contributors () {
|
||||||
return this.contractFor('token');
|
deprecate('Contributors is deprecated use Contributor instead');
|
||||||
|
return this.contractFor('Contributor');
|
||||||
|
}
|
||||||
|
|
||||||
|
get Operator () {
|
||||||
|
return this.Proposal;
|
||||||
|
}
|
||||||
|
|
||||||
|
get Token () {
|
||||||
|
return this.contractFor('Token');
|
||||||
|
}
|
||||||
|
|
||||||
|
get Contribution () {
|
||||||
|
return this.contractFor('Contribution');
|
||||||
|
}
|
||||||
|
|
||||||
|
get Reimbursement () {
|
||||||
|
return this.contractFor('Reimbursement');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should be private
|
// Should be private
|
||||||
contractFor(name) {
|
contractFor (name) {
|
||||||
if (this.contracts[name]) {
|
if (this.contracts[name]) {
|
||||||
return this.contracts[name];
|
return this.contracts[name];
|
||||||
}
|
}
|
||||||
@ -84,16 +119,16 @@ class Kredits {
|
|||||||
throw new Error(`Address or ABI not found for ${contractName}`);
|
throw new Error(`Address or ABI not found for ${contractName}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
let signerOrProvider = this.signer || this.provider;
|
const signerOrProvider = this.signer || this.provider;
|
||||||
let contract = new ethers.Contract(address, abi, signerOrProvider);
|
const contract = new ethers.Contract(address, abi, signerOrProvider);
|
||||||
this.contracts[name] = new Contracts[contractName](contract);
|
this.contracts[name] = new Contracts[contractName](contract);
|
||||||
this.contracts[name].ipfs = this.ipfs;
|
this.contracts[name].ipfs = this.ipfs;
|
||||||
|
|
||||||
return this.contracts[name];
|
return this.contracts[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
healthcheck() {
|
preflightChecks () {
|
||||||
return new Healthcheck(this).check();
|
return new Preflight(this).check();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
49
lib/kreditskit.js
Normal file
49
lib/kreditskit.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
const ethers = require('ethers');
|
||||||
|
|
||||||
|
const ABI = require('./abis/KreditsKit.json');
|
||||||
|
const Addresses = require('./addresses/KreditsKit.json');
|
||||||
|
|
||||||
|
class KreditsKit {
|
||||||
|
|
||||||
|
constructor (provider, signer, options = {}) {
|
||||||
|
let { address, abi } = options;
|
||||||
|
|
||||||
|
this.provider = provider;
|
||||||
|
this.signer = signer;
|
||||||
|
this.options = options;
|
||||||
|
this.address = address;
|
||||||
|
this.abi = abi || ABI;
|
||||||
|
}
|
||||||
|
|
||||||
|
init () {
|
||||||
|
return this.provider.getNetwork().then((network) => {
|
||||||
|
this.address = this.address || Addresses[network.chainId.toString()];
|
||||||
|
this.contract = new ethers.Contract(
|
||||||
|
this.address,
|
||||||
|
this.abi,
|
||||||
|
(this.signer || this.provider)
|
||||||
|
);
|
||||||
|
return this;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
appIdFor (contractName) {
|
||||||
|
// see appIds in KreditsKit.sol for more details
|
||||||
|
const knownContracts = ['Contribution', 'Contributor', 'Proposal', 'Reimbursement', 'Token'];
|
||||||
|
return this.contract.appIds(knownContracts.indexOf(contractName));
|
||||||
|
}
|
||||||
|
|
||||||
|
newDAO (options = {}) {
|
||||||
|
return this.contract.newInstance(options).then(transaction => {
|
||||||
|
return transaction.wait().then(result => {
|
||||||
|
const deployEvent = result.events.find(e => e.event === 'DeployInstance');
|
||||||
|
return {
|
||||||
|
daoAddress: deployEvent.args.dao,
|
||||||
|
transactionHash: transaction.hash,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = KreditsKit;
|
@ -1,19 +1,75 @@
|
|||||||
|
const schemas = require('@kosmos/schemas');
|
||||||
|
const validator = require('../utils/validator');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle serialization for JSON-LD object of the contribution, according to
|
* Serialization and validation for JSON-LD document of the contribution.
|
||||||
* https://github.com/67P/kosmos-schemas/blob/master/schemas/contribution.json
|
|
||||||
*
|
*
|
||||||
* @class
|
* @class
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
class Contribution {
|
class Contribution {
|
||||||
/**
|
|
||||||
* Deserialize JSON to object
|
constructor (attrs) {
|
||||||
*
|
Object.keys(attrs).forEach(a => this[a] = attrs[a]);
|
||||||
* @method
|
}
|
||||||
* @public
|
|
||||||
*/
|
/**
|
||||||
static deserialize(serialized) {
|
* Serialize object to JSON
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
serialize () {
|
||||||
let {
|
let {
|
||||||
|
contributorIpfsHash,
|
||||||
|
date,
|
||||||
|
time,
|
||||||
|
kind,
|
||||||
|
description,
|
||||||
|
url,
|
||||||
|
details,
|
||||||
|
} = this;
|
||||||
|
|
||||||
|
let data = {
|
||||||
|
'@context': 'https://schema.kosmos.org',
|
||||||
|
'@type': 'Contribution',
|
||||||
|
'contributor': {
|
||||||
|
'ipfs': contributorIpfsHash,
|
||||||
|
},
|
||||||
|
date,
|
||||||
|
time,
|
||||||
|
kind,
|
||||||
|
description,
|
||||||
|
'details': details || {},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (url) {
|
||||||
|
data['url'] = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write it pretty to ipfs
|
||||||
|
return JSON.stringify(data, null, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate serialized data against schema
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
validate () {
|
||||||
|
const serialized = JSON.parse(this.serialize());
|
||||||
|
const valid = validator.validate(serialized, schemas['contribution']);
|
||||||
|
return valid ? Promise.resolve() : Promise.reject(validator.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize JSON to object
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
static deserialize (serialized) {
|
||||||
|
let {
|
||||||
|
date,
|
||||||
|
time,
|
||||||
kind,
|
kind,
|
||||||
description,
|
description,
|
||||||
details,
|
details,
|
||||||
@ -21,6 +77,8 @@ class Contribution {
|
|||||||
} = JSON.parse(serialized.toString('utf8'));
|
} = JSON.parse(serialized.toString('utf8'));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
date,
|
||||||
|
time,
|
||||||
kind,
|
kind,
|
||||||
description,
|
description,
|
||||||
details,
|
details,
|
||||||
@ -29,39 +87,6 @@ class Contribution {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize object to JSON
|
|
||||||
*
|
|
||||||
* @method
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
static serialize(deserialized) {
|
|
||||||
let {
|
|
||||||
contributorIpfsHash,
|
|
||||||
kind,
|
|
||||||
description,
|
|
||||||
url,
|
|
||||||
details
|
|
||||||
} = deserialized;
|
|
||||||
|
|
||||||
let data = {
|
|
||||||
"@context": "https://schema.kosmos.org",
|
|
||||||
"@type": "Contribution",
|
|
||||||
"contributor": {
|
|
||||||
"ipfs": contributorIpfsHash
|
|
||||||
},
|
|
||||||
kind,
|
|
||||||
description,
|
|
||||||
"details": details || {}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (url) {
|
|
||||||
data["url"] = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write it pretty to ipfs
|
|
||||||
return JSON.stringify(data, null, 2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Contribution;
|
module.exports = Contribution;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
const schemas = require('@kosmos/schemas');
|
||||||
|
const validator = require('../utils/validator');
|
||||||
/**
|
/**
|
||||||
* Handle serialization for JSON-LD object of the contributor, according to
|
* Handle serialization for JSON-LD object of the contributor, according to
|
||||||
* https://github.com/67P/kosmos-schemas/blob/master/schemas/contributor.json
|
* https://github.com/67P/kosmos-schemas/blob/master/schemas/contributor.json
|
||||||
@ -6,13 +8,95 @@
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
class Contributor {
|
class Contributor {
|
||||||
/**
|
|
||||||
|
constructor (attrs) {
|
||||||
|
Object.keys(attrs).forEach(a => this[a] = attrs[a]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize object to JSON
|
||||||
|
*
|
||||||
|
* @method
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
serialize () {
|
||||||
|
let {
|
||||||
|
name,
|
||||||
|
kind,
|
||||||
|
url,
|
||||||
|
github_uid,
|
||||||
|
github_username,
|
||||||
|
gitea_username,
|
||||||
|
wiki_username,
|
||||||
|
zoom_display_name,
|
||||||
|
} = this;
|
||||||
|
|
||||||
|
let data = {
|
||||||
|
'@context': 'https://schema.kosmos.org',
|
||||||
|
'@type': 'Contributor',
|
||||||
|
kind,
|
||||||
|
name,
|
||||||
|
accounts: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
if (url) {
|
||||||
|
data['url'] = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (github_uid) {
|
||||||
|
data.accounts.push({
|
||||||
|
'site': 'github.com',
|
||||||
|
'uid': github_uid,
|
||||||
|
'username': github_username,
|
||||||
|
'url': `https://github.com/${github_username}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gitea_username) {
|
||||||
|
data.accounts.push({
|
||||||
|
'site': 'gitea.kosmos.org',
|
||||||
|
'username': gitea_username,
|
||||||
|
'url': `https://gitea.kosmos.org/${gitea_username}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wiki_username) {
|
||||||
|
data.accounts.push({
|
||||||
|
'site': 'wiki.kosmos.org',
|
||||||
|
'username': wiki_username,
|
||||||
|
'url': `https://wiki.kosmos.org/User:${wiki_username}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zoom_display_name) {
|
||||||
|
data.accounts.push({
|
||||||
|
'site': 'zoom.us',
|
||||||
|
'username': zoom_display_name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write it pretty to ipfs
|
||||||
|
return JSON.stringify(data, null, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate serialized data against schema
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
validate () {
|
||||||
|
const serialized = JSON.parse(this.serialize());
|
||||||
|
const valid = validator.validate(serialized, schemas['contributor']);
|
||||||
|
return valid ? Promise.resolve() : Promise.reject(validator.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* Deserialize JSON to object
|
* Deserialize JSON to object
|
||||||
*
|
*
|
||||||
* @method
|
* @method
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
static deserialize(serialized) {
|
static deserialize (serialized) {
|
||||||
let {
|
let {
|
||||||
name,
|
name,
|
||||||
kind,
|
kind,
|
||||||
@ -20,16 +104,24 @@ class Contributor {
|
|||||||
accounts,
|
accounts,
|
||||||
} = JSON.parse(serialized.toString('utf8'));
|
} = JSON.parse(serialized.toString('utf8'));
|
||||||
|
|
||||||
let github_username, github_uid, wiki_username;
|
let github_username, github_uid, gitea_username, wiki_username, zoom_display_name;
|
||||||
let github = accounts.find((a) => a.site === 'github.com');
|
let github = accounts.find(a => a.site === 'github.com');
|
||||||
let wiki = accounts.find((a) => a.site === 'wiki.kosmos.org');
|
let gitea = accounts.find(a => a.site === 'gitea.kosmos.org');
|
||||||
|
let wiki = accounts.find(a => a.site === 'wiki.kosmos.org');
|
||||||
|
let zoom = accounts.find(a => a.site === 'zoom.us');
|
||||||
|
|
||||||
if (github) {
|
if (github) {
|
||||||
(({ username: github_username, uid: github_uid} = github));
|
(({ username: github_username, uid: github_uid} = github));
|
||||||
}
|
}
|
||||||
|
if (gitea) {
|
||||||
|
(({ username: gitea_username } = gitea));
|
||||||
|
}
|
||||||
if (wiki) {
|
if (wiki) {
|
||||||
(({ username: wiki_username } = wiki));
|
(({ username: wiki_username } = wiki));
|
||||||
}
|
}
|
||||||
|
if (zoom) {
|
||||||
|
(({ username: zoom_display_name } = zoom));
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
@ -38,59 +130,13 @@ class Contributor {
|
|||||||
accounts,
|
accounts,
|
||||||
github_uid,
|
github_uid,
|
||||||
github_username,
|
github_username,
|
||||||
|
gitea_username,
|
||||||
wiki_username,
|
wiki_username,
|
||||||
|
zoom_display_name,
|
||||||
ipfsData: serialized,
|
ipfsData: serialized,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize object to JSON
|
|
||||||
*
|
|
||||||
* @method
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
static serialize(deserialized) {
|
|
||||||
let {
|
|
||||||
name,
|
|
||||||
kind,
|
|
||||||
url,
|
|
||||||
github_uid,
|
|
||||||
github_username,
|
|
||||||
wiki_username,
|
|
||||||
} = deserialized;
|
|
||||||
|
|
||||||
let data = {
|
|
||||||
"@context": "https://schema.kosmos.org",
|
|
||||||
"@type": "Contributor",
|
|
||||||
kind,
|
|
||||||
name,
|
|
||||||
"accounts": []
|
|
||||||
};
|
|
||||||
|
|
||||||
if (url) {
|
|
||||||
data["url"] = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (github_uid) {
|
|
||||||
data.accounts.push({
|
|
||||||
"site": "github.com",
|
|
||||||
"uid": github_uid,
|
|
||||||
"username": github_username,
|
|
||||||
"url": `https://github.com/${github_username}`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wiki_username) {
|
|
||||||
data.accounts.push({
|
|
||||||
"site": "wiki.kosmos.org",
|
|
||||||
"username": wiki_username,
|
|
||||||
"url": `https://wiki.kosmos.org/User:${wiki_username}`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write it pretty to ipfs
|
|
||||||
return JSON.stringify(data, null, 2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Contributor;
|
module.exports = Contributor;
|
||||||
|
104
lib/serializers/expense.js
Normal file
104
lib/serializers/expense.js
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
const schemas = require('@kosmos/schemas');
|
||||||
|
const validator = require('../utils/validator');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialization and validation for JSON-LD document of the Expense
|
||||||
|
*
|
||||||
|
* @class
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
class ExpenseSerializer {
|
||||||
|
|
||||||
|
constructor (attrs) {
|
||||||
|
Object.keys(attrs).forEach(a => this[a] = attrs[a]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize object to JSON
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
serialize () {
|
||||||
|
// Write it pretty to ipfs
|
||||||
|
return JSON.stringify(this.data, null, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
get data () {
|
||||||
|
const {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
currency,
|
||||||
|
amount,
|
||||||
|
amountSats,
|
||||||
|
date,
|
||||||
|
url,
|
||||||
|
tags,
|
||||||
|
details,
|
||||||
|
} = this;
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
'@context': 'https://schema.kosmos.org',
|
||||||
|
'@type': 'Expense',
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
currency,
|
||||||
|
amount,
|
||||||
|
amountSats,
|
||||||
|
date,
|
||||||
|
'tags': tags || [],
|
||||||
|
'details': details || {},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (url) {
|
||||||
|
data['url'] = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate serialized data against schema
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
validate () {
|
||||||
|
const serialized = JSON.parse(this.serialize());
|
||||||
|
const valid = validator.validate(serialized, schemas['expense']);
|
||||||
|
return valid ? Promise.resolve() : Promise.reject(validator.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize JSON to object
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
static deserialize (serialized) {
|
||||||
|
const {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
currency,
|
||||||
|
amount,
|
||||||
|
amountSats,
|
||||||
|
date,
|
||||||
|
url,
|
||||||
|
tags,
|
||||||
|
details,
|
||||||
|
} = JSON.parse(serialized.toString('utf8'));
|
||||||
|
|
||||||
|
return {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
currency,
|
||||||
|
amount,
|
||||||
|
amountSats,
|
||||||
|
date,
|
||||||
|
url,
|
||||||
|
tags,
|
||||||
|
details,
|
||||||
|
ipfsData: serialized,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ExpenseSerializer;
|
5
lib/utils/deprecate.js
Normal file
5
lib/utils/deprecate.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/*eslint no-console: ["error", { allow: ["warn"] }] */
|
||||||
|
|
||||||
|
module.exports = function deprecate (msg) {
|
||||||
|
console.warn(msg);
|
||||||
|
};
|
10
lib/utils/format-kredits.js
Normal file
10
lib/utils/format-kredits.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
const ethersUtils = require('ethers').utils;
|
||||||
|
|
||||||
|
module.exports = function(value, options = {}) {
|
||||||
|
let etherValue = ethersUtils.formatEther(value);
|
||||||
|
if (options.asFloat) {
|
||||||
|
return parseFloat(etherValue);
|
||||||
|
} else {
|
||||||
|
return parseInt(etherValue);
|
||||||
|
}
|
||||||
|
};
|
@ -1,17 +1,25 @@
|
|||||||
const ipfsAPI = require('ipfs-api');
|
const ipfsClient = require('ipfs-http-client');
|
||||||
const multihashes = require('multihashes');
|
const multihashes = require('multihashes');
|
||||||
|
const fetch = require('node-fetch');
|
||||||
|
|
||||||
class IPFS {
|
class IPFS {
|
||||||
|
constructor (config) {
|
||||||
constructor(config) {
|
|
||||||
if (!config) {
|
if (!config) {
|
||||||
config = { host: 'localhost', port: '5001', protocol: 'http' };
|
config = { host: 'localhost', port: '5001', protocol: 'http' };
|
||||||
}
|
}
|
||||||
this._ipfsAPI = ipfsAPI(config);
|
|
||||||
this._config = config;
|
this._config = config;
|
||||||
|
this._ipfsAPI = ipfsClient.create(config);
|
||||||
|
|
||||||
|
this._ipfsAPI.id().then(res => {
|
||||||
|
console.debug('IPFS ID:', res.id);
|
||||||
|
}).catch(e => {
|
||||||
|
console.debug('IPFS config:', config);
|
||||||
|
console.warn('Failed to initialize IPFS:', e.message);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
catAndMerge(data, deserialize) {
|
async catAndMerge (contractData, deserialize) {
|
||||||
|
let data = {...contractData}; // data from ethers.js is not extensible. this copy the attributes in a new object
|
||||||
// if no hash details are found simply return the data; nothing to merge
|
// if no hash details are found simply return the data; nothing to merge
|
||||||
if (!data.hashSize || data.hashSize === 0) {
|
if (!data.hashSize || data.hashSize === 0) {
|
||||||
return data;
|
return data;
|
||||||
@ -21,42 +29,57 @@ class IPFS {
|
|||||||
|
|
||||||
return this.cat(data.ipfsHash)
|
return this.cat(data.ipfsHash)
|
||||||
.then(deserialize)
|
.then(deserialize)
|
||||||
.then((attributes) => {
|
.then(attributes => {
|
||||||
return Object.assign({}, data, attributes);
|
return Object.assign({}, data, attributes);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
add(data) {
|
async add (data) {
|
||||||
return this._ipfsAPI
|
return this._ipfsAPI.add(data)
|
||||||
.add(new this._ipfsAPI.Buffer(data))
|
.then(res => {
|
||||||
.then((res) => {
|
return this.decodeHash(res.path);
|
||||||
return this.decodeHash(res[0].hash);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
cat(hashData) {
|
async cat (hashData) {
|
||||||
let ipfsHash = hashData; // default - if it is a string
|
let ipfsHash = hashData; // default - if it is a string
|
||||||
if (hashData.hasOwnProperty('hashSize')) {
|
if (Object.prototype.hasOwnProperty.call(hashData, 'hashSize')) {
|
||||||
ipfsHash = this.encodeHash(hashData);
|
ipfsHash = this.encodeHash(hashData);
|
||||||
}
|
}
|
||||||
return this._ipfsAPI.cat(ipfsHash);
|
if (this._config['gatewayUrl']) {
|
||||||
|
return fetch(`${this._config['gatewayUrl']}/${ipfsHash}`).then(r => r.text());
|
||||||
|
} else {
|
||||||
|
const res = this._ipfsAPI.cat(ipfsHash);
|
||||||
|
let str = '';
|
||||||
|
for await (const buffer of res) {
|
||||||
|
str += buffer.toString();
|
||||||
|
}
|
||||||
|
return Promise.resolve(str);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
decodeHash(ipfsHash) {
|
pin (hashData) {
|
||||||
|
let ipfsHash = hashData; // default - if it is a string
|
||||||
|
if (Object.prototype.hasOwnProperty.call(hashData, 'hashSize')) {
|
||||||
|
ipfsHash = this.encodeHash(hashData);
|
||||||
|
}
|
||||||
|
return this._ipfsAPI.pin.add(multihashes.toB58String(ipfsHash));
|
||||||
|
}
|
||||||
|
|
||||||
|
decodeHash (ipfsHash) {
|
||||||
let multihash = multihashes.decode(multihashes.fromB58String(ipfsHash));
|
let multihash = multihashes.decode(multihashes.fromB58String(ipfsHash));
|
||||||
return {
|
return {
|
||||||
hashDigest: '0x' + multihashes.toHexString(multihash.digest),
|
hashDigest: '0x' + multihashes.toHexString(multihash.digest),
|
||||||
hashSize: multihash.length,
|
hashSize: multihash.length,
|
||||||
hashFunction: multihash.code,
|
hashFunction: multihash.code,
|
||||||
ipfsHash: ipfsHash
|
ipfsHash: ipfsHash,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
encodeHash(hashData) {
|
encodeHash (hashData) {
|
||||||
let digest = this._ipfsAPI.Buffer.from(hashData.hashDigest.slice(2), 'hex');
|
const digest = Buffer.from(hashData.hashDigest.slice(2), 'hex');
|
||||||
return multihashes.encode(digest, hashData.hashFunction, hashData.hashSize);
|
return multihashes.encode(digest, hashData.hashFunction, hashData.hashSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = IPFS;
|
module.exports = IPFS;
|
||||||
|
46
lib/utils/pagination.js
Normal file
46
lib/utils/pagination.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
function pageNumber (number, size, recordCount) {
|
||||||
|
let numberOfPages = Math.ceil(recordCount / size);
|
||||||
|
|
||||||
|
number = parseInt(number) || 1;
|
||||||
|
|
||||||
|
// Ensure page number is in range
|
||||||
|
number = number > numberOfPages ? numberOfPages : number;
|
||||||
|
number = number < 1 ? 1 : number;
|
||||||
|
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildIds (order, number, size, recordCount) {
|
||||||
|
let offset = size * (number - 1);
|
||||||
|
|
||||||
|
let start;
|
||||||
|
let mapFunction;
|
||||||
|
|
||||||
|
if (order === 'asc') {
|
||||||
|
start = 1 + offset;
|
||||||
|
mapFunction = (_, i) => start + i;
|
||||||
|
} else {
|
||||||
|
start = recordCount - offset;
|
||||||
|
mapFunction = (_, i) => start - i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure size is in range
|
||||||
|
let end = offset + size;
|
||||||
|
if (end > recordCount) {
|
||||||
|
let diff = end - recordCount;
|
||||||
|
size = size - diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from({ length: size }, mapFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = function paged (recordCount, options = {}) {
|
||||||
|
let { order, page } = options;
|
||||||
|
order = order || 'desc';
|
||||||
|
page = page || {};
|
||||||
|
|
||||||
|
let size = parseInt(page.size) || 25;
|
||||||
|
let number = pageNumber(page.number, size, recordCount);
|
||||||
|
|
||||||
|
return buildIds(order, number, size, recordCount);
|
||||||
|
};
|
@ -1,9 +1,9 @@
|
|||||||
class Healthcheck {
|
class Preflight {
|
||||||
constructor(kredits) {
|
constructor (kredits) {
|
||||||
this.kredits = kredits;
|
this.kredits = kredits;
|
||||||
}
|
}
|
||||||
|
|
||||||
check() {
|
check () {
|
||||||
return this.kredits.ipfs._ipfsAPI.id()
|
return this.kredits.ipfs._ipfsAPI.id()
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
throw new Error(`IPFS node not available; config: ${JSON.stringify(this.kredits.ipfs.config)} - ${error.message}`);
|
throw new Error(`IPFS node not available; config: ${JSON.stringify(this.kredits.ipfs.config)} - ${error.message}`);
|
||||||
@ -25,4 +25,4 @@ class Healthcheck {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Healthcheck;
|
module.exports = Preflight;
|
15
lib/utils/validator.js
Normal file
15
lib/utils/validator.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
const tv4 = require('tv4');
|
||||||
|
const validator = tv4.freshApi();
|
||||||
|
|
||||||
|
validator.addFormat({
|
||||||
|
'date': function(value) {
|
||||||
|
const dateRegexp = /^[0-9]{4,}-[0-9]{2}-[0-9]{2}$/;
|
||||||
|
return dateRegexp.test(value) ? null : 'A valid ISO 8601 full-date string is expected';
|
||||||
|
},
|
||||||
|
'time': function(value) {
|
||||||
|
const timeRegexp = /^([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([Zz])|([+|-]([01][0-9]|2[0-3]):[0-5][0-9]))$/;
|
||||||
|
return timeRegexp.test(value) ? null : 'A valid ISO 8601 full-time string is expected';
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = validator;
|
@ -1,5 +0,0 @@
|
|||||||
var Registry = artifacts.require('./upgradeable/Registry.sol');
|
|
||||||
|
|
||||||
module.exports = function(deployer) {
|
|
||||||
deployer.deploy(Registry);
|
|
||||||
};
|
|
@ -1,15 +0,0 @@
|
|||||||
var Registry = artifacts.require('./Registry.sol');
|
|
||||||
var Token = artifacts.require('./Token.sol');
|
|
||||||
|
|
||||||
module.exports = function(deployer) {
|
|
||||||
deployer.deploy(Token).then(function(token) {
|
|
||||||
console.log('Registry address: ', Registry.address);
|
|
||||||
console.log('Token address: ', Token.address);
|
|
||||||
Registry.deployed().then(function(registry) {
|
|
||||||
registry.addVersion('Token', Token.address);
|
|
||||||
registry.createProxy('Token', 1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
|||||||
var Registry = artifacts.require('./Registry.sol');
|
|
||||||
var Contributors = artifacts.require('./Contributors.sol');
|
|
||||||
|
|
||||||
module.exports = function(deployer) {
|
|
||||||
deployer.deploy(Contributors).then(function(contributors) {
|
|
||||||
console.log('Registry address: ', Registry.address);
|
|
||||||
console.log('Contributors address: ', Contributors.address);
|
|
||||||
Registry.deployed().then(function(registry) {
|
|
||||||
registry.addVersion('Contributors', Contributors.address);
|
|
||||||
registry.createProxy('Contributors', 1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
@ -1,13 +0,0 @@
|
|||||||
var Registry = artifacts.require('./Registry.sol');
|
|
||||||
var Operator = artifacts.require('./Operator.sol');
|
|
||||||
|
|
||||||
module.exports = function(deployer) {
|
|
||||||
deployer.deploy(Operator).then(function(operator) {
|
|
||||||
console.log('Registry address: ', Registry.address);
|
|
||||||
console.log('Operator address: ', Operator.address);
|
|
||||||
Registry.deployed().then(function(registry) {
|
|
||||||
registry.addVersion('Operator', Operator.address);
|
|
||||||
registry.createProxy('Operator', 1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
@ -1,5 +0,0 @@
|
|||||||
var Migrations = artifacts.require("./Migrations.sol");
|
|
||||||
|
|
||||||
module.exports = function(deployer) {
|
|
||||||
deployer.deploy(Migrations);
|
|
||||||
};
|
|
42415
package-lock.json
generated
42415
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
78
package.json
78
package.json
@ -1,38 +1,80 @@
|
|||||||
{
|
{
|
||||||
"name": "kredits-contracts",
|
"name": "@kredits/contracts",
|
||||||
"version": "1.0.0",
|
"version": "7.5.0",
|
||||||
"description": "Ethereum contracts and npm wrapper for Kredits",
|
"description": "Smart contracts and JavaScript API for Kredits",
|
||||||
"main": "./lib/kredits.js",
|
"main": "./lib/kredits.js",
|
||||||
"directories": {
|
"directories": {
|
||||||
"test": "test"
|
"test": "test"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build-json": "truffle compile && node ./scripts/build-json.js",
|
"wallet:create": "hardhat create-wallet",
|
||||||
"bootstrap": "truffle migrate --reset && npm run build-json && truffle exec scripts/seeds.js",
|
"devchain": "hardhat node --network hardhat",
|
||||||
"ganache": "ganache-cli -p 7545 -i 100 --db=./.ganache-db -m kredits",
|
"deploy:dao": "hardhat run scripts/create-proxy.js",
|
||||||
"dev": "truffle migrate && npm run build-json",
|
"postshrinkwrap": "node scripts/fix-package-lock.js &>/dev/null || true",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"build": "npm run build:contracts && npm run build:json",
|
||||||
|
"build:contracts": "hardhat compile --force",
|
||||||
|
"build:json": "node ./scripts/build-json.js",
|
||||||
|
"seeds": "hardhat run scripts/seeds.js",
|
||||||
|
"fund": "hardhat fund",
|
||||||
|
"bootstrap": "npm run build && npm run deploy:dao && npm run seeds",
|
||||||
|
"repl": "hardhat console",
|
||||||
|
"lint:contracts": "solhint \"contracts/**/*.sol\" \"apps/*/contracts/**/*.sol\"",
|
||||||
|
"lint:contract-tests": "eslint apps/*/test",
|
||||||
|
"lint:wrapper": "eslint lib/",
|
||||||
|
"test": "hardhat test",
|
||||||
|
"setup-git-hooks": "sh scripts/git-hooks/install",
|
||||||
|
"preversion": "npm test && npm run build",
|
||||||
|
"version": "git add lib/abis"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/67P/truffle-kredits.git"
|
"url": "git+https://github.com/67P/kredits-contracts.git"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/67P/truffle-kredits/issues"
|
"url": "https://github.com/67P/kredits-contracts/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/67P/truffle-kredits#readme",
|
"homepage": "https://github.com/67P/kredits-contracts#readme",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@nomicfoundation/hardhat-chai-matchers": "^1.0.3",
|
||||||
|
"@nomiclabs/hardhat-ethers": "^2.0.2",
|
||||||
|
"@nomiclabs/hardhat-waffle": "^2.0.1",
|
||||||
|
"@openzeppelin/contracts-upgradeable": "^4.8.3",
|
||||||
|
"@openzeppelin/hardhat-upgrades": "^1.10.0",
|
||||||
"async-each-series": "^1.1.0",
|
"async-each-series": "^1.1.0",
|
||||||
"ganache-cli": "^6.0.3",
|
"chai": "^4.3.6",
|
||||||
|
"cli-table": "^0.3.1",
|
||||||
|
"colors": "^1.0.3",
|
||||||
|
"eslint": "^8.14.0",
|
||||||
|
"eslint-plugin-import": "^2.20.2",
|
||||||
|
"eslint-plugin-node": "^11.1.0",
|
||||||
|
"eslint-plugin-promise": "^6.0.0",
|
||||||
|
"eth-provider": "^0.11.0",
|
||||||
|
"ethereum-block-by-date": "^1.4.0",
|
||||||
|
"ethereum-waffle": "^3.4.0",
|
||||||
|
"hardhat": "^2.6.4",
|
||||||
|
"hardhat-deploy": "^0.11.4",
|
||||||
|
"hardhat-deploy-ethers": "^0.3.0-beta.10",
|
||||||
|
"homedir": "^0.6.0",
|
||||||
|
"mocha": "^10.0.0",
|
||||||
"promptly": "^3.0.3",
|
"promptly": "^3.0.3",
|
||||||
"truffle": "^4.1.3",
|
"solhint": "^3.3.7",
|
||||||
"zeppelin-solidity": "^1.7.0"
|
"truffle-hdwallet-provider": "^1.0.17",
|
||||||
|
"truffle-hdwallet-provider-privkey": "^0.3.0",
|
||||||
|
"web3-providers-ws": "^1.7.3",
|
||||||
|
"yargs": "^15.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ethers": "3.0.15",
|
"@kosmos/schemas": "^3.2.0",
|
||||||
"ipfs-api": "^19.0.0",
|
"ethers": "^5.4.7",
|
||||||
"rsvp": "^4.8.2"
|
"ipfs-http-client": "^56.0.3",
|
||||||
}
|
"multihashes": "^4.0.3",
|
||||||
|
"node-fetch": "^2.6.0",
|
||||||
|
"tv4": "^1.3.0"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"kosmos",
|
||||||
|
"kredits"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
55
scripts/add-contribution.js
Normal file
55
scripts/add-contribution.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
const promptly = require('promptly');
|
||||||
|
const { inspect } = require('util');
|
||||||
|
|
||||||
|
const { ethers } = require("hardhat");
|
||||||
|
const Kredits = require('../lib/kredits');
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
kredits = new Kredits(hre.ethers.provider, hre.ethers.provider.getSigner())
|
||||||
|
await kredits.init();
|
||||||
|
|
||||||
|
console.log(`Using Contributions at: ${kredits.Contribution.contract.address}`);
|
||||||
|
|
||||||
|
let contributor = await promptly.prompt('Contributor (address or id): ');
|
||||||
|
let contributorId;
|
||||||
|
let contributorAccount;
|
||||||
|
if (contributor.length < 5) {
|
||||||
|
contributorId = contributor;
|
||||||
|
contributorAccount = await kredits.Contributor.contract.getContributorAddressById(contributor);
|
||||||
|
} else {
|
||||||
|
contributorAccount = contributor;
|
||||||
|
contributorId = await kredits.Contributor.contract.getContributorIdByAddress(contributor);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Creating a contribution for contributor account ${contributorAccount} ID: ${contributorId}`);
|
||||||
|
|
||||||
|
[ dateNow, timeNow ] = (new Date()).toISOString().split('T');
|
||||||
|
|
||||||
|
let contributionAttributes = {
|
||||||
|
contributorId,
|
||||||
|
date: dateNow,
|
||||||
|
time: timeNow,
|
||||||
|
amount: await promptly.prompt('Amount: '),
|
||||||
|
description: await promptly.prompt('Description: '),
|
||||||
|
kind: await promptly.prompt('Kind: ', { default: 'dev' }),
|
||||||
|
url: await promptly.prompt('URL: ', { default: '' })
|
||||||
|
}
|
||||||
|
|
||||||
|
const contributorData = await kredits.Contributor.getById(contributorId);
|
||||||
|
contributionAttributes.contributorIpfsHash = contributorData.ipfsHash;
|
||||||
|
|
||||||
|
console.log("\nAdding contribution:");
|
||||||
|
console.log(contributionAttributes);
|
||||||
|
|
||||||
|
kredits.Contribution.add(contributionAttributes, { gasLimit: 300000 })
|
||||||
|
.then(result => {
|
||||||
|
console.log("\n\nResult:");
|
||||||
|
console.log(result);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log('Failed to create contribution');
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
@ -1,8 +1,6 @@
|
|||||||
const Registry = artifacts.require('./Registry.sol');
|
|
||||||
const promptly = require('promptly');
|
const promptly = require('promptly');
|
||||||
const bs58 = require('bs58');
|
|
||||||
|
|
||||||
const ethers = require('ethers');
|
const { ethers } = require("hardhat");
|
||||||
const Kredits = require('../lib/kredits');
|
const Kredits = require('../lib/kredits');
|
||||||
|
|
||||||
async function prompt(message, options) {
|
async function prompt(message, options) {
|
||||||
@ -11,40 +9,32 @@ async function prompt(message, options) {
|
|||||||
}
|
}
|
||||||
return await promptly.prompt(message, options);
|
return await promptly.prompt(message, options);
|
||||||
}
|
}
|
||||||
|
async function main() {
|
||||||
|
kredits = new Kredits(hre.ethers.provider, hre.ethers.provider.getSigner())
|
||||||
|
await kredits.init();
|
||||||
|
|
||||||
module.exports = function(callback) {
|
console.log(`Using contributors at: ${kredits.Contributor.contract.address}`);
|
||||||
Registry.deployed().then(async (registry) => {
|
|
||||||
|
|
||||||
const networkId = parseInt(web3.version.network);
|
let contributorAttributes = {
|
||||||
const provider = new ethers.providers.Web3Provider(
|
account: await prompt('Contributor address: ', {}),
|
||||||
web3.currentProvider, { chainId: networkId }
|
name: await prompt('Name: '),
|
||||||
);
|
kind: await prompt('Kind (default person): ', {default: 'person'}),
|
||||||
const kredits = await Kredits.setup(provider, provider.getSigner());
|
url: await prompt('URL: '),
|
||||||
|
github_username: await prompt('GitHub username: '),
|
||||||
|
github_uid: parseInt(await prompt('GitHub UID: ')),
|
||||||
|
wiki_username: await prompt('Wiki username: '),
|
||||||
|
};
|
||||||
|
|
||||||
console.log(`Using contributors at: ${kredits.Contributor.contract.address}`);
|
console.log("\nAdding contributor:");
|
||||||
|
console.log(contributorAttributes);
|
||||||
let contributorAttributes = {
|
|
||||||
account: await prompt('Contributor address: ', {}),
|
|
||||||
name: await prompt('Name: '),
|
|
||||||
isCore: await prompt('core? y/n') === 'y',
|
|
||||||
kind: await prompt('Kind (default person): ', {default: 'person'}),
|
|
||||||
url: await prompt('URL: '),
|
|
||||||
github_username: await prompt('GitHub username: '),
|
|
||||||
github_uid: await prompt('GitHub UID: '),
|
|
||||||
wiki_username: await prompt('Wiki username: '),
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log("\nAdding contributor:");
|
|
||||||
console.log(contributorAttributes);
|
|
||||||
|
|
||||||
kredits.Contributor.add(contributorAttributes, {gasLimit: 250000}).then((result) => {
|
|
||||||
console.log("\n\nResult:");
|
|
||||||
console.log(result);
|
|
||||||
callback();
|
|
||||||
}).catch((error) => {
|
|
||||||
console.log('Failed to create contributor');
|
|
||||||
callback(error);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
kredits.Contributor.add(contributorAttributes, { gasLimit: 350000 }).then((result) => {
|
||||||
|
console.log("\n\nResult:");
|
||||||
|
console.log(result);
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log('Failed to create contributor');
|
||||||
|
console.log(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
const Registry = artifacts.require('./Registry.sol');
|
|
||||||
const Operator = artifacts.require('./Operator.sol');
|
|
||||||
const Contributors = artifacts.require('./Contributors.sol');
|
|
||||||
const promptly = require('promptly');
|
|
||||||
|
|
||||||
const bs58 = require('bs58');
|
|
||||||
|
|
||||||
function getBytes32FromMultiash(multihash) {
|
|
||||||
const decoded = bs58.decode(multihash);
|
|
||||||
|
|
||||||
return {
|
|
||||||
digest: `0x${decoded.slice(2).toString('hex')}`,
|
|
||||||
hashFunction: decoded[0],
|
|
||||||
size: decoded[1],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = function(callback) {
|
|
||||||
Registry.deployed().then(async (registry) => {
|
|
||||||
var operatorAddress = await registry.getProxyFor('Operator');
|
|
||||||
var contributorsAddress = await registry.getProxyFor('Contributors');
|
|
||||||
|
|
||||||
var operator = await Operator.at(operatorAddress);
|
|
||||||
var contributors = await Contributors.at(contributorsAddress);
|
|
||||||
|
|
||||||
let recipientAddress = await promptly.prompt('Contributor address: ');
|
|
||||||
let ipfsHash = await promptly.prompt('IPFS hash (blank for default): ', { default: 'QmQNA1hhVyL1Vm6HiRxXe9xmc6LUMBDyiNMVgsjThtyevs' });
|
|
||||||
|
|
||||||
let multihash = getBytes32FromMultiash(ipfsHash);
|
|
||||||
|
|
||||||
let contributorId = await contributors.getContributorIdByAddress(recipientAddress);
|
|
||||||
|
|
||||||
let result = await operator.addProposal(contributorId.toNumber(), 23, multihash.digest, multihash.hashFunction, multihash.size);
|
|
||||||
console.log('Proposal added, tx: ', result.tx);
|
|
||||||
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,33 +1,18 @@
|
|||||||
const fs = require('fs');
|
const fs = require("fs");
|
||||||
const path = require('path');
|
const path = require("path");
|
||||||
|
|
||||||
const contractsPath = path.join(__dirname, '..', 'build', 'contracts');
|
const contractsPath = path.join(__dirname, "..", "artifacts", "contracts");
|
||||||
const libPath = path.join(__dirname, '..', 'lib');
|
const libPath = path.join(__dirname, "..", "lib");
|
||||||
const abisPath = path.join(libPath, 'abis');
|
const abisPath = path.join(libPath, "abis");
|
||||||
const addressesPath = path.join(libPath, 'addresses');
|
|
||||||
|
|
||||||
const files = [
|
const files = ["Contributor", "Contribution", "Token", "Reimbursement"];
|
||||||
'Contributors',
|
|
||||||
'Operator',
|
|
||||||
'Registry',
|
|
||||||
'Token'
|
|
||||||
];
|
|
||||||
|
|
||||||
files.forEach((fileName) => {
|
files.forEach((fileName) => {
|
||||||
let file = require(`${contractsPath}/${fileName}.json`);
|
let file = require(`${contractsPath}/${fileName}.sol/${fileName}.json`);
|
||||||
let abiFile = path.join(abisPath, `${fileName}.json`);
|
let abiFile = path.join(abisPath, `${fileName}.json`);
|
||||||
fs.writeFileSync(abiFile, JSON.stringify(file.abi));
|
fs.writeFileSync(abiFile, JSON.stringify(file.abi));
|
||||||
|
|
||||||
if (fileName === 'Registry') {
|
|
||||||
let addresseFile = path.join(addressesPath, `${fileName}.json`);
|
|
||||||
let content = fs.readFileSync(addresseFile);
|
|
||||||
let addresses = Object.keys(file.networks)
|
|
||||||
.reduce((addresses, key) => {
|
|
||||||
addresses[key] = file.networks[key].address;
|
|
||||||
return addresses;
|
|
||||||
}, JSON.parse(content));
|
|
||||||
fs.writeFileSync(addresseFile, JSON.stringify(addresses));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("Don't forget to reaload the JSON files from your application; i.e. restart kredits-web");
|
console.log(
|
||||||
|
"Don't forget to reaload the JSON files from your application; i.e. restart kredits-web"
|
||||||
|
);
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
const REPL = require('repl');
|
|
||||||
const promptly = require('promptly');
|
|
||||||
|
|
||||||
const ethers = require('ethers');
|
|
||||||
const Kredits = require('../lib/kredits');
|
|
||||||
|
|
||||||
module.exports = function(callback) {
|
|
||||||
const Registry = artifacts.require('./Registry.sol');
|
|
||||||
Registry.deployed().then(async (registry) => {
|
|
||||||
let contractName = await promptly.prompt('Contract Name: ');
|
|
||||||
let method = await promptly.prompt('Function: ');
|
|
||||||
let argumentInput = await promptly.prompt('Arguments (comma separated): ', { default: '' });
|
|
||||||
let args = [];
|
|
||||||
if (argumentInput !== '') {
|
|
||||||
args = argumentInput.split(',').map(a => a.trim());
|
|
||||||
}
|
|
||||||
|
|
||||||
const networkId = parseInt(web3.version.network);
|
|
||||||
const provider = new ethers.providers.Web3Provider(
|
|
||||||
web3.currentProvider, { chainId: networkId }
|
|
||||||
);
|
|
||||||
const kredits = await Kredits.setup(provider, provider.getSigner());
|
|
||||||
|
|
||||||
const contract = kredits[contractName].contract;
|
|
||||||
console.log(`Using ${contractName} at ${contract.address}`);
|
|
||||||
console.log(`Calling ${method} with ${JSON.stringify(args)}`);
|
|
||||||
|
|
||||||
if (!contract[method]) {
|
|
||||||
callback(new Error(`Method ${method} is not defined on ${contractName}`));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
contract[method](...args).then((result) => {
|
|
||||||
console.log("\nResult:");
|
|
||||||
console.log(result);
|
|
||||||
|
|
||||||
console.log("\nStartig a REPL. (type .exit to exit)");
|
|
||||||
console.log(`defined variables: result, ${contractName}, kredis`);
|
|
||||||
let r = REPL.start();
|
|
||||||
r.context.result = result;
|
|
||||||
r.context[contractName] = contract;
|
|
||||||
r.context.kredits = kredits;
|
|
||||||
|
|
||||||
r.on('exit', () => {
|
|
||||||
console.log('Bye');
|
|
||||||
callback();
|
|
||||||
})
|
|
||||||
}).catch((error) => {
|
|
||||||
console.log("Call failed. Probably the contract raised an error?\n");
|
|
||||||
console.log("...");
|
|
||||||
callback(error);
|
|
||||||
});
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
97
scripts/create-proxy.js
Normal file
97
scripts/create-proxy.js
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
const { ethers, upgrades } = require("hardhat");
|
||||||
|
const path = require("path");
|
||||||
|
const fileInject = require("./helpers/file_inject.js");
|
||||||
|
|
||||||
|
function handleError(error) {
|
||||||
|
console.error(error.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const network = await hre.ethers.provider.getNetwork();
|
||||||
|
const networkId = network.chainId;
|
||||||
|
console.log(`Deploying to network #${networkId}`);
|
||||||
|
|
||||||
|
const contractFactories = {};
|
||||||
|
const contracts = {};
|
||||||
|
|
||||||
|
contractFactories.Contributor = await ethers.getContractFactory("Contributor");
|
||||||
|
contractFactories.Contribution = await ethers.getContractFactory("Contribution");
|
||||||
|
contractFactories.Token = await ethers.getContractFactory("Token");
|
||||||
|
contractFactories.Reimbursement = await ethers.getContractFactory("Reimbursement");
|
||||||
|
|
||||||
|
async function deployContractProxy (contractName, params=[]) {
|
||||||
|
let contract = await upgrades.deployProxy(contractFactories[contractName], params)
|
||||||
|
.catch(handleError);
|
||||||
|
|
||||||
|
contracts[contractName] = contract;
|
||||||
|
|
||||||
|
await contract.deployed().then(() => {
|
||||||
|
console.log(`${contractName} deployed to:`, contract.address);
|
||||||
|
console.log("...waiting for 1 confirmation");
|
||||||
|
}).catch(handleError);
|
||||||
|
|
||||||
|
await contract.deployTransaction.wait().catch(handleError);
|
||||||
|
}
|
||||||
|
|
||||||
|
const blocksVetoPeriod = 40320; // 7 days; 15 seconds block time
|
||||||
|
|
||||||
|
await deployContractProxy('Contributor', [ '0x0000000000000000000000000000000000000000' ] );
|
||||||
|
await deployContractProxy('Contribution', [ blocksVetoPeriod ]);
|
||||||
|
await deployContractProxy('Token');
|
||||||
|
await deployContractProxy('Reimbursement');
|
||||||
|
|
||||||
|
console.log('Calling Contributor#setTokenContract')
|
||||||
|
await contracts.Contributor.functions
|
||||||
|
.setTokenContract(contracts.Token.address)
|
||||||
|
.then(res => {
|
||||||
|
console.log(`...transaction published: ${res.hash}`);
|
||||||
|
return res.wait();
|
||||||
|
}).catch(handleError);
|
||||||
|
|
||||||
|
console.log('Calling Contributor#setContributionContract')
|
||||||
|
await contracts.Contributor.functions
|
||||||
|
.setContributionContract(contracts.Contribution.address)
|
||||||
|
.then(res => {
|
||||||
|
console.log(`...transaction published: ${res.hash}`);
|
||||||
|
return res.wait();
|
||||||
|
}).catch(handleError);
|
||||||
|
|
||||||
|
console.log('Calling Contribution#setContributorContract')
|
||||||
|
await contracts.Contribution.functions
|
||||||
|
.setContributorContract(contracts.Contributor.address)
|
||||||
|
.then(res => {
|
||||||
|
console.log(`...transaction published: ${res.hash}`);
|
||||||
|
return res.wait();
|
||||||
|
}).catch(handleError);
|
||||||
|
|
||||||
|
console.log('Calling Token#setContributorContract')
|
||||||
|
await contracts.Token.functions
|
||||||
|
.setContributorContract(contracts.Contributor.address)
|
||||||
|
.then(res => {
|
||||||
|
console.log(`...transaction published: ${res.hash}`);
|
||||||
|
return res.wait();
|
||||||
|
}).catch(handleError);
|
||||||
|
|
||||||
|
console.log('Calling Reimbursement#setContributorContract')
|
||||||
|
await contracts.Reimbursement.functions
|
||||||
|
.setContributorContract(contracts.Contributor.address)
|
||||||
|
.then(res => {
|
||||||
|
console.log(`...transaction published: ${res.hash}`);
|
||||||
|
return res.wait();
|
||||||
|
}).catch(handleError);
|
||||||
|
|
||||||
|
const addresses = {
|
||||||
|
Contributor: contracts.Contributor.address,
|
||||||
|
Contribution: contracts.Contribution.address,
|
||||||
|
Token: contracts.Token.address,
|
||||||
|
Reimbursement: contracts.Reimbursement.address,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("Writing addresses.json");
|
||||||
|
const libPath = path.join(__dirname, "..", "lib");
|
||||||
|
fileInject(path.join(libPath, "addresses.json"), networkId, addresses);
|
||||||
|
console.log("DONE!");
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
43
scripts/export/contributions.js
Normal file
43
scripts/export/contributions.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const Kredits = require('../../lib/kredits');
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
kredits = new Kredits(hre.ethers.provider, hre.ethers.provider.getSigner())
|
||||||
|
await kredits.init();
|
||||||
|
|
||||||
|
console.log(`Using Contribution at: ${kredits.Contribution.contract.address}`);
|
||||||
|
|
||||||
|
const count = await kredits.Contribution.count;
|
||||||
|
const currentBlockHeight = await hre.ethers.provider.getBlockNumber();
|
||||||
|
|
||||||
|
const backup = {};
|
||||||
|
const promises = [];
|
||||||
|
for (let i = 1; i <= count; i++) {
|
||||||
|
promises.push(new Promise((resolve, reject) => {
|
||||||
|
setTimeout(async () => {
|
||||||
|
console.log(`Loading contribution #${i}`);
|
||||||
|
await kredits.Contribution.contract.getContribution(i).then(contractData => {
|
||||||
|
backup[i] = {
|
||||||
|
amount: contractData.amount,
|
||||||
|
contributorId: contractData.contributorId,
|
||||||
|
hashDigest: contractData.hashDigest,
|
||||||
|
hashFunction: contractData.hashFunction,
|
||||||
|
hashSize: contractData.hashSize,
|
||||||
|
confirmedAtBlock: contractData.confirmedAtBlock,
|
||||||
|
confirmed: contractData.confirmedAtBlock <= currentBlockHeight,
|
||||||
|
vetoed: contractData.vetoed,
|
||||||
|
id: contractData.id,
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}, 100 * i);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(promises).then(() => {
|
||||||
|
fs.writeFileSync("./data/contributions.json", JSON.stringify(backup, null, 2));
|
||||||
|
console.log("Exported");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
38
scripts/export/contributors.js
Normal file
38
scripts/export/contributors.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const Kredits = require('../../lib/kredits');
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
kredits = new Kredits(hre.ethers.provider, hre.ethers.provider.getSigner())
|
||||||
|
await kredits.init();
|
||||||
|
|
||||||
|
console.log(`Using Contributor at: ${kredits.Contributor.contract.address}`);
|
||||||
|
|
||||||
|
const count = await kredits.Contributor.count;
|
||||||
|
|
||||||
|
const backup = {};
|
||||||
|
const promises = [];
|
||||||
|
for (let i = 1; i <= count; i++) {
|
||||||
|
promises.push(new Promise((resolve, reject) => {
|
||||||
|
setTimeout(async () => {
|
||||||
|
console.log(`Loading contributor #${i}`);
|
||||||
|
await kredits.Contributor.contract.getContributorById(i).then(contractData => {
|
||||||
|
backup[i] = {
|
||||||
|
account: contractData.account,
|
||||||
|
hashDigest: contractData.hashDigest,
|
||||||
|
hashFunction: contractData.hashFunction,
|
||||||
|
hashSize: contractData.hashSize,
|
||||||
|
id: contractData.id,
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}, 100 * i);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(promises).then(() => {
|
||||||
|
fs.writeFileSync("./data/contributors.json", JSON.stringify(backup, null, 2));
|
||||||
|
console.log("Exported");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
44
scripts/export/reimbursements.js
Normal file
44
scripts/export/reimbursements.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const Kredits = require('../../lib/kredits');
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
kredits = new Kredits(hre.ethers.provider, hre.ethers.provider.getSigner())
|
||||||
|
await kredits.init();
|
||||||
|
|
||||||
|
console.log(`Using Reimbursement at: ${kredits.Reimbursement.contract.address}`);
|
||||||
|
|
||||||
|
const count = await kredits.Reimbursement.count;
|
||||||
|
const currentBlockHeight = await hre.ethers.provider.getBlockNumber();
|
||||||
|
|
||||||
|
const backup = {};
|
||||||
|
const promises = [];
|
||||||
|
for (let i = 1; i <= count; i++) {
|
||||||
|
promises.push(new Promise((resolve, reject) => {
|
||||||
|
setTimeout(async () => {
|
||||||
|
console.log(`Loading reimbursement #${i}`);
|
||||||
|
await kredits.Reimbursement.contract.get(i).then(contractData => {
|
||||||
|
backup[i] = {
|
||||||
|
recipientId: contractData.recipientId,
|
||||||
|
amount: contractData.amount,
|
||||||
|
token: contractData.token,
|
||||||
|
hashDigest: contractData.hashDigest,
|
||||||
|
hashFunction: contractData.hashFunction,
|
||||||
|
hashSize: contractData.hashSize,
|
||||||
|
confirmedAtBlock: contractData.confirmedAtBlock,
|
||||||
|
confirmed: contractData.confirmedAtBlock <= currentBlockHeight,
|
||||||
|
vetoed: contractData.vetoed,
|
||||||
|
id: contractData.id,
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}, 100 * i);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(promises).then(() => {
|
||||||
|
fs.writeFileSync("./data/reimbursements.json", JSON.stringify(backup, null, 2));
|
||||||
|
console.log("Exported");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
22
scripts/find-block-for-date.js
Normal file
22
scripts/find-block-for-date.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
const promptly = require('promptly');
|
||||||
|
const EthDater = require('ethereum-block-by-date');
|
||||||
|
|
||||||
|
const { ethers } = require("hardhat");
|
||||||
|
const Kredits = require('../lib/kredits');
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
kredits = new Kredits(hre.ethers.provider, hre.ethers.provider.getSigner())
|
||||||
|
await kredits.init();
|
||||||
|
|
||||||
|
const dater = new EthDater(kredits.provider);
|
||||||
|
const dateStr = await promptly.prompt('Specify a date and time (e.g. 2021-05-07T14:00:40Z): ');
|
||||||
|
const blockData = await dater.getDate(dateStr, true);
|
||||||
|
|
||||||
|
console.log(`
|
||||||
|
The closest block is #${blockData.block}:
|
||||||
|
https://rinkeby.etherscan.io/block/${blockData.block}
|
||||||
|
`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
2
scripts/git-hooks/install
Normal file
2
scripts/git-hooks/install
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
cp -f scripts/git-hooks/pre-commit .git/hooks
|
17
scripts/git-hooks/pre-commit
Executable file
17
scripts/git-hooks/pre-commit
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# Run appropriate linter against staged files
|
||||||
|
#
|
||||||
|
if [ $(git diff --name-only --cached lib/ | wc -l) != 0 ]; then
|
||||||
|
./node_modules/.bin/eslint lib/
|
||||||
|
if [ $? != 0 ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# TODO master not linted yet, uncomment this when ready
|
||||||
|
# if [ $(git diff --name-only --cached contracts/ | wc -l) != 0 ]; then
|
||||||
|
# solhint contracts/**/*.sol && apps/*/contracts/**/*.sol
|
||||||
|
# if [ $? != 0 ]; then
|
||||||
|
# exit 1
|
||||||
|
# fi
|
||||||
|
# fi
|
8
scripts/helpers/file_inject.js
Normal file
8
scripts/helpers/file_inject.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// help, give me a better name
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
module.exports = function (file, networkId, data) {
|
||||||
|
let content = JSON.parse(fs.readFileSync(file));
|
||||||
|
content[networkId] = data;
|
||||||
|
fs.writeFileSync(file, JSON.stringify(content, null, 2));
|
||||||
|
}
|
45
scripts/import/contributions.js
Normal file
45
scripts/import/contributions.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const Kredits = require('../../lib/kredits');
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
kredits = new Kredits(hre.ethers.provider, hre.ethers.provider.getSigner())
|
||||||
|
await kredits.init();
|
||||||
|
|
||||||
|
console.log(`Using Contribution at: ${kredits.Contribution.contract.address}`);
|
||||||
|
const count = await kredits.Contribution.count;
|
||||||
|
console.log(`Currently ${count} entries`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = fs.readFileSync("./data/contributions.json");
|
||||||
|
const contributions = JSON.parse(data);
|
||||||
|
const ids = Object.keys(contributions)
|
||||||
|
.map(k => parseInt(k))
|
||||||
|
.sort(function(a, b) { return a - b });
|
||||||
|
|
||||||
|
const currentBlockHeight = await kredits.provider.getBlockNumber();
|
||||||
|
const confirmationPeriod = 40320 // blocks
|
||||||
|
const unconfirmedHeight = currentBlockHeight + confirmationPeriod;
|
||||||
|
|
||||||
|
const startId = parseInt(process.env.START_AT || "0");
|
||||||
|
for (const contributionId of ids) {
|
||||||
|
if (contributionId < startId) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const c = contributions[contributionId.toString()];
|
||||||
|
|
||||||
|
const confirmedAtBlock = c.confirmed ? currentBlockHeight : unconfirmedHeight;
|
||||||
|
|
||||||
|
const result = await kredits.Contribution.contract.add(
|
||||||
|
c.amount, c.contributorId,
|
||||||
|
c.hashDigest, c.hashFunction, c.hashSize,
|
||||||
|
confirmedAtBlock, c.vetoed
|
||||||
|
);
|
||||||
|
console.log(`Adding contribution #${contributionId}: ${result.hash}`);
|
||||||
|
await result.wait();
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
34
scripts/import/contributors.js
Normal file
34
scripts/import/contributors.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const Kredits = require('../../lib/kredits');
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
kredits = new Kredits(hre.ethers.provider, hre.ethers.provider.getSigner())
|
||||||
|
await kredits.init();
|
||||||
|
|
||||||
|
console.log(`Using Contributor at: ${kredits.Contributor.contract.address}`);
|
||||||
|
const count = await kredits.Contributor.count;
|
||||||
|
console.log(`Currently ${count} entries`);
|
||||||
|
try {
|
||||||
|
const data = fs.readFileSync("./data/contributors.json");
|
||||||
|
const contributors = JSON.parse(data);
|
||||||
|
const ids = Object.keys(contributors)
|
||||||
|
.map(k => parseInt(k))
|
||||||
|
.sort(function(a, b){return a-b});
|
||||||
|
|
||||||
|
for (const contributorId of ids) {
|
||||||
|
const contributor = contributors[contributorId.toString()];
|
||||||
|
const result = await kredits.Contributor.contract.addContributor(
|
||||||
|
contributor.account,
|
||||||
|
contributor.hashDigest,
|
||||||
|
contributor.hashFunction,
|
||||||
|
contributor.hashSize,
|
||||||
|
);
|
||||||
|
console.log(`Adding contributor #${contributorId}: ${result.hash}`);
|
||||||
|
await result.wait();
|
||||||
|
};
|
||||||
|
} catch(e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
36
scripts/import/reimbursements.js
Normal file
36
scripts/import/reimbursements.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const Kredits = require('../../lib/kredits');
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
kredits = new Kredits(hre.ethers.provider, hre.ethers.provider.getSigner())
|
||||||
|
await kredits.init();
|
||||||
|
|
||||||
|
console.log(`Using Reimbursement at: ${kredits.Reimbursement.contract.address}`);
|
||||||
|
const count = await kredits.Reimbursement.count;
|
||||||
|
console.log(`Currently ${count} entries`);
|
||||||
|
try {
|
||||||
|
const data = fs.readFileSync("./data/reimbursements.json");
|
||||||
|
const reimbursements = JSON.parse(data);
|
||||||
|
const ids = Object.keys(reimbursements)
|
||||||
|
.map(k => parseInt(k))
|
||||||
|
.sort(function(a, b) { return a - b });
|
||||||
|
|
||||||
|
for (const reimbursementId of ids) {
|
||||||
|
const reimbursement = reimbursements[reimbursementId.toString()];
|
||||||
|
const result = await kredits.Reimbursement.contract.add(
|
||||||
|
reimbursement.amount,
|
||||||
|
reimbursement.token,
|
||||||
|
reimbursement.recipientId,
|
||||||
|
reimbursement.hashDigest,
|
||||||
|
reimbursement.hashFunction,
|
||||||
|
reimbursement.hashSize,
|
||||||
|
);
|
||||||
|
console.log(`Adding reimbursement #${reimbursementId}: ${result.hash}`);
|
||||||
|
await result.wait();
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
68
scripts/list-contributions-per-contributor.js
Normal file
68
scripts/list-contributions-per-contributor.js
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
const promptly = require('promptly');
|
||||||
|
const Table = require('cli-table');
|
||||||
|
|
||||||
|
const { ethers } = require("hardhat");
|
||||||
|
const Kredits = require('../lib/kredits');
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
kredits = new Kredits(hre.ethers.provider, hre.ethers.provider.getSigner())
|
||||||
|
await kredits.init();
|
||||||
|
|
||||||
|
console.log(`Using Contribution at: ${kredits.Contribution.contract.address}`);
|
||||||
|
|
||||||
|
const table = new Table({
|
||||||
|
head: ['ID', 'Name', 'Kredits']
|
||||||
|
})
|
||||||
|
|
||||||
|
let currentBlockNumber = await kredits.provider.getBlockNumber();
|
||||||
|
console.log(`Current block number: ${currentBlockNumber}`);
|
||||||
|
|
||||||
|
let confirmedBeforeBlock = await promptly.prompt('Before block: ');
|
||||||
|
let confirmedAfterBlock = await promptly.prompt('After block: ');
|
||||||
|
|
||||||
|
let tokens = {};
|
||||||
|
let contributors = await kredits.Contributor.all();
|
||||||
|
contributors.forEach(c => {
|
||||||
|
tokens[c.id] = { amount: 0, contributor: c };
|
||||||
|
});
|
||||||
|
|
||||||
|
let contributionId = await kredits.Contribution.contract.contributionsCount();
|
||||||
|
let nextContribution = true;
|
||||||
|
|
||||||
|
while (nextContribution) {
|
||||||
|
console.log(`Getting contribution: ${contributionId}`);
|
||||||
|
let contribution = await kredits.Contribution.getById(contributionId);
|
||||||
|
contributionId = contributionId - 1;
|
||||||
|
|
||||||
|
// if no conribution is found
|
||||||
|
if (!contribution.exists) {
|
||||||
|
nextContribution = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// check if the contribution is older
|
||||||
|
// in that case we assume all other contributions now are older
|
||||||
|
if (contribution.confirmedAtBlock < confirmedAfterBlock) {
|
||||||
|
nextContribution = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the contribution is within the range count it
|
||||||
|
if (!contribution.vetoed && contribution.confirmedAtBlock < confirmedBeforeBlock && contribution.confirmedAtBlock > confirmedAfterBlock) {
|
||||||
|
// init
|
||||||
|
tokens[contribution.contributorId].amount = tokens[contribution.contributorId].amount + contribution.amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(tokens).forEach((contributorId) => {
|
||||||
|
table.push([
|
||||||
|
contributorId,
|
||||||
|
`${tokens[contributorId].contributor.name}`,
|
||||||
|
`${tokens[contributorId].amount}`
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
const total = Object.keys(tokens).map(cid => { return tokens[cid].amount}).reduce((a,b) => { return a+b }, 0);
|
||||||
|
console.log(`Total confirmed Kredits: ${total} between block ${confirmedAfterBlock} and ${confirmedBeforeBlock}`);
|
||||||
|
console.log(table.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
48
scripts/list-contributions.js
Normal file
48
scripts/list-contributions.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
const promptly = require('promptly');
|
||||||
|
const Table = require('cli-table');
|
||||||
|
|
||||||
|
const { ethers } = require("hardhat");
|
||||||
|
const Kredits = require('../lib/kredits');
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
let kredits;
|
||||||
|
kredits = new Kredits(hre.ethers.provider, hre.ethers.provider.getSigner())
|
||||||
|
await kredits.init();
|
||||||
|
|
||||||
|
console.log(`Using Contribution at: ${kredits.Contribution.contract.address}`);
|
||||||
|
|
||||||
|
const table = new Table({
|
||||||
|
head: ['ID', 'Contributor ID', 'Description', 'Amount', 'Confirmed?', 'Vetoed?', 'IPFS']
|
||||||
|
})
|
||||||
|
|
||||||
|
try {
|
||||||
|
let blockNumber = await kredits.provider.getBlockNumber();
|
||||||
|
let contributions = await kredits.Contribution.all({page: {size: 1000}});
|
||||||
|
|
||||||
|
let kreditsSum = 0;
|
||||||
|
console.log(`Current block number: ${blockNumber}`);
|
||||||
|
contributions.forEach((c) => {
|
||||||
|
const confirmed = c.confirmedAtBlock <= blockNumber;
|
||||||
|
|
||||||
|
table.push([
|
||||||
|
c.id.toString(),
|
||||||
|
c.contributorId,
|
||||||
|
`${c.description}`,
|
||||||
|
c.amount.toString(),
|
||||||
|
`${confirmed} (${c.confirmedAtBlock})`,
|
||||||
|
c.vetoed,
|
||||||
|
c.ipfsHash
|
||||||
|
])
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(table.toString());
|
||||||
|
|
||||||
|
let totalKreditsEarnedUnConfirmed = await kredits.Contribution.contract.totalKreditsEarned(false);
|
||||||
|
let totalKreditsEarnedConfirmed = await kredits.Contribution.contract.totalKreditsEarned(true);
|
||||||
|
console.log(`Total Kredits: ${totalKreditsEarnedConfirmed} (confirmed) | ${totalKreditsEarnedUnConfirmed} (including unconfirmed)`);
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
34
scripts/list-contributors.js
Normal file
34
scripts/list-contributors.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
const Table = require('cli-table');
|
||||||
|
const Kredits = require('../lib/kredits');
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
kredits = new Kredits(hre.ethers.provider, hre.ethers.provider.getSigner())
|
||||||
|
await kredits.init();
|
||||||
|
|
||||||
|
console.log(`Using Contributor at: ${kredits.Contributor.contract.address}`);
|
||||||
|
const count = await kredits.Contributors.count;
|
||||||
|
console.log(`Currently ${count} entries`);
|
||||||
|
|
||||||
|
const table = new Table({
|
||||||
|
head: ['ID', 'Account', 'Name', 'Core?', 'Balance', 'Kredits earned', 'Contributions count', 'IPFS']
|
||||||
|
})
|
||||||
|
|
||||||
|
const contributors = await kredits.Contributor.all()
|
||||||
|
|
||||||
|
contributors.forEach((c) => {
|
||||||
|
table.push([
|
||||||
|
c.id.toString(),
|
||||||
|
c.account,
|
||||||
|
`${c.name}`,
|
||||||
|
c.isCore,
|
||||||
|
c.balanceInt.toString(),
|
||||||
|
c.totalKreditsEarned.toString(),
|
||||||
|
c.contributionsCount.toString(),
|
||||||
|
c.ipfsHash
|
||||||
|
])
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(table.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
49
scripts/list-reimbursements.js
Normal file
49
scripts/list-reimbursements.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
const Table = require("cli-table");
|
||||||
|
const Kredits = require("../lib/kredits");
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
kredits = new Kredits(hre.ethers.provider, hre.ethers.provider.getSigner());
|
||||||
|
await kredits.init();
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`Using Reimbursement at: ${kredits.Reimbursement.contract.address}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const table = new Table({
|
||||||
|
head: ["ID", "Amount", "Token", "recipientId", "Confirmed?", "Vetoed?", "IPFS", "Expenses"],
|
||||||
|
});
|
||||||
|
|
||||||
|
const blockNumber = await kredits.provider.getBlockNumber();
|
||||||
|
const reimbursements = await kredits.Reimbursement.all();
|
||||||
|
|
||||||
|
console.log(`Current block number: ${blockNumber}`);
|
||||||
|
|
||||||
|
reimbursements.forEach((r) => {
|
||||||
|
const confirmed = r.confirmedAtBlock <= blockNumber;
|
||||||
|
|
||||||
|
table.push([
|
||||||
|
r.id.toString(),
|
||||||
|
r.amount.toString(),
|
||||||
|
`${r.token}`,
|
||||||
|
`${r.recipientId}`,
|
||||||
|
`${confirmed}`,
|
||||||
|
`${r.vetoed}`,
|
||||||
|
`${r.ipfsHash}`,
|
||||||
|
`${r.expenses.length}`,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(table.toString());
|
||||||
|
|
||||||
|
const totalAmountUnconfirmed = await kredits.Reimbursement.contract.totalAmount(
|
||||||
|
false
|
||||||
|
);
|
||||||
|
const totalAmountConfirmed = await kredits.Reimbursement.contract.totalAmount(
|
||||||
|
true
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
`Total: ${totalAmountConfirmed} (confirmed) | ${totalAmountUnconfirmed} (including unconfirmed)`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user