Compare commits
1 Commits
legacy
...
feature/co
| Author | SHA1 | Date | |
|---|---|---|---|
| c5f98a1b69 |
@@ -1 +0,0 @@
|
||||
/scripts/
|
||||
33
.eslintrc.js
33
.eslintrc.js
@@ -1,33 +0,0 @@
|
||||
module.exports = {
|
||||
'env': {
|
||||
'browser': true,
|
||||
'es6': true,
|
||||
'node': true
|
||||
},
|
||||
'extends': 'eslint:recommended',
|
||||
'globals': {
|
||||
'Atomics': 'readonly',
|
||||
'SharedArrayBuffer': 'readonly'
|
||||
},
|
||||
'parserOptions': {
|
||||
'ecmaVersion': 2018,
|
||||
'sourceType': 'module'
|
||||
},
|
||||
'rules': {
|
||||
'comma-dangle': ['error', {
|
||||
arrays: 'always-multiline',
|
||||
objects: 'always-multiline',
|
||||
imports: 'never',
|
||||
exports: 'never',
|
||||
functions: 'ignore',
|
||||
}],
|
||||
'eol-last': ['error', 'always'],
|
||||
semi: ['error', 'always'],
|
||||
'space-before-function-paren': ['error', {
|
||||
anonymous: 'never',
|
||||
named: 'always',
|
||||
asyncArrow: 'always',
|
||||
}],
|
||||
'indent': ['error', 2]
|
||||
}
|
||||
}
|
||||
4
.github/release-drafter.yml
vendored
4
.github/release-drafter.yml
vendored
@@ -1,4 +0,0 @@
|
||||
template: |
|
||||
## Changes
|
||||
|
||||
$CHANGES
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,9 +1,6 @@
|
||||
data
|
||||
build
|
||||
flattened_contracts
|
||||
node_modules
|
||||
**/node_modules
|
||||
.ganache-db
|
||||
.tm_properties
|
||||
yarn-error.log
|
||||
.DS_Store
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"extends": [
|
||||
"solhint:default",
|
||||
"solhint:recommended"
|
||||
],
|
||||
"rules": {
|
||||
"indent": "2"
|
||||
}
|
||||
}
|
||||
38
.travis.yml
38
.travis.yml
@@ -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
|
||||
116
README.md
116
README.md
@@ -2,20 +2,17 @@
|
||||
|
||||
# Kredits Contracts
|
||||
|
||||
This repository contains the Solidity smart contracts organized as
|
||||
[Aragon](https://hack.aragon.org/) apps and JavaScript API wrapper for [Kosmos
|
||||
Kredits](https://wiki.kosmos.org/Kredits).
|
||||
This repository contains the Solidity smart contracts organized as [Aragon](https://hack.aragon.org/)
|
||||
apps and JavaScript API wrapper for [Kosmos Kredits](https://wiki.kosmos.org/Kredits).
|
||||
|
||||
It is based on [aragonOS](https://hack.aragon.org/docs/aragonos-intro.html) and
|
||||
follows the aragonOS conventions. Aragon itself uses the [Truffle
|
||||
framework](http://truffleframework.com/) for some things.
|
||||
It is based on [aragonOS](https://hack.aragon.org/docs/aragonos-intro.html) and
|
||||
follows the aragonOS conventions.
|
||||
Aragon itself uses the [Truffle framework](http://truffleframework.com/) for some things.
|
||||
|
||||
## Development
|
||||
|
||||
### Installation
|
||||
|
||||
#### App dependencies
|
||||
|
||||
All requirements are defined in `package.json`.
|
||||
|
||||
$ npm install
|
||||
@@ -25,84 +22,59 @@ Each of the aragon apps are separate packages:
|
||||
$ cd apps/[app]
|
||||
$ npm install
|
||||
|
||||
You can use `npm run install-all` to install all app dependencies at once.
|
||||
|
||||
#### 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._
|
||||
or use the bootstrap command (see below)
|
||||
|
||||
### Local development chain
|
||||
|
||||
For local development it is recommended to use
|
||||
[ganache](http://truffleframework.com/ganache/) to run a local development
|
||||
chain. When using the ganache simulator, no full Ethereum node is required.
|
||||
For local development it is recommended to use
|
||||
[ganache](http://truffleframework.com/ganache/) to run a local development
|
||||
chain. Using the ganache simulator no full Ethereum node is required.
|
||||
|
||||
We use the default aragon-cli devchain command to configure and run a local
|
||||
We use the default aragon-cli devchain command to confgure and run a local
|
||||
development ganache.
|
||||
|
||||
$ npm run devchain (or aragon devchain --port 7545)
|
||||
|
||||
To clear/reset the chain use (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)
|
||||
|
||||
We default to port 7545 for development to not get in conflict with the default
|
||||
We default to port 7545 for development to not get in conflict with the default
|
||||
Ethereum RPC port.
|
||||
|
||||
You can also set certain ganache options to configure the devchain, for example
|
||||
if you want to increase the block time to 10 seconds you can add
|
||||
`--block-time=10`.
|
||||
|
||||
### Bootstrap
|
||||
|
||||
1. Run an Ethereum node and ipfs
|
||||
|
||||
|
||||
$ npm run devchain
|
||||
$ ipfs daemon
|
||||
|
||||
2. 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)
|
||||
$ 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:dao
|
||||
|
||||
5. Execute seeds to create demo contributors, contributions, etc. (optional)
|
||||
4. Execute seeds to create demo contributors, contributons, etc. (optional)
|
||||
|
||||
$ npm run seeds
|
||||
|
||||
**Step 2-5 is also summarized in `npm run bootstrap`**
|
||||
|
||||
If you want to reset your local setup:
|
||||
|
||||
$ npm run reset // deploys a new kit and a new DAO
|
||||
$ npm run reset:hard // deploys all apps and does reset
|
||||
**Step 2-4 is also summarized in `npm run bootstrap`**
|
||||
|
||||
## Contract architecture
|
||||
|
||||
Contracts are organized in independent apps (see `/apps`) and are developed and
|
||||
deployed independently. Each app has a version and can be "installed" on the
|
||||
Kredits DAO independently.
|
||||
Contracts are organized in independent apps (see `/apps`) and are developed
|
||||
and deployed independently. Each app has a version and can be "installed"
|
||||
on the Kredits DAO independently.
|
||||
|
||||

|
||||
|
||||
A DAO can be deployed using the `scripts/deploy-kit.js` script or with the
|
||||
`npm run deploy:dao` command. This deploys a new Kredits DAO, installs the
|
||||
latest app versions and sets the required permissions.
|
||||
A DAO can be deployed using the `scripts/deploy-kit.js` script or with the
|
||||
`npm run deploy:dao` command. This deploys a new Kredits DAO, installs
|
||||
the latest app versions and sets the required permissions.
|
||||
|
||||
See each app in `/apps/*` for details.
|
||||
|
||||
@@ -115,8 +87,6 @@ CLI. _At some point these should be moved into a real nice CLI._
|
||||
To run these scripts use `truffle exec`. For example: `truffle exec
|
||||
scripts/add-proposal.js`.
|
||||
|
||||
Some scripts are also defined as npm script, see package.json.
|
||||
|
||||
### cli.js
|
||||
|
||||
Call any function on any contract:
|
||||
@@ -136,11 +106,11 @@ Script to add a new entries to the contracts using the JS wrapper
|
||||
|
||||
$ truffle exec scripts/add-{contributor, contribution, proposal}.js
|
||||
|
||||
### list-{contributors, contributions, proposals}.js
|
||||
### list-{contributor, contribution, proposal}.js
|
||||
|
||||
List contract entries
|
||||
|
||||
$ truffle exec scripts/list-{contributors, contributions, proposals}.js
|
||||
$ truffle exec scripts/list-{contributor, contribution, proposal}.js
|
||||
|
||||
### send-funds.js
|
||||
|
||||
@@ -173,14 +143,8 @@ Deploys a new KreditsKit that allows to create a new DAO
|
||||
or
|
||||
$ npm run deploy:kit
|
||||
|
||||
#### Kredits configuration options:
|
||||
|
||||
Configuration options can be set in an environment specific `kredits` object in the `arapp.json` or using a CLI parameter.
|
||||
|
||||
* daoFactory: Ethereum address of the used DAO Factory. On public networks we use [official aragon factories](https://github.com/aragon/deployments/tree/master/environments/)
|
||||
* apmDomain: the ENS domain of the aragonPM (normally `open.aragonpm.eth`)
|
||||
|
||||
(please also see the [arapp.json related configuration options](https://hack.aragon.org/docs/cli-global-confg#the-arappjson-file))
|
||||
`ENS` address is required as environment variable.
|
||||
`DAO_FACTORY` can optionally be set as environment variable. (see aragon)
|
||||
|
||||
### new-dao.js
|
||||
|
||||
@@ -190,7 +154,7 @@ Creates and configures a new DAO instance.
|
||||
or
|
||||
$ 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.
|
||||
|
||||
### deploy-apps.sh
|
||||
@@ -201,32 +165,14 @@ Runs `npm install` for each app and publishes a new version.
|
||||
or
|
||||
$ npm run deploy:apps
|
||||
|
||||
## Deployment
|
||||
|
||||
### Apps deployment
|
||||
|
||||
To deploy a new app version run:
|
||||
|
||||
$ aragon apm publish major --environment=NETWORK_TO_DEPLOY
|
||||
|
||||
### KreditsKit
|
||||
|
||||
deploy the KreditsKit as Kit to create new DAOs
|
||||
|
||||
$ truffle exec scripts/deploy-kit.js --network=NETWORK_TO_DEPLOY
|
||||
|
||||
### Creating a new DAO
|
||||
|
||||
make sure all apps and the KreditsKit are deployed, then create a new DAO:
|
||||
|
||||
$ truffle exec scripts/new-dao.js --network=NETWORK_TO_DEPLOY
|
||||
|
||||
## ACL / Permissions
|
||||
|
||||
|
||||
## Upgradeable contracts
|
||||
|
||||
We use aragonOS for upgradeability of the different contracts. Refer to the
|
||||
[aragonOS upgradeablity documentation](https://hack.aragon.org/docs/upgradeability-intro)
|
||||
We use aragonOS for upgradeablity of the different contracts.
|
||||
Refer to the [aragonOS upgradeablity documentation](https://hack.aragon.org/docs/upgradeability-intro)
|
||||
for more details.
|
||||
|
||||
### Example
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
module.exports = {
|
||||
'globals': {
|
||||
contract: true,
|
||||
describe: true,
|
||||
it: true,
|
||||
},
|
||||
rules: {
|
||||
'no-unused-vars': ['error', {
|
||||
'argsIgnorePattern': '^_',
|
||||
}],
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@
|
||||
"environments": {
|
||||
"default": {
|
||||
"network": "development",
|
||||
"appName": "kredits-contribution.open.aragonpm.eth"
|
||||
"appName": "kredits-contribution.aragonpm.eth"
|
||||
},
|
||||
"rinkeby": {
|
||||
"registry": "0x98df287b6c145399aaa709692c8d308357bc085d",
|
||||
|
||||
@@ -4,14 +4,7 @@ import "@aragon/os/contracts/apps/AragonApp.sol";
|
||||
import "@aragon/os/contracts/kernel/IKernel.sol";
|
||||
|
||||
interface IToken {
|
||||
function mintFor(address contributorAccount, uint256 amount, uint32 contributionId) public;
|
||||
}
|
||||
|
||||
interface ContributorInterface {
|
||||
function getContributorAddressById(uint32 contributorId) public view returns (address);
|
||||
function getContributorIdByAddress(address contributorAccount) public view returns (uint32);
|
||||
// TODO Maybe use for validation
|
||||
// function exists(uint32 contributorId) public view returns (bool);
|
||||
function mintFor(address contributorAccount, uint256 amount, uint256 contributionId) public;
|
||||
}
|
||||
|
||||
contract Contribution is AragonApp {
|
||||
@@ -19,66 +12,48 @@ contract Contribution is AragonApp {
|
||||
bytes32 public constant VETO_CONTRIBUTION_ROLE = keccak256("VETO_CONTRIBUTION_ROLE");
|
||||
|
||||
bytes32 public constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb;
|
||||
|
||||
// ensure alphabetic order
|
||||
enum Apps { Contribution, Contributor, Proposal, Reimbursement, Token }
|
||||
bytes32[5] public appIds;
|
||||
enum Apps { Contribution, Contributor, Proposal, Token }
|
||||
bytes32[4] public appIds;
|
||||
|
||||
struct ContributionData {
|
||||
uint32 contributorId;
|
||||
uint32 amount;
|
||||
address contributor;
|
||||
uint256 amount;
|
||||
bool claimed;
|
||||
bytes32 hashDigest;
|
||||
uint8 hashFunction;
|
||||
uint8 hashSize;
|
||||
string tokenMetadataURL;
|
||||
uint256 confirmedAtBlock;
|
||||
uint claimAfterBlock;
|
||||
bool vetoed;
|
||||
bool exists;
|
||||
}
|
||||
|
||||
string internal name_;
|
||||
string internal symbol_;
|
||||
|
||||
// map contribution ID to contributor
|
||||
mapping(uint32 => uint32) public contributionOwner;
|
||||
// map contributor to contribution IDs
|
||||
mapping(uint32 => uint32[]) public ownedContributions;
|
||||
mapping(uint256 => address) contributionOwner;
|
||||
mapping(address => uint256[]) ownedContributions;
|
||||
|
||||
mapping(uint32 => ContributionData) public contributions;
|
||||
uint32 public contributionsCount;
|
||||
mapping(uint256 => ContributionData) public contributions;
|
||||
uint256 public contributionsCount;
|
||||
|
||||
uint32 public blocksToWait;
|
||||
uint256 public blocksToWait = 0;
|
||||
|
||||
event ContributionAdded(uint32 id, uint32 indexed contributorId, uint32 amount);
|
||||
event ContributionClaimed(uint32 id, uint32 indexed contributorId, uint32 amount);
|
||||
event ContributionVetoed(uint32 id, address vetoedByAccount);
|
||||
event ContributionAdded(uint256 id, address indexed contributor, uint256 amount);
|
||||
event ContributionClaimed(uint256 id, address indexed contributor, uint256 amount);
|
||||
event ContributionVetoed(uint256 id, address vetoedByAccount);
|
||||
|
||||
function initialize(bytes32[5] _appIds) public onlyInit {
|
||||
function initialize(bytes32[4] _appIds) public onlyInit {
|
||||
appIds = _appIds;
|
||||
blocksToWait = 40320; // 7 days; 15 seconds block time
|
||||
initialized();
|
||||
}
|
||||
|
||||
function getContract(uint8 appId) public view returns (address) {
|
||||
function getTokenContract() public view returns (address) {
|
||||
IKernel k = IKernel(kernel());
|
||||
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[appId]);
|
||||
}
|
||||
|
||||
function getContributorIdByAddress(address contributorAccount) public view returns (uint32) {
|
||||
address contributorContract = getContract(uint8(Apps.Contributor));
|
||||
return ContributorInterface(contributorContract).getContributorIdByAddress(contributorAccount);
|
||||
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[uint8(Apps.Token)]);
|
||||
}
|
||||
|
||||
function getContributorAddressById(uint32 contributorId) public view returns (address) {
|
||||
address contributorContract = getContract(uint8(Apps.Contributor));
|
||||
return ContributorInterface(contributorContract).getContributorAddressById(contributorId);
|
||||
}
|
||||
|
||||
//
|
||||
// Token standard functions (ERC 721)
|
||||
//
|
||||
|
||||
function name() external view returns (string) {
|
||||
return name_;
|
||||
}
|
||||
@@ -87,120 +62,85 @@ contract Contribution is AragonApp {
|
||||
return symbol_;
|
||||
}
|
||||
|
||||
// Balance is amount of ERC271 tokens, not amount of kredits
|
||||
function balanceOf(address owner) public view returns (uint256) {
|
||||
require(owner != address(0));
|
||||
uint32 contributorId = getContributorIdByAddress(owner);
|
||||
return ownedContributions[contributorId].length;
|
||||
return ownedContributions[owner].length;
|
||||
}
|
||||
|
||||
function ownerOf(uint32 contributionId) public view returns (address) {
|
||||
function ownerOf(uint256 contributionId) public view returns (address) {
|
||||
require(exists(contributionId));
|
||||
uint32 contributorId = contributions[contributionId].contributorId;
|
||||
return getContributorAddressById(contributorId);
|
||||
return contributions[contributionId].contributor;
|
||||
}
|
||||
|
||||
function tokenOfOwnerByIndex(address owner, uint32 index) public view returns (uint32) {
|
||||
uint32 contributorId = getContributorIdByAddress(owner);
|
||||
return ownedContributions[contributorId][index];
|
||||
function tokenOfOwnerByIndex(address contributor, uint256 index) public view returns (uint256) {
|
||||
return ownedContributions[contributor][index];
|
||||
}
|
||||
|
||||
function tokenMetadata(uint32 contributionId) public view returns (string) {
|
||||
function tokenMetadata(uint256 contributionId) public view returns (string) {
|
||||
return contributions[contributionId].tokenMetadataURL;
|
||||
}
|
||||
|
||||
//
|
||||
// Custom functions
|
||||
//
|
||||
|
||||
function totalKreditsEarned(bool confirmedOnly) public view returns (uint32 amount) {
|
||||
for (uint32 i = 1; i <= contributionsCount; i++) {
|
||||
ContributionData memory c = contributions[i];
|
||||
if (!c.vetoed && (block.number >= c.confirmedAtBlock || !confirmedOnly)) {
|
||||
amount += c.amount; // should use safemath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function totalKreditsEarnedByContributor(uint32 contributorId, bool confirmedOnly) public view returns (uint32 amount) {
|
||||
uint256 tokenCount = ownedContributions[contributorId].length;
|
||||
for (uint256 i = 0; i < tokenCount; i++) {
|
||||
uint32 cId = ownedContributions[contributorId][i];
|
||||
ContributionData memory c = contributions[cId];
|
||||
if (!c.vetoed && (block.number >= c.confirmedAtBlock || !confirmedOnly)) {
|
||||
amount += c.amount; // should use safemath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getContribution(uint32 contributionId) public view returns (uint32 id, uint32 contributorId, uint32 amount, bool claimed, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint256 confirmedAtBlock, bool exists, bool vetoed) {
|
||||
function getContribution(uint256 contributionId) public view returns (uint256 id, address contributor, uint256 amount, bool claimed, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint claimAfterBlock, bool exists, bool vetoed) {
|
||||
id = contributionId;
|
||||
ContributionData storage c = contributions[id];
|
||||
return (
|
||||
id,
|
||||
c.contributorId,
|
||||
c.amount,
|
||||
c.claimed,
|
||||
c.contributor,
|
||||
c.amount,
|
||||
c.claimed,
|
||||
c.hashDigest,
|
||||
c.hashFunction,
|
||||
c.hashSize,
|
||||
c.confirmedAtBlock,
|
||||
c.claimAfterBlock,
|
||||
c.exists,
|
||||
c.vetoed
|
||||
);
|
||||
}
|
||||
|
||||
function add(uint32 amount, uint32 contributorId, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(ADD_CONTRIBUTION_ROLE) {
|
||||
//require(canPerform(msg.sender, ADD_CONTRIBUTION_ROLE, new uint32[](0)), 'nope');
|
||||
uint32 contributionId = contributionsCount + 1;
|
||||
function add(uint256 amount, address contributorAccount, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(ADD_CONTRIBUTION_ROLE) {
|
||||
//require(canPerform(msg.sender, ADD_CONTRIBUTION_ROLE, new uint256[](0)), 'nope');
|
||||
uint256 contributionId = contributionsCount + 1;
|
||||
ContributionData storage c = contributions[contributionId];
|
||||
c.exists = true;
|
||||
c.amount = amount;
|
||||
c.claimed = false;
|
||||
c.contributorId = contributorId;
|
||||
c.contributor = contributorAccount;
|
||||
c.hashDigest = hashDigest;
|
||||
c.hashFunction = hashFunction;
|
||||
c.hashSize = hashSize;
|
||||
if (contributionId < 10) {
|
||||
c.confirmedAtBlock = block.number;
|
||||
} else {
|
||||
c.confirmedAtBlock = block.number + 1 + blocksToWait;
|
||||
}
|
||||
c.claimAfterBlock = block.number; // + blocksToWait;
|
||||
|
||||
contributionsCount++;
|
||||
|
||||
contributionOwner[contributionId] = contributorId;
|
||||
ownedContributions[contributorId].push(contributionId);
|
||||
|
||||
emit ContributionAdded(contributionId, contributorId, amount);
|
||||
contributionOwner[contributionId] = contributorAccount;
|
||||
ownedContributions[contributorAccount].push(contributionId);
|
||||
|
||||
emit ContributionAdded(contributionId, contributorAccount, amount);
|
||||
}
|
||||
|
||||
function veto(uint32 contributionId) public isInitialized auth(VETO_CONTRIBUTION_ROLE) {
|
||||
function veto(uint256 contributionId) public isInitialized auth(VETO_CONTRIBUTION_ROLE) {
|
||||
ContributionData storage c = contributions[contributionId];
|
||||
require(c.exists, 'NOT_FOUND');
|
||||
require(!c.claimed, 'ALREADY_CLAIMED');
|
||||
require(block.number < c.confirmedAtBlock, 'VETO_PERIOD_ENDED');
|
||||
c.vetoed = true;
|
||||
|
||||
emit ContributionVetoed(contributionId, msg.sender);
|
||||
}
|
||||
|
||||
function claim(uint32 contributionId) public isInitialized {
|
||||
function claim(uint256 contributionId) public isInitialized {
|
||||
ContributionData storage c = contributions[contributionId];
|
||||
require(c.exists, 'NOT_FOUND');
|
||||
require(!c.claimed, 'ALREADY_CLAIMED');
|
||||
require(!c.vetoed, 'VETOED');
|
||||
require(block.number >= c.confirmedAtBlock, 'NOT_CLAIMABLE');
|
||||
require(block.number > c.claimAfterBlock, 'NOT_CLAIMABLE');
|
||||
|
||||
c.claimed = true;
|
||||
address tokenContract = getContract(uint8(Apps.Token));
|
||||
address contributorAccount = getContributorAddressById(c.contributorId);
|
||||
uint256 amount = uint256(c.amount);
|
||||
IToken(tokenContract).mintFor(contributorAccount, amount, contributionId);
|
||||
emit ContributionClaimed(contributionId, c.contributorId, c.amount);
|
||||
address token = getTokenContract();
|
||||
IToken(token).mintFor(c.contributor, c.amount, contributionId);
|
||||
emit ContributionClaimed(contributionId, c.contributor, c.amount);
|
||||
}
|
||||
|
||||
function exists(uint32 contributionId) public view returns (bool) {
|
||||
|
||||
function exists(uint256 contributionId) view public returns (bool) {
|
||||
return contributions[contributionId].exists;
|
||||
}
|
||||
}
|
||||
|
||||
13005
apps/contribution/package-lock.json
generated
13005
apps/contribution/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -3,29 +3,25 @@
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"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"
|
||||
"@aragon/os": "^4.1.0",
|
||||
"@aragon/cli": "^5.5.0"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"scripts": {
|
||||
"start": "npm run start:aragon:ipfs",
|
||||
"start:aragon:ipfs": "aragon run",
|
||||
"start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist",
|
||||
"start:app": "",
|
||||
"start:app": "npm run sync-assets && npm run build:script -- --no-minify && parcel serve app/index.html -p 8001 --out-dir dist/ --no-cache",
|
||||
"test": "aragon contracts test",
|
||||
"compile": "aragon contracts compile",
|
||||
"sync-assets": "",
|
||||
"sync-assets": "copy-aragon-ui-assets -n aragon-ui ./dist",
|
||||
"build:app": "",
|
||||
"build:script": "",
|
||||
"build": "",
|
||||
"publish:patch": "aragon apm publish patch",
|
||||
"publish:minor": "aragon apm publish minor",
|
||||
"publish:major": "aragon apm publish major",
|
||||
"versions": "aragon apm versions",
|
||||
"test": "truffle test"
|
||||
"versions": "aragon apm versions"
|
||||
},
|
||||
"keywords": []
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// const Contribution = artifacts.require('Contribution.sol');
|
||||
const CounterApp = artifacts.require('Contribution.sol')
|
||||
|
||||
contract('Contribution', (_accounts) => {
|
||||
it('should be tested');
|
||||
});
|
||||
contract('Contribution', (accounts) => {
|
||||
it('should be tested')
|
||||
})
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"environments": {
|
||||
"default": {
|
||||
"network": "development",
|
||||
"appName": "kredits-contributor.open.aragonpm.eth"
|
||||
"appName": "kredits-contributor.aragonpm.eth"
|
||||
},
|
||||
"rinkeby": {
|
||||
"registry": "0x98df287b6c145399aaa709692c8d308357bc085d",
|
||||
|
||||
@@ -6,83 +6,87 @@ import "@aragon/os/contracts/kernel/IKernel.sol";
|
||||
interface ITokenBalance {
|
||||
function balanceOf(address contributorAccount) public view returns (uint256);
|
||||
}
|
||||
interface IContributionBalance {
|
||||
function totalKreditsEarnedByContributor(uint32 contributorId, bool confirmedOnly) public view returns (uint32 amount);
|
||||
function balanceOf(address owner) public view returns (uint256);
|
||||
}
|
||||
|
||||
contract Contributor is AragonApp {
|
||||
bytes32 public constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb;
|
||||
|
||||
bytes32 public constant MANAGE_CONTRIBUTORS_ROLE = keccak256("MANAGE_CONTRIBUTORS_ROLE");
|
||||
|
||||
struct Contributor {
|
||||
address account;
|
||||
bytes32 hashDigest;
|
||||
bytes32 ipfsHash;
|
||||
uint8 hashFunction;
|
||||
uint8 hashSize;
|
||||
bool isCore;
|
||||
bool exists;
|
||||
}
|
||||
|
||||
mapping (address => uint32) public contributorIds;
|
||||
mapping (uint32 => Contributor) public contributors;
|
||||
uint32 public contributorsCount;
|
||||
mapping (address => uint) public contributorIds;
|
||||
mapping (uint => Contributor) public contributors;
|
||||
uint256 public contributorsCount;
|
||||
|
||||
// ensure alphabetic order
|
||||
enum Apps { Contribution, Contributor, Proposal, Reimbursement, Token }
|
||||
bytes32[5] public appIds;
|
||||
enum Apps { Contribution, Contributor, Proposal, Token }
|
||||
bytes32[4] public appIds;
|
||||
|
||||
event ContributorProfileUpdated(uint32 id, bytes32 oldHashDigest, bytes32 newHashDigest); // what should be logged
|
||||
event ContributorAccountUpdated(uint32 id, address oldAccount, address newAccount);
|
||||
event ContributorAdded(uint32 id, address account);
|
||||
event ContributorProfileUpdated(uint id, bytes32 oldIpfsHash, bytes32 newIpfsHash);
|
||||
event ContributorAccountUpdated(uint id, address oldAccount, address newAccount);
|
||||
event ContributorAdded(uint id, address account);
|
||||
|
||||
function initialize(address root,bytes32[4] _appIds) public onlyInit {
|
||||
uint _id = contributorsCount + 1;
|
||||
Contributor storage c = contributors[_id];
|
||||
c.exists = true;
|
||||
c.isCore = true;
|
||||
c.account = root;
|
||||
contributorIds[root] = _id;
|
||||
contributorsCount += 1;
|
||||
|
||||
function initialize(address root, bytes32[5] _appIds) public onlyInit {
|
||||
appIds = _appIds;
|
||||
|
||||
initialized();
|
||||
}
|
||||
|
||||
function getContract(uint8 appId) public view returns (address) {
|
||||
function getTokenContract() public view returns (address) {
|
||||
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) {
|
||||
uint32 count = 0;
|
||||
for (uint32 i = 1; i <= contributorsCount; i++) {
|
||||
if (isCoreTeam(i)) {
|
||||
function coreContributorsCount() view public returns (uint) {
|
||||
uint count = 0;
|
||||
for (uint256 i = 1; i <= contributorsCount; i++) {
|
||||
if (contributors[i].isCore) {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
function updateContributorAccount(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");
|
||||
|
||||
function updateContributorAccount(uint id, address oldAccount, address newAccount) public auth(MANAGE_CONTRIBUTORS_ROLE) {
|
||||
contributorIds[oldAccount] = 0;
|
||||
contributorIds[newAccount] = id;
|
||||
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 updateContributorIpfsHash(uint id, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize) public isInitialized auth(MANAGE_CONTRIBUTORS_ROLE) {
|
||||
Contributor storage c = contributors[id];
|
||||
bytes32 oldHashDigest = c.hashDigest;
|
||||
c.hashDigest = hashDigest;
|
||||
bytes32 oldIpfsHash = c.ipfsHash;
|
||||
c.ipfsHash = ipfsHash;
|
||||
c.hashFunction = hashFunction;
|
||||
c.hashSize = hashSize;
|
||||
|
||||
ContributorProfileUpdated(id, oldHashDigest, c.hashDigest);
|
||||
ContributorProfileUpdated(id, oldIpfsHash, c.ipfsHash);
|
||||
}
|
||||
|
||||
function addContributor(address account, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(MANAGE_CONTRIBUTORS_ROLE) {
|
||||
function addContributor(address account, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize, bool isCore) public isInitialized auth(MANAGE_CONTRIBUTORS_ROLE) {
|
||||
require(!addressExists(account));
|
||||
uint32 _id = contributorsCount + 1;
|
||||
uint _id = contributorsCount + 1;
|
||||
assert(!contributors[_id].exists); // this can not be acually
|
||||
Contributor storage c = contributors[_id];
|
||||
c.exists = true;
|
||||
c.hashDigest = hashDigest;
|
||||
c.isCore = isCore;
|
||||
c.ipfsHash = ipfsHash;
|
||||
c.hashFunction = hashFunction;
|
||||
c.hashSize = hashSize;
|
||||
c.account = account;
|
||||
@@ -92,64 +96,49 @@ contract Contributor is AragonApp {
|
||||
emit ContributorAdded(_id, account);
|
||||
}
|
||||
|
||||
function isCoreTeam(uint32 id) view public returns (bool) {
|
||||
// TODO: for simplicity we simply define the first contributors as core
|
||||
// later this needs to be changed to something more dynamic
|
||||
return id < 7;
|
||||
function isCore(uint id) view public returns (bool) {
|
||||
return contributors[id].isCore;
|
||||
}
|
||||
|
||||
function exists(uint32 id) view public returns (bool) {
|
||||
function exists(uint id) view public returns (bool) {
|
||||
return contributors[id].exists;
|
||||
}
|
||||
|
||||
function addressIsCore(address account) view public returns (bool) {
|
||||
uint32 id = getContributorIdByAddress(account);
|
||||
return isCoreTeam(id);
|
||||
return getContributorByAddress(account).isCore;
|
||||
}
|
||||
|
||||
function addressExists(address account) view public returns (bool) {
|
||||
return getContributorByAddress(account).exists;
|
||||
}
|
||||
|
||||
function getContributorIdByAddress(address account) view public returns (uint32) {
|
||||
function getContributorIdByAddress(address account) view public returns (uint) {
|
||||
return contributorIds[account];
|
||||
}
|
||||
|
||||
function getContributorAddressById(uint32 id) view public returns (address) {
|
||||
function getContributorAddressById(uint id) view public returns (address) {
|
||||
return contributors[id].account;
|
||||
}
|
||||
|
||||
function getContributorByAddress(address account) internal view returns (Contributor) {
|
||||
uint32 id = contributorIds[account];
|
||||
uint id = contributorIds[account];
|
||||
return contributors[id];
|
||||
}
|
||||
|
||||
function getContributorById(uint32 _id) public view returns (uint32 id, address account, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, bool isCore, uint256 balance, uint32 totalKreditsEarned, uint256 contributionsCount, bool exists ) {
|
||||
function getContributorById(uint _id) public view returns (uint id, address account, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize, bool isCore, uint256 balance, bool exists ) {
|
||||
id = _id;
|
||||
Contributor storage c = contributors[_id];
|
||||
account = c.account;
|
||||
hashDigest = c.hashDigest;
|
||||
ipfsHash = c.ipfsHash;
|
||||
hashFunction = c.hashFunction;
|
||||
hashSize = c.hashSize;
|
||||
isCore = isCoreTeam(id);
|
||||
address token = getContract(uint8(Apps.Token));
|
||||
isCore = c.isCore;
|
||||
address token = getTokenContract();
|
||||
balance = ITokenBalance(token).balanceOf(c.account);
|
||||
address contribution = getContract(uint8(Apps.Contribution));
|
||||
totalKreditsEarned = IContributionBalance(contribution).totalKreditsEarnedByContributor(_id, true);
|
||||
contributionsCount = IContributionBalance(contribution).balanceOf(c.account);
|
||||
exists = c.exists;
|
||||
}
|
||||
|
||||
function canPerform(address _who, address _where, bytes32 _what, uint256[] memory _how) public returns (bool) {
|
||||
address sender = _who;
|
||||
if (sender == address(-1)) {
|
||||
sender = tx.origin;
|
||||
}
|
||||
// _what == keccak256('VOTE_PROPOSAL_ROLE')
|
||||
if (_what == 0xd61216798314d2fc33e42ff2021d66707b1e38517d3f7166798a9d3a196a9c96) {
|
||||
return contributorIds[sender] != uint256(0);
|
||||
}
|
||||
|
||||
return addressIsCore(sender);
|
||||
function canPerform(address _who, address _where, bytes32 _what, uint256[] _how) public view returns (bool) {
|
||||
return addressExists(_who);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
// ...
|
||||
}
|
||||
13005
apps/contributor/package-lock.json
generated
13005
apps/contributor/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -3,19 +3,16 @@
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"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"
|
||||
"@aragon/os": "^4.1.0",
|
||||
"@aragon/cli": "^5.5.0"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"scripts": {
|
||||
"start": "npm run start:aragon:ipfs",
|
||||
"start:aragon:ipfs": "aragon run",
|
||||
"start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist",
|
||||
"start:app": "",
|
||||
"test": "aragon contracts test",
|
||||
"compile": "aragon contracts compile",
|
||||
"sync-assets": "",
|
||||
"build:app": "",
|
||||
@@ -24,8 +21,7 @@
|
||||
"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"
|
||||
"versions": "aragon apm versions"
|
||||
},
|
||||
"keywords": []
|
||||
}
|
||||
|
||||
5
apps/contributor/test/app.js
Normal file
5
apps/contributor/test/app.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const CounterApp = artifacts.require('CounterApp.sol')
|
||||
|
||||
contract('CounterApp', (accounts) => {
|
||||
it('should be tested')
|
||||
})
|
||||
@@ -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';
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
@@ -14,7 +14,7 @@
|
||||
"environments": {
|
||||
"default": {
|
||||
"network": "development",
|
||||
"appName": "kredits-proposal.open.aragonpm.eth"
|
||||
"appName": "kredits-proposal.aragonpm.eth"
|
||||
},
|
||||
"rinkeby": {
|
||||
"registry": "0x98df287b6c145399aaa709692c8d308357bc085d",
|
||||
|
||||
@@ -4,13 +4,13 @@ import "@aragon/os/contracts/apps/AragonApp.sol";
|
||||
import "@aragon/os/contracts/kernel/IKernel.sol";
|
||||
|
||||
interface IContributor {
|
||||
function getContributorAddressById(uint32 contributorId) public view returns (address);
|
||||
function getContributorIdByAddress(address contributorAccount) public view returns (uint32);
|
||||
function exists(uint32 contributorId) public view returns (bool);
|
||||
function getContributorAddressById(uint256 contributorId) public view returns (address);
|
||||
function getContributorIdByAddress(address contributorAccount) public view returns (uint256);
|
||||
function exists(uint256 contributorId) public view returns (bool);
|
||||
}
|
||||
|
||||
interface IContribution {
|
||||
function add(uint32 amount, uint32 contributorId, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public;
|
||||
function add(uint256 amount, address contributor, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public;
|
||||
}
|
||||
|
||||
contract Proposal is AragonApp {
|
||||
@@ -20,75 +20,78 @@ contract Proposal is AragonApp {
|
||||
|
||||
bytes32 public constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb;
|
||||
// ensure alphabetic order
|
||||
enum Apps { Contribution, Contributor, Proposal, Reimbursement, Token }
|
||||
bytes32[5] public appIds;
|
||||
enum Apps { Contribution, Contributor, Proposal, Token }
|
||||
bytes32[4] public appIds;
|
||||
|
||||
struct Proposal {
|
||||
address creatorAccount;
|
||||
uint32 contributorId;
|
||||
uint16 votesCount;
|
||||
uint16 votesNeeded;
|
||||
uint32 amount;
|
||||
uint contributorId;
|
||||
uint votesCount;
|
||||
uint votesNeeded;
|
||||
uint256 amount;
|
||||
bool executed;
|
||||
bytes32 hashDigest;
|
||||
uint8 hashFunction;
|
||||
uint8 hashSize;
|
||||
uint32[] voterIds;
|
||||
mapping (uint32 => bool) votes;
|
||||
uint256[] voterIds;
|
||||
mapping (uint256 => bool) votes;
|
||||
bool exists;
|
||||
}
|
||||
|
||||
mapping(uint32 => Proposal) public proposals;
|
||||
uint32 public proposalsCount;
|
||||
mapping(uint256 => Proposal) public proposals;
|
||||
uint256 public proposalsCount;
|
||||
|
||||
event ProposalCreated(uint32 id, address creatorAccount, uint32 contributorId, uint32 amount);
|
||||
event ProposalCreated(uint256 id, address creatorAccount, uint256 contributorId, uint256 amount);
|
||||
|
||||
event ProposalVoted(uint32 id, uint32 voterId, uint16 totalVotes);
|
||||
event ProposalExecuted(uint32 id, uint32 contributorId, uint32 amount);
|
||||
event ProposalVoted(uint256 id, uint256 voterId, uint256 totalVotes);
|
||||
event ProposalExecuted(uint256 id, uint256 contributorId, uint256 amount);
|
||||
|
||||
function initialize(bytes32[5] _appIds) public onlyInit {
|
||||
function initialize(bytes32[4] _appIds) public onlyInit {
|
||||
appIds = _appIds;
|
||||
initialized();
|
||||
}
|
||||
|
||||
function getContract(uint8 appId) public view returns (address) {
|
||||
IKernel k = IKernel(kernel());
|
||||
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[appId]);
|
||||
function getContributorContract() public view returns (address) {
|
||||
return IKernel(kernel()).getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[uint8(Apps.Contributor)]);
|
||||
}
|
||||
|
||||
function addProposal(uint32 contributorId, uint32 amount, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(ADD_PROPOSAL_ROLE) {
|
||||
require(IContributor(getContract(uint8(Apps.Contributor))).exists(contributorId), 'CONTRIBUTOR_NOT_FOUND');
|
||||
function getContributionContract() public view returns (address) {
|
||||
return IKernel(kernel()).getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[uint8(Apps.Contribution)]);
|
||||
}
|
||||
|
||||
uint32 proposalId = proposalsCount + 1;
|
||||
uint16 _votesNeeded = 1; //contributorsContract().coreContributorsCount() / 100 * 75;
|
||||
function addProposal(uint contributorId, uint256 amount, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(ADD_PROPOSAL_ROLE) {
|
||||
require(IContributor(getContributorContract()).exists(contributorId), 'CONTRIBUTOR_NOT_FOUND');
|
||||
|
||||
uint256 proposalId = proposalsCount + 1;
|
||||
uint256 _votesNeeded = 1; //contributorsContract().coreContributorsCount() / 100 * 75;
|
||||
|
||||
Proposal storage p = proposals[proposalId];
|
||||
p.creatorAccount = msg.sender;
|
||||
p.contributorId = contributorId;
|
||||
p.amount = amount;
|
||||
p.hashDigest = hashDigest;
|
||||
p.hashDigest = hashDigest;
|
||||
p.hashFunction = hashFunction;
|
||||
p.hashSize = hashSize;
|
||||
p.votesCount = 0;
|
||||
p.hashSize = hashSize;
|
||||
p.votesCount = 0;
|
||||
p.votesNeeded = _votesNeeded;
|
||||
p.exists = true;
|
||||
|
||||
|
||||
proposalsCount++;
|
||||
emit ProposalCreated(proposalId, msg.sender, p.contributorId, p.amount);
|
||||
}
|
||||
|
||||
function getProposal(uint32 proposalId) public view returns (uint32 id, address creatorAccount, uint32 contributorId, uint16 votesCount, uint16 votesNeeded, uint32 amount, bool executed, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint32[] voterIds, bool exists) {
|
||||
function getProposal(uint proposalId) public view returns (uint256 id, address creatorAccount, uint256 contributorId, uint256 votesCount, uint256 votesNeeded, uint256 amount, bool executed, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint256[] voterIds, bool exists) {
|
||||
id = proposalId;
|
||||
Proposal storage p = proposals[id];
|
||||
return (
|
||||
id,
|
||||
p.creatorAccount,
|
||||
p.contributorId,
|
||||
p.votesCount,
|
||||
p.contributorId,
|
||||
p.votesCount,
|
||||
p.votesNeeded,
|
||||
p.amount,
|
||||
p.executed,
|
||||
p.hashDigest,
|
||||
p.executed,
|
||||
p.hashDigest,
|
||||
p.hashFunction,
|
||||
p.hashSize,
|
||||
p.voterIds,
|
||||
@@ -96,10 +99,10 @@ contract Proposal is AragonApp {
|
||||
);
|
||||
}
|
||||
|
||||
function vote(uint32 proposalId) public isInitialized auth(VOTE_PROPOSAL_ROLE) {
|
||||
function vote(uint256 proposalId) public isInitialized auth(VOTE_PROPOSAL_ROLE) {
|
||||
Proposal storage p = proposals[proposalId];
|
||||
require(!p.executed, 'ALREADY_EXECUTED');
|
||||
uint32 voterId = IContributor(getContract(uint8(Apps.Contributor))).getContributorIdByAddress(msg.sender);
|
||||
uint256 voterId = IContributor(getContributorContract()).getContributorIdByAddress(msg.sender);
|
||||
require(p.votes[voterId] != true, 'ALREADY_VOTED');
|
||||
p.voterIds.push(voterId);
|
||||
p.votes[voterId] = true;
|
||||
@@ -111,19 +114,20 @@ contract Proposal is AragonApp {
|
||||
emit ProposalVoted(proposalId, voterId, p.votesCount);
|
||||
}
|
||||
|
||||
function batchVote(uint32[] _proposalIds) public isInitialized auth(VOTE_PROPOSAL_ROLE) {
|
||||
for (uint32 i = 0; i < _proposalIds.length; i++) {
|
||||
function batchVote(uint256[] _proposalIds) public isInitialized auth(VOTE_PROPOSAL_ROLE) {
|
||||
for (uint256 i = 0; i < _proposalIds.length; i++) {
|
||||
vote(_proposalIds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function executeProposal(uint32 proposalId) private {
|
||||
function executeProposal(uint proposalId) private {
|
||||
Proposal storage p = proposals[proposalId];
|
||||
require(!p.executed, 'ALREADY_EXECUTED');
|
||||
require(p.votesCount >= p.votesNeeded, 'MISSING_VOTES');
|
||||
|
||||
|
||||
p.executed = true;
|
||||
IContribution(getContract(uint8(Apps.Contribution))).add(p.amount, p.contributorId, p.hashDigest, p.hashFunction, p.hashSize);
|
||||
address contributorAccount = IContributor(getContributorContract()).getContributorAddressById(p.contributorId);
|
||||
IContribution(getContributionContract()).add(p.amount, contributorAccount, p.hashDigest, p.hashFunction, p.hashSize);
|
||||
emit ProposalExecuted(proposalId, p.contributorId, p.amount);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "Proposal",
|
||||
"description": "Kredits Proposal app"
|
||||
"description": "Kredits proposal app"
|
||||
}
|
||||
|
||||
13005
apps/proposal/package-lock.json
generated
13005
apps/proposal/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -3,19 +3,16 @@
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"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"
|
||||
"@aragon/os": "^4.1.0",
|
||||
"@aragon/cli": "^5.5.0"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"scripts": {
|
||||
"start": "npm run start:aragon:ipfs",
|
||||
"start:aragon:ipfs": "aragon run",
|
||||
"start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist",
|
||||
"start:app": "",
|
||||
"test": "aragon contracts test",
|
||||
"compile": "aragon contracts compile",
|
||||
"sync-assets": "",
|
||||
"build:app": "",
|
||||
@@ -24,8 +21,7 @@
|
||||
"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"
|
||||
"versions": "aragon apm versions"
|
||||
},
|
||||
"keywords": []
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
// const Proposal = artifacts.require('Proposal.sol');
|
||||
|
||||
contract('Proposal', (_accounts) => {
|
||||
it('should be tested');
|
||||
});
|
||||
1
apps/reimbursement/.gitattributes
vendored
1
apps/reimbursement/.gitattributes
vendored
@@ -1 +0,0 @@
|
||||
*.sol linguist-language=Solidity
|
||||
7
apps/reimbursement/.gitignore
vendored
7
apps/reimbursement/.gitignore
vendored
@@ -1,7 +0,0 @@
|
||||
node_modules
|
||||
artifacts
|
||||
.cache
|
||||
cache
|
||||
dist
|
||||
ipfs.cmd
|
||||
package-lock.json
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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
|
||||
},
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
*/
|
||||
@@ -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
|
||||
}
|
||||
|
||||
*/
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = require("../../truffle.js");
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,7 @@
|
||||
"environments": {
|
||||
"default": {
|
||||
"network": "development",
|
||||
"appName": "kredits-token.open.aragonpm.eth"
|
||||
"appName": "kredits-token.aragonpm.eth"
|
||||
},
|
||||
"rinkeby": {
|
||||
"registry": "0x98df287b6c145399aaa709692c8d308357bc085d",
|
||||
|
||||
@@ -7,24 +7,18 @@ contract Token is ERC20Token, AragonApp {
|
||||
bytes32 public constant MINT_TOKEN_ROLE = keccak256("MINT_TOKEN_ROLE");
|
||||
|
||||
// ensure alphabetic order
|
||||
enum Apps { Contribution, Contributor, Proposal, Reimbursement, Token }
|
||||
bytes32[5] public appIds;
|
||||
|
||||
event LogMint(address indexed recipient, uint256 amount, uint32 contributionId);
|
||||
|
||||
function initialize(bytes32[5] _appIds) public onlyInit {
|
||||
enum Apps { Contribution, Contributor, Proposal, Token }
|
||||
bytes32[4] public appIds;
|
||||
|
||||
event LogMint(address indexed recipient, uint256 amount, uint256 contributionId);
|
||||
|
||||
function initialize(bytes32[4] _appIds) public onlyInit {
|
||||
appIds = _appIds;
|
||||
name = 'Kredits';
|
||||
symbol = '₭S';
|
||||
decimals = 18;
|
||||
initialized();
|
||||
}
|
||||
|
||||
function mintFor(address contributorAccount, uint256 amount, uint32 contributionId) public isInitialized auth(MINT_TOKEN_ROLE) {
|
||||
require(amount > 0, "INVALID_AMOUNT");
|
||||
|
||||
uint256 amountInWei = amount.mul(1 ether);
|
||||
_mint(contributorAccount, amountInWei);
|
||||
function mintFor(address contributorAccount, uint256 amount, uint256 contributionId) public isInitialized auth(MINT_TOKEN_ROLE) {
|
||||
_mint(contributorAccount, amount);
|
||||
emit LogMint(contributorAccount, amount, contributionId);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
// ...
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "Token",
|
||||
"description": "Kredits Token app"
|
||||
"description": "Kredits token app"
|
||||
}
|
||||
|
||||
13005
apps/token/package-lock.json
generated
13005
apps/token/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -3,19 +3,16 @@
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"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"
|
||||
"@aragon/os": "^4.1.0",
|
||||
"@aragon/cli": "^5.5.0"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"scripts": {
|
||||
"start": "npm run start:aragon:ipfs",
|
||||
"start:aragon:ipfs": "aragon run",
|
||||
"start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist",
|
||||
"start:app": "",
|
||||
"test": "aragon contracts test",
|
||||
"compile": "aragon contracts compile",
|
||||
"sync-assets": "",
|
||||
"build:app": "",
|
||||
@@ -24,8 +21,7 @@
|
||||
"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"
|
||||
"versions": "aragon apm versions"
|
||||
},
|
||||
"keywords": []
|
||||
}
|
||||
|
||||
5
apps/token/test/app.js
Normal file
5
apps/token/test/app.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const CounterApp = artifacts.require('CounterApp.sol')
|
||||
|
||||
contract('CounterApp', (accounts) => {
|
||||
it('should be tested')
|
||||
})
|
||||
@@ -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
|
||||
});
|
||||
});
|
||||
});
|
||||
13
arapp.json
13
arapp.json
@@ -21,7 +21,7 @@
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "Add proposal",
|
||||
"name": "Add proposal",
|
||||
"id": "ADD_PROPOSAL_ROLE",
|
||||
"params": []
|
||||
},
|
||||
@@ -34,17 +34,17 @@
|
||||
"environments": {
|
||||
"development": {
|
||||
"network": "development",
|
||||
"apm": "aragonpm.eth",
|
||||
"registry": "0x5f6f7e8cc7346a11ca2def8f827b7a0b612c56a1",
|
||||
"appName": "dummy.open.aragonpm.eth"
|
||||
"appName": "dummy.aragonpm.eth"
|
||||
},
|
||||
"rinkeby": {
|
||||
"network": "rinkeby",
|
||||
"registry": "0x98Df287B6C145399Aaa709692c8D308357bC085D",
|
||||
"wsRPC": "wss://rinkeby.eth.aragon.network/ws",
|
||||
"daoFactory": "0x2298d27a9b847c681d2b2c2828ab9d79013f5f1d",
|
||||
"appName": "dummy.open.aragonpm.eth",
|
||||
"kredits": {
|
||||
"daoFactory": "0x2298d27a9b847c681d2b2c2828ab9d79013f5f1d"
|
||||
}
|
||||
"apm": "open.aragonpm.eth"
|
||||
},
|
||||
"kovan": {
|
||||
"network": "kovan",
|
||||
@@ -52,7 +52,8 @@
|
||||
},
|
||||
"default": {
|
||||
"network": "development",
|
||||
"appName": "dummy.aragonpm.eth"
|
||||
"appName": "dummy.aragonpm.eth",
|
||||
"apm": "open.aragonpm.eth"
|
||||
}
|
||||
},
|
||||
"path": "contracts/misc/DummyApp.sol"
|
||||
|
||||
@@ -1,54 +1,14 @@
|
||||
const contractCalls = [
|
||||
['Contributor', 'add', [{
|
||||
account: '0x7e8f313c56f809188313aa274fa67ee58c31515d',
|
||||
name: 'bumi',
|
||||
kind: 'person',
|
||||
url: '',
|
||||
github_username: 'bumi',
|
||||
github_uid: 318,
|
||||
gitea_username: 'bumi',
|
||||
wiki_username: 'Bumi',
|
||||
}, { gasLimit: 200000 }]],
|
||||
|
||||
['Contributor', 'add', [{
|
||||
account: '0x49575f3DD9a0d60aE661BC992f72D837A77f05Bc',
|
||||
name: 'raucao',
|
||||
kind: 'person',
|
||||
url: '',
|
||||
github_username: 'skddc',
|
||||
github_uid: 842,
|
||||
gitea_username: 'raucao',
|
||||
wiki_username: 'Basti',
|
||||
}, { gasLimit: 200000 }]],
|
||||
|
||||
['Contributor', 'add', [{
|
||||
account: '0xF722709ECC3B05c19d02E82a2a4A4021B8F48C62',
|
||||
name: 'Manuel',
|
||||
kind: 'person',
|
||||
url: '',
|
||||
github_username: 'fsmanuel',
|
||||
github_uid: 54812,
|
||||
wiki_username: 'Manuel',
|
||||
}, { gasLimit: 200000 }]],
|
||||
|
||||
['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 }]],
|
||||
let contractCalls = [
|
||||
['Contributor', 'add', [{ account: '0x7e8f313c56f809188313aa274fa67ee58c31515d', name: 'bumi', isCore: true, kind: 'preson', url: '', github_username: 'bumi', github_uid: 318, wiki_username: 'bumi' }, {gasLimit: 200000}]],
|
||||
['Contributor', 'add', [{ account: '0xa502eb4021f3b9ab62f75b57a94e1cfbf81fd827', name: 'raucau', isCore: true, kind: 'person', url: '', github_username: 'skddc', github_uid: 842, wiki_username: 'raucau' }, {gasLimit: 200000}]],
|
||||
['Proposal', 'addProposal', [{ contributorId: 2, amount: 42, kind: 'code', description: 'runs the seeds', url: '' }, {gasLimit: 350000}]],
|
||||
['Proposal', 'addProposal', [{ contributorId: 3, amount: 23, kind: 'code', description: 'runs the seeds', url: '' }, {gasLimit: 350000}]],
|
||||
['Proposal', 'addProposal', [{contributorId: 3, amount: 100, kind: 'code', description: 'hacks on kredits', url: '' }, {gasLimit: 350000}]],
|
||||
['Proposal', 'vote', [1, {gasLimit: 550000}]],
|
||||
['Contribution', 'addContribution', [{contributorAccount: '0xa502eb4021f3b9ab62f75b57a94e1cfbf81fd827', amount: 100, kind: 'code', description: 'hacks on kredits', url: '' }, {gasLimit: 350000}]],
|
||||
];
|
||||
|
||||
const funds = [
|
||||
let funds = [
|
||||
'0x7e8f313c56f809188313aa274fa67ee58c31515d',
|
||||
'0xa502eb4021f3b9ab62f75b57a94e1cfbf81fd827',
|
||||
'0xa502eb4021f3b9ab62f75b57a94e1cfbf81fd827'
|
||||
];
|
||||
|
||||
module.exports = { contractCalls, funds };
|
||||
|
||||
@@ -10,18 +10,17 @@ import "../apps/contribution/contracts/Contribution.sol";
|
||||
import "../apps/contributor/contracts/Contributor.sol";
|
||||
import "../apps/token/contracts/Token.sol";
|
||||
import "../apps/proposal/contracts/Proposal.sol";
|
||||
import "../apps/reimbursement/contracts/Reimbursement.sol";
|
||||
|
||||
contract KreditsKit is KitBase {
|
||||
|
||||
// ensure alphabetic order
|
||||
enum Apps { Contribution, Contributor, Proposal, Reimbursement, Token }
|
||||
bytes32[5] public appIds;
|
||||
enum Apps { Contribution, Contributor, Proposal, Token }
|
||||
bytes32[4] public appIds;
|
||||
|
||||
event DeployInstance(address dao);
|
||||
event InstalledApp(address dao, address appProxy, bytes32 appId);
|
||||
|
||||
constructor (DAOFactory _fac, ENS _ens, bytes32[5] _appIds) public KitBase(_fac, _ens) {
|
||||
constructor (DAOFactory _fac, ENS _ens, bytes32[4] _appIds) public KitBase(_fac, _ens) {
|
||||
appIds = _appIds;
|
||||
}
|
||||
|
||||
@@ -34,51 +33,40 @@ contract KreditsKit is KitBase {
|
||||
|
||||
Contributor contributor = Contributor(_installApp(dao, appIds[uint8(Apps.Contributor)]));
|
||||
contributor.initialize(root, appIds);
|
||||
acl.createPermission(root, contributor, contributor.MANAGE_CONTRIBUTORS_ROLE(), this);
|
||||
|
||||
acl.createPermission(root, contributor, contributor.MANAGE_CONTRIBUTORS_ROLE(), root);
|
||||
|
||||
Token token = Token(_installApp(dao, appIds[uint8(Apps.Token)]));
|
||||
token.initialize(appIds);
|
||||
|
||||
|
||||
Contribution contribution = Contribution(_installApp(dao, appIds[uint8(Apps.Contribution)]));
|
||||
contribution.initialize(appIds);
|
||||
|
||||
Proposal proposal = Proposal(_installApp(dao, appIds[uint8(Apps.Proposal)]));
|
||||
proposal.initialize(appIds);
|
||||
|
||||
acl.createPermission(root, contribution, contribution.ADD_CONTRIBUTION_ROLE(), this);
|
||||
acl.createPermission(root, contribution, contribution.VETO_CONTRIBUTION_ROLE(), this);
|
||||
acl.grantPermission(proposal, contribution, contribution.ADD_CONTRIBUTION_ROLE());
|
||||
|
||||
Proposal proposal = Proposal(_installApp(dao, appIds[uint8(Apps.Proposal)]));
|
||||
proposal.initialize(appIds);
|
||||
|
||||
Reimbursement reimbursement = Reimbursement(_installApp(dao, appIds[uint8(Apps.Reimbursement)]));
|
||||
reimbursement.initialize();
|
||||
acl.createPermission(root, reimbursement, reimbursement.ADD_REIMBURSEMENT_ROLE(), this);
|
||||
acl.createPermission(root, reimbursement, reimbursement.VETO_REIMBURSEMENT_ROLE(), this);
|
||||
|
||||
|
||||
uint256[] memory params = new uint256[](1);
|
||||
params[0] = uint256(203) << 248 | uint256(1) << 240 | uint240(contributor);
|
||||
acl.grantPermissionP(acl.ANY_ENTITY(), contribution, contribution.ADD_CONTRIBUTION_ROLE(), params);
|
||||
acl.grantPermissionP(acl.ANY_ENTITY(), contribution, contribution.VETO_CONTRIBUTION_ROLE(), params);
|
||||
acl.grantPermissionP(acl.ANY_ENTITY(), contributor, contributor.MANAGE_CONTRIBUTORS_ROLE(), params);
|
||||
acl.grantPermissionP(acl.ANY_ENTITY(), reimbursement, reimbursement.ADD_REIMBURSEMENT_ROLE(), params);
|
||||
acl.grantPermissionP(root, contribution, contribution.ADD_CONTRIBUTION_ROLE(), params);
|
||||
acl.grantPermissionP(root, contribution, contribution.VETO_CONTRIBUTION_ROLE(), params);
|
||||
|
||||
//acl.setPermissionManager(this, proposal, proposal.VOTE_PROPOSAL_ROLE();
|
||||
acl.createPermission(root, proposal, proposal.VOTE_PROPOSAL_ROLE(), this);
|
||||
acl.grantPermissionP(acl.ANY_ENTITY(), proposal, proposal.VOTE_PROPOSAL_ROLE(), params);
|
||||
|
||||
acl.grantPermissionP(root, proposal, proposal.VOTE_PROPOSAL_ROLE(), params);
|
||||
|
||||
acl.createPermission(root, proposal, proposal.ADD_PROPOSAL_ROLE(), this);
|
||||
//acl.grantPermissionP(address(-1), proposal, proposal.ADD_PROPOSAL_ROLE(), params);
|
||||
acl.grantPermission(acl.ANY_ENTITY(), proposal, proposal.ADD_PROPOSAL_ROLE());
|
||||
acl.grantPermissionP(root, proposal, proposal.ADD_PROPOSAL_ROLE(), params);
|
||||
|
||||
acl.setPermissionManager(root, proposal, proposal.VOTE_PROPOSAL_ROLE());
|
||||
acl.setPermissionManager(root, proposal, proposal.ADD_PROPOSAL_ROLE());
|
||||
acl.setPermissionManager(root, contribution, contribution.ADD_CONTRIBUTION_ROLE());
|
||||
acl.setPermissionManager(root, contribution, contribution.VETO_CONTRIBUTION_ROLE());
|
||||
acl.setPermissionManager(root, contributor, contributor.MANAGE_CONTRIBUTORS_ROLE());
|
||||
acl.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.grantPermission(contribution, token, token.MINT_TOKEN_ROLE());
|
||||
acl.grantPermission(contribution, token, token.MINT_TOKEN_ROLE());
|
||||
acl.setPermissionManager(root, token, token.MINT_TOKEN_ROLE());
|
||||
|
||||
|
||||
|
||||
19000
data/contributions.json
19000
data/contributions.json
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -1,60 +1,5 @@
|
||||
# Contribution deployments
|
||||
|
||||
aragon apm publish major --environment=rinkeby"
|
||||
|
||||
## 20212-01-14
|
||||
|
||||
apps/contribution@master » aragon apm publish major --environment=rinkeby
|
||||
eth-provider | Invalid provider preset/location: "local"
|
||||
✔ Start IPFS
|
||||
✔ Applying version bump (major)
|
||||
↓ Building frontend [skipped]
|
||||
→ build script not defined in package.json
|
||||
✔ Deploy contract
|
||||
✔ Determine contract address for version
|
||||
✔ Prepare files for publishing
|
||||
✔ Generate application artifact
|
||||
✔ Publish intent
|
||||
|
||||
⚠ Publishing files from the project's root folder is not recommended. Consider using the distribution folder of your project: "--files <folder>".
|
||||
|
||||
The following information will be published:
|
||||
Contract address: 0x914Da982ef17B56D2e868E3a67E923EbED1aE017
|
||||
Content (ipfs): QmdVrY2R48NFqwLopd8ix1anAK1d6WafDGauou3ZJrB9gf
|
||||
|
||||
? Publish to kredits-contribution.open.aragonpm.eth repo Yes
|
||||
|
||||
✔ Publish kredits-contribution.open.aragonpm.eth
|
||||
|
||||
Successfully published kredits-contribution.open.aragonpm.eth v7.0.0 :
|
||||
|
||||
Transaction hash: 0xb817b2e80e90a6be60b45dd39987498e3132c9962c0501feb7549ad30186c6d5
|
||||
|
||||
|
||||
|
||||
## 2019-04-24 update balances
|
||||
|
||||
✔ Successfully published kredits-contribution.open.aragonpm.eth v6.0.0:
|
||||
ℹ Contract address: 0x2c083EEA83fd3a99C93759D97D0317A43261c758
|
||||
ℹ Content (ipfs): QmULpSqz7BgTFmDu8AL7YZZEz525xkcEzf3dPKtbRdUtFs
|
||||
ℹ Transaction hash: 0x8b01c4c00162e918659d267a2beaf33b578e2aaf9f427f1aa9a43029333c5cd7
|
||||
|
||||
## 2019-04-10 - Weltempfänger release
|
||||
|
||||
✔ Successfully published kredits-contribution.open.aragonpm.eth v5.0.0:
|
||||
ℹ Contract address: 0xe0f7dB486321b917e3A986Bdb2F2b9d51BA98fa9
|
||||
ℹ Content (ipfs): QmU3XEBb4f5jU8MFFEpwaa95C1mhc82UeYLRWLrKsvcQNw
|
||||
ℹ Transaction hash: 0xd736ff5f79f8142be3fad1a50580fb40aa468838da397f8630285fd91a445af3
|
||||
|
||||
|
||||
## 2019-04-04
|
||||
|
||||
✔ Successfully published kredits-contribution.open.aragonpm.eth v4.0.0:
|
||||
ℹ Contract address: 0x7485e8fbde0112C53587079db450Eb002D359372
|
||||
ℹ Content (ipfs): QmZKxYPm8wz4phgL4428Gh7MjmbyFFDMP2st7qhNL9qGDQ
|
||||
ℹ Transaction hash: 0x7d9be7920db675be88e8f60ebf08dc62ee77c4e6e454c5fbf0f91f6ac97e4c26
|
||||
|
||||
|
||||
## 2019-03-26
|
||||
|
||||
### v3.0.0 appids
|
||||
|
||||
@@ -1,37 +1,5 @@
|
||||
# Contributor deployments
|
||||
|
||||
aragon apm publish major --environment=rinkeby
|
||||
|
||||
## 2019-04-25 canPerform fix
|
||||
|
||||
✔ Successfully published kredits-contributor.open.aragonpm.eth v6.0.0:
|
||||
ℹ Contract address: 0xA5379D49C718845A1BD7720c6BE3872bA69906cc
|
||||
ℹ Content (ipfs): QmdennNV6s2FNpe6QNYxrUsUXPVdnQGvh1vCi22Tqs8ojq
|
||||
ℹ Transaction hash: 0x51077afeff70a24e87c78bb23ea13bdb9b4445bd43ea7a74a4178fadfeeb6c35
|
||||
|
||||
## 2019-04-24 update balances
|
||||
|
||||
✔ Successfully published kredits-contributor.open.aragonpm.eth v5.0.0:
|
||||
ℹ Contract address: 0xadefa3b66b68a127Fe38bEa1813b844EE69CFD86
|
||||
ℹ Content (ipfs): QmeygbQgoj2McLWzo9hJayLWuBZqFaK4HTpa5qLeQdkn5K
|
||||
ℹ Transaction hash: 0x4237a9636f6e4a8190e0d5bcfa85a452da097bf654a173a88e0e1de3d078f08d
|
||||
|
||||
|
||||
## 2019-04-10 - Weltempfänger release
|
||||
|
||||
✔ Successfully published kredits-contributor.open.aragonpm.eth v4.0.0:
|
||||
ℹ Contract address: 0x08a6D4D915FCAA5524F05F5F715a6C17cB6eeA6B
|
||||
ℹ Content (ipfs): QmR62PWwe1EzommfkhJDYcTvHoZjbXuv9dTG6vCn5dWCsb
|
||||
ℹ Transaction hash: 0xd5317c9e207a413485c55ec3046b09d467d978443680304737a6d7d3db0c90e1
|
||||
|
||||
|
||||
## 2019-04-04
|
||||
|
||||
✔ Successfully published kredits-contributor.open.aragonpm.eth v3.0.0:
|
||||
ℹ Contract address: 0x95D8458E28C8de7216279512601A3B9Dc512D70B
|
||||
ℹ Content (ipfs): QmSpHnmsf8FybvqfD49kwxvDixwiBUR7D5yG4BWSnnwCPS
|
||||
ℹ Transaction hash: 0x736f9751b07a91178f453c279ecb57604b3b5686ae5d7115880f3b27fbeb50f2
|
||||
|
||||
## 2019-03-26
|
||||
|
||||
### v2.0.0 support for appids
|
||||
|
||||
@@ -1,46 +1,5 @@
|
||||
# Kredits deployment
|
||||
|
||||
|
||||
## 2021-01-14
|
||||
|
||||
apps/contribution@master » aragon dao upgrade 0xc34edf7d11b7f8433d597f0bb0697acdff55ef14 kredits-contribution.open.aragonpm.eth --environment=rinkeby
|
||||
eth-provider | Invalid provider preset/location: "local"
|
||||
✔ Fetching kredits-contribution.open.aragonpm.eth@latest
|
||||
✔ Fetching kredits-contribution.open.aragonpm.eth@latest
|
||||
✔ Upgrading app
|
||||
|
||||
✔ Successfully executed: "Upgrade 'kredits-contribution.open.aragonpm.eth' app instances to v7.0.0"
|
||||
|
||||
## 2019-04-25 canPerfom fix
|
||||
|
||||
aragon dao upgrade 0xc34edf7d11b7f8433d597f0bb0697acdff55ef14 kredits-contributor.open.aragonpm.eth --environment=rinkeby
|
||||
✔ Fetching kredits-contributor.open.aragonpm.eth@latest
|
||||
✔ Upgrading app
|
||||
✔ Successfully executed: "Set the resolving address of 'kredits-contributor.open.aragonpm.eth' in namespace 'App code' to 0xA5379D49C718845A1BD7720c6BE3872bA69906cc"
|
||||
|
||||
## 2019-04-24 upgrade contributor and contribution
|
||||
|
||||
aragon dao upgrade 0xc34edf7d11b7f8433d597f0bb0697acdff55ef14 kredits-contributor.open.aragonpm.eth --environment=rinkeby
|
||||
eth-provider | Invalid provider preset/location: "local"
|
||||
✔ Fetching kredits-contributor.open.aragonpm.eth@latest
|
||||
✔ Upgrading app
|
||||
✔ Successfully executed: "Set the resolving address of 'kredits-contributor.open.aragonpm.eth' in namespace 'App code' to 0xadefa3b66b68a127Fe38bEa1813b844EE69CFD86"
|
||||
|
||||
aragon dao upgrade 0xc34edf7d11b7f8433d597f0bb0697acdff55ef14 kredits-contribution.open.aragonpm.eth --environment=rinkeby
|
||||
✔ Fetching kredits-contribution.open.aragonpm.eth@latest
|
||||
✔ Upgrading app
|
||||
✔ Successfully executed: "Set the resolving address of 'kredits-contribution.open.aragonpm.eth' in namespace 'App code' to 0x2c083EEA83fd3a99C93759D97D0317A43261c758"
|
||||
|
||||
## 2019-04-10 - Weltempfänger release
|
||||
|
||||
Using KreditsKit at: 0x76e069b47b79442657eaf0555a32c6b16fa1b8b4
|
||||
Created new DAO at: 0xc34edf7d11b7f8433d597f0bb0697acdff55ef14
|
||||
|
||||
## 2019-04-04
|
||||
|
||||
Using KreditsKit at: 0x76e069b47b79442657eaf0555a32c6b16fa1b8b4
|
||||
Created new DAO at: 0xcd75458fbc4aa2231252d5b21f1391fd031e5cb2
|
||||
|
||||
### 2019-03-26
|
||||
|
||||
kredits/truffle-kredits@aragonos » truffle exec scripts/new-dao.js --network=rinkeby
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
# KreditsKit deployments
|
||||
|
||||
## 2019-04-04
|
||||
|
||||
Deploying to networkId: 4
|
||||
Using ENS at: 0x98Df287B6C145399Aaa709692c8D308357bC085D
|
||||
Using DAOFactory at: 0x2298d27a9b847c681d2b2c2828ab9d79013f5f1d
|
||||
Found apps: [contribution,contributor,proposal,token].open.aragonpm.eth
|
||||
Deployed KreditsKit at: 0x76e069b47b79442657eaf0555a32c6b16fa1b8b4
|
||||
|
||||
## 2019-03-26
|
||||
|
||||
Using network 'rinkeby'.
|
||||
|
||||
@@ -1,21 +1,5 @@
|
||||
# Proposal deployments
|
||||
|
||||
## 2019-04-10 - Weltempfänger release
|
||||
|
||||
✔ Successfully published kredits-proposal.open.aragonpm.eth v5.0.0:
|
||||
ℹ Contract address: 0x4ce5b0286483c66b861e5599a199054687434552
|
||||
ℹ Content (ipfs): QmNYXEcmvKTGxYiob7WUf85oZhdmFDCuGiA6TsRrDE9TYb
|
||||
ℹ Transaction hash: 0x0482b58a1ba87d494c6391026399d0ac41b45384330d916f3f99ba70e501584b
|
||||
|
||||
|
||||
## 2019-04-04
|
||||
|
||||
✔ Successfully published kredits-proposal.open.aragonpm.eth v4.0.0:
|
||||
ℹ Contract address: 0x4993275362Ba50D76f349A262B7b842e7FbD7490
|
||||
ℹ Content (ipfs): QmesCPKLapASPTB3rnPpSdD7ULkg8BQwRcpdE1B8WwfSh7
|
||||
ℹ Transaction hash: 0xe4be7b1a75b663eb345b957b216a8476b0f75e2bbfc5880943b05c82fab15dff
|
||||
|
||||
|
||||
## 2019-03-26
|
||||
|
||||
### v3.0.0 appids
|
||||
|
||||
@@ -1,21 +1,5 @@
|
||||
# Token deployments
|
||||
|
||||
## 2019-04-10 - Weltempfänger release
|
||||
|
||||
✔ Successfully published kredits-token.open.aragonpm.eth v4.0.0:
|
||||
ℹ Contract address: 0x05E0C2bbdA8e5BeE22AC1E20C1457dA4de63aE26
|
||||
ℹ Content (ipfs): QmUuYLRMRNZcundUk2pxVaSjMNvtB7hzdf3eyoaUNqDPow
|
||||
ℹ Transaction hash: 0x98c28b5ca645904d56eb83c4783682f018c0fcee015b3a3d5fa8bd609223fb89
|
||||
|
||||
|
||||
## 2019-04-04
|
||||
|
||||
✔ Successfully published kredits-token.open.aragonpm.eth v3.0.0:
|
||||
ℹ Contract address: 0xd9913A96e087f50E71BF14c62cBCa3b9635392A9
|
||||
ℹ Content (ipfs): QmfZ7LwjkQRuLsEnVjrEF3ryTaHsL6YTxffiiDS5KWmHQG
|
||||
ℹ Transaction hash: 0x102b27e154d1e144ec084f5e8dc78e1626fda9f26c20db3e3b230fac381c317a
|
||||
|
||||
|
||||
## 2019-03-26
|
||||
|
||||
### v2.0.0 support for appIds
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +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
@@ -1,3 +1,4 @@
|
||||
{
|
||||
"4": "0x76e069b47b79442657eaf0555a32c6b16fa1b8b4"
|
||||
}
|
||||
"4": "0x1d6a9c2146a330575ee860eef9a012b5ff7caa68",
|
||||
"41787949": "0xa35aacdfccac54d3d96e0d29050c773b251c2c83"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{
|
||||
"4": "0xc34edf7d11b7f8433d597f0bb0697acdff55ef14"
|
||||
}
|
||||
"4": "0x95a7ce185efc2d1f13efd2a00ee9247c51ea7009",
|
||||
"41787949": "0x183af3950364390a266edff2a0e7c4c2f95c0691"
|
||||
}
|
||||
|
||||
@@ -2,15 +2,11 @@ const Base = require('./base');
|
||||
const EthersUtils = require('ethers').utils;
|
||||
|
||||
class Acl extends Base {
|
||||
hasPermission (fromAddress, contractAddress, roleID, params = null) {
|
||||
let roleHash = EthersUtils.keccak256(EthersUtils.toUtf8Bytes(roleID));
|
||||
|
||||
return this.hasPermission(
|
||||
fromAddress,
|
||||
contractAddress,
|
||||
roleHash,
|
||||
params
|
||||
);
|
||||
hasPermission(fromAddress, contractAddress, roleID, params = null) {
|
||||
let roleHash = EthersUtils.keccak256(EthersUtils.toUtf8Bytes(roleID));
|
||||
console.log(roleHash)
|
||||
return this.functions.hasPermission(fromAddress, contractAddress, roleHash, params);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,31 +1,23 @@
|
||||
const deprecate = require('../utils/deprecate');
|
||||
|
||||
class Base {
|
||||
constructor (contract) {
|
||||
constructor(contract) {
|
||||
this.contract = contract;
|
||||
}
|
||||
|
||||
get functions () {
|
||||
deprecate('The property `functions` is deprecated. contract functions are now directly defined on the ethers contract object. https://github.com/ethers-io/ethers.js/issues/920#issuecomment-650836642');
|
||||
return this.contract;
|
||||
get functions() {
|
||||
return this.contract.functions;
|
||||
}
|
||||
|
||||
get address () {
|
||||
return this.contract.address;
|
||||
}
|
||||
|
||||
get ipfs () {
|
||||
get ipfs() {
|
||||
if (!this._ipfsAPI) { throw new Error('IPFS API not configured; please set an ipfs instance'); }
|
||||
return this._ipfsAPI;
|
||||
}
|
||||
|
||||
set ipfs (ipfsAPI) {
|
||||
set ipfs(ipfsAPI) {
|
||||
this._ipfsAPI = ipfsAPI;
|
||||
}
|
||||
|
||||
on (type, callback) {
|
||||
on(type, callback) {
|
||||
return this.contract.on(type, callback);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Base;
|
||||
|
||||
@@ -1,72 +1,70 @@
|
||||
const Record = require('./record');
|
||||
const ethers = require('ethers');
|
||||
const RSVP = require('rsvp');
|
||||
|
||||
const ContributionSerializer = require('../serializers/contribution');
|
||||
const deprecate = require('../utils/deprecate');
|
||||
const Base = require('./base');
|
||||
|
||||
class Contribution extends Record {
|
||||
get count () {
|
||||
return this.contract.contributionsCount();
|
||||
}
|
||||
class Contribution extends Base {
|
||||
all() {
|
||||
return this.functions.contributionsCount()
|
||||
.then((count) => {
|
||||
count = count.toNumber();
|
||||
let contributions = [];
|
||||
|
||||
getById (id) {
|
||||
return this.contract.getContribution(id)
|
||||
.then(data => {
|
||||
return this.ipfs.catAndMerge(data, ContributionSerializer.deserialize);
|
||||
});
|
||||
}
|
||||
|
||||
getData (id) {
|
||||
return this.contract.getContribution(id);
|
||||
}
|
||||
|
||||
getByContributorId (contributorId) {
|
||||
return this.contract.getContributorAddressById(contributorId)
|
||||
.then(address => this.getByContributorAddress(address));
|
||||
}
|
||||
|
||||
getByContributorAddress (address) {
|
||||
return this.contract.balanceOf(address)
|
||||
.then(async (balance) => {
|
||||
const count = balance.toNumber();
|
||||
const contributions = [];
|
||||
|
||||
for (let index = 0; index < count; index++) {
|
||||
const id = await this.contract.tokenOfOwnerByIndex(address, index);
|
||||
const contribution = await this.getById(id);
|
||||
contributions.push(contribution);
|
||||
for (let id = 1; id <= count; id++) {
|
||||
contributions.push(this.getById(id));
|
||||
}
|
||||
|
||||
return contributions;
|
||||
return RSVP.all(contributions);
|
||||
});
|
||||
}
|
||||
|
||||
async add (contributionAttr, callOptions = {}) {
|
||||
const contribution = new ContributionSerializer(contributionAttr);
|
||||
getById(id) {
|
||||
id = ethers.utils.bigNumberify(id);
|
||||
|
||||
try { await contribution.validate(); }
|
||||
catch (error) { return Promise.reject(error); }
|
||||
return this.functions.getContribution(id)
|
||||
.then((data) => {
|
||||
return this.ipfs.catAndMerge(data, ContributionSerializer.deserialize);
|
||||
});
|
||||
|
||||
const jsonStr = contribution.serialize();
|
||||
}
|
||||
|
||||
getByContributor(contributor) {
|
||||
return this.functions.balanceOf(contributor)
|
||||
then((balance) => {
|
||||
count = balance.toNumber();
|
||||
|
||||
let contributions = [];
|
||||
|
||||
for (let index = 0; index <= count; index++) {
|
||||
this.functions.tokenOfOwnerByIndex(contributor, index)
|
||||
.then((id) => {
|
||||
contributions.push(this.getById(id));
|
||||
});
|
||||
}
|
||||
|
||||
return RSVP.all(contributions);
|
||||
});
|
||||
}
|
||||
|
||||
addContribution(contributionAttr, callOptions = {}) {
|
||||
let json = ContributionSerializer.serialize(contributionAttr);
|
||||
// TODO: validate against schema
|
||||
|
||||
return this.ipfs
|
||||
.add(jsonStr)
|
||||
.then(ipfsHashAttr => {
|
||||
.add(json)
|
||||
.then((ipfsHashAttr) => {
|
||||
let contribution = [
|
||||
contributionAttr.amount,
|
||||
contributionAttr.contributorId,
|
||||
contributionAttr.contributorAccount,
|
||||
ipfsHashAttr.hashDigest,
|
||||
ipfsHashAttr.hashFunction,
|
||||
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;
|
||||
|
||||
@@ -1,34 +1,48 @@
|
||||
const Record = require('./record');
|
||||
const ContributorSerializer = require('../serializers/contributor');
|
||||
const formatKredits = require('../utils/format-kredits');
|
||||
const ethers = require('ethers');
|
||||
const RSVP = require('rsvp');
|
||||
|
||||
class Contributor extends Record {
|
||||
get count () {
|
||||
return this.contract.contributorsCount();
|
||||
const ContributorSerializer = require('../serializers/contributor');
|
||||
const Base = require('./base');
|
||||
|
||||
class Contributor extends Base {
|
||||
all() {
|
||||
return this.functions.contributorsCount()
|
||||
.then((count) => {
|
||||
count = count.toNumber();
|
||||
let contributors = [];
|
||||
|
||||
for (let id = 1; id <= count; id++) {
|
||||
contributors.push(this.getById(id));
|
||||
}
|
||||
|
||||
return RSVP.all(contributors);
|
||||
});
|
||||
}
|
||||
|
||||
getById (id) {
|
||||
return this.contract.getContributorById(id)
|
||||
.then(contractData => {
|
||||
let data = {...contractData};
|
||||
data.balanceInt = formatKredits(data.balance);
|
||||
getById(id) {
|
||||
id = ethers.utils.bigNumberify(id);
|
||||
|
||||
return this.functions.getContributorById(id)
|
||||
.then((data) => {
|
||||
// TODO: remove when naming updated on the contract
|
||||
data.hashDigest = data.ipfsHash;
|
||||
return data;
|
||||
})
|
||||
// Fetch IPFS data if available
|
||||
.then((data) => {
|
||||
return this.ipfs.catAndMerge(data, ContributorSerializer.deserialize);
|
||||
});
|
||||
}
|
||||
|
||||
getData (id) {
|
||||
return this.contract.getContributorById(id);
|
||||
}
|
||||
|
||||
filterByAccount (search) {
|
||||
filterByAccount(search) {
|
||||
return this._byAccount(search, 'filter');
|
||||
}
|
||||
|
||||
findByAccount (search) {
|
||||
findByAccount(search) {
|
||||
return this._byAccount(search, 'find');
|
||||
}
|
||||
|
||||
_byAccount (search, method = 'filter') {
|
||||
_byAccount(search, method = 'filter') {
|
||||
return this.all().then((contributors) => {
|
||||
const searchEntries = Object.entries(search);
|
||||
|
||||
@@ -44,52 +58,24 @@ class Contributor extends Record {
|
||||
});
|
||||
}
|
||||
|
||||
async add (contributorAttr, callOptions = {}) {
|
||||
let contributor = new ContributorSerializer(contributorAttr);
|
||||
|
||||
try { await contributor.validate(); }
|
||||
catch (error) { return Promise.reject(error); }
|
||||
|
||||
const jsonStr = contributor.serialize();
|
||||
add(contributorAttr, callOptions = {}) {
|
||||
let json = ContributorSerializer.serialize(contributorAttr);
|
||||
// TODO: validate against schema
|
||||
|
||||
return this.ipfs
|
||||
.add(jsonStr)
|
||||
.add(json)
|
||||
.then((ipfsHashAttr) => {
|
||||
let contributor = [
|
||||
contributorAttr.account,
|
||||
ipfsHashAttr.hashDigest,
|
||||
ipfsHashAttr.hashFunction,
|
||||
ipfsHashAttr.hashSize,
|
||||
contributorAttr.isCore,
|
||||
];
|
||||
|
||||
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;
|
||||
|
||||
@@ -3,7 +3,6 @@ module.exports = {
|
||||
Contribution: require('./contribution'),
|
||||
Proposal: require('./proposal'),
|
||||
Token: require('./token'),
|
||||
Reimbursement: require('./reimbursement'),
|
||||
Kernel: require('./kernel'),
|
||||
Acl: require('./acl'),
|
||||
Acl: require('./acl')
|
||||
};
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
const namehash = require('ethers').utils.namehash;
|
||||
const namehash = require('eth-ens-namehash').hash;
|
||||
const Base = require('./base');
|
||||
|
||||
const KERNEL_APP_ADDR_NAMESPACE = '0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb';
|
||||
|
||||
class Kernel extends Base {
|
||||
constructor (contract) {
|
||||
constructor(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') {
|
||||
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}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,39 @@
|
||||
const Record = require('./record');
|
||||
const ContributionSerializer = require('../serializers/contribution');
|
||||
const deprecate = require('../utils/deprecate');
|
||||
const ethers = require('ethers');
|
||||
const RSVP = require('rsvp');
|
||||
|
||||
class Proposal extends Record {
|
||||
get count () {
|
||||
return this.contract.proposalsCount();
|
||||
const ContributionSerializer = require('../serializers/contribution');
|
||||
const Base = require('./base');
|
||||
|
||||
class Proposal extends Base {
|
||||
all() {
|
||||
return this.functions.proposalsCount()
|
||||
.then((count) => {
|
||||
count = count.toNumber();
|
||||
let proposals = [];
|
||||
|
||||
for (let id = 1; id <= count; id++) {
|
||||
proposals.push(this.getById(id));
|
||||
}
|
||||
|
||||
return RSVP.all(proposals);
|
||||
});
|
||||
}
|
||||
|
||||
getById (id) {
|
||||
return this.contract.getProposal(id)
|
||||
.then(data => {
|
||||
getById(id) {
|
||||
id = ethers.utils.bigNumberify(id);
|
||||
|
||||
return this.functions.getProposal(id)
|
||||
.then((data) => {
|
||||
return this.ipfs.catAndMerge(data, ContributionSerializer.deserialize);
|
||||
});
|
||||
}
|
||||
|
||||
async add (proposalAttr, callOptions = {}) {
|
||||
const contribution = new ContributionSerializer(proposalAttr);
|
||||
|
||||
try { await contribution.validate(); }
|
||||
catch (error) { return Promise.reject(error); }
|
||||
|
||||
const jsonStr = contribution.serialize();
|
||||
addProposal(proposalAttr, callOptions = {}) {
|
||||
let json = ContributionSerializer.serialize(proposalAttr);
|
||||
// TODO: validate against schema
|
||||
|
||||
return this.ipfs
|
||||
.add(jsonStr)
|
||||
.add(json)
|
||||
.then((ipfsHashAttr) => {
|
||||
let proposal = [
|
||||
proposalAttr.contributorId,
|
||||
@@ -33,14 +43,9 @@ class Proposal extends Record {
|
||||
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
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -4,3 +4,4 @@ class Token extends Base {
|
||||
}
|
||||
|
||||
module.exports = Token;
|
||||
|
||||
|
||||
@@ -1,39 +1,37 @@
|
||||
const ethers = require('ethers');
|
||||
const RSVP = require('rsvp');
|
||||
|
||||
const Preflight = require('./utils/preflight');
|
||||
const deprecate = require('./utils/deprecate');
|
||||
|
||||
const ABIS = {
|
||||
Contributor: require('./abis/Contributor.json'),
|
||||
Contribution: require('./abis/Contribution.json'),
|
||||
Reimbursement: require('./abis/Reimbursement.json'),
|
||||
Token: require('./abis/Token.json'),
|
||||
Proposal: require('./abis/Proposal.json'),
|
||||
Kernel: require('./abis/Kernel.json'),
|
||||
Acl: require('./abis/ACL.json'),
|
||||
Acl: require('./abis/ACL.json')
|
||||
};
|
||||
const APP_CONTRACTS = [
|
||||
'Contributor',
|
||||
'Contribution',
|
||||
'Token',
|
||||
'Proposal',
|
||||
'Reimbursement',
|
||||
'Acl',
|
||||
'Acl'
|
||||
];
|
||||
const DaoAddresses = require('./addresses/dao.json');
|
||||
|
||||
const Contracts = require('./contracts');
|
||||
const IPFS = require('./utils/ipfs');
|
||||
const IPFS = require('./utils/ipfs')
|
||||
|
||||
// Helpers
|
||||
function capitalize (word) {
|
||||
function capitalize(word) {
|
||||
let [first, ...rest] = word;
|
||||
return `${first.toUpperCase()}${rest.join('')}`;
|
||||
}
|
||||
|
||||
class Kredits {
|
||||
|
||||
constructor (provider, signer, options = {}) {
|
||||
constructor(provider, signer, options = {}) {
|
||||
let { addresses, abis, ipfsConfig } = options;
|
||||
|
||||
this.provider = provider;
|
||||
@@ -45,7 +43,7 @@ class Kredits {
|
||||
this.contracts = {};
|
||||
}
|
||||
|
||||
init (names) {
|
||||
init(names) {
|
||||
let contractsToLoad = names || APP_CONTRACTS;
|
||||
return this.provider.getNetwork().then(network => {
|
||||
this.addresses['Kernel'] = this.addresses['Kernel'] || DaoAddresses[network.chainId.toString()];
|
||||
@@ -53,43 +51,22 @@ class Kredits {
|
||||
return this.Kernel.getApp(contractName).then((address) => {
|
||||
this.addresses[contractName] = address;
|
||||
}).catch((error) => {
|
||||
console.log(error);
|
||||
throw new Error(`Failed to get address for ${contractName} from DAO at ${this.Kernel.contract.address}
|
||||
- ${error.message}`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
return Promise.all(addressPromises).then(() => { return this; });
|
||||
return RSVP.all(addressPromises).then(() => { return this });
|
||||
});
|
||||
}
|
||||
|
||||
static setup (provider, signer, ipfsConfig = null) {
|
||||
deprecate('Kredits.setup() is deprecated use new Kredits().init() instead');
|
||||
static setup(provider, signer, ipfsConfig = null) {
|
||||
console.log('Kredits.setup() is deprecated use new Kredits().init() instead');
|
||||
return new Kredits(provider, signer, { ipfsConfig: ipfsConfig }).init();
|
||||
}
|
||||
|
||||
static for (connectionOptions, kreditsOptions) {
|
||||
let { network, rpcUrl, wallet } = connectionOptions;
|
||||
if (!rpcUrl && network === 'local') { rpcUrl = 'http://localhost:8545'; }
|
||||
let ethProvider, signer;
|
||||
if (rpcUrl) {
|
||||
ethProvider = new ethers.providers.JsonRpcProvider(rpcUrl);
|
||||
} else {
|
||||
ethProvider = new ethers.getDefaultProvider(network);
|
||||
}
|
||||
if (wallet) {
|
||||
signer = wallet.connect(ethProvider);
|
||||
} else if (ethProvider.getSigner) {
|
||||
signer = ethProvider.getSigner();
|
||||
}
|
||||
return new Kredits(ethProvider, signer, kreditsOptions);
|
||||
}
|
||||
|
||||
static availableNetworks () {
|
||||
return Object.keys(DaoAddresses);
|
||||
}
|
||||
|
||||
get Kernel () {
|
||||
get Kernel() {
|
||||
let k = this.contractFor('Kernel');
|
||||
// in case we want to use a special apm (e.g. development vs. production)
|
||||
if (this.options.apm) {
|
||||
@@ -98,41 +75,37 @@ class Kredits {
|
||||
return k;
|
||||
}
|
||||
|
||||
get Contributor () {
|
||||
get Contributor() {
|
||||
return this.contractFor('Contributor');
|
||||
}
|
||||
|
||||
get Contributors () {
|
||||
deprecate('Contributors is deprecated use Contributor instead');
|
||||
get Contributors() {
|
||||
console.log('Contributors is deprecated use Contributor instead');
|
||||
return this.Contributor;
|
||||
}
|
||||
|
||||
get Proposal () {
|
||||
get Proposal() {
|
||||
return this.contractFor('Proposal');
|
||||
}
|
||||
|
||||
get Operator () {
|
||||
get Operator() {
|
||||
return this.Proposal;
|
||||
}
|
||||
|
||||
get Token () {
|
||||
get Token() {
|
||||
return this.contractFor('Token');
|
||||
}
|
||||
|
||||
get Contribution () {
|
||||
get Contribution() {
|
||||
return this.contractFor('Contribution');
|
||||
}
|
||||
|
||||
get Reimbursement () {
|
||||
return this.contractFor('Reimbursement');
|
||||
}
|
||||
|
||||
get Acl () {
|
||||
get Acl() {
|
||||
return this.contractFor('Acl');
|
||||
}
|
||||
|
||||
// Should be private
|
||||
contractFor (name) {
|
||||
contractFor(name) {
|
||||
if (this.contracts[name]) {
|
||||
return this.contracts[name];
|
||||
}
|
||||
@@ -152,7 +125,7 @@ class Kredits {
|
||||
return this.contracts[name];
|
||||
}
|
||||
|
||||
preflightChecks () {
|
||||
preflightChecks() {
|
||||
return new Preflight(this).check();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
const ethers = require('ethers');
|
||||
|
||||
const ABI = require('./abis/KreditsKit.json');
|
||||
const Addresses = require('./addresses/KreditsKit.json');
|
||||
|
||||
class KreditsKit {
|
||||
|
||||
constructor (provider, signer, options = {}) {
|
||||
let { address, abi } = options;
|
||||
|
||||
this.provider = provider;
|
||||
this.signer = signer;
|
||||
this.options = options;
|
||||
this.address = address;
|
||||
this.abi = abi || ABI;
|
||||
}
|
||||
|
||||
init () {
|
||||
return this.provider.getNetwork().then((network) => {
|
||||
this.address = this.address || Addresses[network.chainId.toString()];
|
||||
this.contract = new ethers.Contract(
|
||||
this.address,
|
||||
this.abi,
|
||||
(this.signer || this.provider)
|
||||
);
|
||||
return this;
|
||||
});
|
||||
}
|
||||
|
||||
appIdFor (contractName) {
|
||||
// see appIds in KreditsKit.sol for more details
|
||||
const knownContracts = ['Contribution', 'Contributor', 'Proposal', 'Reimbursement', 'Token'];
|
||||
return this.contract.appIds(knownContracts.indexOf(contractName));
|
||||
}
|
||||
|
||||
newDAO (options = {}) {
|
||||
return this.contract.newInstance(options).then(transaction => {
|
||||
return transaction.wait().then(result => {
|
||||
const deployEvent = result.events.find(e => e.event === 'DeployInstance');
|
||||
return {
|
||||
daoAddress: deployEvent.args.dao,
|
||||
transactionHash: transaction.hash,
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = KreditsKit;
|
||||
@@ -1,75 +1,19 @@
|
||||
const schemas = require('@kosmos/schemas');
|
||||
const validator = require('../utils/validator');
|
||||
|
||||
/**
|
||||
* Serialization and validation for JSON-LD document of the contribution.
|
||||
* Handle serialization for JSON-LD object of the contribution, according to
|
||||
* https://github.com/67P/kosmos-schemas/blob/master/schemas/contribution.json
|
||||
*
|
||||
* @class
|
||||
* @public
|
||||
*/
|
||||
class Contribution {
|
||||
|
||||
constructor (attrs) {
|
||||
Object.keys(attrs).forEach(a => this[a] = attrs[a]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize object to JSON
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
serialize () {
|
||||
/**
|
||||
* Deserialize JSON to object
|
||||
*
|
||||
* @method
|
||||
* @public
|
||||
*/
|
||||
static deserialize(serialized) {
|
||||
let {
|
||||
contributorIpfsHash,
|
||||
date,
|
||||
time,
|
||||
kind,
|
||||
description,
|
||||
url,
|
||||
details,
|
||||
} = this;
|
||||
|
||||
let data = {
|
||||
'@context': 'https://schema.kosmos.org',
|
||||
'@type': 'Contribution',
|
||||
'contributor': {
|
||||
'ipfs': contributorIpfsHash,
|
||||
},
|
||||
date,
|
||||
time,
|
||||
kind,
|
||||
description,
|
||||
'details': details || {},
|
||||
};
|
||||
|
||||
if (url) {
|
||||
data['url'] = url;
|
||||
}
|
||||
|
||||
// Write it pretty to ipfs
|
||||
return JSON.stringify(data, null, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate serialized data against schema
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
validate () {
|
||||
const serialized = JSON.parse(this.serialize());
|
||||
const valid = validator.validate(serialized, schemas['contribution']);
|
||||
return valid ? Promise.resolve() : Promise.reject(validator.error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize JSON to object
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
static deserialize (serialized) {
|
||||
let {
|
||||
date,
|
||||
time,
|
||||
kind,
|
||||
description,
|
||||
details,
|
||||
@@ -77,8 +21,6 @@ class Contribution {
|
||||
} = JSON.parse(serialized.toString('utf8'));
|
||||
|
||||
return {
|
||||
date,
|
||||
time,
|
||||
kind,
|
||||
description,
|
||||
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;
|
||||
|
||||
@@ -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
|
||||
* https://github.com/67P/kosmos-schemas/blob/master/schemas/contributor.json
|
||||
@@ -8,95 +6,13 @@ const validator = require('../utils/validator');
|
||||
* @public
|
||||
*/
|
||||
class Contributor {
|
||||
|
||||
constructor (attrs) {
|
||||
Object.keys(attrs).forEach(a => this[a] = attrs[a]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize object to JSON
|
||||
*
|
||||
* @method
|
||||
* @public
|
||||
*/
|
||||
serialize () {
|
||||
let {
|
||||
name,
|
||||
kind,
|
||||
url,
|
||||
github_uid,
|
||||
github_username,
|
||||
gitea_username,
|
||||
wiki_username,
|
||||
zoom_display_name,
|
||||
} = this;
|
||||
|
||||
let data = {
|
||||
'@context': 'https://schema.kosmos.org',
|
||||
'@type': 'Contributor',
|
||||
kind,
|
||||
name,
|
||||
accounts: [],
|
||||
};
|
||||
|
||||
if (url) {
|
||||
data['url'] = url;
|
||||
}
|
||||
|
||||
if (github_uid) {
|
||||
data.accounts.push({
|
||||
'site': 'github.com',
|
||||
'uid': github_uid,
|
||||
'username': github_username,
|
||||
'url': `https://github.com/${github_username}`,
|
||||
});
|
||||
}
|
||||
|
||||
if (gitea_username) {
|
||||
data.accounts.push({
|
||||
'site': 'gitea.kosmos.org',
|
||||
'username': gitea_username,
|
||||
'url': `https://gitea.kosmos.org/${gitea_username}`,
|
||||
});
|
||||
}
|
||||
|
||||
if (wiki_username) {
|
||||
data.accounts.push({
|
||||
'site': 'wiki.kosmos.org',
|
||||
'username': wiki_username,
|
||||
'url': `https://wiki.kosmos.org/User:${wiki_username}`,
|
||||
});
|
||||
}
|
||||
|
||||
if (zoom_display_name) {
|
||||
data.accounts.push({
|
||||
'site': 'zoom.us',
|
||||
'username': zoom_display_name,
|
||||
});
|
||||
}
|
||||
|
||||
// Write it pretty to ipfs
|
||||
return JSON.stringify(data, null, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate serialized data against schema
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
validate () {
|
||||
const serialized = JSON.parse(this.serialize());
|
||||
const valid = validator.validate(serialized, schemas['contributor']);
|
||||
return valid ? Promise.resolve() : Promise.reject(validator.error);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Deserialize JSON to object
|
||||
*
|
||||
* @method
|
||||
* @public
|
||||
*/
|
||||
static deserialize (serialized) {
|
||||
static deserialize(serialized) {
|
||||
let {
|
||||
name,
|
||||
kind,
|
||||
@@ -104,24 +20,16 @@ class Contributor {
|
||||
accounts,
|
||||
} = JSON.parse(serialized.toString('utf8'));
|
||||
|
||||
let github_username, github_uid, gitea_username, wiki_username, zoom_display_name;
|
||||
let github = accounts.find(a => a.site === 'github.com');
|
||||
let gitea = accounts.find(a => a.site === 'gitea.kosmos.org');
|
||||
let wiki = accounts.find(a => a.site === 'wiki.kosmos.org');
|
||||
let zoom = accounts.find(a => a.site === 'zoom.us');
|
||||
let github_username, github_uid, wiki_username;
|
||||
let github = accounts.find((a) => a.site === 'github.com');
|
||||
let wiki = accounts.find((a) => a.site === 'wiki.kosmos.org');
|
||||
|
||||
if (github) {
|
||||
(({ username: github_username, uid: github_uid} = github));
|
||||
}
|
||||
if (gitea) {
|
||||
(({ username: gitea_username } = gitea));
|
||||
}
|
||||
if (wiki) {
|
||||
(({ username: wiki_username } = wiki));
|
||||
}
|
||||
if (zoom) {
|
||||
(({ username: zoom_display_name } = zoom));
|
||||
}
|
||||
|
||||
return {
|
||||
name,
|
||||
@@ -130,13 +38,59 @@ class Contributor {
|
||||
accounts,
|
||||
github_uid,
|
||||
github_username,
|
||||
gitea_username,
|
||||
wiki_username,
|
||||
zoom_display_name,
|
||||
ipfsData: serialized,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize object to JSON
|
||||
*
|
||||
* @method
|
||||
* @public
|
||||
*/
|
||||
static serialize(deserialized) {
|
||||
let {
|
||||
name,
|
||||
kind,
|
||||
url,
|
||||
github_uid,
|
||||
github_username,
|
||||
wiki_username,
|
||||
} = deserialized;
|
||||
|
||||
let data = {
|
||||
"@context": "https://schema.kosmos.org",
|
||||
"@type": "Contributor",
|
||||
kind,
|
||||
name,
|
||||
"accounts": []
|
||||
};
|
||||
|
||||
if (url) {
|
||||
data["url"] = url;
|
||||
}
|
||||
|
||||
if (github_uid) {
|
||||
data.accounts.push({
|
||||
"site": "github.com",
|
||||
"uid": github_uid,
|
||||
"username": github_username,
|
||||
"url": `https://github.com/${github_username}`
|
||||
});
|
||||
}
|
||||
|
||||
if (wiki_username) {
|
||||
data.accounts.push({
|
||||
"site": "wiki.kosmos.org",
|
||||
"username": wiki_username,
|
||||
"url": `https://wiki.kosmos.org/User:${wiki_username}`
|
||||
});
|
||||
}
|
||||
|
||||
// Write it pretty to ipfs
|
||||
return JSON.stringify(data, null, 2);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Contributor;
|
||||
|
||||
@@ -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;
|
||||
@@ -1,5 +0,0 @@
|
||||
/*eslint no-console: ["error", { allow: ["warn"] }] */
|
||||
|
||||
module.exports = function deprecate (msg) {
|
||||
console.warn(msg);
|
||||
};
|
||||
@@ -1,10 +0,0 @@
|
||||
const ethersUtils = require('ethers').utils;
|
||||
|
||||
module.exports = function(value, options = {}) {
|
||||
let etherValue = ethersUtils.formatEther(value);
|
||||
if (options.asFloat) {
|
||||
return parseFloat(etherValue);
|
||||
} else {
|
||||
return parseInt(etherValue);
|
||||
}
|
||||
};
|
||||
@@ -1,18 +1,17 @@
|
||||
const ipfsClient = require('ipfs-http-client');
|
||||
const ipfsAPI = require('ipfs-api');
|
||||
const multihashes = require('multihashes');
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
class IPFS {
|
||||
constructor (config) {
|
||||
|
||||
constructor(config) {
|
||||
if (!config) {
|
||||
config = { host: 'localhost', port: '5001', protocol: 'http' };
|
||||
}
|
||||
this._ipfsAPI = ipfsAPI(config);
|
||||
this._config = config;
|
||||
this._ipfsAPI = ipfsClient(config);
|
||||
}
|
||||
|
||||
catAndMerge (contractData, deserialize) {
|
||||
let data = {...contractData}; // data from ethers.js is not extensible. this copy the attributes in a new object
|
||||
catAndMerge(data, deserialize) {
|
||||
// if no hash details are found simply return the data; nothing to merge
|
||||
if (!data.hashSize || data.hashSize === 0) {
|
||||
return data;
|
||||
@@ -27,48 +26,37 @@ class IPFS {
|
||||
});
|
||||
}
|
||||
|
||||
add (data) {
|
||||
add(data) {
|
||||
return this._ipfsAPI
|
||||
.add(ipfsClient.Buffer.from(data))
|
||||
.add(new this._ipfsAPI.Buffer(data))
|
||||
.then((res) => {
|
||||
return this.decodeHash(res[0].hash);
|
||||
});
|
||||
}
|
||||
|
||||
cat (hashData) {
|
||||
cat(hashData) {
|
||||
let ipfsHash = hashData; // default - if it is a string
|
||||
if (Object.prototype.hasOwnProperty.call(hashData, 'hashSize')) {
|
||||
if (hashData.hasOwnProperty('hashSize')) {
|
||||
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) {
|
||||
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) {
|
||||
decodeHash(ipfsHash) {
|
||||
let multihash = multihashes.decode(multihashes.fromB58String(ipfsHash));
|
||||
return {
|
||||
hashDigest: '0x' + multihashes.toHexString(multihash.digest),
|
||||
hashSize: multihash.length,
|
||||
hashFunction: multihash.code,
|
||||
ipfsHash: ipfsHash,
|
||||
ipfsHash: ipfsHash
|
||||
};
|
||||
}
|
||||
|
||||
encodeHash (hashData) {
|
||||
let digest = ipfsClient.Buffer.from(hashData.hashDigest.slice(2), 'hex');
|
||||
encodeHash(hashData) {
|
||||
let digest = this._ipfsAPI.Buffer.from(hashData.hashDigest.slice(2), 'hex');
|
||||
return multihashes.encode(digest, hashData.hashFunction, hashData.hashSize);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = IPFS;
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
function pageNumber (number, size, recordCount) {
|
||||
let numberOfPages = Math.ceil(recordCount / size);
|
||||
|
||||
number = parseInt(number) || 1;
|
||||
|
||||
// Ensure page number is in range
|
||||
number = number > numberOfPages ? numberOfPages : number;
|
||||
number = number < 1 ? 1 : number;
|
||||
|
||||
return number;
|
||||
}
|
||||
|
||||
function buildIds (order, number, size, recordCount) {
|
||||
let offset = size * (number - 1);
|
||||
|
||||
let start;
|
||||
let mapFunction;
|
||||
|
||||
if (order === 'asc') {
|
||||
start = 1 + offset;
|
||||
mapFunction = (_, i) => start + i;
|
||||
} else {
|
||||
start = recordCount - offset;
|
||||
mapFunction = (_, i) => start - i;
|
||||
}
|
||||
|
||||
// Ensure size is in range
|
||||
let end = offset + size;
|
||||
if (end > recordCount) {
|
||||
let diff = end - recordCount;
|
||||
size = size - diff;
|
||||
}
|
||||
|
||||
return Array.from({ length: size }, mapFunction);
|
||||
}
|
||||
|
||||
module.exports = function paged (recordCount, options = {}) {
|
||||
let { order, page } = options;
|
||||
order = order || 'desc';
|
||||
page = page || {};
|
||||
|
||||
let size = parseInt(page.size) || 25;
|
||||
let number = pageNumber(page.number, size, recordCount);
|
||||
|
||||
return buildIds(order, number, size, recordCount);
|
||||
};
|
||||
@@ -1,9 +1,9 @@
|
||||
class Preflight {
|
||||
constructor (kredits) {
|
||||
constructor(kredits) {
|
||||
this.kredits = kredits;
|
||||
}
|
||||
|
||||
check () {
|
||||
check() {
|
||||
return this.kredits.ipfs._ipfsAPI.id()
|
||||
.catch((error) => {
|
||||
throw new Error(`IPFS node not available; config: ${JSON.stringify(this.kredits.ipfs.config)} - ${error.message}`);
|
||||
|
||||
@@ -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;
|
||||
15605
package-lock.json
generated
15605
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
55
package.json
55
package.json
@@ -1,35 +1,24 @@
|
||||
{
|
||||
"name": "kredits-contracts",
|
||||
"version": "6.0.0",
|
||||
"version": "3.0.2",
|
||||
"description": "Ethereum contracts and npm wrapper for Kredits",
|
||||
"main": "./lib/kredits.js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"install-all": "./scripts/every-app.sh \"npm install\"",
|
||||
"postshrinkwrap": "node scripts/fix-package-lock.js &>/dev/null || true",
|
||||
"build-json": "npm run compile-contracts && node ./scripts/build-json.js",
|
||||
"repl": "truffle exec scripts/repl.js",
|
||||
"seeds": "truffle exec scripts/seeds.js",
|
||||
"compile-contracts": "truffle compile --all",
|
||||
"bootstrap": "npm run reset:hard && npm run seeds",
|
||||
"reset": "npm run deploy:kit && npm run deploy:dao",
|
||||
"reset:hard": "npm run compile-contracts && npm run deploy:apps && npm run reset",
|
||||
"deploy:kit": "truffle exec scripts/deploy-kit.js",
|
||||
"deploy:dao": "truffle exec scripts/new-dao.js",
|
||||
"deploy:apps": "./scripts/every-app.sh \"aragon apm publish major --propagate-content=false --build=false --prepublish=false --skip-confirmation\"",
|
||||
"compile-contracts": "aragon contracts compile --all",
|
||||
"bootstrap": "npm run reset && truffle exec scripts/seeds.js",
|
||||
"reset": "npm run deploy:apps && npm run deploy:kit && npm run deploy:dao",
|
||||
"deploy:kit": "npm run compile-contracts && ENS=0x5f6f7e8cc7346a11ca2def8f827b7a0b612c56a1 aragon contracts exec scripts/deploy-kit.js",
|
||||
"deploy:dao": "aragon contracts exec scripts/new-dao.js",
|
||||
"deploy:apps": "./scripts/deploy-apps.sh",
|
||||
"devchain": "aragon devchain --port 7545",
|
||||
"dao:address": "truffle exec scripts/current-address.js",
|
||||
"lint:contracts": "solhint \"contracts/**/*.sol\" \"apps/*/contracts/**/*.sol\"",
|
||||
"lint:contract-tests": "eslint apps/*/test",
|
||||
"lint:wrapper": "eslint lib/",
|
||||
"test": "npm run test:token && npm run test:contributor && npm run test:contribution && npm run test:proposal",
|
||||
"test:token": "cd apps/token && npm run test",
|
||||
"test:contributor": "cd apps/contributor && npm run test",
|
||||
"test:contribution": "cd apps/contribution && npm run test",
|
||||
"test:proposal": "cd apps/proposal && npm run test",
|
||||
"setup-git-hooks": "sh scripts/git-hooks/install"
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -42,30 +31,20 @@
|
||||
},
|
||||
"homepage": "https://github.com/67P/truffle-kredits#readme",
|
||||
"devDependencies": {
|
||||
"@aragon/cli": "^5.5.0",
|
||||
"@aragon/kits-base": "^1.0.0",
|
||||
"@aragon/os": "^4.4.0",
|
||||
"@aragon/os": "^4.1.0",
|
||||
"async-each-series": "^1.1.0",
|
||||
"cli-table": "^0.3.1",
|
||||
"eslint": "^7.1.0",
|
||||
"eslint-plugin-import": "^2.20.2",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"eth-provider": "^0.2.5",
|
||||
"ethereum-block-by-date": "^1.4.0",
|
||||
"homedir": "^0.6.0",
|
||||
"eth-provider": "^0.2.2",
|
||||
"openzeppelin-solidity": "^2.2.0",
|
||||
"promptly": "^3.0.3",
|
||||
"solc": "^0.6.8",
|
||||
"solhint": "^2.3.1",
|
||||
"truffle-hdwallet-provider": "^1.0.17",
|
||||
"truffle-hdwallet-provider-privkey": "^0.3.0",
|
||||
"yargs": "^15.0.0"
|
||||
"solc": "^0.4.25"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethers": "^5.0.2",
|
||||
"ipfs-http-client": "^41.0.1",
|
||||
"@kosmos/schemas": "^3.0.0",
|
||||
"node-fetch": "^2.6.0",
|
||||
"tv4": "^1.3.0"
|
||||
"eth-ens-namehash": "^2.0.8",
|
||||
"ethers": "^4.0.27",
|
||||
"ipfs-api": "^19.0.0",
|
||||
"rsvp": "^4.8.2"
|
||||
},
|
||||
"keywords": [
|
||||
"kosmos",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
const promptly = require('promptly');
|
||||
const { inspect } = require('util');
|
||||
|
||||
const initKredits = require('./helpers/init_kredits.js');
|
||||
|
||||
@@ -19,40 +18,31 @@ module.exports = async function(callback) {
|
||||
let contributorAccount;
|
||||
if (contributor.length < 5) {
|
||||
contributorId = contributor;
|
||||
contributorAccount = await kredits.Contributor.contract.getContributorAddressById(contributor);
|
||||
contributorAccount = await kredits.Contributor.functions.getContributorAddressById(contributor);
|
||||
} else {
|
||||
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}`);
|
||||
|
||||
[ dateNow, timeNow ] = (new Date()).toISOString().split('T');
|
||||
|
||||
let contributionAttributes = {
|
||||
contributorId,
|
||||
date: dateNow,
|
||||
time: timeNow,
|
||||
contributorAccount,
|
||||
amount: await promptly.prompt('Amount: '),
|
||||
description: await promptly.prompt('Description: '),
|
||||
kind: await promptly.prompt('Kind: ', { default: 'dev' }),
|
||||
url: await promptly.prompt('URL: ', { default: '' })
|
||||
}
|
||||
|
||||
const contributorData = await kredits.Contributor.getById(contributorId);
|
||||
contributionAttributes.contributorIpfsHash = contributorData.ipfsHash;
|
||||
|
||||
console.log("\nAdding contribution:");
|
||||
console.log(contributionAttributes);
|
||||
|
||||
kredits.Contribution.add(contributionAttributes, { gasLimit: 300000 })
|
||||
.then(result => {
|
||||
console.log("\n\nResult:");
|
||||
console.log(result);
|
||||
callback();
|
||||
})
|
||||
.catch(error => {
|
||||
console.log('Failed to create contribution');
|
||||
callback(inspect(error));
|
||||
});
|
||||
kredits.Contribution.addContribution(contributionAttributes, { gasLimit: 300000 }).then((result) => {
|
||||
console.log("\n\nResult:");
|
||||
console.log(result);
|
||||
callback();
|
||||
}).catch((error) => {
|
||||
console.log('Failed to create contribution');
|
||||
callback(error);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -23,17 +23,18 @@ module.exports = async function(callback) {
|
||||
let contributorAttributes = {
|
||||
account: await prompt('Contributor address: ', {}),
|
||||
name: await prompt('Name: '),
|
||||
isCore: await prompt('core? y/n') === 'y',
|
||||
kind: await prompt('Kind (default person): ', {default: 'person'}),
|
||||
url: await prompt('URL: '),
|
||||
github_username: await prompt('GitHub username: '),
|
||||
github_uid: parseInt(await prompt('GitHub UID: ')),
|
||||
github_uid: await prompt('GitHub UID: '),
|
||||
wiki_username: await prompt('Wiki username: '),
|
||||
};
|
||||
|
||||
console.log("\nAdding contributor:");
|
||||
console.log(contributorAttributes);
|
||||
|
||||
kredits.Contributor.add(contributorAttributes, { gasLimit: 350000 }).then((result) => {
|
||||
kredits.Contributor.add(contributorAttributes, { gasLimit: 250000 }).then((result) => {
|
||||
console.log("\n\nResult:");
|
||||
console.log(result);
|
||||
callback();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
const promptly = require('promptly');
|
||||
const { inspect } = require('util');
|
||||
|
||||
const initKredits = require('./helpers/init_kredits.js');
|
||||
|
||||
@@ -19,38 +18,30 @@ module.exports = async function(callback) {
|
||||
let contributorAccount;
|
||||
if (contributor.length < 5) {
|
||||
contributorId = contributor;
|
||||
contributorAccount = await kredits.Contributor.contract.getContributorAddressById(contributor);
|
||||
contributorAccount = await kredits.Contributor.functions.getContributorAddressById(contributor);
|
||||
} else {
|
||||
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}`);
|
||||
|
||||
[ dateNow, timeNow ] = (new Date()).toISOString().split('T');
|
||||
|
||||
let contributionAttributes = {
|
||||
contributorId,
|
||||
date: dateNow,
|
||||
time: timeNow,
|
||||
amount: await promptly.prompt('Amount: '),
|
||||
description: await promptly.prompt('Description: '),
|
||||
kind: await promptly.prompt('Kind: ', { default: 'dev' }),
|
||||
url: await promptly.prompt('URL: ', { default: '' })
|
||||
}
|
||||
|
||||
const contributorData = await kredits.Contributor.getById(contributorId);
|
||||
contributionAttributes.contributorIpfsHash = contributorData.ipfsHash;
|
||||
|
||||
console.log("\nAdding proposal:");
|
||||
console.log(contributionAttributes);
|
||||
|
||||
kredits.Proposal.addProposal(contributionAttributes, { gasLimit: 300000 })
|
||||
.then((result) => {
|
||||
console.log("\n\nResult:");
|
||||
console.log(result);
|
||||
callback();
|
||||
}).catch((error) => {
|
||||
console.log('Failed to create proposal');
|
||||
callback(inspect(error));
|
||||
});
|
||||
kredits.Proposal.addProposal(contributionAttributes, { gasLimit: 300000 }).then((result) => {
|
||||
console.log("\n\nResult:");
|
||||
console.log(result);
|
||||
callback();
|
||||
}).catch((error) => {
|
||||
console.log('Failed to create proposal');
|
||||
callback(error);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ const files = [
|
||||
'Kernel',
|
||||
'Proposal',
|
||||
'Token',
|
||||
'Reimbursement',
|
||||
'ACL'
|
||||
];
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
@@ -17,7 +17,7 @@ module.exports = async function(callback) {
|
||||
|
||||
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}`));
|
||||
return;
|
||||
}
|
||||
@@ -33,7 +33,7 @@ module.exports = async function(callback) {
|
||||
if (contractWrapper[method]) {
|
||||
func = contractWrapper[method];
|
||||
} else {
|
||||
func = contractWrapper.contract[method];
|
||||
func = contractWrapper.functions[method];
|
||||
}
|
||||
func.apply(contractWrapper, args).then((result) => {
|
||||
console.log("\nResult:");
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
const knownDAOAddresses = require('../lib/addresses/dao.json');
|
||||
const knownKreditsKitAddresses = require('../lib/addresses/KreditsKit.json');
|
||||
const getNetworkId = require('./helpers/networkid.js')
|
||||
const ethers = require('ethers');
|
||||
|
||||
module.exports = async function(callback) {
|
||||
const provider = new ethers.providers.Web3Provider(web3.currentProvider);
|
||||
let network = await provider.getNetwork();
|
||||
let networkId = network.chainId;
|
||||
const networkId = await getNetworkId(web3)
|
||||
|
||||
console.log('# All known DAO addresses');
|
||||
Object.keys(knownDAOAddresses).forEach((networkId) => {
|
||||
|
||||
@@ -10,7 +10,13 @@ echo "Setting up each aragon app in ./apps"
|
||||
echo "a new app version will be deployed"
|
||||
echo "----"
|
||||
|
||||
./scripts/every-app.sh "npm install"
|
||||
./scripts/every-app.sh "aragon apm publish major"
|
||||
for dir in ./apps/*/; do
|
||||
set -x
|
||||
cd $dir
|
||||
npm install
|
||||
aragon apm publish major
|
||||
cd $rootDir
|
||||
set +x
|
||||
done
|
||||
|
||||
echo "Done, new versions of all apps deployed"
|
||||
|
||||
@@ -3,33 +3,23 @@ const deployDAOFactory = require('@aragon/os/scripts/deploy-daofactory.js')
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const argv = require('yargs').argv
|
||||
const ethers = require('ethers');
|
||||
const namehash = ethers.utils.namehash;
|
||||
const namehash = require('eth-ens-namehash').hash
|
||||
|
||||
const fileInject = require('./helpers/file_inject.js')
|
||||
const getNetworkId = require('./helpers/networkid.js')
|
||||
|
||||
const DAOFactory = artifacts.require('DAOFactory')
|
||||
const KreditsKit = artifacts.require('KreditsKit')
|
||||
|
||||
const arapp = require('../arapp.json')
|
||||
const environment = argv['network'] || argv['environment'] || 'development'
|
||||
const apm = arapp.environments[environment].apm
|
||||
const ensAddr = arapp.environments[environment].registry || process.env.ENS
|
||||
const daoFactoryAddress = arapp.environments[environment].daoFactory || process.env.DAO_FACTORY
|
||||
|
||||
const kreditsArappConfig = arapp.environments[environment].kredits || {}
|
||||
|
||||
// typically we use the open.aragonpm.eth aragonpm.
|
||||
const apm = kreditsArappConfig.apmDomain || argv['apmDomain'] || 'open.aragonpm.eth'
|
||||
|
||||
// daoFactory is environment specific.
|
||||
// See https://github.com/aragon/deployments/tree/master/environments/ for the official daoFactory
|
||||
// Locally we deploy our own daoFactory and no daoFactory is required (`daoFactoryAddress` is null).
|
||||
const daoFactoryAddress = kreditsArappConfig.daoFactory || argv['daoFactory']
|
||||
|
||||
const ensAddr = arapp.environments[environment].registry || argv['ensAddress']
|
||||
|
||||
module.exports = async function(callback) {
|
||||
const provider = new ethers.providers.Web3Provider(web3.currentProvider);
|
||||
const network = await provider.getNetwork();
|
||||
const networkId = network.chainId;
|
||||
const networkId = await getNetworkId(web3)
|
||||
console.log(`Deploying to networkId: ${networkId}`)
|
||||
|
||||
if (!ensAddr) {
|
||||
@@ -45,9 +35,7 @@ module.exports = async function(callback) {
|
||||
}
|
||||
console.log(`Using DAOFactory at: ${daoFactory.address}`)
|
||||
|
||||
const apps = fs.readdirSync('./apps', { withFileTypes: true })
|
||||
.filter(e => e.isDirectory())
|
||||
.map(e => e.name);
|
||||
const apps = fs.readdirSync('./apps')
|
||||
console.log(`Found apps: [${apps}].${apm}`)
|
||||
let appIds = {}
|
||||
apps.sort().forEach((app) => {
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
rootDir=`pwd`
|
||||
|
||||
for dir in ./apps/*/; do
|
||||
set -x
|
||||
cd $dir
|
||||
eval $1
|
||||
cd $rootDir
|
||||
set +x
|
||||
done
|
||||
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user