Compare commits
1 Commits
master
...
feature/co
Author | SHA1 | Date | |
---|---|---|---|
759436f870 |
@ -1,6 +0,0 @@
|
|||||||
.openzeppelin
|
|
||||||
artifacts
|
|
||||||
cache
|
|
||||||
deployments
|
|
||||||
gitno
|
|
||||||
node_modules
|
|
40
.drone.yml
40
.drone.yml
@ -1,40 +0,0 @@
|
|||||||
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
|
|
@ -1,2 +0,0 @@
|
|||||||
/scripts/
|
|
||||||
/test/
|
|
33
.eslintrc.js
33
.eslintrc.js
@ -1,33 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
'env': {
|
|
||||||
'browser': true,
|
|
||||||
'es6': true,
|
|
||||||
'node': true
|
|
||||||
},
|
|
||||||
'extends': 'eslint:recommended',
|
|
||||||
'globals': {
|
|
||||||
'Atomics': 'readonly',
|
|
||||||
'SharedArrayBuffer': 'readonly'
|
|
||||||
},
|
|
||||||
'parserOptions': {
|
|
||||||
'ecmaVersion': 2018,
|
|
||||||
'sourceType': 'module'
|
|
||||||
},
|
|
||||||
'rules': {
|
|
||||||
'comma-dangle': ['error', {
|
|
||||||
arrays: 'always-multiline',
|
|
||||||
objects: 'always-multiline',
|
|
||||||
imports: 'never',
|
|
||||||
exports: 'never',
|
|
||||||
functions: 'ignore',
|
|
||||||
}],
|
|
||||||
'eol-last': ['error', 'always'],
|
|
||||||
semi: ['error', 'always'],
|
|
||||||
'space-before-function-paren': ['error', {
|
|
||||||
anonymous: 'never',
|
|
||||||
named: 'always',
|
|
||||||
asyncArrow: 'always',
|
|
||||||
}],
|
|
||||||
'indent': ['error', 2]
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
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
|
|
@ -1,11 +0,0 @@
|
|||||||
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
4
.github/release-drafter.yml
vendored
@ -1,4 +0,0 @@
|
|||||||
template: |
|
|
||||||
## Changes
|
|
||||||
|
|
||||||
$CHANGES
|
|
12
.gitignore
vendored
12
.gitignore
vendored
@ -1,13 +1,3 @@
|
|||||||
|
build
|
||||||
node_modules
|
node_modules
|
||||||
**/node_modules
|
|
||||||
.ganache-db
|
.ganache-db
|
||||||
.tm_properties
|
|
||||||
yarn-error.log
|
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
data
|
|
||||||
|
|
||||||
cache
|
|
||||||
artifacts
|
|
||||||
.openzeppelin/unknown-1337.json
|
|
||||||
.env
|
|
||||||
|
@ -1,937 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": [
|
|
||||||
"solhint:default",
|
|
||||||
"solhint:recommended"
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"max-line-length": "warn"
|
|
||||||
}
|
|
||||||
}
|
|
13
Dockerfile
13
Dockerfile
@ -1,13 +0,0 @@
|
|||||||
# 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
158
README.md
@ -1,158 +0,0 @@
|
|||||||
[](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
Normal file
153
README.mdown
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
# 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,91 +1,9 @@
|
|||||||
const contractCalls = [
|
let contractCalls = [
|
||||||
['Contributor', 'add', [{
|
['Contributor', 'add', [{ account: '0x7e8f313c56f809188313aa274fa67ee58c31515d', name: 'bumi', isCore: true, kind: 'preson', url: '', github_username: 'bumi', github_uid: 318, wiki_username: 'bumi' }, {gasLimit: 200000}]],
|
||||||
account: '0x7e8f313c56f809188313aa274fa67ee58c31515d',
|
['Contributor', 'add', [{ account: '0xa502eb4021f3b9ab62f75b57a94e1cfbf81fd827', name: 'raucau', isCore: true, kind: 'person', url: '', github_username: 'skddc', github_uid: 842, wiki_username: 'raucau' }, {gasLimit: 200000}]],
|
||||||
name: 'bumi',
|
['Operator', 'addProposal', [{ contributorId: 2, amount: 42, kind: 'code', description: 'runs the seeds', url: '' }, {gasLimit: 350000}]],
|
||||||
kind: 'person',
|
['Operator', 'addProposal', [{ contributorId: 3, amount: 23, kind: 'code', description: 'runs the seeds', url: '' }, {gasLimit: 350000}]],
|
||||||
url: '',
|
['Operator', 'addProposal', [{contributorId: 3, amount: 100, kind: 'code', description: 'hacks on kredits', url: '' }, {gasLimit: 350000}]],
|
||||||
github_username: 'bumi',
|
['Operator', 'vote', ['1', {gasLimit: 250000}]]
|
||||||
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 };
|
|
||||||
|
@ -1,199 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,178 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
128
contracts/Contributors.sol
Normal file
128
contracts/Contributors.sol
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
23
contracts/Migrations.sol
Normal file
23
contracts/Migrations.sol
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
123
contracts/Operator.sol
Normal file
123
contracts/Operator.sol
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,139 +0,0 @@
|
|||||||
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,41 +1,27 @@
|
|||||||
pragma solidity ^0.8.0;
|
pragma solidity ^0.4.18;
|
||||||
|
|
||||||
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
|
import 'zeppelin-solidity/contracts/token/ERC20/BasicToken.sol';
|
||||||
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";
|
import './upgradeable/Upgradeable.sol';
|
||||||
|
|
||||||
|
contract Token is Upgradeable, BasicToken {
|
||||||
|
string public name;
|
||||||
|
string public symbol;
|
||||||
|
uint8 public decimals;
|
||||||
|
|
||||||
|
event LogMint(address indexed recipient, uint256 amount, uint256 proposalId);
|
||||||
|
|
||||||
|
function initialize(address sender) public payable {
|
||||||
|
require(msg.sender == address(registry));
|
||||||
|
name = 'Kredits';
|
||||||
|
symbol = 'K';
|
||||||
|
decimals = 18;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mintFor(address contributorAccount, uint256 amount, uint proposalId) onlyRegistryContractFor('Operator') public {
|
||||||
|
totalSupply_ = totalSupply_.add(amount);
|
||||||
|
balances[contributorAccount] = balances[contributorAccount].add(amount);
|
||||||
|
|
||||||
|
LogMint(contributorAccount, amount, proposalId);
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
53
contracts/upgradeable/IRegistry.sol
Normal file
53
contracts/upgradeable/IRegistry.sol
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
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);
|
||||||
|
}
|
23
contracts/upgradeable/Migrations.sol
Normal file
23
contracts/upgradeable/Migrations.sol
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
36
contracts/upgradeable/Proxy.sol
Normal file
36
contracts/upgradeable/Proxy.sol
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
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) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
86
contracts/upgradeable/Registry.sol
Normal file
86
contracts/upgradeable/Registry.sol
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
28
contracts/upgradeable/UpgradeabilityProxy.sol
Normal file
28
contracts/upgradeable/UpgradeabilityProxy.sol
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
37
contracts/upgradeable/UpgradeabilityStorage.sol
Normal file
37
contracts/upgradeable/UpgradeabilityStorage.sol
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
19
contracts/upgradeable/Upgradeable.sol
Normal file
19
contracts/upgradeable/Upgradeable.sol
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
@ -1,83 +0,0 @@
|
|||||||
# 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
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
|||||||
# 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
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
|||||||
# 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
|
|
@ -1,62 +0,0 @@
|
|||||||
# 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"
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
|||||||
# 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
|
|
@ -1,34 +0,0 @@
|
|||||||
# 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"
|
|
Binary file not shown.
Before Width: | Height: | Size: 15 KiB |
@ -1,90 +0,0 @@
|
|||||||
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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
lib/abis/Contributors.json
Normal file
1
lib/abis/Contributors.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"contributors","outputs":[{"name":"account","type":"address"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"isCore","type":"bool"},{"name":"exists","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"implementation","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_proxiedContractName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"contributorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"contributorIds","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"oldIpfsHash","type":"bytes32"},{"indexed":false,"name":"newIpfsHash","type":"bytes32"}],"name":"ContributorProfileUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"oldAccount","type":"address"},{"indexed":false,"name":"newAccount","type":"address"}],"name":"ContributorAccountUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"account","type":"address"}],"name":"ContributorAdded","type":"event"},{"constant":false,"inputs":[{"name":"sender","type":"address"}],"name":"initialize","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"coreContributorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"id","type":"uint256"},{"name":"oldAccount","type":"address"},{"name":"newAccount","type":"address"}],"name":"updateContributorAccount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"id","type":"uint256"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"}],"name":"updateContributorIpfsHash","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"account","type":"address"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"isCore","type":"bool"}],"name":"addContributor","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"id","type":"uint256"}],"name":"isCore","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"id","type":"uint256"}],"name":"exists","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"addressIsCore","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"addressExists","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"getContributorIdByAddress","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"id","type":"uint256"}],"name":"getContributorAddressById","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_id","type":"uint256"}],"name":"getContributorById","outputs":[{"name":"id","type":"uint256"},{"name":"account","type":"address"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"isCore","type":"bool"},{"name":"balance","type":"uint256"},{"name":"exists","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}]
|
1
lib/abis/Operator.json
Normal file
1
lib/abis/Operator.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"proposals","outputs":[{"name":"creatorAccount","type":"address"},{"name":"contributorId","type":"uint256"},{"name":"votesCount","type":"uint256"},{"name":"votesNeeded","type":"uint256"},{"name":"amount","type":"uint256"},{"name":"executed","type":"bool"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"exists","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proposalsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"implementation","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_proxiedContractName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"sender","type":"address"}],"name":"initialize","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"creatorAccount","type":"address"},{"indexed":false,"name":"contributorId","type":"uint256"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"ProposalCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"voterId","type":"uint256"},{"indexed":false,"name":"totalVotes","type":"uint256"}],"name":"ProposalVoted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"contributorId","type":"uint256"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"ProposalExecuted","type":"event"},{"constant":true,"inputs":[],"name":"contributorsContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"contributorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"coreContributorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"contributorId","type":"uint256"},{"name":"amount","type":"uint256"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"}],"name":"addProposal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"proposalId","type":"uint256"}],"name":"getProposal","outputs":[{"name":"id","type":"uint256"},{"name":"creatorAccount","type":"address"},{"name":"contributorId","type":"uint256"},{"name":"votesCount","type":"uint256"},{"name":"votesNeeded","type":"uint256"},{"name":"amount","type":"uint256"},{"name":"executed","type":"bool"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"voterIds","type":"uint256[]"},{"name":"exists","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"proposalId","type":"uint256"}],"name":"vote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]
|
1
lib/abis/Registry.json
Normal file
1
lib/abis/Registry.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
[{"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 +0,0 @@
|
|||||||
[{"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 @@
|
|||||||
[{"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"}]
|
[{"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"}]
|
@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"31": {
|
|
||||||
"Contributor": "0x95DC31665D193E377f54b70C535fcDb205525291",
|
|
||||||
"Contribution": "0x049bA8E70FEbFfd6d03C71211bDA37B4ff064115",
|
|
||||||
"Token": "0x7ab26A0f00eF0D6e05e5BDE047505a4eD53aF809",
|
|
||||||
"Reimbursement": "0x99EC72b34295b62f4bC1527Da461262c615a0b2c"
|
|
||||||
},
|
|
||||||
"1337": {
|
|
||||||
"Contributor": "0xCc66f9A3cA2670972938FAD91d0865c4a62DFB25",
|
|
||||||
"Contribution": "0x8999CaBc43E28202c5A2257f2a95A45b1F8A62BD",
|
|
||||||
"Token": "0xe082678eCF749982e33Ea6839852a8cd989aEDE2",
|
|
||||||
"Reimbursement": "0x984f797d26d3da2E9b9f8Ae4eeFEACC60fCAA90C"
|
|
||||||
}
|
|
||||||
}
|
|
1
lib/addresses/Contributors.json
Normal file
1
lib/addresses/Contributors.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"42":"0x205fe1b3dac678b594c5f0535e7d158e38591f93","100":"0xa7fc9b1f678c41396b53904f94f50a42ff44d826"}
|
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"4": "0x76e069b47b79442657eaf0555a32c6b16fa1b8b4"
|
|
||||||
}
|
|
1
lib/addresses/Operator.json
Normal file
1
lib/addresses/Operator.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"42":"0x9fd66ee78a5ebe86006f12b37ff59c63f9caa15b","100":"0x95d3bd7d136bb0b7ac9988097e964236f8a9976e"}
|
1
lib/addresses/Registry.json
Normal file
1
lib/addresses/Registry.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"42":"0xc270e6ea4fe303df9f1a3d4a132ac425264082e7","100":"0x7458dea485d9d8301e3ce43e8a1ec1456be5ba83"}
|
1
lib/addresses/Token.json
Normal file
1
lib/addresses/Token.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"42":"0xf71ccf7ab48044ef9ae0b5e6983dbd3266b78b36","100":"0x3fc29fbe40c2d0ca78c7e81342f00226650fe2ad"}
|
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"4": "0xc34edf7d11b7f8433d597f0bb0697acdff55ef14"
|
|
||||||
}
|
|
@ -1,22 +1,27 @@
|
|||||||
const deprecate = require('../utils/deprecate');
|
|
||||||
|
|
||||||
class Base {
|
class Base {
|
||||||
constructor (contract) {
|
constructor(contract) {
|
||||||
this.contract = contract;
|
this.contract = contract;
|
||||||
}
|
}
|
||||||
|
|
||||||
get functions () {
|
get functions() {
|
||||||
deprecate('The property `functions` is deprecated. contract functions are now directly defined on the ethers contract object. https://github.com/ethers-io/ethers.js/issues/920#issuecomment-650836642');
|
return this.contract.functions;
|
||||||
return this.contract;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get address () {
|
get ipfs() {
|
||||||
return this.contract.address;
|
if (!this._ipfsAPI) { throw new Error('IPFS API not configured; please set an ipfs instance'); }
|
||||||
|
return this._ipfsAPI;
|
||||||
}
|
}
|
||||||
|
|
||||||
on (type, callback) {
|
set ipfs(ipfsAPI) {
|
||||||
return this.contract.on(type, callback);
|
this._ipfsAPI = ipfsAPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
@ -1,76 +0,0 @@
|
|||||||
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,100 +1,57 @@
|
|||||||
const Record = require('./record');
|
const ethers = require('ethers');
|
||||||
const ContributorSerializer = require('../serializers/contributor');
|
const RSVP = require('rsvp');
|
||||||
const formatKredits = require('../utils/format-kredits');
|
|
||||||
|
|
||||||
class Contributor extends Record {
|
const ContributorSerializer = require('../serializers/contributor');
|
||||||
get count () {
|
const Base = require('./base');
|
||||||
return this.contract.contributorsCount();
|
|
||||||
|
class Contributor extends Base {
|
||||||
|
all() {
|
||||||
|
return this.functions.contributorsCount()
|
||||||
|
.then((count) => {
|
||||||
|
count = count.toNumber();
|
||||||
|
let contributors = [];
|
||||||
|
|
||||||
|
for (let id = 1; id <= count; id++) {
|
||||||
|
contributors.push(this.getById(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
getById (id) {
|
return RSVP.all(contributors);
|
||||||
return this.contract.getContributorById(id)
|
});
|
||||||
.then(contractData => {
|
}
|
||||||
let data = {...contractData};
|
|
||||||
data.balanceInt = formatKredits(data.balance);
|
getById(id) {
|
||||||
|
id = ethers.utils.bigNumberify(id);
|
||||||
|
|
||||||
|
return this.functions.getContributorById(id)
|
||||||
|
.then((data) => {
|
||||||
|
// TODO: remove when naming updated on the contract
|
||||||
|
data.hashDigest = data.ipfsHash;
|
||||||
|
return data;
|
||||||
|
})
|
||||||
|
// Fetch IPFS data if available
|
||||||
|
.then((data) => {
|
||||||
return this.ipfs.catAndMerge(data, ContributorSerializer.deserialize);
|
return this.ipfs.catAndMerge(data, ContributorSerializer.deserialize);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getData (id) {
|
add(contributorAttr, callOptions = {}) {
|
||||||
return this.contract.getContributorById(id);
|
let json = ContributorSerializer.serialize(contributorAttr);
|
||||||
}
|
// 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(jsonStr)
|
.add(json)
|
||||||
.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,
|
||||||
];
|
];
|
||||||
|
|
||||||
// console.log('Adding onchain record for', contributorAttr.account);
|
return this.functions.addContributor(...contributor, callOptions);
|
||||||
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,7 +1,6 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
Contributor: require('./contributor'),
|
Contributors: require('./contributor'),
|
||||||
Contribution: require('./contribution'),
|
Operator: require('./operator'),
|
||||||
Proposal: require('./proposal'),
|
|
||||||
Token: require('./token'),
|
Token: require('./token'),
|
||||||
Reimbursement: require('./reimbursement'),
|
Registry: require('./registry')
|
||||||
};
|
};
|
||||||
|
57
lib/contracts/operator.js
Normal file
57
lib/contracts/operator.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
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;
|
@ -1,46 +0,0 @@
|
|||||||
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;
|
|
@ -1,14 +0,0 @@
|
|||||||
const Base = require('./base');
|
|
||||||
const paged = require('../utils/pagination');
|
|
||||||
|
|
||||||
class Record extends Base {
|
|
||||||
all (options = {}) {
|
|
||||||
return this.count
|
|
||||||
.then((count) => {
|
|
||||||
let records = paged(count, options).map((id) => this.getById(id));
|
|
||||||
return Promise.all(records);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Record;
|
|
6
lib/contracts/registry.js
Normal file
6
lib/contracts/registry.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
const Base = require('./base');
|
||||||
|
|
||||||
|
class Registry extends Base {
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Registry;
|
@ -1,71 +0,0 @@
|
|||||||
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,3 +4,4 @@ class Token extends Base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Token;
|
module.exports = Token;
|
||||||
|
|
||||||
|
117
lib/kredits.js
117
lib/kredits.js
@ -1,113 +1,78 @@
|
|||||||
const ethers = require('ethers');
|
const ethers = require('ethers');
|
||||||
|
const RSVP = require('rsvp');
|
||||||
|
|
||||||
const Preflight = require('./utils/preflight');
|
const Healthcheck = require('./utils/healthcheck');
|
||||||
const deprecate = require('./utils/deprecate');
|
|
||||||
|
|
||||||
const ABIS = {
|
const ABIS = {
|
||||||
Contributor: require('./abis/Contributor.json'),
|
Contributors: require('./abis/Contributors.json'),
|
||||||
Contribution: require('./abis/Contribution.json'),
|
Operator: require('./abis/Operator.json'),
|
||||||
Reimbursement: require('./abis/Reimbursement.json'),
|
Registry: require('./abis/Registry.json'),
|
||||||
Token: require('./abis/Token.json'),
|
Token: require('./abis/Token.json')
|
||||||
};
|
};
|
||||||
// const APP_CONTRACTS = [
|
const RegistryAddress = require('./addresses/Registry.json');
|
||||||
// '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 = {}) {
|
||||||
const { addresses, abis, ipfsConfig } = options;
|
let { addresses, abis, ipfsConfig } = options;
|
||||||
|
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this.signer = signer;
|
this.signer = signer;
|
||||||
this.options = options;
|
// by default we only need the registry address.
|
||||||
this.addresses = addresses || {};
|
// the rest is loaded from there in the init() function
|
||||||
|
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) {
|
||||||
// TODO implement
|
let contractsToLoad = names || Object.keys(ABIS);
|
||||||
// const contractsToLoad = names || APP_CONTRACTS;
|
let addressPromises = contractsToLoad.map((contractName) => {
|
||||||
|
return this.Registry.functions.getProxyFor(contractName).then((address) => {
|
||||||
return this.provider.getNetwork().then(network => {
|
this.addresses[contractName] = address;
|
||||||
if (Object.keys(this.addresses).length === 0) {
|
}).catch((error) => {
|
||||||
this.addresses = Addresses[network.chainId.toString()];
|
throw new Error(`Failed to get address for ${contractName} from registry at ${this.Registry.contract.address}
|
||||||
}
|
- correct registry? does it have version entry? - ${error.message}`
|
||||||
return this;
|
);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
return RSVP.all(addressPromises).then(() => { return this });
|
||||||
}
|
}
|
||||||
|
|
||||||
static setup (provider, signer, ipfsConfig = null) {
|
static setup(provider, signer, ipfsConfig = null) {
|
||||||
deprecate('Kredits.setup() is deprecated use new Kredits().init() instead');
|
console.log('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();
|
||||||
}
|
}
|
||||||
|
|
||||||
static for (connectionOptions, kreditsOptions) {
|
get Registry() {
|
||||||
let { network, rpcUrl, wallet } = connectionOptions;
|
return this.contractFor('registry');
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static availableNetworks () {
|
get Contributor() {
|
||||||
return Object.keys(Addresses);
|
// TODO: rename to contributor
|
||||||
|
return this.contractFor('contributors');
|
||||||
}
|
}
|
||||||
|
|
||||||
get Contributor () {
|
get Operator() {
|
||||||
return this.contractFor('Contributor');
|
return this.contractFor('operator');
|
||||||
}
|
}
|
||||||
|
|
||||||
get Contributors () {
|
get Token() {
|
||||||
deprecate('Contributors is deprecated use Contributor instead');
|
return this.contractFor('token');
|
||||||
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];
|
||||||
}
|
}
|
||||||
@ -119,16 +84,16 @@ class Kredits {
|
|||||||
throw new Error(`Address or ABI not found for ${contractName}`);
|
throw new Error(`Address or ABI not found for ${contractName}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const signerOrProvider = this.signer || this.provider;
|
let signerOrProvider = this.signer || this.provider;
|
||||||
const contract = new ethers.Contract(address, abi, signerOrProvider);
|
let 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];
|
||||||
}
|
}
|
||||||
|
|
||||||
preflightChecks () {
|
healthcheck() {
|
||||||
return new Preflight(this).check();
|
return new Healthcheck(this).check();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
const ethers = require('ethers');
|
|
||||||
|
|
||||||
const ABI = require('./abis/KreditsKit.json');
|
|
||||||
const Addresses = require('./addresses/KreditsKit.json');
|
|
||||||
|
|
||||||
class KreditsKit {
|
|
||||||
|
|
||||||
constructor (provider, signer, options = {}) {
|
|
||||||
let { address, abi } = options;
|
|
||||||
|
|
||||||
this.provider = provider;
|
|
||||||
this.signer = signer;
|
|
||||||
this.options = options;
|
|
||||||
this.address = address;
|
|
||||||
this.abi = abi || ABI;
|
|
||||||
}
|
|
||||||
|
|
||||||
init () {
|
|
||||||
return this.provider.getNetwork().then((network) => {
|
|
||||||
this.address = this.address || Addresses[network.chainId.toString()];
|
|
||||||
this.contract = new ethers.Contract(
|
|
||||||
this.address,
|
|
||||||
this.abi,
|
|
||||||
(this.signer || this.provider)
|
|
||||||
);
|
|
||||||
return this;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
appIdFor (contractName) {
|
|
||||||
// see appIds in KreditsKit.sol for more details
|
|
||||||
const knownContracts = ['Contribution', 'Contributor', 'Proposal', '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,75 +1,19 @@
|
|||||||
const schemas = require('@kosmos/schemas');
|
|
||||||
const validator = require('../utils/validator');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialization and validation for JSON-LD document of the contribution.
|
* Handle serialization for JSON-LD object of the contribution, according to
|
||||||
|
* https://github.com/67P/kosmos-schemas/blob/master/schemas/contribution.json
|
||||||
*
|
*
|
||||||
* @class
|
* @class
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
class Contribution {
|
class Contribution {
|
||||||
|
|
||||||
constructor (attrs) {
|
|
||||||
Object.keys(attrs).forEach(a => this[a] = attrs[a]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize object to JSON
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
serialize () {
|
|
||||||
let {
|
|
||||||
contributorIpfsHash,
|
|
||||||
date,
|
|
||||||
time,
|
|
||||||
kind,
|
|
||||||
description,
|
|
||||||
url,
|
|
||||||
details,
|
|
||||||
} = this;
|
|
||||||
|
|
||||||
let data = {
|
|
||||||
'@context': 'https://schema.kosmos.org',
|
|
||||||
'@type': 'Contribution',
|
|
||||||
'contributor': {
|
|
||||||
'ipfs': contributorIpfsHash,
|
|
||||||
},
|
|
||||||
date,
|
|
||||||
time,
|
|
||||||
kind,
|
|
||||||
description,
|
|
||||||
'details': details || {},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (url) {
|
|
||||||
data['url'] = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write it pretty to ipfs
|
|
||||||
return JSON.stringify(data, null, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate serialized data against schema
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
validate () {
|
|
||||||
const serialized = JSON.parse(this.serialize());
|
|
||||||
const valid = validator.validate(serialized, schemas['contribution']);
|
|
||||||
return valid ? Promise.resolve() : Promise.reject(validator.error);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize JSON to object
|
* Deserialize JSON to object
|
||||||
*
|
*
|
||||||
|
* @method
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
static deserialize (serialized) {
|
static deserialize(serialized) {
|
||||||
let {
|
let {
|
||||||
date,
|
|
||||||
time,
|
|
||||||
kind,
|
kind,
|
||||||
description,
|
description,
|
||||||
details,
|
details,
|
||||||
@ -77,8 +21,6 @@ class Contribution {
|
|||||||
} = JSON.parse(serialized.toString('utf8'));
|
} = JSON.parse(serialized.toString('utf8'));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
date,
|
|
||||||
time,
|
|
||||||
kind,
|
kind,
|
||||||
description,
|
description,
|
||||||
details,
|
details,
|
||||||
@ -87,6 +29,39 @@ class Contribution {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize object to JSON
|
||||||
|
*
|
||||||
|
* @method
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
static serialize(deserialized) {
|
||||||
|
let {
|
||||||
|
contributorIpfsHash,
|
||||||
|
kind,
|
||||||
|
description,
|
||||||
|
url,
|
||||||
|
details
|
||||||
|
} = deserialized;
|
||||||
|
|
||||||
|
let data = {
|
||||||
|
"@context": "https://schema.kosmos.org",
|
||||||
|
"@type": "Contribution",
|
||||||
|
"contributor": {
|
||||||
|
"ipfs": contributorIpfsHash
|
||||||
|
},
|
||||||
|
kind,
|
||||||
|
description,
|
||||||
|
"details": details || {}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (url) {
|
||||||
|
data["url"] = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write it pretty to ipfs
|
||||||
|
return JSON.stringify(data, null, 2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Contribution;
|
module.exports = Contribution;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
const schemas = require('@kosmos/schemas');
|
|
||||||
const validator = require('../utils/validator');
|
|
||||||
/**
|
/**
|
||||||
* Handle serialization for JSON-LD object of the contributor, according to
|
* 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
|
||||||
@ -8,95 +6,13 @@ const validator = require('../utils/validator');
|
|||||||
* @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,
|
||||||
@ -104,24 +20,16 @@ class Contributor {
|
|||||||
accounts,
|
accounts,
|
||||||
} = JSON.parse(serialized.toString('utf8'));
|
} = JSON.parse(serialized.toString('utf8'));
|
||||||
|
|
||||||
let github_username, github_uid, gitea_username, wiki_username, zoom_display_name;
|
let github_username, github_uid, wiki_username;
|
||||||
let github = accounts.find(a => a.site === 'github.com');
|
let github = accounts.find((a) => a.site === 'github.com');
|
||||||
let gitea = accounts.find(a => a.site === 'gitea.kosmos.org');
|
let wiki = accounts.find((a) => a.site === 'wiki.kosmos.org');
|
||||||
let 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,
|
||||||
@ -130,13 +38,59 @@ 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;
|
||||||
|
@ -1,104 +0,0 @@
|
|||||||
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;
|
|
@ -1,5 +0,0 @@
|
|||||||
/*eslint no-console: ["error", { allow: ["warn"] }] */
|
|
||||||
|
|
||||||
module.exports = function deprecate (msg) {
|
|
||||||
console.warn(msg);
|
|
||||||
};
|
|
@ -1,10 +0,0 @@
|
|||||||
const ethersUtils = require('ethers').utils;
|
|
||||||
|
|
||||||
module.exports = function(value, options = {}) {
|
|
||||||
let etherValue = ethersUtils.formatEther(value);
|
|
||||||
if (options.asFloat) {
|
|
||||||
return parseFloat(etherValue);
|
|
||||||
} else {
|
|
||||||
return parseInt(etherValue);
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,9 +1,9 @@
|
|||||||
class Preflight {
|
class Healthcheck {
|
||||||
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 Preflight {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Preflight;
|
module.exports = Healthcheck;
|
@ -1,25 +1,17 @@
|
|||||||
const ipfsClient = require('ipfs-http-client');
|
const ipfsAPI = require('ipfs-api');
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async catAndMerge (contractData, deserialize) {
|
catAndMerge(data, 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;
|
||||||
@ -29,57 +21,42 @@ 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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async add (data) {
|
add(data) {
|
||||||
return this._ipfsAPI.add(data)
|
return this._ipfsAPI
|
||||||
.then(res => {
|
.add(new this._ipfsAPI.Buffer(data))
|
||||||
return this.decodeHash(res.path);
|
.then((res) => {
|
||||||
|
return this.decodeHash(res[0].hash);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async cat (hashData) {
|
cat(hashData) {
|
||||||
let ipfsHash = hashData; // default - if it is a string
|
let ipfsHash = hashData; // default - if it is a string
|
||||||
if (Object.prototype.hasOwnProperty.call(hashData, 'hashSize')) {
|
if (hashData.hasOwnProperty('hashSize')) {
|
||||||
ipfsHash = this.encodeHash(hashData);
|
ipfsHash = this.encodeHash(hashData);
|
||||||
}
|
}
|
||||||
if (this._config['gatewayUrl']) {
|
return this._ipfsAPI.cat(ipfsHash);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pin (hashData) {
|
decodeHash(ipfsHash) {
|
||||||
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) {
|
||||||
const digest = Buffer.from(hashData.hashDigest.slice(2), 'hex');
|
let digest = this._ipfsAPI.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;
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
function pageNumber (number, size, recordCount) {
|
|
||||||
let numberOfPages = Math.ceil(recordCount / size);
|
|
||||||
|
|
||||||
number = parseInt(number) || 1;
|
|
||||||
|
|
||||||
// Ensure page number is in range
|
|
||||||
number = number > numberOfPages ? numberOfPages : number;
|
|
||||||
number = number < 1 ? 1 : number;
|
|
||||||
|
|
||||||
return number;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildIds (order, number, size, recordCount) {
|
|
||||||
let offset = size * (number - 1);
|
|
||||||
|
|
||||||
let start;
|
|
||||||
let mapFunction;
|
|
||||||
|
|
||||||
if (order === 'asc') {
|
|
||||||
start = 1 + offset;
|
|
||||||
mapFunction = (_, i) => start + i;
|
|
||||||
} else {
|
|
||||||
start = recordCount - offset;
|
|
||||||
mapFunction = (_, i) => start - i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure size is in range
|
|
||||||
let end = offset + size;
|
|
||||||
if (end > recordCount) {
|
|
||||||
let diff = end - recordCount;
|
|
||||||
size = size - diff;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Array.from({ length: size }, mapFunction);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = function paged (recordCount, options = {}) {
|
|
||||||
let { order, page } = options;
|
|
||||||
order = order || 'desc';
|
|
||||||
page = page || {};
|
|
||||||
|
|
||||||
let size = parseInt(page.size) || 25;
|
|
||||||
let number = pageNumber(page.number, size, recordCount);
|
|
||||||
|
|
||||||
return buildIds(order, number, size, recordCount);
|
|
||||||
};
|
|
@ -1,15 +0,0 @@
|
|||||||
const tv4 = require('tv4');
|
|
||||||
const validator = tv4.freshApi();
|
|
||||||
|
|
||||||
validator.addFormat({
|
|
||||||
'date': function(value) {
|
|
||||||
const dateRegexp = /^[0-9]{4,}-[0-9]{2}-[0-9]{2}$/;
|
|
||||||
return dateRegexp.test(value) ? null : 'A valid ISO 8601 full-date string is expected';
|
|
||||||
},
|
|
||||||
'time': function(value) {
|
|
||||||
const timeRegexp = /^([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([Zz])|([+|-]([01][0-9]|2[0-3]):[0-5][0-9]))$/;
|
|
||||||
return timeRegexp.test(value) ? null : 'A valid ISO 8601 full-time string is expected';
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = validator;
|
|
5
migrations/1519035571_registry.js
Normal file
5
migrations/1519035571_registry.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
var Registry = artifacts.require('./upgradeable/Registry.sol');
|
||||||
|
|
||||||
|
module.exports = function(deployer) {
|
||||||
|
deployer.deploy(Registry);
|
||||||
|
};
|
15
migrations/1520798600_token.js
Normal file
15
migrations/1520798600_token.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
13
migrations/1522427321_contributors.js
Normal file
13
migrations/1522427321_contributors.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
13
migrations/1522430935_operator.js
Normal file
13
migrations/1522430935_operator.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
5
migrations/1_initial_migration.js
Normal file
5
migrations/1_initial_migration.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
var Migrations = artifacts.require("./Migrations.sol");
|
||||||
|
|
||||||
|
module.exports = function(deployer) {
|
||||||
|
deployer.deploy(Migrations);
|
||||||
|
};
|
39085
package-lock.json
generated
39085
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
78
package.json
78
package.json
@ -1,80 +1,38 @@
|
|||||||
{
|
{
|
||||||
"name": "@kredits/contracts",
|
"name": "kredits-contracts",
|
||||||
"version": "7.5.0",
|
"version": "1.0.0",
|
||||||
"description": "Smart contracts and JavaScript API for Kredits",
|
"description": "Ethereum contracts and npm wrapper for Kredits",
|
||||||
"main": "./lib/kredits.js",
|
"main": "./lib/kredits.js",
|
||||||
"directories": {
|
"directories": {
|
||||||
"test": "test"
|
"test": "test"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"wallet:create": "hardhat create-wallet",
|
"build-json": "truffle compile && node ./scripts/build-json.js",
|
||||||
"devchain": "hardhat node --network hardhat",
|
"bootstrap": "truffle migrate --reset && npm run build-json && truffle exec scripts/seeds.js",
|
||||||
"deploy:dao": "hardhat run scripts/create-proxy.js",
|
"ganache": "ganache-cli -p 7545 -i 100 --db=./.ganache-db -m kredits",
|
||||||
"postshrinkwrap": "node scripts/fix-package-lock.js &>/dev/null || true",
|
"dev": "truffle migrate && npm run build-json",
|
||||||
"build": "npm run build:contracts && npm run build:json",
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
"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/kredits-contracts.git"
|
"url": "git+https://github.com/67P/truffle-kredits.git"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/67P/kredits-contracts/issues"
|
"url": "https://github.com/67P/truffle-kredits/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/67P/kredits-contracts#readme",
|
"homepage": "https://github.com/67P/truffle-kredits#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",
|
||||||
"chai": "^4.3.6",
|
"ganache-cli": "^6.0.3",
|
||||||
"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",
|
||||||
"solhint": "^3.3.7",
|
"truffle": "^4.1.3",
|
||||||
"truffle-hdwallet-provider": "^1.0.17",
|
"zeppelin-solidity": "^1.7.0"
|
||||||
"truffle-hdwallet-provider-privkey": "^0.3.0",
|
|
||||||
"web3-providers-ws": "^1.7.3",
|
|
||||||
"yargs": "^15.0.0"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kosmos/schemas": "^3.2.0",
|
"ethers": "3.0.15",
|
||||||
"ethers": "^5.4.7",
|
"ipfs-api": "^19.0.0",
|
||||||
"ipfs-http-client": "^56.0.3",
|
"rsvp": "^4.8.2"
|
||||||
"multihashes": "^4.0.3",
|
}
|
||||||
"node-fetch": "^2.6.0",
|
|
||||||
"tv4": "^1.3.0"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"kosmos",
|
|
||||||
"kredits"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
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,6 +1,8 @@
|
|||||||
|
const Registry = artifacts.require('./Registry.sol');
|
||||||
const promptly = require('promptly');
|
const promptly = require('promptly');
|
||||||
|
const bs58 = require('bs58');
|
||||||
|
|
||||||
const { ethers } = require("hardhat");
|
const ethers = require('ethers');
|
||||||
const Kredits = require('../lib/kredits');
|
const Kredits = require('../lib/kredits');
|
||||||
|
|
||||||
async function prompt(message, options) {
|
async function prompt(message, options) {
|
||||||
@ -9,32 +11,40 @@ 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())
|
module.exports = function(callback) {
|
||||||
await kredits.init();
|
Registry.deployed().then(async (registry) => {
|
||||||
|
|
||||||
|
const networkId = parseInt(web3.version.network);
|
||||||
|
const provider = new ethers.providers.Web3Provider(
|
||||||
|
web3.currentProvider, { chainId: networkId }
|
||||||
|
);
|
||||||
|
const kredits = await Kredits.setup(provider, provider.getSigner());
|
||||||
|
|
||||||
console.log(`Using contributors at: ${kredits.Contributor.contract.address}`);
|
console.log(`Using contributors at: ${kredits.Contributor.contract.address}`);
|
||||||
|
|
||||||
let contributorAttributes = {
|
let contributorAttributes = {
|
||||||
account: await prompt('Contributor address: ', {}),
|
account: await prompt('Contributor address: ', {}),
|
||||||
name: await prompt('Name: '),
|
name: await prompt('Name: '),
|
||||||
|
isCore: await prompt('core? y/n') === 'y',
|
||||||
kind: await prompt('Kind (default person): ', {default: 'person'}),
|
kind: await prompt('Kind (default person): ', {default: 'person'}),
|
||||||
url: await prompt('URL: '),
|
url: await prompt('URL: '),
|
||||||
github_username: await prompt('GitHub username: '),
|
github_username: await prompt('GitHub username: '),
|
||||||
github_uid: parseInt(await prompt('GitHub UID: ')),
|
github_uid: await prompt('GitHub UID: '),
|
||||||
wiki_username: await prompt('Wiki username: '),
|
wiki_username: await prompt('Wiki username: '),
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log("\nAdding contributor:");
|
console.log("\nAdding contributor:");
|
||||||
console.log(contributorAttributes);
|
console.log(contributorAttributes);
|
||||||
|
|
||||||
kredits.Contributor.add(contributorAttributes, { gasLimit: 350000 }).then((result) => {
|
kredits.Contributor.add(contributorAttributes, {gasLimit: 250000}).then((result) => {
|
||||||
console.log("\n\nResult:");
|
console.log("\n\nResult:");
|
||||||
console.log(result);
|
console.log(result);
|
||||||
|
callback();
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.log('Failed to create contributor');
|
console.log('Failed to create contributor');
|
||||||
console.log(error);
|
callback(error);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
main();
|
|
||||||
|
38
scripts/add-proposal.js
Normal file
38
scripts/add-proposal.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
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,18 +1,33 @@
|
|||||||
const fs = require("fs");
|
const fs = require('fs');
|
||||||
const path = require("path");
|
const path = require('path');
|
||||||
|
|
||||||
const contractsPath = path.join(__dirname, "..", "artifacts", "contracts");
|
const contractsPath = path.join(__dirname, '..', 'build', '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 = ["Contributor", "Contribution", "Token", "Reimbursement"];
|
const files = [
|
||||||
|
'Contributors',
|
||||||
|
'Operator',
|
||||||
|
'Registry',
|
||||||
|
'Token'
|
||||||
|
];
|
||||||
|
|
||||||
files.forEach((fileName) => {
|
files.forEach((fileName) => {
|
||||||
let file = require(`${contractsPath}/${fileName}.sol/${fileName}.json`);
|
let file = require(`${contractsPath}/${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(
|
console.log("Don't forget to reaload the JSON files from your application; i.e. restart kredits-web");
|
||||||
"Don't forget to reaload the JSON files from your application; i.e. restart kredits-web"
|
|
||||||
);
|
|
||||||
|
56
scripts/cli.js
Normal file
56
scripts/cli.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
@ -1,97 +0,0 @@
|
|||||||
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();
|
|
@ -1,43 +0,0 @@
|
|||||||
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();
|
|
@ -1,38 +0,0 @@
|
|||||||
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();
|
|
@ -1,44 +0,0 @@
|
|||||||
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();
|
|
@ -1,22 +0,0 @@
|
|||||||
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();
|
|
@ -1,2 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
cp -f scripts/git-hooks/pre-commit .git/hooks
|
|
@ -1,17 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
#
|
|
||||||
# Run appropriate linter against staged files
|
|
||||||
#
|
|
||||||
if [ $(git diff --name-only --cached lib/ | wc -l) != 0 ]; then
|
|
||||||
./node_modules/.bin/eslint lib/
|
|
||||||
if [ $? != 0 ]; then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
# TODO master not linted yet, uncomment this when ready
|
|
||||||
# if [ $(git diff --name-only --cached contracts/ | wc -l) != 0 ]; then
|
|
||||||
# solhint contracts/**/*.sol && apps/*/contracts/**/*.sol
|
|
||||||
# if [ $? != 0 ]; then
|
|
||||||
# exit 1
|
|
||||||
# fi
|
|
||||||
# fi
|
|
@ -1,8 +0,0 @@
|
|||||||
// 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));
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
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();
|
|
@ -1,34 +0,0 @@
|
|||||||
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();
|
|
@ -1,36 +0,0 @@
|
|||||||
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();
|
|
@ -1,68 +0,0 @@
|
|||||||
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();
|
|
@ -1,48 +0,0 @@
|
|||||||
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();
|
|
@ -1,34 +0,0 @@
|
|||||||
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();
|
|
@ -1,49 +0,0 @@
|
|||||||
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