Compare commits

...

19 Commits

Author SHA1 Message Date
2a1ae117e8 5.1.1 2019-04-16 12:50:07 +01:00
8abc7ba77f Merge pull request #104 from 67P/chore/update-kosmos-schema
Update kosmos-schema dependency
2019-04-16 11:19:03 +00:00
f696721918 Readme: add note about locally running out of funds 2019-04-16 13:17:54 +02:00
00b58dec66 Update kosmos-schema dependency 2019-04-16 13:16:05 +02:00
560315cbca Merge pull request #102 from 67P/chore/add-yarn-support
Add yarn lock files
2019-04-15 15:54:38 +00:00
24933f31a7 Add yarn lock files 2019-04-15 17:34:48 +02:00
b13bf6e8b0 Readme: add note about locally running out of funds 2019-04-15 15:59:54 +02:00
8dcad88372 Merge pull request #99 from 67P/update-seeds
Update seeds
2019-04-15 10:39:10 +00:00
dc6d2716aa Update seeds 2019-04-15 12:33:51 +02:00
80ad9db630 5.1.0 2019-04-13 12:41:49 +01:00
1b5f7ff95d Merge pull request #93 from 67P/deployments/weltempfaenger-release
Production release to rinkeby
2019-04-13 11:07:58 +01:00
b6c06c289c Merge pull request #95 from 67P/cosmetics
Cosmetic cleanups
2019-04-12 18:55:59 +00:00
f405e39c04 Merge pull request #94 from 67P/bugfix/schema_changes
Refactor contribution serializer and validation
2019-04-12 18:54:56 +00:00
1dbf3b5742 Cosmetic cleanups 2019-04-12 20:16:24 +02:00
952b5153fd Validate proposed contributions 2019-04-11 13:46:42 +02:00
d953141f52 Refactor contribution serializer and validation 2019-04-11 13:05:40 +02:00
aa57d7c70b Adjust seeds for new requirements 2019-04-11 11:02:15 +02:00
4c0bb879e8 Show IPFS hash in list script output 2019-04-11 11:01:45 +02:00
27e5190e29 Production release to rinkeby
Deployed all apps and created a new Kredits DAO
2019-04-10 18:28:24 +02:00
34 changed files with 41024 additions and 106 deletions

2
.gitignore vendored
View File

@@ -4,3 +4,5 @@ node_modules
**/node_modules
.ganache-db
.tm_properties
yarn-error.log
.DS_Store

View File

@@ -36,7 +36,7 @@ development ganache.
$ npm run devchain (or aragon devchain --port 7545)
To clear/reset the chain use:
To clear/reset the chain use (e.g. if you run out of funds on your devchain)
$ npm run devchain -- --reset (or aragon devchain --port 7545 --reset)
@@ -79,9 +79,9 @@ Kredits DAO independently.
![](docs/kredits-diagram.png)
A DAO can be deployed using the `scripts/deploy-kit.js` script or with the `npm
run deploy:dao` command. This deploys a new Kredits DAO, installs the latest
app versions and sets the required permissions.
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,11 +115,11 @@ Script to add a new entries to the contracts using the JS wrapper
$ truffle exec scripts/add-{contributor, contribution, proposal}.js
### list-{contributor, contribution, proposal}.js
### list-{contributors, contributions, proposals}.js
List contract entries
$ truffle exec scripts/list-{contributor, contribution, proposal}.js
$ truffle exec scripts/list-{contributors, contributions, proposals}.js
### send-funds.js
@@ -152,7 +152,7 @@ Deploys a new KreditsKit that allows to create a new DAO
or
$ npm run deploy:kit
`ENS` address is required as environment variable.
`ENS` address is required as environment variable.
`DAO_FACTORY` can optionally be set as environment variable. (see aragon)
### new-dao.js
@@ -174,6 +174,26 @@ 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

View File

@@ -11,10 +11,10 @@
"start": "npm run start:aragon:ipfs",
"start:aragon:ipfs": "aragon run",
"start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist",
"start:app": "npm run sync-assets && npm run build:script -- --no-minify && parcel serve app/index.html -p 8001 --out-dir dist/ --no-cache",
"start:app": "",
"test": "aragon contracts test",
"compile": "aragon contracts compile",
"sync-assets": "copy-aragon-ui-assets -n aragon-ui ./dist",
"sync-assets": "",
"build:app": "",
"build:script": "",
"build": "",

View File

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

8145
apps/contribution/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

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

8145
apps/contributor/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

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

8145
apps/proposal/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

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

8145
apps/token/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,5 +1,13 @@
# Contribution deployments
## 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:

View File

@@ -1,5 +1,13 @@
# Contributor deployments
## 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:

View File

@@ -1,5 +1,10 @@
# Kredits deployment
## 2019-04-10 - Weltempfänger release
Using KreditsKit at: 0x76e069b47b79442657eaf0555a32c6b16fa1b8b4
Created new DAO at: 0xc34edf7d11b7f8433d597f0bb0697acdff55ef14
## 2019-04-04
Using KreditsKit at: 0x76e069b47b79442657eaf0555a32c6b16fa1b8b4

View File

@@ -1,5 +1,13 @@
# 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:

View File

@@ -1,5 +1,13 @@
# 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:

View File

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

View File

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

View File

@@ -20,4 +20,5 @@ class Base {
return this.contract.on(type, callback);
}
}
module.exports = Base;

View File

@@ -1,20 +1,5 @@
const ethers = require('ethers');
const schemas = require('kosmos-schemas');
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";
}
})
const ContributionSerializer = require('../serializers/contribution');
const Base = require('./base');
@@ -62,12 +47,13 @@ class Contribution extends Base {
});
}
addContribution(contributionAttr, callOptions = {}) {
let jsonStr = ContributionSerializer.serialize(contributionAttr);
async addContribution(contributionAttr, callOptions = {}) {
const contribution = new ContributionSerializer(contributionAttr);
// Validate JSON document against schema
let result = validator.validate(JSON.parse(jsonStr), schemas['contribution']);
if (!result) { return Promise.reject(validator.error); }
try { await contribution.validate(); }
catch (error) { return Promise.reject(error); }
const jsonStr = contribution.serialize();
return this.ipfs
.add(jsonStr)

View File

@@ -1,4 +1,3 @@
const ethers = require('ethers');
const RSVP = require('rsvp');
const ContributorSerializer = require('../serializers/contributor');
@@ -20,7 +19,6 @@ class Contributor extends Base {
getById(id) {
return this.functions.getContributorById(id)
// Fetch IPFS data if available
.then((data) => {
return this.ipfs.catAndMerge(data, ContributorSerializer.deserialize);
});

View File

@@ -1,4 +1,3 @@
const ethers = require('ethers');
const RSVP = require('rsvp');
const ContributionSerializer = require('../serializers/contribution');
@@ -25,12 +24,16 @@ class Proposal extends Base {
});
}
addProposal(proposalAttr, callOptions = {}) {
let json = ContributionSerializer.serialize(proposalAttr);
// TODO: validate against schema
async addProposal(proposalAttr, callOptions = {}) {
const contribution = new ContributionSerializer(proposalAttr);
try { await contribution.validate(); }
catch (error) { return Promise.reject(error); }
const jsonStr = contribution.serialize();
return this.ipfs
.add(json)
.add(jsonStr)
.then((ipfsHashAttr) => {
let proposal = [
proposalAttr.contributorId,

View File

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

View File

@@ -1,45 +1,24 @@
const schemas = require('kosmos-schemas');
const validator = require('../utils/validator');
/**
* Handle serialization for JSON-LD object of the contribution, according to
* https://github.com/67P/kosmos-schemas/blob/master/schemas/contribution.json
* Serialization and validation for JSON-LD document of the contribution.
*
* @class
* @public
*/
class Contribution {
/**
* Deserialize JSON to object
*
* @method
* @public
*/
static deserialize(serialized) {
let {
date,
time,
kind,
description,
details,
url,
} = JSON.parse(serialized.toString('utf8'));
return {
date,
time,
kind,
description,
details,
url,
ipfsData: serialized,
};
constructor(attrs) {
Object.keys(attrs).forEach(a => this[a] = attrs[a]);
}
/**
* Serialize object to JSON
*
* @method
* @public
*/
static serialize(deserialized) {
/**
* Serialize object to JSON
*
* @public
*/
serialize () {
let {
contributorIpfsHash,
date,
@@ -48,7 +27,7 @@ class Contribution {
description,
url,
details
} = deserialized;
} = this;
let data = {
"@context": "https://schema.kosmos.org",
@@ -70,6 +49,44 @@ class Contribution {
// 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,
url,
} = JSON.parse(serialized.toString('utf8'));
return {
date,
time,
kind,
description,
details,
url,
ipfsData: serialized,
};
}
}
module.exports = Contribution;

15
lib/utils/validator.js Normal file
View File

@@ -0,0 +1,15 @@
const tv4 = require('tv4');
const validator = tv4.freshApi();
validator.addFormat({
'date': function(value) {
const dateRegexp = /^[0-9]{4,}-[0-9]{2}-[0-9]{2}$/;
return dateRegexp.test(value) ? null : "A valid ISO 8601 full-date string is expected";
},
'time': function(value) {
const timeRegexp = /^([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([Zz])|([\+|\-]([01][0-9]|2[0-3]):[0-5][0-9]))$/;
return timeRegexp.test(value) ? null : "A valid ISO 8601 full-time string is expected";
}
})
module.exports = validator;

7
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "kredits-contracts",
"version": "5.0.0",
"version": "5.1.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -11175,8 +11175,9 @@
}
},
"kosmos-schemas": {
"version": "github:67P/kosmos-schemas#28a5e9f1fc2e083165e4e02ce3361c3f4fafa592",
"from": "github:67P/kosmos-schemas#feature/contribution_date_time",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/kosmos-schemas/-/kosmos-schemas-2.0.0.tgz",
"integrity": "sha512-zIjWcDxaN94m1vPgUaI5LRX6y07Ihw9ScPoGKf1NkZ0sLgD/CRV8YIKRyDafH5mThe3uBN2+F6H6Gp5qhNhALg==",
"requires": {
"brfs-babel": "^1.0.0"
}

View File

@@ -1,6 +1,6 @@
{
"name": "kredits-contracts",
"version": "5.0.0",
"version": "5.1.1",
"description": "Ethereum contracts and npm wrapper for Kredits",
"main": "./lib/kredits.js",
"directories": {
@@ -45,7 +45,7 @@
"dependencies": {
"ethers": "^4.0.27",
"ipfs-http-client": "^30.1.1",
"kosmos-schemas": "github:67P/kosmos-schemas#feature/contribution_date_time",
"kosmos-schemas": "^2.0.0",
"rsvp": "^4.8.2",
"tv4": "^1.3.0"
},

View File

@@ -1,4 +1,5 @@
const promptly = require('promptly');
const { inspect } = require('util');
const initKredits = require('./helpers/init_kredits.js');
@@ -25,23 +26,31 @@ module.exports = async function(callback) {
}
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(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(inspect(error));
});
}

View File

@@ -15,7 +15,7 @@ module.exports = async function(callback) {
console.log(`Using Contribution at: ${kredits.Contribution.contract.address}`);
const table = new Table({
head: ['ID', 'Contributor ID', 'Description', 'Amount', 'Confirmed?', 'Vetoed?', 'Claimed?']
head: ['ID', 'Contributor ID', 'Description', 'Amount', 'Confirmed?', 'Vetoed?', 'Claimed?', 'IPFS']
})
try {
@@ -33,6 +33,7 @@ module.exports = async function(callback) {
confirmed,
c.vetoed,
c.claimed,
c.ipfsHash
])
});

View File

@@ -17,7 +17,7 @@ module.exports = async function(callback) {
const table = new Table({
head: ['ID', 'Account', 'Core?', 'Name', 'Balance']
head: ['ID', 'Account', 'Name', 'Core?', 'Balance', 'IPFS']
})
let contributors = await kredits.Contributor.all()
@@ -26,9 +26,10 @@ module.exports = async function(callback) {
table.push([
c.id.toString(),
c.account,
c.isCore,
`${c.name}`,
ethers.utils.formatEther(c.balance)
c.isCore,
ethers.utils.formatEther(c.balance),
c.ipfsHash
])
})
console.log(table.toString())

8242
yarn.lock Normal file

File diff suppressed because it is too large Load Diff