Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6778b9060f | |||
| 4d36f78824 | |||
| 98ecf28262 | |||
| f5564fa923 | |||
| 096a9f3cea | |||
| 3d24835908 | |||
| 8cbef8458d | |||
| 9ca0580db6 | |||
| dfe38e7d21 | |||
| 377560805f | |||
| 65d9cf2a06 | |||
| b35dc2049b | |||
| 26c2710149 | |||
| cc24c27444 | |||
| f717968402 | |||
| 14d7fbd75e | |||
| 7eb1fedb42 | |||
| cbfe39f804 | |||
| a0e4f5410d | |||
| 766463b57b |
48
README.md
48
README.md
@@ -2,12 +2,13 @@
|
||||
|
||||
# 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
|
||||
|
||||
@@ -22,35 +23,36 @@ Each of the aragon apps are separate packages:
|
||||
$ cd apps/[app]
|
||||
$ npm install
|
||||
|
||||
or use the bootstrap command (see below)
|
||||
You can use `npm run install-all` to install all app dependencies at once.
|
||||
|
||||
### Local development chain
|
||||
|
||||
For local development it is recommended to use
|
||||
[ganache](http://truffleframework.com/ganache/) to run a local development
|
||||
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 confgure 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:
|
||||
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.
|
||||
|
||||
### Bootstrap
|
||||
|
||||
1. Run an Ethereum node and ipfs
|
||||
|
||||
|
||||
$ npm run devchain
|
||||
$ ipfs daemon
|
||||
|
||||
2. Deploy each app to the devchain
|
||||
|
||||
(make sure you've run `npm install` for every app - see installation)
|
||||
$ npm run deploy:apps
|
||||
|
||||
3. Deploy a new KreditsKit and create a new DAO with the latest app versions
|
||||
@@ -58,7 +60,7 @@ Ethereum RPC port.
|
||||
$ npm run deploy:kit
|
||||
$ npm run deploy:dao
|
||||
|
||||
4. Execute seeds to create demo contributors, contributons, etc. (optional)
|
||||
4. Execute seeds to create demo contributors, contributons, etc. (optional)
|
||||
|
||||
$ npm run seeds
|
||||
|
||||
@@ -71,15 +73,15 @@ If you want to reset your local setup:
|
||||
|
||||
## 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.
|
||||
|
||||
@@ -161,7 +163,7 @@ Creates and configures a new DAO instance.
|
||||
or
|
||||
$ npm run deploy:dao
|
||||
|
||||
KreditsKit address is load 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
|
||||
@@ -172,14 +174,12 @@ Runs `npm install` for each app and publishes a new version.
|
||||
or
|
||||
$ npm run deploy:apps
|
||||
|
||||
|
||||
## ACL / Permissions
|
||||
|
||||
|
||||
## Upgradeable contracts
|
||||
|
||||
We use aragonOS for upgradeablity 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
|
||||
|
||||
@@ -32,7 +32,7 @@ contract Contribution is AragonApp {
|
||||
uint8 hashFunction;
|
||||
uint8 hashSize;
|
||||
string tokenMetadataURL;
|
||||
uint claimAfterBlock;
|
||||
uint256 confirmedAtBlock;
|
||||
bool vetoed;
|
||||
bool exists;
|
||||
}
|
||||
@@ -48,7 +48,7 @@ contract Contribution is AragonApp {
|
||||
mapping(uint32 => ContributionData) public contributions;
|
||||
uint32 public contributionsCount;
|
||||
|
||||
uint32 public blocksToWait = 0;
|
||||
uint32 public blocksToWait;
|
||||
|
||||
event ContributionAdded(uint32 id, uint32 indexed contributorId, uint32 amount);
|
||||
event ContributionClaimed(uint32 id, uint32 indexed contributorId, uint32 amount);
|
||||
@@ -56,6 +56,7 @@ contract Contribution is AragonApp {
|
||||
|
||||
function initialize(bytes32[4] _appIds) public onlyInit {
|
||||
appIds = _appIds;
|
||||
blocksToWait = 40320; // 7 days; 15 seconds block time
|
||||
initialized();
|
||||
}
|
||||
|
||||
@@ -117,7 +118,7 @@ contract Contribution is AragonApp {
|
||||
// Custom functions
|
||||
//
|
||||
|
||||
function getContribution(uint32 contributionId) public view returns (uint32 id, uint32 contributorId, uint32 amount, bool claimed, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint claimAfterBlock, bool exists, bool vetoed) {
|
||||
function getContribution(uint32 contributionId) public view returns (uint32 id, uint32 contributorId, uint32 amount, bool claimed, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint256 confirmedAtBlock, bool exists, bool vetoed) {
|
||||
id = contributionId;
|
||||
ContributionData storage c = contributions[id];
|
||||
return (
|
||||
@@ -128,7 +129,7 @@ contract Contribution is AragonApp {
|
||||
c.hashDigest,
|
||||
c.hashFunction,
|
||||
c.hashSize,
|
||||
c.claimAfterBlock,
|
||||
c.confirmedAtBlock,
|
||||
c.exists,
|
||||
c.vetoed
|
||||
);
|
||||
@@ -145,7 +146,11 @@ contract Contribution is AragonApp {
|
||||
c.hashDigest = hashDigest;
|
||||
c.hashFunction = hashFunction;
|
||||
c.hashSize = hashSize;
|
||||
c.claimAfterBlock = block.number; // + blocksToWait;
|
||||
if (contributionId < 10) {
|
||||
c.confirmedAtBlock = block.number;
|
||||
} else {
|
||||
c.confirmedAtBlock = block.number + blocksToWait;
|
||||
}
|
||||
|
||||
contributionsCount++;
|
||||
|
||||
@@ -159,6 +164,7 @@ contract Contribution is AragonApp {
|
||||
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);
|
||||
@@ -169,7 +175,7 @@ contract Contribution is AragonApp {
|
||||
require(c.exists, 'NOT_FOUND');
|
||||
require(!c.claimed, 'ALREADY_CLAIMED');
|
||||
require(!c.vetoed, 'VETOED');
|
||||
require(block.number > c.claimAfterBlock, 'NOT_CLAIMABLE');
|
||||
require(block.number >= c.confirmedAtBlock, 'NOT_CLAIMABLE');
|
||||
|
||||
c.claimed = true;
|
||||
address token = getTokenContract();
|
||||
|
||||
@@ -13,10 +13,9 @@ contract Contributor is AragonApp {
|
||||
|
||||
struct Contributor {
|
||||
address account;
|
||||
bytes32 ipfsHash;
|
||||
bytes32 hashDigest;
|
||||
uint8 hashFunction;
|
||||
uint8 hashSize;
|
||||
bool isCore;
|
||||
bool exists;
|
||||
}
|
||||
|
||||
@@ -28,19 +27,11 @@ contract Contributor is AragonApp {
|
||||
enum Apps { Contribution, Contributor, Proposal, Token }
|
||||
bytes32[4] public appIds;
|
||||
|
||||
event ContributorProfileUpdated(uint32 id, bytes32 oldIpfsHash, bytes32 newIpfsHash);
|
||||
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);
|
||||
|
||||
function initialize(address root,bytes32[4] _appIds) public onlyInit {
|
||||
uint32 _id = contributorsCount + 1;
|
||||
Contributor storage c = contributors[_id];
|
||||
c.exists = true;
|
||||
c.isCore = true;
|
||||
c.account = root;
|
||||
contributorIds[root] = _id;
|
||||
contributorsCount += 1;
|
||||
|
||||
appIds = _appIds;
|
||||
|
||||
initialized();
|
||||
@@ -55,7 +46,7 @@ contract Contributor is AragonApp {
|
||||
function coreContributorsCount() view public returns (uint32) {
|
||||
uint32 count = 0;
|
||||
for (uint32 i = 1; i <= contributorsCount; i++) {
|
||||
if (contributors[i].isCore) {
|
||||
if (isCoreTeam(i)) {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
@@ -69,24 +60,23 @@ contract Contributor is AragonApp {
|
||||
ContributorAccountUpdated(id, oldAccount, newAccount);
|
||||
}
|
||||
|
||||
function updateContributorIpfsHash(uint32 id, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize) public isInitialized auth(MANAGE_CONTRIBUTORS_ROLE) {
|
||||
function updateContributorProfileHash(uint32 id, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(MANAGE_CONTRIBUTORS_ROLE) {
|
||||
Contributor storage c = contributors[id];
|
||||
bytes32 oldIpfsHash = c.ipfsHash;
|
||||
c.ipfsHash = ipfsHash;
|
||||
bytes32 oldHashDigest = c.hashDigest;
|
||||
c.hashDigest = hashDigest;
|
||||
c.hashFunction = hashFunction;
|
||||
c.hashSize = hashSize;
|
||||
|
||||
ContributorProfileUpdated(id, oldIpfsHash, c.ipfsHash);
|
||||
ContributorProfileUpdated(id, oldHashDigest, c.hashDigest);
|
||||
}
|
||||
|
||||
function addContributor(address account, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize, bool isCore) public isInitialized auth(MANAGE_CONTRIBUTORS_ROLE) {
|
||||
function addContributor(address account, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(MANAGE_CONTRIBUTORS_ROLE) {
|
||||
require(!addressExists(account));
|
||||
uint32 _id = contributorsCount + 1;
|
||||
assert(!contributors[_id].exists); // this can not be acually
|
||||
Contributor storage c = contributors[_id];
|
||||
c.exists = true;
|
||||
c.isCore = isCore;
|
||||
c.ipfsHash = ipfsHash;
|
||||
c.hashDigest = hashDigest;
|
||||
c.hashFunction = hashFunction;
|
||||
c.hashSize = hashSize;
|
||||
c.account = account;
|
||||
@@ -96,8 +86,10 @@ contract Contributor is AragonApp {
|
||||
emit ContributorAdded(_id, account);
|
||||
}
|
||||
|
||||
function isCore(uint32 id) view public returns (bool) {
|
||||
return contributors[id].isCore;
|
||||
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 exists(uint32 id) view public returns (bool) {
|
||||
@@ -105,7 +97,8 @@ contract Contributor is AragonApp {
|
||||
}
|
||||
|
||||
function addressIsCore(address account) view public returns (bool) {
|
||||
return getContributorByAddress(account).isCore;
|
||||
uint32 id = getContributorIdByAddress(account);
|
||||
return isCoreTeam(id);
|
||||
}
|
||||
|
||||
function addressExists(address account) view public returns (bool) {
|
||||
@@ -125,14 +118,14 @@ contract Contributor is AragonApp {
|
||||
return contributors[id];
|
||||
}
|
||||
|
||||
function getContributorById(uint32 _id) public view returns (uint32 id, address account, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize, bool isCore, uint256 balance, bool exists ) {
|
||||
function getContributorById(uint32 _id) public view returns (uint32 id, address account, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, bool isCore, uint256 balance, bool exists ) {
|
||||
id = _id;
|
||||
Contributor storage c = contributors[_id];
|
||||
account = c.account;
|
||||
ipfsHash = c.ipfsHash;
|
||||
hashDigest = c.hashDigest;
|
||||
hashFunction = c.hashFunction;
|
||||
hashSize = c.hashSize;
|
||||
isCore = c.isCore;
|
||||
isCore = isCoreTeam(id);
|
||||
address token = getTokenContract();
|
||||
balance = ITokenBalance(token).balanceOf(c.account);
|
||||
exists = c.exists;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
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: 3, amount: 500, kind: 'code', description: '[67P/kredits-contracts] Ran the seeds', url: '' }, { gasLimit: 350000 }]],
|
||||
['Proposal', 'addProposal', [{ contributorId: 3, amount: 500, kind: 'code', description: '[67P/kredits-contracts] Hacked on kredits', url: '' }, { gasLimit: 350000 }]],
|
||||
['Proposal', 'addProposal', [{ contributorId: 2, amount: 500, kind: 'code', description: '[67P/kredits-contracts] Hacked on kredits', url: '' }, { gasLimit: 350000 }]],
|
||||
['Proposal', 'vote', [1, { gasLimit: 550000 }]],
|
||||
['Contribution', 'addContribution', [{ contributorId: 2, amount: 5000, kind: 'dev', description: '[67P/kredits-contracts] Introduce contribution token', url: '' }, { gasLimit: 350000 }]],
|
||||
['Contribution', 'addContribution', [{ contributorId: 3, amount: 1500, kind: 'dev', description: '[67P/kredits-web] Reviewed stuff', url: '' }, { gasLimit: 350000 }]],
|
||||
['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', 'claim', [1, { gasLimit: 300000 }]]
|
||||
];
|
||||
const funds = [
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -20,11 +20,6 @@ class Contributor extends Base {
|
||||
|
||||
getById(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);
|
||||
@@ -67,7 +62,6 @@ class Contributor extends Base {
|
||||
ipfsHashAttr.hashDigest,
|
||||
ipfsHashAttr.hashFunction,
|
||||
ipfsHashAttr.hashSize,
|
||||
contributorAttr.isCore,
|
||||
];
|
||||
|
||||
return this.functions.addContributor(...contributor, callOptions);
|
||||
|
||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "kredits-contracts",
|
||||
"version": "4.0.0",
|
||||
"version": "4.0.2",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
{
|
||||
"name": "kredits-contracts",
|
||||
"version": "4.0.0",
|
||||
"version": "4.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\"",
|
||||
"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": "aragon contracts compile --all",
|
||||
"bootstrap": "./scripts/every-app \"npm install \" && npm run reset:hard && npm run seeds",
|
||||
"bootstrap": "npm run reset:hard && npm run seeds",
|
||||
"reset": "npm run deploy:kit && npm run deploy:dao",
|
||||
"reset:hard": "npm run deploy:apps && npm run reset",
|
||||
"deploy:kit": "npm run compile-contracts && aragon contracts exec scripts/deploy-kit.js",
|
||||
|
||||
@@ -23,7 +23,6 @@ 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: '),
|
||||
@@ -34,7 +33,7 @@ module.exports = async function(callback) {
|
||||
console.log("\nAdding contributor:");
|
||||
console.log(contributorAttributes);
|
||||
|
||||
kredits.Contributor.add(contributorAttributes, { gasLimit: 250000 }).then((result) => {
|
||||
kredits.Contributor.add(contributorAttributes, { gasLimit: 350000 }).then((result) => {
|
||||
console.log("\n\nResult:");
|
||||
console.log(result);
|
||||
callback();
|
||||
|
||||
@@ -15,21 +15,31 @@ module.exports = async function(callback) {
|
||||
console.log(`Using Contribution at: ${kredits.Contribution.contract.address}`);
|
||||
|
||||
const table = new Table({
|
||||
head: ['ID', 'Contributor account', 'Amount', 'Claimed?', 'Vetoed?', 'Description']
|
||||
head: ['ID', 'Contributor ID', 'Description', 'Amount', 'Confirmed?', 'Vetoed?', 'Claimed?']
|
||||
})
|
||||
|
||||
let contributions = await kredits.Contribution.all()
|
||||
try {
|
||||
let blockNumber = await kredits.provider.getBlockNumber();
|
||||
let contributions = await kredits.Contribution.all()
|
||||
|
||||
contributions.forEach((c) => {
|
||||
table.push([
|
||||
c.id.toString(),
|
||||
c.contributorId,
|
||||
c.amount.toString(),
|
||||
c.claimed,
|
||||
c.vetoed,
|
||||
`${c.description}`
|
||||
])
|
||||
})
|
||||
console.log(table.toString())
|
||||
callback()
|
||||
contributions.forEach((c) => {
|
||||
const confirmed = !!(c.claimAtBlock < blockNumber)
|
||||
|
||||
table.push([
|
||||
c.id.toString(),
|
||||
c.contributorId,
|
||||
`${c.description}`,
|
||||
c.amount.toString(),
|
||||
confirmed,
|
||||
c.vetoed,
|
||||
c.claimed,
|
||||
])
|
||||
})
|
||||
|
||||
console.log(table.toString())
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
callback();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user