Compare commits

..

1 Commits

Author SHA1 Message Date
ccd4c3410b 4.0.1 2019-04-09 12:46:24 +02:00
112 changed files with 49364 additions and 48874 deletions

View File

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

View File

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

View File

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

3
.gitignore vendored
View File

@@ -1,9 +1,6 @@
data
build build
flattened_contracts flattened_contracts
node_modules node_modules
**/node_modules **/node_modules
.ganache-db .ganache-db
.tm_properties .tm_properties
yarn-error.log
.DS_Store

1
.nvmrc
View File

@@ -1 +0,0 @@
12

View File

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

View File

@@ -1,38 +0,0 @@
---
language: node_js
node_js:
- "12"
sudo: false
dist: xenial
cache:
directories:
- node_modules
- apps/contribution/node_modules
- apps/contributor/node_modules
- apps/proposal/node_modules
- apps/token/node_modules
- apps/vault/node_modules
install:
- npm install -g @aragon/cli
- npm install -g truffle
- npm install
before_script:
- npm run devchain &
- sleep 10
script:
- npm run lint:wrapper
- npm run lint:contract-tests
# FIXME Fix tests
# - npm run test:token
# - npm run test:contributor
# - npm run test:contribution
# - npm run test:proposal
branches:
only:
- master

View File

@@ -14,8 +14,6 @@ framework](http://truffleframework.com/) for some things.
### Installation ### Installation
#### App dependencies
All requirements are defined in `package.json`. All requirements are defined in `package.json`.
$ npm install $ npm install
@@ -27,38 +25,24 @@ Each of the aragon apps are separate packages:
You can use `npm run install-all` to install all app dependencies at once. You can use `npm run install-all` to install all app dependencies at once.
#### Sytem dependencies
Aragon CLI and Truffle need to be installed on your sytem as well:
npm install -g @aragon/cli
npm install -g truffle
_Note: `@aragon/cli` currently fails to install on node.js 14. Please use
node.js 12 until the issue has been resolved upstream._
### Local development chain ### Local development chain
For local development it is recommended to use For local development it is recommended to use
[ganache](http://truffleframework.com/ganache/) to run a local development [ganache](http://truffleframework.com/ganache/) to run a local development
chain. When using the ganache simulator, no full Ethereum node is required. chain. Using the ganache simulator no full Ethereum node is required.
We use the default aragon-cli devchain command to configure and run a local We use the default aragon-cli devchain command to confgure and run a local
development ganache. development ganache.
$ npm run devchain (or aragon devchain --port 7545) $ npm run devchain (or aragon devchain --port 7545)
To clear/reset the chain use (e.g. if you run out of funds on your devchain) To clear/reset the chain use:
$ npm run devchain -- --reset (or aragon devchain --port 7545 --reset) $ npm run devchain -- --reset (or aragon devchain --port 7545 --reset)
We default to port 7545 for development to not get in conflict with the default We default to port 7545 for development to not get in conflict with the default
Ethereum RPC port. Ethereum RPC port.
You can also set certain ganache options to configure the devchain, for example
if you want to increase the block time to 10 seconds you can add
`--block-time=10`.
### Bootstrap ### Bootstrap
1. Run an Ethereum node and ipfs 1. Run an Ethereum node and ipfs
@@ -66,26 +50,21 @@ if you want to increase the block time to 10 seconds you can add
$ npm run devchain $ npm run devchain
$ ipfs daemon $ ipfs daemon
2. Compile contracts 2. Deploy each app to the devchain
(compiled contracts will be in `/build`)
$ npm run compile-contracts
3. Deploy each app to the devchain
(make sure you've run `npm install` for every app - see installation) (make sure you've run `npm install` for every app - see installation)
$ npm run deploy:apps $ npm run deploy:apps
4. Deploy a new KreditsKit and create a new DAO with the latest app versions 3. Deploy a new KreditsKit and create a new DAO with the latest app versions
$ npm run deploy:kit $ npm run deploy:kit
$ npm run deploy:dao $ npm run deploy:dao
5. Execute seeds to create demo contributors, contributions, etc. (optional) 4. Execute seeds to create demo contributors, contributons, etc. (optional)
$ npm run seeds $ npm run seeds
**Step 2-5 is also summarized in `npm run bootstrap`** **Step 2-4 is also summarized in `npm run bootstrap`**
If you want to reset your local setup: If you want to reset your local setup:
@@ -100,9 +79,9 @@ Kredits DAO independently.
![](docs/kredits-diagram.png) ![](docs/kredits-diagram.png)
A DAO can be deployed using the `scripts/deploy-kit.js` script or with the A DAO can be deployed using the `scripts/deploy-kit.js` script or with the `npm
`npm run deploy:dao` command. This deploys a new Kredits DAO, installs the run deploy:dao` command. This deploys a new Kredits DAO, installs the latest
latest app versions and sets the required permissions. app versions and sets the required permissions.
See each app in `/apps/*` for details. See each app in `/apps/*` for details.
@@ -136,11 +115,11 @@ Script to add a new entries to the contracts using the JS wrapper
$ truffle exec scripts/add-{contributor, contribution, proposal}.js $ truffle exec scripts/add-{contributor, contribution, proposal}.js
### list-{contributors, contributions, proposals}.js ### list-{contributor, contribution, proposal}.js
List contract entries List contract entries
$ truffle exec scripts/list-{contributors, contributions, proposals}.js $ truffle exec scripts/list-{contributor, contribution, proposal}.js
### send-funds.js ### send-funds.js
@@ -173,14 +152,8 @@ Deploys a new KreditsKit that allows to create a new DAO
or or
$ npm run deploy:kit $ npm run deploy:kit
#### Kredits configuration options: `ENS` address is required as environment variable.
`DAO_FACTORY` can optionally be set as environment variable. (see aragon)
Configuration options can be set in an environment specific `kredits` object in the `arapp.json` or using a CLI parameter.
* daoFactory: Ethereum address of the used DAO Factory. On public networks we use [official aragon factories](https://github.com/aragon/deployments/tree/master/environments/)
* apmDomain: the ENS domain of the aragonPM (normally `open.aragonpm.eth`)
(please also see the [arapp.json related configuration options](https://hack.aragon.org/docs/cli-global-confg#the-arappjson-file))
### new-dao.js ### new-dao.js
@@ -190,7 +163,7 @@ Creates and configures a new DAO instance.
or or
$ npm run deploy:dao $ npm run deploy:dao
KreditsKit address is loaded from `lib/addresses/KreditsKit.json` or can be KreditsKit address is load from `lib/addresses/KreditsKit.json` or can be
configured through the `KREDITS_KIT` environment variable. configured through the `KREDITS_KIT` environment variable.
### deploy-apps.sh ### deploy-apps.sh
@@ -201,31 +174,11 @@ Runs `npm install` for each app and publishes a new version.
or or
$ npm run deploy:apps $ npm run deploy:apps
## Deployment
### Apps deployment
To deploy a new app version run:
$ aragon apm publish major --environment=NETWORK_TO_DEPLOY
### KreditsKit
deploy the KreditsKit as Kit to create new DAOs
$ truffle exec scripts/deploy-kit.js --network=NETWORK_TO_DEPLOY
### Creating a new DAO
make sure all apps and the KreditsKit are deployed, then create a new DAO:
$ truffle exec scripts/new-dao.js --network=NETWORK_TO_DEPLOY
## ACL / Permissions ## ACL / Permissions
## Upgradeable contracts ## Upgradeable contracts
We use aragonOS for upgradeability of the different contracts. Refer to the We use aragonOS for upgradeablity of the different contracts. Refer to the
[aragonOS upgradeablity documentation](https://hack.aragon.org/docs/upgradeability-intro) [aragonOS upgradeablity documentation](https://hack.aragon.org/docs/upgradeability-intro)
for more details. for more details.

View File

@@ -1,12 +0,0 @@
module.exports = {
'globals': {
contract: true,
describe: true,
it: true,
},
rules: {
'no-unused-vars': ['error', {
'argsIgnorePattern': '^_',
}],
}
}

View File

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

View File

@@ -21,8 +21,8 @@ contract Contribution is AragonApp {
bytes32 public constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb; bytes32 public constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb;
// ensure alphabetic order // ensure alphabetic order
enum Apps { Contribution, Contributor, Proposal, Reimbursement, Token } enum Apps { Contribution, Contributor, Proposal, Token }
bytes32[5] public appIds; bytes32[4] public appIds;
struct ContributionData { struct ContributionData {
uint32 contributorId; uint32 contributorId;
@@ -54,25 +54,30 @@ contract Contribution is AragonApp {
event ContributionClaimed(uint32 id, uint32 indexed contributorId, uint32 amount); event ContributionClaimed(uint32 id, uint32 indexed contributorId, uint32 amount);
event ContributionVetoed(uint32 id, address vetoedByAccount); event ContributionVetoed(uint32 id, address vetoedByAccount);
function initialize(bytes32[5] _appIds) public onlyInit { function initialize(bytes32[4] _appIds) public onlyInit {
appIds = _appIds; appIds = _appIds;
blocksToWait = 40320; // 7 days; 15 seconds block time blocksToWait = 40320; // 7 days; 15 seconds block time
initialized(); initialized();
} }
function getContract(uint8 appId) public view returns (address) { // TODO refactor into a single function
function getTokenContract() public view returns (address) {
IKernel k = IKernel(kernel()); IKernel k = IKernel(kernel());
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[appId]); return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[uint8(Apps.Token)]);
}
function getContributorContract() public view returns (address) {
IKernel k = IKernel(kernel());
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[uint8(Apps.Contributor)]);
} }
function getContributorIdByAddress(address contributorAccount) public view returns (uint32) { function getContributorIdByAddress(address contributorAccount) public view returns (uint32) {
address contributorContract = getContract(uint8(Apps.Contributor)); address contributor = getContributorContract();
return ContributorInterface(contributorContract).getContributorIdByAddress(contributorAccount); return ContributorInterface(contributor).getContributorIdByAddress(contributorAccount);
} }
function getContributorAddressById(uint32 contributorId) public view returns (address) { function getContributorAddressById(uint32 contributorId) public view returns (address) {
address contributorContract = getContract(uint8(Apps.Contributor)); address contributor = getContributorContract();
return ContributorInterface(contributorContract).getContributorAddressById(contributorId); return ContributorInterface(contributor).getContributorAddressById(contributorId);
} }
// //
@@ -113,26 +118,6 @@ contract Contribution is AragonApp {
// Custom functions // Custom functions
// //
function totalKreditsEarned(bool confirmedOnly) public view returns (uint32 amount) {
for (uint32 i = 1; i <= contributionsCount; i++) {
ContributionData memory c = contributions[i];
if (!c.vetoed && (block.number >= c.confirmedAtBlock || !confirmedOnly)) {
amount += c.amount; // should use safemath
}
}
}
function totalKreditsEarnedByContributor(uint32 contributorId, bool confirmedOnly) public view returns (uint32 amount) {
uint256 tokenCount = ownedContributions[contributorId].length;
for (uint256 i = 0; i < tokenCount; i++) {
uint32 cId = ownedContributions[contributorId][i];
ContributionData memory c = contributions[cId];
if (!c.vetoed && (block.number >= c.confirmedAtBlock || !confirmedOnly)) {
amount += c.amount; // should use safemath
}
}
}
function getContribution(uint32 contributionId) public view returns (uint32 id, uint32 contributorId, uint32 amount, bool claimed, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint256 confirmedAtBlock, bool exists, bool vetoed) { function getContribution(uint32 contributionId) public view returns (uint32 id, uint32 contributorId, uint32 amount, bool claimed, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint256 confirmedAtBlock, bool exists, bool vetoed) {
id = contributionId; id = contributionId;
ContributionData storage c = contributions[id]; ContributionData storage c = contributions[id];
@@ -164,7 +149,7 @@ contract Contribution is AragonApp {
if (contributionId < 10) { if (contributionId < 10) {
c.confirmedAtBlock = block.number; c.confirmedAtBlock = block.number;
} else { } else {
c.confirmedAtBlock = block.number + 1 + blocksToWait; c.confirmedAtBlock = block.number + blocksToWait;
} }
contributionsCount++; contributionsCount++;
@@ -193,14 +178,14 @@ contract Contribution is AragonApp {
require(block.number >= c.confirmedAtBlock, 'NOT_CLAIMABLE'); require(block.number >= c.confirmedAtBlock, 'NOT_CLAIMABLE');
c.claimed = true; c.claimed = true;
address tokenContract = getContract(uint8(Apps.Token)); address token = getTokenContract();
address contributorAccount = getContributorAddressById(c.contributorId); address contributorAccount = getContributorAddressById(c.contributorId);
uint256 amount = uint256(c.amount); uint256 amount = uint256(c.amount);
IToken(tokenContract).mintFor(contributorAccount, amount, contributionId); IToken(token).mintFor(contributorAccount, amount, contributionId);
emit ContributionClaimed(contributionId, c.contributorId, c.amount); emit ContributionClaimed(contributionId, c.contributorId, c.amount);
} }
function exists(uint32 contributionId) public view returns (bool) { function exists(uint32 contributionId) view public returns (bool) {
return contributions[contributionId].exists; return contributions[contributionId].exists;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -3,29 +3,25 @@
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "",
"dependencies": { "dependencies": {
"@aragon/os": "^4.4.0" "@aragon/os": "^4.1.0",
}, "@aragon/cli": "^5.5.0"
"devDependencies": {
"@aragon/test-helpers": "^2.1.0",
"eth-gas-reporter": "^0.2.17",
"ganache-cli": "^6.9.1",
"solidity-coverage": "^0.5.11"
}, },
"devDependencies": {},
"scripts": { "scripts": {
"start": "npm run start:aragon:ipfs", "start": "npm run start:aragon:ipfs",
"start:aragon:ipfs": "aragon run", "start:aragon:ipfs": "aragon run",
"start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist", "start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist",
"start:app": "", "start:app": "npm run sync-assets && npm run build:script -- --no-minify && parcel serve app/index.html -p 8001 --out-dir dist/ --no-cache",
"test": "aragon contracts test",
"compile": "aragon contracts compile", "compile": "aragon contracts compile",
"sync-assets": "", "sync-assets": "copy-aragon-ui-assets -n aragon-ui ./dist",
"build:app": "", "build:app": "",
"build:script": "", "build:script": "",
"build": "", "build": "",
"publish:patch": "aragon apm publish patch", "publish:patch": "aragon apm publish patch",
"publish:minor": "aragon apm publish minor", "publish:minor": "aragon apm publish minor",
"publish:major": "aragon apm publish major", "publish:major": "aragon apm publish major",
"versions": "aragon apm versions", "versions": "aragon apm versions"
"test": "truffle test"
}, },
"keywords": [] "keywords": []
} }

View File

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

View File

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

View File

@@ -6,10 +6,6 @@ import "@aragon/os/contracts/kernel/IKernel.sol";
interface ITokenBalance { interface ITokenBalance {
function balanceOf(address contributorAccount) public view returns (uint256); function balanceOf(address contributorAccount) public view returns (uint256);
} }
interface IContributionBalance {
function totalKreditsEarnedByContributor(uint32 contributorId, bool confirmedOnly) public view returns (uint32 amount);
function balanceOf(address owner) public view returns (uint256);
}
contract Contributor is AragonApp { contract Contributor is AragonApp {
bytes32 public constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb; bytes32 public constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb;
@@ -28,25 +24,26 @@ contract Contributor is AragonApp {
uint32 public contributorsCount; uint32 public contributorsCount;
// ensure alphabetic order // ensure alphabetic order
enum Apps { Contribution, Contributor, Proposal, Reimbursement, Token } enum Apps { Contribution, Contributor, Proposal, Token }
bytes32[5] public appIds; bytes32[4] public appIds;
event ContributorProfileUpdated(uint32 id, bytes32 oldHashDigest, bytes32 newHashDigest); // what should be logged event ContributorProfileUpdated(uint32 id, bytes32 oldHashDigest, bytes32 newHashDigest); // what should be logged
event ContributorAccountUpdated(uint32 id, address oldAccount, address newAccount); event ContributorAccountUpdated(uint32 id, address oldAccount, address newAccount);
event ContributorAdded(uint32 id, address account); event ContributorAdded(uint32 id, address account);
function initialize(address root, bytes32[5] _appIds) public onlyInit { function initialize(address root,bytes32[4] _appIds) public onlyInit {
appIds = _appIds; appIds = _appIds;
initialized(); initialized();
} }
function getContract(uint8 appId) public view returns (address) { function getTokenContract() public view returns (address) {
IKernel k = IKernel(kernel()); IKernel k = IKernel(kernel());
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[appId]);
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[uint8(Apps.Token)]);
} }
function coreContributorsCount() public view returns (uint32) { function coreContributorsCount() view public returns (uint32) {
uint32 count = 0; uint32 count = 0;
for (uint32 i = 1; i <= contributorsCount; i++) { for (uint32 i = 1; i <= contributorsCount; i++) {
if (isCoreTeam(i)) { if (isCoreTeam(i)) {
@@ -57,13 +54,10 @@ contract Contributor is AragonApp {
} }
function updateContributorAccount(uint32 id, address oldAccount, address newAccount) public auth(MANAGE_CONTRIBUTORS_ROLE) { function updateContributorAccount(uint32 id, address oldAccount, address newAccount) public auth(MANAGE_CONTRIBUTORS_ROLE) {
require(newAccount != address(0), "invalid new account address");
require(getContributorAddressById(id) == oldAccount, "contributor does not exist");
contributorIds[oldAccount] = 0; contributorIds[oldAccount] = 0;
contributorIds[newAccount] = id; contributorIds[newAccount] = id;
contributors[id].account = newAccount; contributors[id].account = newAccount;
emit ContributorAccountUpdated(id, oldAccount, newAccount); ContributorAccountUpdated(id, oldAccount, newAccount);
} }
function updateContributorProfileHash(uint32 id, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(MANAGE_CONTRIBUTORS_ROLE) { function updateContributorProfileHash(uint32 id, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(MANAGE_CONTRIBUTORS_ROLE) {
@@ -124,7 +118,7 @@ contract Contributor is AragonApp {
return contributors[id]; 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 ) { function getContributorById(uint32 _id) public view returns (uint32 id, address account, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, bool isCore, uint256 balance, bool exists ) {
id = _id; id = _id;
Contributor storage c = contributors[_id]; Contributor storage c = contributors[_id];
account = c.account; account = c.account;
@@ -132,15 +126,12 @@ contract Contributor is AragonApp {
hashFunction = c.hashFunction; hashFunction = c.hashFunction;
hashSize = c.hashSize; hashSize = c.hashSize;
isCore = isCoreTeam(id); isCore = isCoreTeam(id);
address token = getContract(uint8(Apps.Token)); address token = getTokenContract();
balance = ITokenBalance(token).balanceOf(c.account); balance = ITokenBalance(token).balanceOf(c.account);
address contribution = getContract(uint8(Apps.Contribution));
totalKreditsEarned = IContributionBalance(contribution).totalKreditsEarnedByContributor(_id, true);
contributionsCount = IContributionBalance(contribution).balanceOf(c.account);
exists = c.exists; exists = c.exists;
} }
function canPerform(address _who, address _where, bytes32 _what, uint256[] memory _how) public returns (bool) { function canPerform(address _who, address _where, bytes32 _what/*, uint256[] memory _how*/) public returns (bool) {
address sender = _who; address sender = _who;
if (sender == address(-1)) { if (sender == address(-1)) {
sender = tx.origin; sender = tx.origin;

View File

@@ -1,16 +0,0 @@
pragma solidity ^0.4.24;
import "@aragon/os/contracts/acl/ACL.sol";
import "@aragon/os/contracts/kernel/Kernel.sol";
import "@aragon/os/contracts/factory/DAOFactory.sol";
// You might think this file is a bit odd, but let me explain.
// We only use for now those imported contracts in our tests, which
// means Truffle will not compile them for us, because they are from
// an external dependency.
// solium-disable-next-line no-empty-blocks
contract Spoof {
// ...
}

File diff suppressed because it is too large Load Diff

View File

@@ -3,19 +3,16 @@
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "",
"dependencies": { "dependencies": {
"@aragon/os": "^4.4.0" "@aragon/os": "^4.1.0",
}, "@aragon/cli": "^5.5.0"
"devDependencies": {
"@aragon/test-helpers": "^2.1.0",
"eth-gas-reporter": "^0.2.17",
"ganache-cli": "^6.9.1",
"solidity-coverage": "^0.5.11"
}, },
"devDependencies": {},
"scripts": { "scripts": {
"start": "npm run start:aragon:ipfs", "start": "npm run start:aragon:ipfs",
"start:aragon:ipfs": "aragon run", "start:aragon:ipfs": "aragon run",
"start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist", "start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist",
"start:app": "", "start:app": "",
"test": "aragon contracts test",
"compile": "aragon contracts compile", "compile": "aragon contracts compile",
"sync-assets": "", "sync-assets": "",
"build:app": "", "build:app": "",
@@ -24,8 +21,7 @@
"publish:patch": "aragon apm publish patch", "publish:patch": "aragon apm publish patch",
"publish:minor": "aragon apm publish minor", "publish:minor": "aragon apm publish minor",
"publish:major": "aragon apm publish major", "publish:major": "aragon apm publish major",
"versions": "aragon apm versions", "versions": "aragon apm versions"
"test": "truffle test"
}, },
"keywords": [] "keywords": []
} }

View File

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

View File

@@ -1,170 +0,0 @@
const namehash = require('ethers').utils.namehash;
// eslint-disable-next-line no-undef
const Contributor = artifacts.require("Contributor.sol");
// eslint-disable-next-line no-undef
const getContract = name => artifacts.require(name);
const { assertRevert } = require('@aragon/test-helpers/assertThrow');
const ZERO_ADDR = '0x0000000000000000000000000000000000000000';
contract('Contributor app', (accounts) => {
let kernelBase, aclBase, daoFactory, r, dao, acl, contributor;
const root = accounts[0];
const member1 = accounts[1];
// eslint-disable-next-line no-undef
before(async () => {
kernelBase = await getContract('Kernel').new(true); // petrify immediately
aclBase = await getContract('ACL').new();
daoFactory = await getContract('DAOFactory').new(kernelBase.address, aclBase.address, ZERO_ADDR);
r = await daoFactory.newDAO(root);
dao = await getContract('Kernel').at(r.logs.filter(l => l.event == 'DeployDAO')[0].args.dao);
acl = await getContract('ACL').at(await dao.acl());
//create dao mamnager permission for coin owner
await acl.createPermission(
root,
dao.address,
await dao.APP_MANAGER_ROLE(),
root,
{ from: root }
);
//get new app instance from DAO
const receipt = await dao.newAppInstance(
'0x1234',
(await Contributor.new()).address,
0x0,
false,
{ from: root }
);
contributor = Contributor.at(
receipt.logs.filter(l => l.event == 'NewAppProxy')[0].args.proxy
);
//apps id
let appsId = [];
appsId[0] = namehash("kredits-contribution");
appsId[1] = namehash("kredits-contributor");
appsId[2] = namehash("kredits-proposal");
appsId[3] = namehash("kredits-token");
//init contributor (app)
await contributor.initialize(root, appsId);
//create manage contributors role
await acl.createPermission(
root,
contributor.address,
await contributor.MANAGE_CONTRIBUTORS_ROLE(),
root,
{ from: root }
);
});
describe("Owner default permissions", async () => {
it('check owner is contributors manager', async () => {
let manageContributorPermission = await acl.hasPermission(root, contributor.address, await contributor.MANAGE_CONTRIBUTORS_ROLE());
// eslint-disable-next-line no-undef
assert.equal(manageContributorPermission, true);
});
});
describe("Add contributor", async () => {
let account = root;
let hashDigest = '0x0';
let hashFunction = 0;
let hashSize = 0;
it("should revert when add contributor from an address that does not have permission", async () => {
return assertRevert(async () => {
await contributor.addContributor(account, hashDigest, hashFunction, hashSize, { from: member1});
'sender does not have permission';
});
});
it('add contributor', async () => {
let contributorCount = await contributor.coreContributorsCount();
await contributor.addContributor(account, hashDigest, hashFunction, hashSize);
// eslint-disable-next-line no-undef
assert.equal(await contributor.addressExists(account), true);
let contributorCountAfter = await contributor.coreContributorsCount();
// eslint-disable-next-line no-undef
assert.equal(await contributorCountAfter.toNumber(), parseInt(contributorCount)+1);
});
it("should revert when add contributor with an address that already exist", async () => {
return assertRevert(async () => {
await contributor.addContributor(account, hashDigest, hashFunction, hashSize, { from: member1});
'address already exist';
});
});
});
describe("Update contributor", async () => {
let id;
let oldAccount;
let newAccount;
let hashDigest;
let hashFunction;
let hashSize;
// eslint-disable-next-line no-undef
before(async () => {
id = await contributor.coreContributorsCount();
oldAccount = root;
newAccount = member1;
hashDigest = '0x1000000000000000000000000000000000000000000000000000000000000000';
hashFunction = 1;
hashSize = 1;
});
it('update contributor account', async () => {
await contributor.updateContributorAccount(id.toNumber(), oldAccount, newAccount);
let contributorId = await contributor.getContributorIdByAddress(oldAccount);
// eslint-disable-next-line no-undef
assert.equal(contributorId.toNumber(), 0);
});
it("should revert when update contributor account from address that does not have permission", async () => {
return assertRevert(async () => {
await contributor.updateContributorAccount(id.toNumber(), oldAccount, newAccount, {from: member1});
'sender does not have permission';
});
});
it("should revert when update contributor account that does not exist", async () => {
return assertRevert(async () => {
await contributor.updateContributorAccount(id.toNumber(), accounts[3], newAccount);
'contributor does not exist';
});
});
it("should revert when update contributor account with address(0)", async () => {
return assertRevert(async () => {
await contributor.updateContributorAccount(id.toNumber(), oldAccount, ZERO_ADDR);
'contributor does not exist';
});
});
it('update contributor profile hash', async () => {
await contributor.updateContributorProfileHash(id.toNumber(), hashDigest, hashFunction, hashSize);
let contributorProfile = await contributor.contributors(id.toNumber());
assert.equal(hashDigest, contributorProfile[1]); // eslint-disable-line no-undef
assert.equal(hashFunction, contributorProfile[2].toNumber()); // eslint-disable-line no-undef
assert.equal(hashSize, contributorProfile[3].toNumber()); // eslint-disable-line no-undef
});
it("should revert when update contributor profile hash from address that does not have permission", async () => {
return assertRevert(async () => {
await contributor.updateContributorProfileHash(id.toNumber(), hashDigest, hashFunction, hashSize, {from: member1});
'sender does not have permission';
});
});
});
});

View File

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

View File

@@ -20,8 +20,8 @@ contract Proposal is AragonApp {
bytes32 public constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb; bytes32 public constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb;
// ensure alphabetic order // ensure alphabetic order
enum Apps { Contribution, Contributor, Proposal, Reimbursement, Token } enum Apps { Contribution, Contributor, Proposal, Token }
bytes32[5] public appIds; bytes32[4] public appIds;
struct Proposal { struct Proposal {
address creatorAccount; address creatorAccount;
@@ -46,18 +46,21 @@ contract Proposal is AragonApp {
event ProposalVoted(uint32 id, uint32 voterId, uint16 totalVotes); event ProposalVoted(uint32 id, uint32 voterId, uint16 totalVotes);
event ProposalExecuted(uint32 id, uint32 contributorId, uint32 amount); event ProposalExecuted(uint32 id, uint32 contributorId, uint32 amount);
function initialize(bytes32[5] _appIds) public onlyInit { function initialize(bytes32[4] _appIds) public onlyInit {
appIds = _appIds; appIds = _appIds;
initialized(); initialized();
} }
function getContract(uint8 appId) public view returns (address) { function getContributorContract() public view returns (address) {
IKernel k = IKernel(kernel()); return IKernel(kernel()).getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[uint8(Apps.Contributor)]);
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[appId]); }
function getContributionContract() public view returns (address) {
return IKernel(kernel()).getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[uint8(Apps.Contribution)]);
} }
function addProposal(uint32 contributorId, uint32 amount, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(ADD_PROPOSAL_ROLE) { function addProposal(uint32 contributorId, uint32 amount, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(ADD_PROPOSAL_ROLE) {
require(IContributor(getContract(uint8(Apps.Contributor))).exists(contributorId), 'CONTRIBUTOR_NOT_FOUND'); require(IContributor(getContributorContract()).exists(contributorId), 'CONTRIBUTOR_NOT_FOUND');
uint32 proposalId = proposalsCount + 1; uint32 proposalId = proposalsCount + 1;
uint16 _votesNeeded = 1; //contributorsContract().coreContributorsCount() / 100 * 75; uint16 _votesNeeded = 1; //contributorsContract().coreContributorsCount() / 100 * 75;
@@ -99,7 +102,7 @@ contract Proposal is AragonApp {
function vote(uint32 proposalId) public isInitialized auth(VOTE_PROPOSAL_ROLE) { function vote(uint32 proposalId) public isInitialized auth(VOTE_PROPOSAL_ROLE) {
Proposal storage p = proposals[proposalId]; Proposal storage p = proposals[proposalId];
require(!p.executed, 'ALREADY_EXECUTED'); require(!p.executed, 'ALREADY_EXECUTED');
uint32 voterId = IContributor(getContract(uint8(Apps.Contributor))).getContributorIdByAddress(msg.sender); uint32 voterId = IContributor(getContributorContract()).getContributorIdByAddress(msg.sender);
require(p.votes[voterId] != true, 'ALREADY_VOTED'); require(p.votes[voterId] != true, 'ALREADY_VOTED');
p.voterIds.push(voterId); p.voterIds.push(voterId);
p.votes[voterId] = true; p.votes[voterId] = true;
@@ -123,7 +126,7 @@ contract Proposal is AragonApp {
require(p.votesCount >= p.votesNeeded, 'MISSING_VOTES'); require(p.votesCount >= p.votesNeeded, 'MISSING_VOTES');
p.executed = true; p.executed = true;
IContribution(getContract(uint8(Apps.Contribution))).add(p.amount, p.contributorId, p.hashDigest, p.hashFunction, p.hashSize); IContribution(getContributionContract()).add(p.amount, p.contributorId, p.hashDigest, p.hashFunction, p.hashSize);
emit ProposalExecuted(proposalId, p.contributorId, p.amount); emit ProposalExecuted(proposalId, p.contributorId, p.amount);
} }

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -3,19 +3,16 @@
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "",
"dependencies": { "dependencies": {
"@aragon/os": "^4.4.0" "@aragon/os": "^4.1.0",
}, "@aragon/cli": "^5.5.0"
"devDependencies": {
"@aragon/test-helpers": "^2.1.0",
"eth-gas-reporter": "^0.2.17",
"ganache-cli": "^6.9.1",
"solidity-coverage": "^0.5.11"
}, },
"devDependencies": {},
"scripts": { "scripts": {
"start": "npm run start:aragon:ipfs", "start": "npm run start:aragon:ipfs",
"start:aragon:ipfs": "aragon run", "start:aragon:ipfs": "aragon run",
"start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist", "start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist",
"start:app": "", "start:app": "",
"test": "aragon contracts test",
"compile": "aragon contracts compile", "compile": "aragon contracts compile",
"sync-assets": "", "sync-assets": "",
"build:app": "", "build:app": "",
@@ -24,8 +21,7 @@
"publish:patch": "aragon apm publish patch", "publish:patch": "aragon apm publish patch",
"publish:minor": "aragon apm publish minor", "publish:minor": "aragon apm publish minor",
"publish:major": "aragon apm publish major", "publish:major": "aragon apm publish major",
"versions": "aragon apm versions", "versions": "aragon apm versions"
"test": "truffle test"
}, },
"keywords": [] "keywords": []
} }

View File

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

View File

@@ -1 +0,0 @@
*.sol linguist-language=Solidity

View File

@@ -1,7 +0,0 @@
node_modules
artifacts
.cache
cache
dist
ipfs.cmd
package-lock.json

View File

@@ -1,34 +0,0 @@
{
"roles": [
{
"name": "Add reimursements",
"id": "ADD_REIMBURSEMENT_ROLE",
"params": []
},
{
"name": "Veto reimbursement",
"id": "VETO_REIMBURSEMENT_ROLE",
"params": []
}
],
"environments": {
"default": {
"network": "development",
"appName": "kredits-reimbursement.open.aragonpm.eth"
},
"rinkeby": {
"registry": "0x98df287b6c145399aaa709692c8d308357bc085d",
"appName": "kredits-reimbursement.open.aragonpm.eth",
"wsRPC": "wss://rinkeby.eth.aragon.network/ws",
"network": "rinkeby"
},
"production": {
"registry": "0x314159265dd8dbb310642f98f50c066173c1259b",
"appName": "kredits-reimbursement.aragonpm.eth",
"wsRPC": "wss://mainnet.eth.aragon.network/ws",
"network": "mainnet"
}
},
"appName": "kredits-reimbursement.aragonpm.eth",
"path": "contracts/Reimbursement.sol"
}

View File

@@ -1,34 +0,0 @@
const { usePlugin } = require('@nomiclabs/buidler/config')
const hooks = require('./scripts/buidler-hooks')
usePlugin('@aragon/buidler-aragon')
module.exports = {
// Default Buidler configurations. Read more about it at https://buidler.dev/config/
defaultNetwork: 'localhost',
networks: {
localhost: {
url: 'http://localhost:8545',
},
},
solc: {
version: '0.4.24',
optimizer: {
enabled: true,
runs: 10000,
},
},
// Etherscan plugin configuration. Learn more at https://github.com/nomiclabs/buidler/tree/master/packages/buidler-etherscan
etherscan: {
apiKey: '', // API Key for smart contract verification. Get yours at https://etherscan.io/apis
},
// Aragon plugin configuration
aragon: {
appServePort: 8001,
clientServePort: 3000,
appSrcPath: 'app/',
appBuildOutputPath: 'dist/',
appName: 'expenses',
hooks, // Path to script hooks
},
}

View File

@@ -1,94 +0,0 @@
pragma solidity ^0.4.24;
import "@aragon/os/contracts/apps/AragonApp.sol";
import "@aragon/os/contracts/kernel/IKernel.sol";
contract Reimbursement is AragonApp {
bytes32 public constant ADD_REIMBURSEMENT_ROLE = keccak256("ADD_REIMBURSEMENT_ROLE");
bytes32 public constant VETO_REIMBURSEMENT_ROLE = keccak256("VETO_REIMBURSEMENT_ROLE");
// bytes32 public constant MANAGE_APPS_ROLE = keccak256("MANAGE_APPS_ROLE");
struct ReimbursementData {
uint32 recipientId;
uint256 amount;
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;
event ReimbursementAdded(uint32 id, address indexed addedByAccount, uint256 amount);
event ReimbursementVetoed(uint32 id, address vetoedByAccount);
function initialize() public onlyInit {
blocksToWait = 40320; // 7 days; 15 seconds block time
initialized();
}
// function setApps() public isInitialized auth(MANAGE_APPS_ROLE) {
// }
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) public isInitialized auth(ADD_REIMBURSEMENT_ROLE) {
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;
r.confirmedAtBlock = block.number + blocksToWait;
reimbursementsCount++;
emit ReimbursementAdded(reimbursementId, msg.sender, amount);
}
function veto(uint32 reimbursementId) public isInitialized auth(VETO_REIMBURSEMENT_ROLE) {
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;
}
}

View File

@@ -1,16 +0,0 @@
{
"name": "kredits Reimbursement",
"author": "Placeholder-author",
"description": "An application for Aragon",
"details_url": "/meta/details.md",
"source_url": "https://<placeholder-repository-url>",
"icons": [
{
"src": "/meta/icon.svg",
"sizes": "56x56"
}
],
"screenshots": [{ "src": "/meta/screenshot-1.png" }],
"start_url": "/index.html",
"script": "/script.js"
}

View File

@@ -1,30 +0,0 @@
{
"name": "kredits-reimbursement",
"version": "1.0.0",
"description": "",
"scripts": {
"start": "npm run start:aragon:ipfs",
"start:aragon:ipfs": "aragon run",
"start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist",
"start:app": "",
"compile": "aragon contracts compile",
"sync-assets": "",
"build:app": "",
"build:script": "",
"build": "",
"publish:patch": "aragon apm publish patch",
"publish:minor": "aragon apm publish minor",
"publish:major": "aragon apm publish major",
"versions": "aragon apm versions",
"test": "truffle test"
},
"dependencies": {
"@aragon/os": "^4.4.0"
},
"devDependencies": {
"@aragon/test-helpers": "^2.1.0",
"eth-gas-reporter": "^0.2.17",
"ganache-cli": "^6.9.1",
"solidity-coverage": "^0.5.11"
}
}

View File

@@ -1,65 +0,0 @@
/*
const { hash } = require('eth-ens-namehash')
const { getEventArgument } = require('@aragon/contract-test-helpers/events')
const Kernel = artifacts.require('@aragon/os/build/contracts/kernel/Kernel')
const ACL = artifacts.require('@aragon/os/build/contracts/acl/ACL')
const EVMScriptRegistryFactory = artifacts.require(
'@aragon/os/build/contracts/factory/EVMScriptRegistryFactory'
)
const DAOFactory = artifacts.require(
'@aragon/os/build/contracts/factory/DAOFactory'
)
const newDao = async (rootAccount) => {
// Deploy a DAOFactory.
const kernelBase = await Kernel.new(true)
const aclBase = await ACL.new()
const registryFactory = await EVMScriptRegistryFactory.new()
const daoFactory = await DAOFactory.new(
kernelBase.address,
aclBase.address,
registryFactory.address
)
// Create a DAO instance.
const daoReceipt = await daoFactory.newDAO(rootAccount)
const dao = await Kernel.at(getEventArgument(daoReceipt, 'DeployDAO', 'dao'))
// Grant the rootAccount address permission to install apps in the DAO.
const acl = await ACL.at(await dao.acl())
const APP_MANAGER_ROLE = await kernelBase.APP_MANAGER_ROLE()
await acl.createPermission(
rootAccount,
dao.address,
APP_MANAGER_ROLE,
rootAccount,
{ from: rootAccount }
)
return { dao, acl }
}
const newApp = async (dao, appName, baseAppAddress, rootAccount) => {
const receipt = await dao.newAppInstance(
hash(`${appName}.aragonpm.test`), // appId - Unique identifier for each app installed in the DAO; can be any bytes32 string in the tests.
baseAppAddress, // appBase - Location of the app's base implementation.
'0x', // initializePayload - Used to instantiate and initialize the proxy in the same call (if given a non-empty bytes string).
false, // setDefault - Whether the app proxy is the default proxy.
{ from: rootAccount }
)
// Find the deployed proxy address in the tx logs.
const logs = receipt.logs
const log = logs.find((l) => l.event === 'NewAppProxy')
const proxyAddress = log.args.proxy
return proxyAddress
}
module.exports = {
newDao,
newApp,
}
*/

View File

@@ -1,21 +0,0 @@
/*
const ANY_ADDRESS = '0xffffffffffffffffffffffffffffffffffffffff'
const setOpenPermission = async (acl, appAddress, role, rootAddress) => {
// Note: Setting a permission to 0xffffffffffffffffffffffffffffffffffffffff
// is interpreted by aragonOS as allowing the role for any address.
await acl.createPermission(
ANY_ADDRESS, // entity (who?) - The entity or address that will have the permission.
appAddress, // app (where?) - The app that holds the role involved in this permission.
role, // role (what?) - The particular role that the entity is being assigned to in this permission.
rootAddress, // manager - Can grant/revoke further permissions for this role.
{ from: rootAddress}
)
}
module.exports = {
setOpenPermission
}
*/

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -7,24 +7,18 @@ contract Token is ERC20Token, AragonApp {
bytes32 public constant MINT_TOKEN_ROLE = keccak256("MINT_TOKEN_ROLE"); bytes32 public constant MINT_TOKEN_ROLE = keccak256("MINT_TOKEN_ROLE");
// ensure alphabetic order // ensure alphabetic order
enum Apps { Contribution, Contributor, Proposal, Reimbursement, Token } enum Apps { Contribution, Contributor, Proposal, Token }
bytes32[5] public appIds; bytes32[4] public appIds;
event LogMint(address indexed recipient, uint256 amount, uint32 contributionId); event LogMint(address indexed recipient, uint256 amount, uint32 contributionId);
function initialize(bytes32[5] _appIds) public onlyInit { function initialize(bytes32[4] _appIds) public onlyInit {
appIds = _appIds; appIds = _appIds;
name = 'Kredits';
symbol = '₭S';
decimals = 18;
initialized(); initialized();
} }
function mintFor(address contributorAccount, uint256 amount, uint32 contributionId) public isInitialized auth(MINT_TOKEN_ROLE) { function mintFor(address contributorAccount, uint256 amount, uint32 contributionId) public isInitialized auth(MINT_TOKEN_ROLE) {
require(amount > 0, "INVALID_AMOUNT"); _mint(contributorAccount, amount);
uint256 amountInWei = amount.mul(1 ether);
_mint(contributorAccount, amountInWei);
emit LogMint(contributorAccount, amount, contributionId); emit LogMint(contributorAccount, amount, contributionId);
} }

View File

@@ -1,16 +0,0 @@
pragma solidity ^0.4.24;
import "@aragon/os/contracts/acl/ACL.sol";
import "@aragon/os/contracts/kernel/Kernel.sol";
import "@aragon/os/contracts/factory/DAOFactory.sol";
// You might think this file is a bit odd, but let me explain.
// We only use for now those imported contracts in our tests, which
// means Truffle will not compile them for us, because they are from
// an external dependency.
// solium-disable-next-line no-empty-blocks
contract Spoof {
// ...
}

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -3,19 +3,16 @@
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "",
"dependencies": { "dependencies": {
"@aragon/os": "^4.4.0" "@aragon/os": "^4.1.0",
}, "@aragon/cli": "^5.5.0"
"devDependencies": {
"@aragon/test-helpers": "^2.1.0",
"eth-gas-reporter": "^0.2.17",
"ganache-cli": "^6.9.1",
"solidity-coverage": "^0.5.11"
}, },
"devDependencies": {},
"scripts": { "scripts": {
"start": "npm run start:aragon:ipfs", "start": "npm run start:aragon:ipfs",
"start:aragon:ipfs": "aragon run", "start:aragon:ipfs": "aragon run",
"start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist", "start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist",
"start:app": "", "start:app": "",
"test": "aragon contracts test",
"compile": "aragon contracts compile", "compile": "aragon contracts compile",
"sync-assets": "", "sync-assets": "",
"build:app": "", "build:app": "",
@@ -24,8 +21,7 @@
"publish:patch": "aragon apm publish patch", "publish:patch": "aragon apm publish patch",
"publish:minor": "aragon apm publish minor", "publish:minor": "aragon apm publish minor",
"publish:major": "aragon apm publish major", "publish:major": "aragon apm publish major",
"versions": "aragon apm versions", "versions": "aragon apm versions"
"test": "truffle test"
}, },
"keywords": [] "keywords": []
} }

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

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

View File

@@ -1,123 +0,0 @@
const namehash = require('ethers').utils.namehash;
// eslint-disable-next-line no-undef
const Token = artifacts.require("Token.sol");
// eslint-disable-next-line no-undef
const getContract = name => artifacts.require(name);
const { assertRevert } = require('@aragon/test-helpers/assertThrow');
const ZERO_ADDR = '0x0000000000000000000000000000000000000000';
contract('Token app', (accounts) => {
let kernelBase, aclBase, daoFactory, dao, r, acl, token;
const root = accounts[0];
const member1 = accounts[1];
// eslint-disable-next-line no-undef
before(async () => {
kernelBase = await getContract('Kernel').new(true); // petrify immediately
aclBase = await getContract('ACL').new();
daoFactory = await getContract('DAOFactory').new(kernelBase.address, aclBase.address, ZERO_ADDR);
r = await daoFactory.newDAO(root);
dao = await getContract('Kernel').at(r.logs.filter(l => l.event == 'DeployDAO')[0].args.dao);
acl = await getContract('ACL').at(await dao.acl());
//create dao mamnager permission for coin owner
await acl.createPermission(
root,
dao.address,
await dao.APP_MANAGER_ROLE(),
root,
{ from: root }
);
//get new app instance from DAO
const receipt = await dao.newAppInstance(
'0x1234',
(await Token.new()).address,
0x0,
false,
{ from: root }
);
token = Token.at(
receipt.logs.filter(l => l.event == 'NewAppProxy')[0].args.proxy
);
//apps id
let appsId = [];
appsId[0] = namehash("kredits-contribution");
appsId[1] = namehash("kredits-contributor");
appsId[2] = namehash("kredits-proposal");
appsId[3] = namehash("kredits-token");
//init token (app)
await token.initialize(appsId);
//create token mint permission for coin owner
await acl.createPermission(
root,
token.address,
await token.MINT_TOKEN_ROLE(),
root,
{ from: root }
);
});
describe("Owner default space permissions", async () => {
it('check owner is token issuer', async () => {
let tokenIssuerPermission = await acl.hasPermission(root, token.address, await token.MINT_TOKEN_ROLE());
// eslint-disable-next-line no-undef
assert.equal(tokenIssuerPermission, true);
});
});
describe("Token issuing", async () => {
let name = "Kredits";
let symbol = "₭S";
let decimals = 18;
it("check token properties", async () => {
assert.equal(await token.name(), name); // eslint-disable-line no-undef
assert.equal(await token.symbol(), symbol); // eslint-disable-line no-undef
assert.equal(await token.decimals(), decimals); // eslint-disable-line no-undef
});
});
describe("Token minting", async () => {
let tokenToMint = 250;
let ether = 1000000000000000000;
it("should revert when mint tokens from an address that does not have minting permission", async () => {
return assertRevert(async () => {
await token.mintFor(root, tokenToMint, 1, { from: member1});
'address does not have permission to mint tokens';
});
});
it("should revert when mint tokens to address(0)", async () => {
return assertRevert(async () => {
await token.mintFor(ZERO_ADDR, tokenToMint, 1, { from: root});
'invalid contributor address';
});
});
it("should revert when mint amount of tokens equal to 0", async () => {
return assertRevert(async () => {
await token.mintFor(root, 0, 1, { from: root});
'amount to mint should be greater than zero';
});
});
it("mint tokens", async () => {
await token.mintFor(root, tokenToMint, 1, { from: root });
let ownerBalance = await token.balanceOf(root);
let totalSupply = await token.totalSupply();
assert.equal(ownerBalance.toNumber(), tokenToMint*ether); // eslint-disable-line no-undef
assert.equal(totalSupply.toNumber(), tokenToMint*ether); // eslint-disable-line no-undef
});
});
});

View File

@@ -34,17 +34,17 @@
"environments": { "environments": {
"development": { "development": {
"network": "development", "network": "development",
"apm": "aragonpm.eth",
"registry": "0x5f6f7e8cc7346a11ca2def8f827b7a0b612c56a1", "registry": "0x5f6f7e8cc7346a11ca2def8f827b7a0b612c56a1",
"appName": "dummy.open.aragonpm.eth" "appName": "dummy.aragonpm.eth"
}, },
"rinkeby": { "rinkeby": {
"network": "rinkeby", "network": "rinkeby",
"registry": "0x98Df287B6C145399Aaa709692c8D308357bC085D", "registry": "0x98Df287B6C145399Aaa709692c8D308357bC085D",
"wsRPC": "wss://rinkeby.eth.aragon.network/ws", "wsRPC": "wss://rinkeby.eth.aragon.network/ws",
"daoFactory": "0x2298d27a9b847c681d2b2c2828ab9d79013f5f1d",
"appName": "dummy.open.aragonpm.eth", "appName": "dummy.open.aragonpm.eth",
"kredits": { "apm": "open.aragonpm.eth"
"daoFactory": "0x2298d27a9b847c681d2b2c2828ab9d79013f5f1d"
}
}, },
"kovan": { "kovan": {
"network": "kovan", "network": "kovan",
@@ -52,7 +52,8 @@
}, },
"default": { "default": {
"network": "development", "network": "development",
"appName": "dummy.aragonpm.eth" "appName": "dummy.aragonpm.eth",
"apm": "open.aragonpm.eth"
} }
}, },
"path": "contracts/misc/DummyApp.sol" "path": "contracts/misc/DummyApp.sol"

View File

@@ -1,54 +1,16 @@
const contractCalls = [ const contractCalls = [
['Contributor', 'add', [{ ['Contributor', 'add', [{ account: '0x7e8f313c56f809188313aa274fa67ee58c31515d', name: 'bumi', isCore: true, kind: 'person', url: '', github_username: 'bumi', github_uid: 318, wiki_username: 'bumi' }, { gasLimit: 200000 }]],
account: '0x7e8f313c56f809188313aa274fa67ee58c31515d', ['Contributor', 'add', [{ account: '0x49575f3DD9a0d60aE661BC992f72D837A77f05Bc', name: 'raucao', isCore: true, kind: 'person', url: '', github_username: 'skddc', github_uid: 842, wiki_username: 'raucau' }, { gasLimit: 200000 }]],
name: 'bumi', ['Proposal', 'addProposal', [{ contributorId: 1, amount: 500, kind: 'code', description: '[67P/kredits-contracts] Ran the seeds', url: '' }, { gasLimit: 350000 }]],
kind: 'person', ['Proposal', 'addProposal', [{ contributorId: 2, amount: 500, kind: 'code', description: '[67P/kredits-contracts] Ran the seeds', url: '' }, { gasLimit: 350000 }]],
url: '', ['Proposal', 'addProposal', [{ contributorId: 2, amount: 500, kind: 'code', description: '[67P/kredits-contracts] Hacked on kredits', url: '' }, { gasLimit: 350000 }]],
github_username: 'bumi', ['Proposal', 'vote', [1, { gasLimit: 550000 }]],
github_uid: 318, ['Contribution', 'addContribution', [{ contributorId: 1, amount: 5000, kind: 'dev', description: '[67P/kredits-contracts] Introduce contribution token', url: '' }, { gasLimit: 350000 }]],
gitea_username: 'bumi', ['Contribution', 'addContribution', [{ contributorId: 2, amount: 1500, kind: 'dev', description: '[67P/kredits-web] Reviewed stuff', url: '' }, { gasLimit: 350000 }]],
wiki_username: 'Bumi', ['Contribution', 'claim', [1, { gasLimit: 300000 }]]
}, { gasLimit: 200000 }]],
['Contributor', 'add', [{
account: '0x49575f3DD9a0d60aE661BC992f72D837A77f05Bc',
name: 'raucao',
kind: 'person',
url: '',
github_username: 'skddc',
github_uid: 842,
gitea_username: 'raucao',
wiki_username: 'Basti',
}, { gasLimit: 200000 }]],
['Contributor', 'add', [{
account: '0xF722709ECC3B05c19d02E82a2a4A4021B8F48C62',
name: 'Manuel',
kind: 'person',
url: '',
github_username: 'fsmanuel',
github_uid: 54812,
wiki_username: 'Manuel',
}, { gasLimit: 200000 }]],
['Contribution', 'add', [{ contributorId: 1, contributorIpfsHash: 'QmWKCYGr2rSf6abUPaTYqf98urvoZxGrb7dbspFZA6oyVF', date: '2019-04-11', amount: 500, kind: 'dev', description: '[67P/kredits-contracts] Test this thing', url: '' }, { gasLimit: 350000 }]],
['Contribution', 'add', [{ contributorId: 2, contributorIpfsHash: 'QmcHzEeAM26HV2zHTf5HnZrCtCtGdEccL5kUtDakAB7ozB', date: '2019-04-11', amount: 1500, kind: 'dev', description: '[67P/kredits-web] Reviewed stuff', url: '' }, { gasLimit: 350000 }]],
['Contribution', 'add', [{ contributorId: 1, contributorIpfsHash: 'QmWKCYGr2rSf6abUPaTYqf98urvoZxGrb7dbspFZA6oyVF', date: '2019-04-11', amount: 1500, kind: 'dev', description: '[67P/kredits-contracts] Add tests', url: '' }, { 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: 5000, kind: 'dev', description: '[67P/kredits-web] Expense UI, first draft', url: '' }, { gasLimit: 350000 }]],
['Reimbursement', 'add', [{amount: 1116000, recipientId: 1, token: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', expenses: [
{ title: 'Server rent', description: 'Dedicated server: andromeda.kosmos.org, April 2020', amount: 61, currency: 'EUR', date: '2020-05-28' },
{ title: 'Server rent', description: 'Dedicated server: centaurus.kosmos.org, April 2020', amount: 32, currency: 'EUR', date: '2020-05-28' }
]}, { 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', date: '2020-05-30' }
]}, { gasLimit: 300000 }]],
]; ];
const funds = [ const funds = [
'0x7e8f313c56f809188313aa274fa67ee58c31515d', '0x7e8f313c56f809188313aa274fa67ee58c31515d',
'0xa502eb4021f3b9ab62f75b57a94e1cfbf81fd827', '0xa502eb4021f3b9ab62f75b57a94e1cfbf81fd827'
]; ];
module.exports = { contractCalls, funds }; module.exports = { contractCalls, funds };

View File

@@ -10,18 +10,17 @@ import "../apps/contribution/contracts/Contribution.sol";
import "../apps/contributor/contracts/Contributor.sol"; import "../apps/contributor/contracts/Contributor.sol";
import "../apps/token/contracts/Token.sol"; import "../apps/token/contracts/Token.sol";
import "../apps/proposal/contracts/Proposal.sol"; import "../apps/proposal/contracts/Proposal.sol";
import "../apps/reimbursement/contracts/Reimbursement.sol";
contract KreditsKit is KitBase { contract KreditsKit is KitBase {
// ensure alphabetic order // ensure alphabetic order
enum Apps { Contribution, Contributor, Proposal, Reimbursement, Token } enum Apps { Contribution, Contributor, Proposal, Token }
bytes32[5] public appIds; bytes32[4] public appIds;
event DeployInstance(address dao); event DeployInstance(address dao);
event InstalledApp(address dao, address appProxy, bytes32 appId); event InstalledApp(address dao, address appProxy, bytes32 appId);
constructor (DAOFactory _fac, ENS _ens, bytes32[5] _appIds) public KitBase(_fac, _ens) { constructor (DAOFactory _fac, ENS _ens, bytes32[4] _appIds) public KitBase(_fac, _ens) {
appIds = _appIds; appIds = _appIds;
} }
@@ -42,24 +41,18 @@ contract KreditsKit is KitBase {
Contribution contribution = Contribution(_installApp(dao, appIds[uint8(Apps.Contribution)])); Contribution contribution = Contribution(_installApp(dao, appIds[uint8(Apps.Contribution)]));
contribution.initialize(appIds); contribution.initialize(appIds);
acl.createPermission(root, contribution, contribution.ADD_CONTRIBUTION_ROLE(), this);
acl.createPermission(root, contribution, contribution.VETO_CONTRIBUTION_ROLE(), this);
acl.grantPermission(proposal, contribution, contribution.ADD_CONTRIBUTION_ROLE());
Proposal proposal = Proposal(_installApp(dao, appIds[uint8(Apps.Proposal)])); Proposal proposal = Proposal(_installApp(dao, appIds[uint8(Apps.Proposal)]));
proposal.initialize(appIds); proposal.initialize(appIds);
Reimbursement reimbursement = Reimbursement(_installApp(dao, appIds[uint8(Apps.Reimbursement)])); acl.createPermission(root, contribution, contribution.ADD_CONTRIBUTION_ROLE(), this);
reimbursement.initialize(); acl.createPermission(root, contribution, contribution.VETO_CONTRIBUTION_ROLE(), this);
acl.createPermission(root, reimbursement, reimbursement.ADD_REIMBURSEMENT_ROLE(), this); acl.grantPermission(proposal, contribution, contribution.ADD_CONTRIBUTION_ROLE());
acl.createPermission(root, reimbursement, reimbursement.VETO_REIMBURSEMENT_ROLE(), this);
uint256[] memory params = new uint256[](1); uint256[] memory params = new uint256[](1);
params[0] = uint256(203) << 248 | uint256(1) << 240 | uint240(contributor); params[0] = uint256(203) << 248 | uint256(1) << 240 | uint240(contributor);
acl.grantPermissionP(acl.ANY_ENTITY(), contribution, contribution.ADD_CONTRIBUTION_ROLE(), params); acl.grantPermissionP(acl.ANY_ENTITY(), contribution, contribution.ADD_CONTRIBUTION_ROLE(), params);
acl.grantPermissionP(acl.ANY_ENTITY(), contribution, contribution.VETO_CONTRIBUTION_ROLE(), params); acl.grantPermissionP(acl.ANY_ENTITY(), contribution, contribution.VETO_CONTRIBUTION_ROLE(), params);
acl.grantPermissionP(acl.ANY_ENTITY(), contributor, contributor.MANAGE_CONTRIBUTORS_ROLE(), params); acl.grantPermissionP(acl.ANY_ENTITY(), contributor, contributor.MANAGE_CONTRIBUTORS_ROLE(), params);
acl.grantPermissionP(acl.ANY_ENTITY(), reimbursement, reimbursement.ADD_REIMBURSEMENT_ROLE(), params);
//acl.setPermissionManager(this, proposal, proposal.VOTE_PROPOSAL_ROLE(); //acl.setPermissionManager(this, proposal, proposal.VOTE_PROPOSAL_ROLE();
acl.createPermission(root, proposal, proposal.VOTE_PROPOSAL_ROLE(), this); acl.createPermission(root, proposal, proposal.VOTE_PROPOSAL_ROLE(), this);
@@ -74,8 +67,6 @@ contract KreditsKit is KitBase {
acl.setPermissionManager(root, contribution, contribution.ADD_CONTRIBUTION_ROLE()); acl.setPermissionManager(root, contribution, contribution.ADD_CONTRIBUTION_ROLE());
acl.setPermissionManager(root, contribution, contribution.VETO_CONTRIBUTION_ROLE()); acl.setPermissionManager(root, contribution, contribution.VETO_CONTRIBUTION_ROLE());
acl.setPermissionManager(root, contributor, contributor.MANAGE_CONTRIBUTORS_ROLE()); acl.setPermissionManager(root, contributor, contributor.MANAGE_CONTRIBUTORS_ROLE());
acl.setPermissionManager(root, reimbursement, reimbursement.ADD_REIMBURSEMENT_ROLE());
acl.setPermissionManager(root, reimbursement, reimbursement.VETO_REIMBURSEMENT_ROLE());
acl.createPermission(root, token, token.MINT_TOKEN_ROLE(), this); acl.createPermission(root, token, token.MINT_TOKEN_ROLE(), this);
acl.grantPermission(contribution, token, token.MINT_TOKEN_ROLE()); acl.grantPermission(contribution, token, token.MINT_TOKEN_ROLE());

File diff suppressed because it is too large Load Diff

View File

@@ -1,121 +0,0 @@
{
"1": {
"account": "0x7E8f313C56F809188313aa274Fa67EE58c31515d",
"hashDigest": "0x99b8afd7b266e19990924a8be9099e81054b70c36b20937228a77a5cf75723b8",
"hashFunction": 18,
"hashSize": 32,
"id": 1
},
"2": {
"account": "0x49575f3DD9a0d60aE661BC992f72D837A77f05Bc",
"hashDigest": "0xeb78574922d8606419b714174b31961007afefe313111c9db817aa9d3f82e157",
"hashFunction": 18,
"hashSize": 32,
"id": 2
},
"3": {
"account": "0xF722709ECC3B05c19d02E82a2a4A4021B8F48C62",
"hashDigest": "0x7acd73632c7cc8d317fa9e72e925eaef1d700e69f185b84e3f0168471e17cf2d",
"hashFunction": 18,
"hashSize": 32,
"id": 3
},
"4": {
"account": "0xD4a64570B12dA659Ee4BBd41c3509B7b1F9c51AC",
"hashDigest": "0x4d394c58e4b0534446be10a9b7f8b50e5478258719c23a3e158a5ea9fca14bdd",
"hashFunction": 18,
"hashSize": 32,
"id": 4
},
"5": {
"account": "0x35d9e68a5F7A935C64b08221d5DC7f161a415184",
"hashDigest": "0xd6f726f37ad52998b8341b7021cb5d9f4dcf7ce09af575358505a2a7c2f81edb",
"hashFunction": 18,
"hashSize": 32,
"id": 5
},
"6": {
"account": "0x8345E84D848792bf750A1d032d76f20f00aeC1a7",
"hashDigest": "0x1acdc784e20f62c44ec222552531bf89daafaaca1893a68057a9ada920ea0984",
"hashFunction": 18,
"hashSize": 32,
"id": 6
},
"7": {
"account": "0x4D99d767477Fbb2B47EFeb17E2a78970AD22CCc1",
"hashDigest": "0xd6d16bde5ee3d3374dd5981dab0296938b24e3c812e72ba4fa3d38011b6fab24",
"hashFunction": 18,
"hashSize": 32,
"id": 7
},
"8": {
"account": "0x6a6cD99ab0335C92E55fbb4403D67A4f4B52AfEc",
"hashDigest": "0xec30c91b31a85eef2af7afce0ba66b0636f1d61a75a12d8ca188a2d9e1626cf2",
"hashFunction": 18,
"hashSize": 32,
"id": 8
},
"9": {
"account": "0x21aB0B3527326dcA4467245654Cf881F5F7a8c5e",
"hashDigest": "0xd6c8b0f285e614b12f700d6e66e2e67901c16c420308625f844357283bd555ba",
"hashFunction": 18,
"hashSize": 32,
"id": 9
},
"10": {
"account": "0x150A69bAfA216FD55C7CeF8eA2002cd582dc5982",
"hashDigest": "0x7490aabadf995751211686b3b9f4b8cfee3354947df23cec043c15bfb519c817",
"hashFunction": 18,
"hashSize": 32,
"id": 10
},
"11": {
"account": "0x2f65679cAf0c3abCbF77fC68fA56759f4D96C73c",
"hashDigest": "0xd147d2d26f7eb1cbe1c5da53c345565df2d3c6c33a8a3e3331952d4b878b2bb0",
"hashFunction": 18,
"hashSize": 32,
"id": 11
},
"12": {
"account": "0x13a24319Abb4e00c383D8a80dACb20690699Ac8C",
"hashDigest": "0x84c63c2d3f9510f87717e54a2cecb41f88dce6a41ae6392480ebf6561e30670e",
"hashFunction": 18,
"hashSize": 32,
"id": 12
},
"13": {
"account": "0x0e0F02508b80e401C26F584fF02eE80Ab094CdF9",
"hashDigest": "0xe4c34e15b87114e9edd599fa1bbd6d4b1bf45c3b0163d0772158fb93503c1fe1",
"hashFunction": 18,
"hashSize": 32,
"id": 13
},
"14": {
"account": "0x49750D74930b20b2937DDF77A6C30d81882d888E",
"hashDigest": "0xa1f18c54e77d1e8f0fa106463b3f66a34a6287bcd9b643febea0ab2e97009665",
"hashFunction": 18,
"hashSize": 32,
"id": 14
},
"15": {
"account": "0x608FD4b95116Ea616990Aaeb1d4f1ce07612f261",
"hashDigest": "0x1d9de6de5c72eedca6d7a5e8a9159e2f5fe676506aece3000acefcc821723429",
"hashFunction": 18,
"hashSize": 32,
"id": 15
},
"16": {
"account": "0xCaBba4560c96FADe3Dd6C29cF24Cfb16a228bC1c",
"hashDigest": "0x0c81914b8a332808879b06b1a68398edccea7e7facb50767e4116c33914d2c64",
"hashFunction": 18,
"hashSize": 32,
"id": 16
},
"17": {
"account": "0x765E88b4F9a59C3a3b300C6eFF9E6E9fDDf9FbD9",
"hashDigest": "0xcfbeeadc244dfdc55bbad50d431871439df067970db84c73023956c96a6f5df2",
"hashFunction": 18,
"hashSize": 32,
"id": 17
}
}

View File

@@ -1,52 +1,5 @@
# Contribution deployments # 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 ## 2019-04-04
✔ Successfully published kredits-contribution.open.aragonpm.eth v4.0.0: ✔ Successfully published kredits-contribution.open.aragonpm.eth v4.0.0:

View File

@@ -1,30 +1,5 @@
# Contributor deployments # 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 ## 2019-04-04
✔ Successfully published kredits-contributor.open.aragonpm.eth v3.0.0: ✔ Successfully published kredits-contributor.open.aragonpm.eth v3.0.0:

View File

@@ -1,41 +1,5 @@
# Kredits deployment # 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 ## 2019-04-04
Using KreditsKit at: 0x76e069b47b79442657eaf0555a32c6b16fa1b8b4 Using KreditsKit at: 0x76e069b47b79442657eaf0555a32c6b16fa1b8b4

View File

@@ -1,13 +1,5 @@
# Proposal deployments # 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 ## 2019-04-04
✔ Successfully published kredits-proposal.open.aragonpm.eth v4.0.0: ✔ Successfully published kredits-proposal.open.aragonpm.eth v4.0.0:

View File

@@ -1,13 +1,5 @@
# Token deployments # 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 ## 2019-04-04
✔ Successfully published kredits-token.open.aragonpm.eth v3.0.0: ✔ Successfully published kredits-token.open.aragonpm.eth v3.0.0:

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
[{"constant":true,"inputs":[],"name":"ens","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"fac","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"appId","type":"bytes32"}],"name":"latestVersionAppBase","outputs":[{"name":"base","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"appIds","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_fac","type":"address"},{"name":"_ens","type":"address"},{"name":"_appIds","type":"bytes32[4]"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"dao","type":"address"}],"name":"DeployInstance","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"dao","type":"address"},{"indexed":false,"name":"appProxy","type":"address"},{"indexed":false,"name":"appId","type":"bytes32"}],"name":"InstalledApp","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"appProxy","type":"address"},{"indexed":false,"name":"appId","type":"bytes32"}],"name":"InstalledApp","type":"event"},{"constant":false,"inputs":[],"name":"newInstance","outputs":[{"name":"dao","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

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

View File

@@ -1,31 +1,23 @@
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;
}
get ipfs () {
if (!this._ipfsAPI) { throw new Error('IPFS API not configured; please set an ipfs instance'); } if (!this._ipfsAPI) { throw new Error('IPFS API not configured; please set an ipfs instance'); }
return this._ipfsAPI; return this._ipfsAPI;
} }
set ipfs (ipfsAPI) { set ipfs(ipfsAPI) {
this._ipfsAPI = ipfsAPI; this._ipfsAPI = ipfsAPI;
} }
on (type, callback) { on(type, callback) {
return this.contract.on(type, callback); return this.contract.on(type, callback);
} }
} }
module.exports = Base; module.exports = Base;

View File

@@ -1,36 +1,44 @@
const Record = require('./record'); const ethers = require('ethers');
const ContributionSerializer = require('../serializers/contribution');
const deprecate = require('../utils/deprecate');
class Contribution extends Record { const ContributionSerializer = require('../serializers/contribution');
get count () { const Base = require('./base');
return this.contract.contributionsCount();
class Contribution extends Base {
all() {
return this.functions.contributionsCount()
.then(async (count) => {
let contributions = [];
for (let id = 1; id <= count; id++) {
const contribution = await this.getById(id)
contributions.push(contribution);
} }
getById (id) { return contributions;
return this.contract.getContribution(id)
.then(data => {
return this.ipfs.catAndMerge(data, ContributionSerializer.deserialize);
}); });
} }
getData (id) { getById(id) {
return this.contract.getContribution(id); return this.functions.getContribution(id)
.then(data => {
return this.ipfs.catAndMerge(data, ContributionSerializer.deserialize);
});
} }
getByContributorId (contributorId) { getByContributorId(contributorId) {
return this.contract.getContributorAddressById(contributorId) return this.functions.getContributorAddressById(contributorId)
.then(address => this.getByContributorAddress(address)); .then(address => this.getByContributorAddress(address));
} }
getByContributorAddress (address) { getByContributorAddress(address) {
return this.contract.balanceOf(address) return this.functions.balanceOf(address)
.then(async (balance) => { .then(async (balance) => {
const count = balance.toNumber(); const count = balance.toNumber();
const contributions = []; const contributions = [];
for (let index = 0; index < count; index++) { for (let index = 0; index < count; index++) {
const id = await this.contract.tokenOfOwnerByIndex(address, index); const id = await this.functions.tokenOfOwnerByIndex(address, index);
const contribution = await this.getById(id); const contribution = await this.getById(id);
contributions.push(contribution); contributions.push(contribution);
} }
@@ -39,17 +47,13 @@ class Contribution extends Record {
}); });
} }
async add (contributionAttr, callOptions = {}) { addContribution(contributionAttr, callOptions = {}) {
const contribution = new ContributionSerializer(contributionAttr); let json = ContributionSerializer.serialize(contributionAttr);
// TODO: validate against schema
try { await contribution.validate(); }
catch (error) { return Promise.reject(error); }
const jsonStr = contribution.serialize();
return this.ipfs return this.ipfs
.add(jsonStr) .add(json)
.then(ipfsHashAttr => { .then((ipfsHashAttr) => {
let contribution = [ let contribution = [
contributionAttr.amount, contributionAttr.amount,
contributionAttr.contributorId, contributionAttr.contributorId,
@@ -58,15 +62,9 @@ class Contribution extends Record {
ipfsHashAttr.hashSize, ipfsHashAttr.hashSize,
]; ];
return this.contract.add(...contribution, callOptions); return this.functions.add(...contribution, callOptions);
}); });
} }
addContribution () {
deprecate('The function `addContribution()` is deprecated and will be removed in the next major version. Use `add()` instead');
return this.add(...arguments);
}
} }
module.exports = Contribution; module.exports = Contribution;

View File

@@ -1,34 +1,40 @@
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 => {
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) {
return this.functions.getContributorById(id)
// Fetch IPFS data if available
.then((data) => {
return this.ipfs.catAndMerge(data, ContributorSerializer.deserialize); return this.ipfs.catAndMerge(data, ContributorSerializer.deserialize);
}); });
} }
getData (id) { filterByAccount(search) {
return this.contract.getContributorById(id);
}
filterByAccount (search) {
return this._byAccount(search, 'filter'); return this._byAccount(search, 'filter');
} }
findByAccount (search) { findByAccount(search) {
return this._byAccount(search, 'find'); return this._byAccount(search, 'find');
} }
_byAccount (search, method = 'filter') { _byAccount(search, method = 'filter') {
return this.all().then((contributors) => { return this.all().then((contributors) => {
const searchEntries = Object.entries(search); const searchEntries = Object.entries(search);
@@ -44,16 +50,12 @@ class Contributor extends Record {
}); });
} }
async add (contributorAttr, callOptions = {}) { add(contributorAttr, callOptions = {}) {
let contributor = new ContributorSerializer(contributorAttr); let json = ContributorSerializer.serialize(contributorAttr);
// TODO: validate against schema
try { await contributor.validate(); }
catch (error) { return Promise.reject(error); }
const jsonStr = contributor.serialize();
return this.ipfs return this.ipfs
.add(jsonStr) .add(json)
.then((ipfsHashAttr) => { .then((ipfsHashAttr) => {
let contributor = [ let contributor = [
contributorAttr.account, contributorAttr.account,
@@ -62,34 +64,9 @@ class Contributor extends Record {
ipfsHashAttr.hashSize, ipfsHashAttr.hashSize,
]; ];
return this.contract.addContributor(...contributor, callOptions); return this.functions.addContributor(...contributor, callOptions);
}); });
} }
updateProfile (contributorId, updateAttr, callOptions = {}) {
return this.getById(contributorId).then(async (contributor) => {
let updatedContributorAttr = Object.assign(contributor, updateAttr);
let updatedContributor = new ContributorSerializer(updatedContributorAttr);
try { await updatedContributor.validate(); }
catch (error) { return Promise.reject(error); }
const jsonStr = updatedContributor.serialize();
return this.ipfs
.add(jsonStr)
.then(ipfsHashAttr => {
return this.contract.updateContributorProfileHash(
contributorId,
ipfsHashAttr.hashDigest,
ipfsHashAttr.hashFunction,
ipfsHashAttr.hashSize,
callOptions
);
});
});
}
} }
module.exports = Contributor; module.exports = Contributor;

View File

@@ -3,7 +3,6 @@ module.exports = {
Contribution: require('./contribution'), Contribution: require('./contribution'),
Proposal: require('./proposal'), Proposal: require('./proposal'),
Token: require('./token'), Token: require('./token'),
Reimbursement: require('./reimbursement'),
Kernel: require('./kernel'), Kernel: require('./kernel'),
Acl: require('./acl'), Acl: require('./acl')
}; };

View File

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

View File

@@ -1,29 +1,36 @@
const Record = require('./record'); const ethers = require('ethers');
const ContributionSerializer = require('../serializers/contribution'); const RSVP = require('rsvp');
const deprecate = require('../utils/deprecate');
class Proposal extends Record { const ContributionSerializer = require('../serializers/contribution');
get count () { const Base = require('./base');
return this.contract.proposalsCount();
class Proposal extends Base {
all() {
return this.functions.proposalsCount()
.then(count => {
let proposals = [];
for (let id = 1; id <= count; id++) {
proposals.push(this.getById(id));
} }
getById (id) { return RSVP.all(proposals);
return this.contract.getProposal(id) });
}
getById(id) {
return this.functions.getProposal(id)
.then(data => { .then(data => {
return this.ipfs.catAndMerge(data, ContributionSerializer.deserialize); return this.ipfs.catAndMerge(data, ContributionSerializer.deserialize);
}); });
} }
async add (proposalAttr, callOptions = {}) { addProposal(proposalAttr, callOptions = {}) {
const contribution = new ContributionSerializer(proposalAttr); let json = ContributionSerializer.serialize(proposalAttr);
// TODO: validate against schema
try { await contribution.validate(); }
catch (error) { return Promise.reject(error); }
const jsonStr = contribution.serialize();
return this.ipfs return this.ipfs
.add(jsonStr) .add(json)
.then((ipfsHashAttr) => { .then((ipfsHashAttr) => {
let proposal = [ let proposal = [
proposalAttr.contributorId, proposalAttr.contributorId,
@@ -33,14 +40,9 @@ class Proposal extends Record {
ipfsHashAttr.hashSize, ipfsHashAttr.hashSize,
]; ];
return this.contract.addProposal(...proposal, callOptions); return this.functions.addProposal(...proposal, callOptions);
}); });
} }
addProposal () {
deprecate('The function `addProposal()` is deprecated and will be removed in the next major version. Use `add()` instead');
return this.add(...arguments);
}
} }
module.exports = Proposal; module.exports = Proposal

View File

@@ -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;

View File

@@ -1,65 +0,0 @@
const Record = require('./record');
const ExpenseSerializer = require('../serializers/expense');
class Reimbursement extends Record {
get count () {
return this.functions.reimbursementsCount();
}
getById (id) {
return this.functions.get(id)
.then(data => {
return this.ipfs.catAndMerge(data, (ipfsDocument) => {
const expenses = JSON.parse(ipfsDocument);
return { expenses };
});
});
}
getData (id) {
return this.functions.getReimbursement(id);
}
async add (attrs, callOptions = {}) {
const amount = parseInt(attrs.amount);
const token = attrs.token;
const recipientId = attrs.recipientId;
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,
];
return this.functions.add(...reimbursement, callOptions);
});
});
}
}
module.exports = Reimbursement;

View File

@@ -4,3 +4,4 @@ class Token extends Base {
} }
module.exports = Token; module.exports = Token;

View File

@@ -1,39 +1,37 @@
const ethers = require('ethers'); const ethers = require('ethers');
const RSVP = require('rsvp');
const Preflight = require('./utils/preflight'); const Preflight = require('./utils/preflight');
const deprecate = require('./utils/deprecate');
const ABIS = { const ABIS = {
Contributor: require('./abis/Contributor.json'), Contributor: require('./abis/Contributor.json'),
Contribution: require('./abis/Contribution.json'), Contribution: require('./abis/Contribution.json'),
Reimbursement: require('./abis/Reimbursement.json'),
Token: require('./abis/Token.json'), Token: require('./abis/Token.json'),
Proposal: require('./abis/Proposal.json'), Proposal: require('./abis/Proposal.json'),
Kernel: require('./abis/Kernel.json'), Kernel: require('./abis/Kernel.json'),
Acl: require('./abis/ACL.json'), Acl: require('./abis/ACL.json')
}; };
const APP_CONTRACTS = [ const APP_CONTRACTS = [
'Contributor', 'Contributor',
'Contribution', 'Contribution',
'Token', 'Token',
'Proposal', 'Proposal',
'Reimbursement', 'Acl'
'Acl',
]; ];
const DaoAddresses = require('./addresses/dao.json'); const DaoAddresses = require('./addresses/dao.json');
const Contracts = require('./contracts'); const Contracts = require('./contracts');
const IPFS = require('./utils/ipfs'); const IPFS = require('./utils/ipfs')
// Helpers // Helpers
function capitalize (word) { function capitalize(word) {
let [first, ...rest] = word; let [first, ...rest] = word;
return `${first.toUpperCase()}${rest.join('')}`; return `${first.toUpperCase()}${rest.join('')}`;
} }
class Kredits { class Kredits {
constructor (provider, signer, options = {}) { constructor(provider, signer, options = {}) {
let { addresses, abis, ipfsConfig } = options; let { addresses, abis, ipfsConfig } = options;
this.provider = provider; this.provider = provider;
@@ -45,7 +43,7 @@ class Kredits {
this.contracts = {}; this.contracts = {};
} }
init (names) { init(names) {
let contractsToLoad = names || APP_CONTRACTS; let contractsToLoad = names || APP_CONTRACTS;
return this.provider.getNetwork().then(network => { return this.provider.getNetwork().then(network => {
this.addresses['Kernel'] = this.addresses['Kernel'] || DaoAddresses[network.chainId.toString()]; this.addresses['Kernel'] = this.addresses['Kernel'] || DaoAddresses[network.chainId.toString()];
@@ -53,43 +51,22 @@ class Kredits {
return this.Kernel.getApp(contractName).then((address) => { return this.Kernel.getApp(contractName).then((address) => {
this.addresses[contractName] = address; this.addresses[contractName] = address;
}).catch((error) => { }).catch((error) => {
console.log(error);
throw new Error(`Failed to get address for ${contractName} from DAO at ${this.Kernel.contract.address} throw new Error(`Failed to get address for ${contractName} from DAO at ${this.Kernel.contract.address}
- ${error.message}` - ${error.message}`
); );
}); });
}); });
return RSVP.all(addressPromises).then(() => { return this });
return Promise.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 Kernel() {
let { network, rpcUrl, wallet } = connectionOptions;
if (!rpcUrl && network === 'local') { rpcUrl = 'http://localhost:8545'; }
let ethProvider, signer;
if (rpcUrl) {
ethProvider = new ethers.providers.JsonRpcProvider(rpcUrl);
} else {
ethProvider = new ethers.getDefaultProvider(network);
}
if (wallet) {
signer = wallet.connect(ethProvider);
} else if (ethProvider.getSigner) {
signer = ethProvider.getSigner();
}
return new Kredits(ethProvider, signer, kreditsOptions);
}
static availableNetworks () {
return Object.keys(DaoAddresses);
}
get Kernel () {
let k = this.contractFor('Kernel'); let k = this.contractFor('Kernel');
// in case we want to use a special apm (e.g. development vs. production) // in case we want to use a special apm (e.g. development vs. production)
if (this.options.apm) { if (this.options.apm) {
@@ -98,41 +75,37 @@ class Kredits {
return k; return k;
} }
get Contributor () { get Contributor() {
return this.contractFor('Contributor'); return this.contractFor('Contributor');
} }
get Contributors () { get Contributors() {
deprecate('Contributors is deprecated use Contributor instead'); console.log('Contributors is deprecated use Contributor instead');
return this.Contributor; return this.Contributor;
} }
get Proposal () { get Proposal() {
return this.contractFor('Proposal'); return this.contractFor('Proposal');
} }
get Operator () { get Operator() {
return this.Proposal; return this.Proposal;
} }
get Token () { get Token() {
return this.contractFor('Token'); return this.contractFor('Token');
} }
get Contribution () { get Contribution() {
return this.contractFor('Contribution'); return this.contractFor('Contribution');
} }
get Reimbursement () { get Acl() {
return this.contractFor('Reimbursement');
}
get Acl () {
return this.contractFor('Acl'); return this.contractFor('Acl');
} }
// 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];
} }
@@ -152,7 +125,7 @@ class Kredits {
return this.contracts[name]; return this.contracts[name];
} }
preflightChecks () { preflightChecks() {
return new Preflight(this).check(); return new Preflight(this).check();
} }
} }

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -1,100 +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,
date,
url,
tags,
details,
} = this;
const data = {
'@context': 'https://schema.kosmos.org',
'@type': 'Expense',
title,
description,
currency,
amount,
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,
date,
url,
tags,
details,
} = JSON.parse(serialized.toString('utf8'));
return {
title,
description,
currency,
amount,
date,
url,
tags,
details,
ipfsData: serialized,
};
}
}
module.exports = ExpenseSerializer;

View File

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

View File

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

View File

@@ -1,18 +1,17 @@
const ipfsClient = require('ipfs-http-client'); const ipfsClient = require('ipfs-http-client');
const multihashes = require('multihashes'); const multihashes = require('multihashes');
const fetch = require('node-fetch');
class IPFS { class IPFS {
constructor (config) {
constructor(config) {
if (!config) { if (!config) {
config = { host: 'localhost', port: '5001', protocol: 'http' }; config = { host: 'localhost', port: '5001', protocol: 'http' };
} }
this._config = config;
this._ipfsAPI = ipfsClient(config); this._ipfsAPI = ipfsClient(config);
this._config = config;
} }
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;
@@ -27,7 +26,7 @@ class IPFS {
}); });
} }
add (data) { add(data) {
return this._ipfsAPI return this._ipfsAPI
.add(ipfsClient.Buffer.from(data)) .add(ipfsClient.Buffer.from(data))
.then((res) => { .then((res) => {
@@ -35,40 +34,29 @@ class IPFS {
}); });
} }
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 fetch(`${this._config['gatewayUrl']}/${ipfsHash}`).then(r => r.text());
} else {
return this._ipfsAPI.cat(ipfsHash); return this._ipfsAPI.cat(ipfsHash);
} }
}
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) {
let digest = ipfsClient.Buffer.from(hashData.hashDigest.slice(2), 'hex'); let digest = ipfsClient.Buffer.from(hashData.hashDigest.slice(2), 'hex');
return multihashes.encode(digest, hashData.hashFunction, hashData.hashSize); return multihashes.encode(digest, hashData.hashFunction, hashData.hashSize);
} }
} }
module.exports = IPFS; module.exports = IPFS;

View File

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

View File

@@ -1,9 +1,9 @@
class Preflight { class Preflight {
constructor (kredits) { constructor(kredits) {
this.kredits = kredits; this.kredits = kredits;
} }
check () { check() {
return this.kredits.ipfs._ipfsAPI.id() return this.kredits.ipfs._ipfsAPI.id()
.catch((error) => { .catch((error) => {
throw new Error(`IPFS node not available; config: ${JSON.stringify(this.kredits.ipfs.config)} - ${error.message}`); throw new Error(`IPFS node not available; config: ${JSON.stringify(this.kredits.ipfs.config)} - ${error.message}`);

View File

@@ -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;

15471
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "kredits-contracts", "name": "kredits-contracts",
"version": "6.0.0", "version": "4.0.1",
"description": "Ethereum contracts and npm wrapper for Kredits", "description": "Ethereum contracts and npm wrapper for Kredits",
"main": "./lib/kredits.js", "main": "./lib/kredits.js",
"directories": { "directories": {
@@ -8,28 +8,19 @@
}, },
"scripts": { "scripts": {
"install-all": "./scripts/every-app.sh \"npm install\"", "install-all": "./scripts/every-app.sh \"npm install\"",
"postshrinkwrap": "node scripts/fix-package-lock.js &>/dev/null || true",
"build-json": "npm run compile-contracts && node ./scripts/build-json.js", "build-json": "npm run compile-contracts && node ./scripts/build-json.js",
"repl": "truffle exec scripts/repl.js", "repl": "truffle exec scripts/repl.js",
"seeds": "truffle exec scripts/seeds.js", "seeds": "truffle exec scripts/seeds.js",
"compile-contracts": "truffle compile --all", "compile-contracts": "aragon contracts compile --all",
"bootstrap": "npm run reset:hard && npm run seeds", "bootstrap": "./scripts/every-app.sh \"npm install \" && npm run reset:hard && npm run seeds",
"reset": "npm run deploy:kit && npm run deploy:dao", "reset": "npm run deploy:kit && npm run deploy:dao",
"reset:hard": "npm run compile-contracts && npm run deploy:apps && npm run reset", "reset:hard": "npm run deploy:apps && npm run reset",
"deploy:kit": "truffle exec scripts/deploy-kit.js", "deploy:kit": "npm run compile-contracts && aragon contracts exec scripts/deploy-kit.js",
"deploy:dao": "truffle exec scripts/new-dao.js", "deploy:dao": "aragon contracts exec scripts/new-dao.js",
"deploy:apps": "./scripts/every-app.sh \"aragon apm publish major --propagate-content=false --build=false --prepublish=false --skip-confirmation\"", "deploy:apps": "./scripts/every-app.sh \"aragon apm publish major\"",
"devchain": "aragon devchain --port 7545", "devchain": "aragon devchain --port 7545",
"dao:address": "truffle exec scripts/current-address.js", "dao:address": "truffle exec scripts/current-address.js",
"lint:contracts": "solhint \"contracts/**/*.sol\" \"apps/*/contracts/**/*.sol\"", "test": "echo \"Error: no test specified\" && exit 1"
"lint:contract-tests": "eslint apps/*/test",
"lint:wrapper": "eslint lib/",
"test": "npm run test:token && npm run test:contributor && npm run test:contribution && npm run test:proposal",
"test:token": "cd apps/token && npm run test",
"test:contributor": "cd apps/contributor && npm run test",
"test:contribution": "cd apps/contribution && npm run test",
"test:proposal": "cd apps/proposal && npm run test",
"setup-git-hooks": "sh scripts/git-hooks/install"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -42,30 +33,19 @@
}, },
"homepage": "https://github.com/67P/truffle-kredits#readme", "homepage": "https://github.com/67P/truffle-kredits#readme",
"devDependencies": { "devDependencies": {
"@aragon/cli": "^5.5.0",
"@aragon/kits-base": "^1.0.0", "@aragon/kits-base": "^1.0.0",
"@aragon/os": "^4.4.0", "@aragon/os": "^4.1.0",
"async-each-series": "^1.1.0", "async-each-series": "^1.1.0",
"cli-table": "^0.3.1", "eth-provider": "^0.2.2",
"eslint": "^7.1.0", "openzeppelin-solidity": "^2.2.0",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eth-provider": "^0.2.5",
"ethereum-block-by-date": "^1.4.0",
"homedir": "^0.6.0",
"promptly": "^3.0.3", "promptly": "^3.0.3",
"solc": "^0.6.8", "solc": "^0.4.25"
"solhint": "^2.3.1",
"truffle-hdwallet-provider": "^1.0.17",
"truffle-hdwallet-provider-privkey": "^0.3.0",
"yargs": "^15.0.0"
}, },
"dependencies": { "dependencies": {
"ethers": "^5.0.2", "ethers": "^4.0.27",
"ipfs-http-client": "^41.0.1", "ipfs-http-client": "^30.1.1",
"@kosmos/schemas": "^3.0.0", "rsvp": "^4.8.2"
"node-fetch": "^2.6.0",
"tv4": "^1.3.0"
}, },
"keywords": [ "keywords": [
"kosmos", "kosmos",

View File

@@ -1,5 +1,4 @@
const promptly = require('promptly'); const promptly = require('promptly');
const { inspect } = require('util');
const initKredits = require('./helpers/init_kredits.js'); const initKredits = require('./helpers/init_kredits.js');
@@ -19,40 +18,31 @@ module.exports = async function(callback) {
let contributorAccount; let contributorAccount;
if (contributor.length < 5) { if (contributor.length < 5) {
contributorId = contributor; contributorId = contributor;
contributorAccount = await kredits.Contributor.contract.getContributorAddressById(contributor); contributorAccount = await kredits.Contributor.functions.getContributorAddressById(contributor);
} else { } else {
contributorAccount = contributor; contributorAccount = contributor;
contributorId = await kredits.Contributor.contract.getContributorIdByAddress(contributor); contributorId = await kredits.Contributor.functions.getContributorIdByAddress(contributor);
} }
console.log(`Creating a contribution for contributor account ${contributorAccount} ID: ${contributorId}`); console.log(`Creating a contribution for contributor account ${contributorAccount} ID: ${contributorId}`);
[ dateNow, timeNow ] = (new Date()).toISOString().split('T');
let contributionAttributes = { let contributionAttributes = {
contributorId, contributorId,
date: dateNow,
time: timeNow,
amount: await promptly.prompt('Amount: '), amount: await promptly.prompt('Amount: '),
description: await promptly.prompt('Description: '), description: await promptly.prompt('Description: '),
kind: await promptly.prompt('Kind: ', { default: 'dev' }), kind: await promptly.prompt('Kind: ', { default: 'dev' }),
url: await promptly.prompt('URL: ', { default: '' }) url: await promptly.prompt('URL: ', { default: '' })
} }
const contributorData = await kredits.Contributor.getById(contributorId);
contributionAttributes.contributorIpfsHash = contributorData.ipfsHash;
console.log("\nAdding contribution:"); console.log("\nAdding contribution:");
console.log(contributionAttributes); console.log(contributionAttributes);
kredits.Contribution.add(contributionAttributes, { gasLimit: 300000 }) kredits.Contribution.addContribution(contributionAttributes, { gasLimit: 300000 }).then((result) => {
.then(result => {
console.log("\n\nResult:"); console.log("\n\nResult:");
console.log(result); console.log(result);
callback(); callback();
}) }).catch((error) => {
.catch(error => {
console.log('Failed to create contribution'); console.log('Failed to create contribution');
callback(inspect(error)); callback(error);
}); });
} }

View File

@@ -26,7 +26,7 @@ module.exports = async function(callback) {
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: '),
}; };

View File

@@ -1,5 +1,4 @@
const promptly = require('promptly'); const promptly = require('promptly');
const { inspect } = require('util');
const initKredits = require('./helpers/init_kredits.js'); const initKredits = require('./helpers/init_kredits.js');
@@ -19,38 +18,30 @@ module.exports = async function(callback) {
let contributorAccount; let contributorAccount;
if (contributor.length < 5) { if (contributor.length < 5) {
contributorId = contributor; contributorId = contributor;
contributorAccount = await kredits.Contributor.contract.getContributorAddressById(contributor); contributorAccount = await kredits.Contributor.functions.getContributorAddressById(contributor);
} else { } else {
contributorAccount = contributor; contributorAccount = contributor;
contributorId = await kredits.Contributor.contract.getContributorIdByAddress(contributor); contributorId = await kredits.Contributor.functions.getContributorIdByAddress(contributor);
} }
console.log(`Creating a proposal for contributor ID #${contributorId} account: ${contributorAccount}`); console.log(`Creating a proposal for contributor ID #${contributorId} account: ${contributorAccount}`);
[ dateNow, timeNow ] = (new Date()).toISOString().split('T');
let contributionAttributes = { let contributionAttributes = {
contributorId, contributorId,
date: dateNow,
time: timeNow,
amount: await promptly.prompt('Amount: '), amount: await promptly.prompt('Amount: '),
description: await promptly.prompt('Description: '), description: await promptly.prompt('Description: '),
kind: await promptly.prompt('Kind: ', { default: 'dev' }), kind: await promptly.prompt('Kind: ', { default: 'dev' }),
url: await promptly.prompt('URL: ', { default: '' }) url: await promptly.prompt('URL: ', { default: '' })
} }
const contributorData = await kredits.Contributor.getById(contributorId);
contributionAttributes.contributorIpfsHash = contributorData.ipfsHash;
console.log("\nAdding proposal:"); console.log("\nAdding proposal:");
console.log(contributionAttributes); console.log(contributionAttributes);
kredits.Proposal.addProposal(contributionAttributes, { gasLimit: 300000 }) kredits.Proposal.addProposal(contributionAttributes, { gasLimit: 300000 }).then((result) => {
.then((result) => {
console.log("\n\nResult:"); console.log("\n\nResult:");
console.log(result); console.log(result);
callback(); callback();
}).catch((error) => { }).catch((error) => {
console.log('Failed to create proposal'); console.log('Failed to create proposal');
callback(inspect(error)); callback(error);
}); });
} }

View File

@@ -11,7 +11,6 @@ const files = [
'Kernel', 'Kernel',
'Proposal', 'Proposal',
'Token', 'Token',
'Reimbursement',
'ACL' 'ACL'
]; ];

View File

@@ -1,53 +0,0 @@
const promptly = require('promptly');
const Table = require('cli-table');
const initKredits = require('./helpers/init_kredits.js');
module.exports = async function(callback) {
let kredits;
try {
kredits = await initKredits(web3);
} catch(e) {
callback(e);
return;
}
console.log(`Using Contribution at: ${kredits.Contribution.contract.address}`);
let recipient = await promptly.prompt('Contributor ID: ');
recipient = parseInt(recipient);
const table = new Table({
head: ['ID', 'Description', 'Amount', 'Claim Transaction'],
});
try {
let blockNumber = await kredits.provider.getBlockNumber();
let contributions = await kredits.Contribution.all({page: {size: 200}});
console.log(`Current block number: ${blockNumber}`);
let claimPromises = contributions.map(async (c) => {
const confirmed = c.confirmedAtBlock <= blockNumber;
if (c.contributorId === recipient && confirmed && !c.vetoed && !c.claimed) {
console.log(`Claiming contribution ID=${c.id}`);
return kredits.Contribution.contract.claim(c.id, { gasLimit: 500000 }).then(tx => {
table.push([
c.id.toString(),
`${c.description}`,
c.amount.toString(),
tx.hash,
]);
});
}
});
Promise.all(claimPromises).then(_ => {
console.log(table.toString());
callback();
});
} catch (err) {
console.log(err);
callback();
}
};

View File

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

View File

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

View File

@@ -3,33 +3,23 @@ const deployDAOFactory = require('@aragon/os/scripts/deploy-daofactory.js')
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const argv = require('yargs').argv const argv = require('yargs').argv
const ethers = require('ethers'); const namehash = require('ethers').utils.namehash;
const namehash = ethers.utils.namehash;
const fileInject = require('./helpers/file_inject.js') const fileInject = require('./helpers/file_inject.js')
const getNetworkId = require('./helpers/networkid.js')
const DAOFactory = artifacts.require('DAOFactory') const DAOFactory = artifacts.require('DAOFactory')
const KreditsKit = artifacts.require('KreditsKit') const KreditsKit = artifacts.require('KreditsKit')
const arapp = require('../arapp.json') const arapp = require('../arapp.json')
const environment = argv['network'] || argv['environment'] || 'development' const environment = argv['network'] || argv['environment'] || 'development'
const apm = arapp.environments[environment].apm
const ensAddr = arapp.environments[environment].registry || process.env.ENS
const daoFactoryAddress = arapp.environments[environment].daoFactory || process.env.DAO_FACTORY
const kreditsArappConfig = arapp.environments[environment].kredits || {}
// typically we use the open.aragonpm.eth aragonpm.
const apm = kreditsArappConfig.apmDomain || argv['apmDomain'] || 'open.aragonpm.eth'
// daoFactory is environment specific.
// See https://github.com/aragon/deployments/tree/master/environments/ for the official daoFactory
// Locally we deploy our own daoFactory and no daoFactory is required (`daoFactoryAddress` is null).
const daoFactoryAddress = kreditsArappConfig.daoFactory || argv['daoFactory']
const ensAddr = arapp.environments[environment].registry || argv['ensAddress']
module.exports = async function(callback) { module.exports = async function(callback) {
const provider = new ethers.providers.Web3Provider(web3.currentProvider); const networkId = await getNetworkId(web3)
const network = await provider.getNetwork();
const networkId = network.chainId;
console.log(`Deploying to networkId: ${networkId}`) console.log(`Deploying to networkId: ${networkId}`)
if (!ensAddr) { if (!ensAddr) {
@@ -45,9 +35,7 @@ module.exports = async function(callback) {
} }
console.log(`Using DAOFactory at: ${daoFactory.address}`) console.log(`Using DAOFactory at: ${daoFactory.address}`)
const apps = fs.readdirSync('./apps', { withFileTypes: true }) const apps = fs.readdirSync('./apps')
.filter(e => e.isDirectory())
.map(e => e.name);
console.log(`Found apps: [${apps}].${apm}`) console.log(`Found apps: [${apps}].${apm}`)
let appIds = {} let appIds = {}
apps.sort().forEach((app) => { apps.sort().forEach((app) => {

View File

@@ -1,49 +0,0 @@
const fs = require('fs');
const ethers = require('ethers');
const Kredits = require('../../lib/kredits');
const provider = new ethers.providers.JsonRpcProvider('https://rpc.ankr.com/eth_rinkeby');
const arapp = require('../../arapp.json');
const apm = arapp.environments['rinkeby'].apm;
async function main() {
const kredits = await new Kredits(provider, null, { apm });
//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 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();

View File

@@ -1,44 +0,0 @@
const fs = require('fs');
const ethers = require('ethers');
const Kredits = require('../../lib/kredits');
const provider = new ethers.providers.JsonRpcProvider('https://rpc.ankr.com/eth_rinkeby');
const arapp = require('../../arapp.json');
const apm = arapp.environments['rinkeby'].apm;
async function main() {
const kredits = await new Kredits(provider, null, { apm });
//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();

View File

@@ -1,19 +0,0 @@
const promptly = require('promptly');
const EthDater = require('ethereum-block-by-date');
const initKredits = require('./helpers/init_kredits.js');
module.exports = async function(callback) {
let kredits;
try { kredits = await initKredits(web3); } catch(e) { callback(e); return; }
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}
`);
callback();
}

View File

@@ -1,47 +0,0 @@
#!/usr/bin/env node
// whatever npm does?! and for whatever this is needed..
// https://github.com/aragon/aragon-cli/blob/master/packages/aragon-cli/scripts/fix-lockfile
// https://github.com/aragon/aragon-cli/blob/master/docs-internal/Dependencies.md#regenerate-the-lockfiles
const fs = require('fs')
const path = require('path')
function replaceAll(string, mapObject) {
const regex = new RegExp(Object.keys(mapObject).join('|'), 'gi')
let occurrences = 0
const result = string.replace(regex, matched => {
occurrences++
return mapObject[matched]
})
console.log(`[fix-lockfile] Replaced ${occurrences} occurrences.`)
return result
}
async function fixLockfile(path, replacementMap) {
const originalJson = require(path)
const originalText = JSON.stringify(originalJson, null, 2)
const fixedText = replaceAll(originalText, replacementMap)
const fixedJson = JSON.parse(fixedText)
console.log('writing file', path);
await fs.writeFileSync(path, JSON.stringify(fixedJson, null, 2))
}
//
const LOCKFILE_PATH = path.join(__dirname, '..', 'package-lock.json')
const replacementMap = {
//
'"version": "github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c"':
'"version": "0.2.3"',
//
'"from": "github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c"':
'"resolved": "github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c"',
//
'"from": "async-eventemitter@github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c"':
'"resolved": "github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c"',
//
'"async-eventemitter": "async-eventemitter@github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c"':
'"async-eventemitter": "github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c"',
}
fixLockfile(LOCKFILE_PATH, replacementMap)

View File

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

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