refactor contrats with aragonos
This commit is contained in:
parent
d687ff604e
commit
6c569239de
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,4 +1,6 @@
|
|||||||
build
|
build
|
||||||
|
flattened_contracts
|
||||||
node_modules
|
node_modules
|
||||||
|
**/node_modules
|
||||||
.ganache-db
|
.ganache-db
|
||||||
.tm_properties
|
.tm_properties
|
||||||
|
4
apps/contribution/.gitignore
vendored
Normal file
4
apps/contribution/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
node_modules
|
||||||
|
build
|
||||||
|
.cache
|
||||||
|
dist
|
14
apps/contribution/.ipfsignore
Normal file
14
apps/contribution/.ipfsignore
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Git files
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# Build files
|
||||||
|
.cache
|
||||||
|
node_modules
|
||||||
|
build
|
||||||
|
|
||||||
|
# Lock files
|
||||||
|
package-lock.json
|
||||||
|
yarn.lock
|
||||||
|
|
||||||
|
# Others
|
||||||
|
test
|
162
apps/contribution/README.md
Normal file
162
apps/contribution/README.md
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
# Aragon React Boilerplate
|
||||||
|
|
||||||
|
> 🕵️ [Find more boilerplates using GitHub](https://github.com/search?q=topic:aragon-boilerplate) |
|
||||||
|
> ✨ [Official boilerplates](https://github.com/search?q=topic:aragon-boilerplate+org:aragon)
|
||||||
|
|
||||||
|
React boilerplate for Aragon applications.
|
||||||
|
|
||||||
|
This boilerplate also includes a fully working example app, complete with a background worker and a front-end in React (with Aragon UI).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```sh
|
||||||
|
aragon init app.aragonpm.eth react
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running your app
|
||||||
|
|
||||||
|
### Using HTTP
|
||||||
|
|
||||||
|
Running your app using HTTP will allow for a faster development process of your app's front-end, as it can be hot-reloaded without the need to execute `aragon run` every time a change is made.
|
||||||
|
|
||||||
|
- First start your app's development server running `npm run start:app`, and keep that process running. By default it will rebuild the app and reload the server when changes to the source are made.
|
||||||
|
|
||||||
|
- After that, you can run `npm run start:aragon:http` which will compile your app's contracts, publish the app locally and create a DAO. You will need to stop it and run it again after making changes to your smart contracts.
|
||||||
|
|
||||||
|
Changes to the app's background script (`app/script.js`) cannot be hot-reloaded, after making changes to the script, you will need to either restart the development server (`npm run start:app`) or rebuild the script `npm run build:script`.
|
||||||
|
|
||||||
|
### Using IPFS
|
||||||
|
|
||||||
|
Running your app using IPFS will mimic the production environment that will be used for running your app. `npm run start:aragon:ipfs` will run your app using IPFS. Whenever a change is made to any file in your front-end, a new version of the app needs to be published, so the command needs to be restarted.
|
||||||
|
|
||||||
|
## What's in the box?
|
||||||
|
|
||||||
|
### npm Scripts
|
||||||
|
|
||||||
|
- **start** or **start:aragon:ipfs**: Runs your app inside a DAO served from IPFS
|
||||||
|
- **start:aragon:http**: Runs your app inside a DAO served with HTTP (hot reloading)
|
||||||
|
- **start:app**: Starts a development server for your app
|
||||||
|
- **compile**: Compile the smart contracts
|
||||||
|
- **build**: Builds the front-end and background script
|
||||||
|
- **build:app**: Builds the front-end
|
||||||
|
- **build:script**: Builds the background script
|
||||||
|
- **test**: Runs tests for the contracts
|
||||||
|
- **publish:patch**: Release a patch version to aragonPM (only frontend/content changes allowed)
|
||||||
|
- **publish:minor**: Release a minor version to aragonPM (only frontend/content changes allowed)
|
||||||
|
- **publish:major**: Release a major version to aragonPM (frontend **and** contract changes)
|
||||||
|
- **versions**: Check the currently installed versions of the app
|
||||||
|
|
||||||
|
### Libraries
|
||||||
|
|
||||||
|
- [**@aragon/os**](https://github.com/aragon/aragonos): Aragon interfaces
|
||||||
|
- [**@aragon/client**](https://github.com/aragon/aragon.js/tree/master/packages/aragon-client): Wrapper for Aragon application RPC
|
||||||
|
- [**@aragon/ui**](https://github.com/aragon/aragon-ui): Aragon UI components (in React)
|
||||||
|
|
||||||
|
## Publish
|
||||||
|
|
||||||
|
This app has 3 environments defined in `arapp.json`:
|
||||||
|
|
||||||
|
| Environment | Network |
|
||||||
|
|--- |--- |
|
||||||
|
| default | localhost |
|
||||||
|
| staging | rinkeby |
|
||||||
|
| production | mainnet |
|
||||||
|
|
||||||
|
Prerequisites:
|
||||||
|
- ENS Registry address
|
||||||
|
|
||||||
|
Note: the `default` environment which points to `localhost` does not have an ENS Registry address specified because the `@aragon/cli` will default the value to `0xB9462EF3441346dBc6E49236Edbb0dF207db09B7` (the ENS Registry pre-deployed on the local development chain).
|
||||||
|
|
||||||
|
### Introduction to environments
|
||||||
|
|
||||||
|
Environments are defined in `arapp.json`, for example `staging` points to:
|
||||||
|
- an ENS registry (`0x314159265dd8dbb310642f98f50c066173c1259b`)
|
||||||
|
- an APM registry (`open.aragonpm.eth`)
|
||||||
|
- an APM repository (`app`)
|
||||||
|
- an Ethereum network (`rinkeby`)
|
||||||
|
- an Ethereum websockets provider (`wss://rinkeby.eth.aragon.network/ws` - to **read** from the blockchain)
|
||||||
|
|
||||||
|
The `rinkeby` network is further defined in `truffle.js`, and has:
|
||||||
|
- an Ethereum provider (to **write** to the blockchain):
|
||||||
|
- an address (`https://rinkeby.infura.io`)
|
||||||
|
- an Ethereum Account (`0xb4124cEB3451635DAcedd11767f004d8a28c6eE7`)
|
||||||
|
(which is the first account generated from the `DEFAULT_MNEMONIC` variable, to use a different account see [here](#Using-a-different-Ethereum-account))
|
||||||
|
|
||||||
|
### Major version: content + contract
|
||||||
|
|
||||||
|
Command:
|
||||||
|
```
|
||||||
|
npm run publish:major -- --environment staging
|
||||||
|
```
|
||||||
|
|
||||||
|
This will:
|
||||||
|
1. _build_ the app's frontend (the output lives in `dist`)
|
||||||
|
2. _compile_ the app's contract (the output lives in `build`)
|
||||||
|
3. publish the app to the **staging** environment.
|
||||||
|
|
||||||
|
Sample output:
|
||||||
|
```
|
||||||
|
> aragon apm publish major "--environment" "staging"
|
||||||
|
|
||||||
|
✔ Successfully published app.open.aragonpm.eth v1.0.0:
|
||||||
|
ℹ Contract address: 0xE636bcA5B95e94F749F63E322a04DB59362299F1
|
||||||
|
ℹ Content (ipfs): QmR695Wu5KrHNec7pRP3kPvwYihABDAyVYdX5D5vwLgxCn
|
||||||
|
ℹ Transaction hash: 0x3d752db29cc106e9ff98b260a90615921eb32471425a29ead8cbb830fb224d8
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: the contract location is defined in `arapp.json` under `path`.
|
||||||
|
Note: you can also deploy a major version with only frontend changes by passing `--only-content`.
|
||||||
|
|
||||||
|
### Minor/patch version: content only
|
||||||
|
|
||||||
|
Command:
|
||||||
|
```
|
||||||
|
npm run publish:patch -- --environment staging
|
||||||
|
```
|
||||||
|
|
||||||
|
This will:
|
||||||
|
1. _build_ the app's frontend (which lives in `dist`)
|
||||||
|
2. publish the app to the **staging** environment.
|
||||||
|
|
||||||
|
Sample output:
|
||||||
|
```
|
||||||
|
✔ Successfully published app.open.aragonpm.eth v1.1.1:
|
||||||
|
ℹ Contract address: 0xE636bcA5B95e94F749F63E322a04DB59362299F1
|
||||||
|
ℹ Content (ipfs): QmUYv9cjyNVxCyAJGK2YXjkbzh6u4iW2ak81Z9obdefM1q
|
||||||
|
ℹ Transaction hash: 0x57864d8efd8d439008621b494b19a3e8f876a8a46b38475f9626802f0a1403c2
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check published versions
|
||||||
|
|
||||||
|
Command:
|
||||||
|
```
|
||||||
|
npm run versions -- --environment staging
|
||||||
|
```
|
||||||
|
|
||||||
|
Sample output:
|
||||||
|
```
|
||||||
|
ℹ app.open.aragonpm.eth has 4 published versions
|
||||||
|
✔ 1.0.0: 0xE636bcA5B95e94F749F63E322a04DB59362299F1 ipfs:QmR695Wu5KrHNec7pRP3kPvwYihABDAyVYdX5D5vwLgxCn
|
||||||
|
✔ 1.1.0: 0xE636bcA5B95e94F749F63E322a04DB59362299F1 ipfs:QmSwjUZFpv2c2e9fLoxtgFrAsAmBN4DyQGJp4RcqQcW3z3
|
||||||
|
✔ 1.1.1: 0xE636bcA5B95e94F749F63E322a04DB59362299F1 ipfs:QmUYv9cjyNVxCyAJGK2YXjkbzh6u4iW2ak81Z9obdefM1q
|
||||||
|
✔ 2.0.0: 0x74CBbbC932d7C344FCd789Eba24BfD40e52980c9 ipfs:Qmadb3hzwLDKtb93fF367Vg1epkdsLZF4dhpapNYynjgZF
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using a different Ethereum account
|
||||||
|
|
||||||
|
To deploy from a different account, you can:
|
||||||
|
- define a `~/.aragon/mnemonic.json` file
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"mnemonic": "explain tackle mirror kit ..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
or
|
||||||
|
- define a `~/.aragon/${network_name}_key.json` file, for example: `~/.aragon/rinkeby_key.json`
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"keys": [
|
||||||
|
"a8a54b2d8197bc0b19bb8a084031be71835580a01e70a45a13babd16c9bc1563"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
33
apps/contribution/arapp.json
Normal file
33
apps/contribution/arapp.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"name": "Add contributions",
|
||||||
|
"id": "ADD_CONTRIBUTION_ROLE",
|
||||||
|
"params": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Manage token contract",
|
||||||
|
"id": "MANAGE_TOKEN_CONTRACT_ROLE",
|
||||||
|
"params": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"environments": {
|
||||||
|
"default": {
|
||||||
|
"network": "development",
|
||||||
|
"appName": "contribution.aragonpm.eth"
|
||||||
|
},
|
||||||
|
"staging": {
|
||||||
|
"registry": "0x98df287b6c145399aaa709692c8d308357bc085d",
|
||||||
|
"appName": "contribution.open.aragonpm.eth",
|
||||||
|
"wsRPC": "wss://rinkeby.eth.aragon.network/ws",
|
||||||
|
"network": "rinkeby"
|
||||||
|
},
|
||||||
|
"production": {
|
||||||
|
"registry": "0x314159265dd8dbb310642f98f50c066173c1259b",
|
||||||
|
"appName": "contribution.open.aragonpm.eth",
|
||||||
|
"wsRPC": "wss://mainnet.eth.aragon.network/ws",
|
||||||
|
"network": "mainnet"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path": "contracts/Contribution.sol"
|
||||||
|
}
|
139
apps/contribution/contracts/Contribution.sol
Normal file
139
apps/contribution/contracts/Contribution.sol
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
pragma solidity ^0.4.24;
|
||||||
|
|
||||||
|
import "@aragon/os/contracts/apps/AragonApp.sol";
|
||||||
|
import "@aragon/os/contracts/kernel/IKernel.sol";
|
||||||
|
|
||||||
|
interface IToken {
|
||||||
|
//function mintFor(address contributorAccount, uint256 amount, uint256 proposalId) external;
|
||||||
|
function mintFor(address contributorAccount, uint256 amount, uint256 contributionId) public;
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Contribution is AragonApp {
|
||||||
|
bytes32 public constant ADD_CONTRIBUTION_ROLE = keccak256("ADD_CONTRIBUTION_ROLE");
|
||||||
|
|
||||||
|
bytes32 public constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb;
|
||||||
|
bytes32 public constant TOKEN_APP_ID = 0xe04a882e7a6adf5603207d545ea49aec17e6b936c4d9eae3d74dbe482264991a;
|
||||||
|
|
||||||
|
struct ContributionData {
|
||||||
|
address contributor;
|
||||||
|
uint256 amount;
|
||||||
|
bool claimed;
|
||||||
|
bytes32 hashDigest;
|
||||||
|
uint8 hashFunction;
|
||||||
|
uint8 hashSize;
|
||||||
|
string tokenMetadataURL;
|
||||||
|
uint claimAfterBlock;
|
||||||
|
bool exists;
|
||||||
|
}
|
||||||
|
string internal name_;
|
||||||
|
string internal symbol_;
|
||||||
|
|
||||||
|
address public tokenContract;
|
||||||
|
|
||||||
|
mapping(uint256 => address) contributionOwner;
|
||||||
|
mapping(address => uint256[]) ownedContributions;
|
||||||
|
|
||||||
|
mapping(uint256 => ContributionData) public contributions;
|
||||||
|
uint256 public contributionsCount;
|
||||||
|
|
||||||
|
uint256 public blocksToWait = 0;
|
||||||
|
|
||||||
|
event ContributionAdded(uint256 id, address indexed contributor, uint256 amount);
|
||||||
|
event ContributionClaimed(uint256 id, address indexed contributor, uint256 amount);
|
||||||
|
|
||||||
|
function initialize() public onlyInit {
|
||||||
|
initialized();
|
||||||
|
}
|
||||||
|
|
||||||
|
function name() external view returns (string) {
|
||||||
|
return name_;
|
||||||
|
}
|
||||||
|
|
||||||
|
function symbol() external view returns (string) {
|
||||||
|
return symbol_;
|
||||||
|
}
|
||||||
|
|
||||||
|
function balanceOf(address owner) public view returns (uint256) {
|
||||||
|
require(owner != address(0));
|
||||||
|
return ownedContributions[owner].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ownerOf(uint256 contributionId) public view returns (address) {
|
||||||
|
require(exists(contributionId));
|
||||||
|
return contributions[contributionId].contributor;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenOfOwnerByIndex(address contributor, uint256 index) public view returns (uint256) {
|
||||||
|
return ownedContributions[contributor][index];
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenMetadata(uint256 contributionId) public view returns (string) {
|
||||||
|
return contributions[contributionId].tokenMetadataURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
id = contributionId;
|
||||||
|
ContributionData storage c = contributions[id];
|
||||||
|
return (
|
||||||
|
id,
|
||||||
|
c.contributor,
|
||||||
|
c.amount,
|
||||||
|
c.claimed,
|
||||||
|
c.hashDigest,
|
||||||
|
c.hashFunction,
|
||||||
|
c.hashSize,
|
||||||
|
c.claimAfterBlock,
|
||||||
|
c.exists
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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.contributor = contributorAccount;
|
||||||
|
c.hashDigest = hashDigest;
|
||||||
|
c.hashFunction = hashFunction;
|
||||||
|
c.hashSize = hashSize;
|
||||||
|
c.claimAfterBlock = block.number; // + blocksToWait;
|
||||||
|
|
||||||
|
contributionsCount++;
|
||||||
|
|
||||||
|
contributionOwner[contributionId] = contributorAccount;
|
||||||
|
ownedContributions[contributorAccount].push(contributionId);
|
||||||
|
|
||||||
|
emit ContributionAdded(contributionId, contributorAccount, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function claim(uint256 contributionId) public isInitialized {
|
||||||
|
ContributionData storage c = contributions[contributionId];
|
||||||
|
require(c.exists, 'NOT_FOUND');
|
||||||
|
require(!c.claimed, 'ALREADY_CLAIMED');
|
||||||
|
require(block.number > c.claimAfterBlock, 'NOT_CLAIMABLE');
|
||||||
|
//c.claimed = true;
|
||||||
|
//IToken(getTokenContract()).mintFor(c.contributor, c.amount, contributionId); // somehow this does not work
|
||||||
|
bytes4 sig = bytes4(keccak256("mintFor()"));
|
||||||
|
address token = getTokenContract();
|
||||||
|
//IToken token = IToken(getTokenContract());
|
||||||
|
|
||||||
|
//token.call(sig);
|
||||||
|
//IToken(token).mintFor(c.contributor, c.amount, contributionId);
|
||||||
|
IToken(token).mintFor(c.contributor, c.amount, contributionId);
|
||||||
|
|
||||||
|
//emit ContributionClaimed(contributionId, c.contributor, c.amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTokenContract() public view returns (address) {
|
||||||
|
return 0x8779fc70eeea01d00efa9044c29d3c930fdb874a;
|
||||||
|
//IKernel k = IKernel(kernel());
|
||||||
|
|
||||||
|
//return k.getApp(KERNEL_APP_ADDR_NAMESPACE, TOKEN_APP_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exists(uint256 contributionId) view public returns (bool) {
|
||||||
|
return contributions[contributionId].exists;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
pragma solidity ^0.4.17;
|
pragma solidity ^0.4.4;
|
||||||
|
|
||||||
contract Migrations {
|
contract Migrations {
|
||||||
address public owner;
|
address public owner;
|
||||||
@ -8,15 +8,15 @@ contract Migrations {
|
|||||||
if (msg.sender == owner) _;
|
if (msg.sender == owner) _;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Migrations() public {
|
function Migrations() {
|
||||||
owner = msg.sender;
|
owner = msg.sender;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setCompleted(uint completed) public restricted {
|
function setCompleted(uint completed) restricted {
|
||||||
last_completed_migration = completed;
|
last_completed_migration = completed;
|
||||||
}
|
}
|
||||||
|
|
||||||
function upgrade(address new_address) public restricted {
|
function upgrade(address new_address) restricted {
|
||||||
Migrations upgraded = Migrations(new_address);
|
Migrations upgraded = Migrations(new_address);
|
||||||
upgraded.setCompleted(last_completed_migration);
|
upgraded.setCompleted(last_completed_migration);
|
||||||
}
|
}
|
4
apps/contribution/manifest.json
Normal file
4
apps/contribution/manifest.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "Contribution",
|
||||||
|
"description": "Kredits contribution app"
|
||||||
|
}
|
5
apps/contribution/migrations/1_initial_migration.js
Normal file
5
apps/contribution/migrations/1_initial_migration.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
var Migrations = artifacts.require('./Migrations.sol')
|
||||||
|
|
||||||
|
module.exports = function (deployer) {
|
||||||
|
deployer.deploy(Migrations)
|
||||||
|
}
|
5
apps/contribution/migrations/2_deploy_contracts.js
Normal file
5
apps/contribution/migrations/2_deploy_contracts.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
var Contribution = artifacts.require('Contribution.sol')
|
||||||
|
|
||||||
|
module.exports = function (deployer) {
|
||||||
|
deployer.deploy(Contribution)
|
||||||
|
}
|
21093
apps/contribution/package-lock.json
generated
Normal file
21093
apps/contribution/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
28
apps/contribution/package.json
Normal file
28
apps/contribution/package.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": "app-name",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"dependencies": {
|
||||||
|
"@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": "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": "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"
|
||||||
|
},
|
||||||
|
"keywords": []
|
||||||
|
}
|
5
apps/contribution/test/app.js
Normal file
5
apps/contribution/test/app.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
const CounterApp = artifacts.require('Contribution.sol')
|
||||||
|
|
||||||
|
contract('Contribution', (accounts) => {
|
||||||
|
it('should be tested')
|
||||||
|
})
|
63
apps/contribution/truffle.js
Normal file
63
apps/contribution/truffle.js
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* https://github.com/aragon/aragonOS/blob/v4.0.0/truffle-config.js
|
||||||
|
*/
|
||||||
|
const homedir = require('homedir')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
const HDWalletProvider = require('truffle-hdwallet-provider')
|
||||||
|
const HDWalletProviderPrivkey = require('truffle-hdwallet-provider-privkey')
|
||||||
|
|
||||||
|
const DEFAULT_MNEMONIC = 'explain tackle mirror kit van hammer degree position ginger unfair soup bonus'
|
||||||
|
|
||||||
|
const defaultRPC = (network) =>
|
||||||
|
`https://${network}.infura.io`
|
||||||
|
|
||||||
|
const configFilePath = (filename) =>
|
||||||
|
path.join(homedir(), `.aragon/${filename}`)
|
||||||
|
|
||||||
|
const mnemonic = () => {
|
||||||
|
try {
|
||||||
|
return require(configFilePath('mnemonic.json')).mnemonic
|
||||||
|
} catch (e) {
|
||||||
|
return DEFAULT_MNEMONIC
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const settingsForNetwork = (network) => {
|
||||||
|
try {
|
||||||
|
return require(configFilePath(`${network}_key.json`))
|
||||||
|
} catch (e) {
|
||||||
|
return { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lazily loaded provider
|
||||||
|
const providerForNetwork = (network) => (
|
||||||
|
() => {
|
||||||
|
let { rpc, keys } = settingsForNetwork(network)
|
||||||
|
rpc = rpc || defaultRPC(network)
|
||||||
|
|
||||||
|
if (!keys || keys.length == 0) {
|
||||||
|
return new HDWalletProvider(mnemonic(), rpc)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HDWalletProviderPrivkey(keys, rpc)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
module.exports = {
|
||||||
|
networks: {
|
||||||
|
development: {
|
||||||
|
host: 'localhost',
|
||||||
|
port: 8545,
|
||||||
|
network_id: '*'
|
||||||
|
},
|
||||||
|
mainnet: {
|
||||||
|
network_id: 1,
|
||||||
|
provider: providerForNetwork('mainnet')
|
||||||
|
},
|
||||||
|
rinkeby: {
|
||||||
|
network_id: 4,
|
||||||
|
provider: providerForNetwork('rinkeby')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
apps/contributor/.gitignore
vendored
Normal file
4
apps/contributor/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
node_modules
|
||||||
|
build
|
||||||
|
.cache
|
||||||
|
dist
|
14
apps/contributor/.ipfsignore
Normal file
14
apps/contributor/.ipfsignore
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Git files
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# Build files
|
||||||
|
.cache
|
||||||
|
node_modules
|
||||||
|
build
|
||||||
|
|
||||||
|
# Lock files
|
||||||
|
package-lock.json
|
||||||
|
yarn.lock
|
||||||
|
|
||||||
|
# Others
|
||||||
|
test
|
162
apps/contributor/README.md
Normal file
162
apps/contributor/README.md
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
# Aragon React Boilerplate
|
||||||
|
|
||||||
|
> 🕵️ [Find more boilerplates using GitHub](https://github.com/search?q=topic:aragon-boilerplate) |
|
||||||
|
> ✨ [Official boilerplates](https://github.com/search?q=topic:aragon-boilerplate+org:aragon)
|
||||||
|
|
||||||
|
React boilerplate for Aragon applications.
|
||||||
|
|
||||||
|
This boilerplate also includes a fully working example app, complete with a background worker and a front-end in React (with Aragon UI).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```sh
|
||||||
|
aragon init app.aragonpm.eth react
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running your app
|
||||||
|
|
||||||
|
### Using HTTP
|
||||||
|
|
||||||
|
Running your app using HTTP will allow for a faster development process of your app's front-end, as it can be hot-reloaded without the need to execute `aragon run` every time a change is made.
|
||||||
|
|
||||||
|
- First start your app's development server running `npm run start:app`, and keep that process running. By default it will rebuild the app and reload the server when changes to the source are made.
|
||||||
|
|
||||||
|
- After that, you can run `npm run start:aragon:http` which will compile your app's contracts, publish the app locally and create a DAO. You will need to stop it and run it again after making changes to your smart contracts.
|
||||||
|
|
||||||
|
Changes to the app's background script (`app/script.js`) cannot be hot-reloaded, after making changes to the script, you will need to either restart the development server (`npm run start:app`) or rebuild the script `npm run build:script`.
|
||||||
|
|
||||||
|
### Using IPFS
|
||||||
|
|
||||||
|
Running your app using IPFS will mimic the production environment that will be used for running your app. `npm run start:aragon:ipfs` will run your app using IPFS. Whenever a change is made to any file in your front-end, a new version of the app needs to be published, so the command needs to be restarted.
|
||||||
|
|
||||||
|
## What's in the box?
|
||||||
|
|
||||||
|
### npm Scripts
|
||||||
|
|
||||||
|
- **start** or **start:aragon:ipfs**: Runs your app inside a DAO served from IPFS
|
||||||
|
- **start:aragon:http**: Runs your app inside a DAO served with HTTP (hot reloading)
|
||||||
|
- **start:app**: Starts a development server for your app
|
||||||
|
- **compile**: Compile the smart contracts
|
||||||
|
- **build**: Builds the front-end and background script
|
||||||
|
- **build:app**: Builds the front-end
|
||||||
|
- **build:script**: Builds the background script
|
||||||
|
- **test**: Runs tests for the contracts
|
||||||
|
- **publish:patch**: Release a patch version to aragonPM (only frontend/content changes allowed)
|
||||||
|
- **publish:minor**: Release a minor version to aragonPM (only frontend/content changes allowed)
|
||||||
|
- **publish:major**: Release a major version to aragonPM (frontend **and** contract changes)
|
||||||
|
- **versions**: Check the currently installed versions of the app
|
||||||
|
|
||||||
|
### Libraries
|
||||||
|
|
||||||
|
- [**@aragon/os**](https://github.com/aragon/aragonos): Aragon interfaces
|
||||||
|
- [**@aragon/client**](https://github.com/aragon/aragon.js/tree/master/packages/aragon-client): Wrapper for Aragon application RPC
|
||||||
|
- [**@aragon/ui**](https://github.com/aragon/aragon-ui): Aragon UI components (in React)
|
||||||
|
|
||||||
|
## Publish
|
||||||
|
|
||||||
|
This app has 3 environments defined in `arapp.json`:
|
||||||
|
|
||||||
|
| Environment | Network |
|
||||||
|
|--- |--- |
|
||||||
|
| default | localhost |
|
||||||
|
| staging | rinkeby |
|
||||||
|
| production | mainnet |
|
||||||
|
|
||||||
|
Prerequisites:
|
||||||
|
- ENS Registry address
|
||||||
|
|
||||||
|
Note: the `default` environment which points to `localhost` does not have an ENS Registry address specified because the `@aragon/cli` will default the value to `0xB9462EF3441346dBc6E49236Edbb0dF207db09B7` (the ENS Registry pre-deployed on the local development chain).
|
||||||
|
|
||||||
|
### Introduction to environments
|
||||||
|
|
||||||
|
Environments are defined in `arapp.json`, for example `staging` points to:
|
||||||
|
- an ENS registry (`0x314159265dd8dbb310642f98f50c066173c1259b`)
|
||||||
|
- an APM registry (`open.aragonpm.eth`)
|
||||||
|
- an APM repository (`app`)
|
||||||
|
- an Ethereum network (`rinkeby`)
|
||||||
|
- an Ethereum websockets provider (`wss://rinkeby.eth.aragon.network/ws` - to **read** from the blockchain)
|
||||||
|
|
||||||
|
The `rinkeby` network is further defined in `truffle.js`, and has:
|
||||||
|
- an Ethereum provider (to **write** to the blockchain):
|
||||||
|
- an address (`https://rinkeby.infura.io`)
|
||||||
|
- an Ethereum Account (`0xb4124cEB3451635DAcedd11767f004d8a28c6eE7`)
|
||||||
|
(which is the first account generated from the `DEFAULT_MNEMONIC` variable, to use a different account see [here](#Using-a-different-Ethereum-account))
|
||||||
|
|
||||||
|
### Major version: content + contract
|
||||||
|
|
||||||
|
Command:
|
||||||
|
```
|
||||||
|
npm run publish:major -- --environment staging
|
||||||
|
```
|
||||||
|
|
||||||
|
This will:
|
||||||
|
1. _build_ the app's frontend (the output lives in `dist`)
|
||||||
|
2. _compile_ the app's contract (the output lives in `build`)
|
||||||
|
3. publish the app to the **staging** environment.
|
||||||
|
|
||||||
|
Sample output:
|
||||||
|
```
|
||||||
|
> aragon apm publish major "--environment" "staging"
|
||||||
|
|
||||||
|
✔ Successfully published app.open.aragonpm.eth v1.0.0:
|
||||||
|
ℹ Contract address: 0xE636bcA5B95e94F749F63E322a04DB59362299F1
|
||||||
|
ℹ Content (ipfs): QmR695Wu5KrHNec7pRP3kPvwYihABDAyVYdX5D5vwLgxCn
|
||||||
|
ℹ Transaction hash: 0x3d752db29cc106e9ff98b260a90615921eb32471425a29ead8cbb830fb224d8
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: the contract location is defined in `arapp.json` under `path`.
|
||||||
|
Note: you can also deploy a major version with only frontend changes by passing `--only-content`.
|
||||||
|
|
||||||
|
### Minor/patch version: content only
|
||||||
|
|
||||||
|
Command:
|
||||||
|
```
|
||||||
|
npm run publish:patch -- --environment staging
|
||||||
|
```
|
||||||
|
|
||||||
|
This will:
|
||||||
|
1. _build_ the app's frontend (which lives in `dist`)
|
||||||
|
2. publish the app to the **staging** environment.
|
||||||
|
|
||||||
|
Sample output:
|
||||||
|
```
|
||||||
|
✔ Successfully published app.open.aragonpm.eth v1.1.1:
|
||||||
|
ℹ Contract address: 0xE636bcA5B95e94F749F63E322a04DB59362299F1
|
||||||
|
ℹ Content (ipfs): QmUYv9cjyNVxCyAJGK2YXjkbzh6u4iW2ak81Z9obdefM1q
|
||||||
|
ℹ Transaction hash: 0x57864d8efd8d439008621b494b19a3e8f876a8a46b38475f9626802f0a1403c2
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check published versions
|
||||||
|
|
||||||
|
Command:
|
||||||
|
```
|
||||||
|
npm run versions -- --environment staging
|
||||||
|
```
|
||||||
|
|
||||||
|
Sample output:
|
||||||
|
```
|
||||||
|
ℹ app.open.aragonpm.eth has 4 published versions
|
||||||
|
✔ 1.0.0: 0xE636bcA5B95e94F749F63E322a04DB59362299F1 ipfs:QmR695Wu5KrHNec7pRP3kPvwYihABDAyVYdX5D5vwLgxCn
|
||||||
|
✔ 1.1.0: 0xE636bcA5B95e94F749F63E322a04DB59362299F1 ipfs:QmSwjUZFpv2c2e9fLoxtgFrAsAmBN4DyQGJp4RcqQcW3z3
|
||||||
|
✔ 1.1.1: 0xE636bcA5B95e94F749F63E322a04DB59362299F1 ipfs:QmUYv9cjyNVxCyAJGK2YXjkbzh6u4iW2ak81Z9obdefM1q
|
||||||
|
✔ 2.0.0: 0x74CBbbC932d7C344FCd789Eba24BfD40e52980c9 ipfs:Qmadb3hzwLDKtb93fF367Vg1epkdsLZF4dhpapNYynjgZF
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using a different Ethereum account
|
||||||
|
|
||||||
|
To deploy from a different account, you can:
|
||||||
|
- define a `~/.aragon/mnemonic.json` file
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"mnemonic": "explain tackle mirror kit ..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
or
|
||||||
|
- define a `~/.aragon/${network_name}_key.json` file, for example: `~/.aragon/rinkeby_key.json`
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"keys": [
|
||||||
|
"a8a54b2d8197bc0b19bb8a084031be71835580a01e70a45a13babd16c9bc1563"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
28
apps/contributor/arapp.json
Normal file
28
apps/contributor/arapp.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"name": "Manage contributors",
|
||||||
|
"id": "MANAGE_CONTRIBUTORS_ROLE",
|
||||||
|
"params": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"environments": {
|
||||||
|
"default": {
|
||||||
|
"network": "development",
|
||||||
|
"appName": "contributor.aragonpm.eth"
|
||||||
|
},
|
||||||
|
"staging": {
|
||||||
|
"registry": "0x98df287b6c145399aaa709692c8d308357bc085d",
|
||||||
|
"appName": "contributor.open.aragonpm.eth",
|
||||||
|
"wsRPC": "wss://rinkeby.eth.aragon.network/ws",
|
||||||
|
"network": "rinkeby"
|
||||||
|
},
|
||||||
|
"production": {
|
||||||
|
"registry": "0x314159265dd8dbb310642f98f50c066173c1259b",
|
||||||
|
"appName": "contributor.open.aragonpm.eth",
|
||||||
|
"wsRPC": "wss://mainnet.eth.aragon.network/ws",
|
||||||
|
"network": "mainnet"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path": "contracts/Contributor.sol"
|
||||||
|
}
|
@ -1,10 +1,9 @@
|
|||||||
pragma solidity ^0.4.18;
|
pragma solidity ^0.4.24;
|
||||||
|
|
||||||
// import basic ERC20 details to be able to call balanceOf
|
import "@aragon/os/contracts/apps/AragonApp.sol";
|
||||||
import 'zeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol';
|
|
||||||
import './upgradeable/Upgradeable.sol';
|
|
||||||
|
|
||||||
contract Contributors is Upgradeable {
|
contract Contributor is AragonApp {
|
||||||
|
bytes32 public constant MANAGE_CONTRIBUTORS_ROLE = keccak256("MANAGE_CONTRIBUTORS_ROLE");
|
||||||
|
|
||||||
struct Contributor {
|
struct Contributor {
|
||||||
address account;
|
address account;
|
||||||
@ -23,20 +22,16 @@ contract Contributors is Upgradeable {
|
|||||||
event ContributorAccountUpdated(uint id, address oldAccount, address newAccount);
|
event ContributorAccountUpdated(uint id, address oldAccount, address newAccount);
|
||||||
event ContributorAdded(uint id, address account);
|
event ContributorAdded(uint id, address account);
|
||||||
|
|
||||||
modifier onlyCoreOrOperator() {
|
function initialize(address root) public onlyInit {
|
||||||
require(msg.sender == registry.getProxyFor('Operator') || addressIsCore(msg.sender));
|
uint _id = contributorsCount + 1;
|
||||||
_;
|
|
||||||
}
|
|
||||||
|
|
||||||
function initialize(address sender) public payable {
|
|
||||||
require(msg.sender == address(registry));
|
|
||||||
uint _id = 1;
|
|
||||||
Contributor storage c = contributors[_id];
|
Contributor storage c = contributors[_id];
|
||||||
c.exists = true;
|
c.exists = true;
|
||||||
c.isCore = true;
|
c.isCore = true;
|
||||||
c.account = sender;
|
c.account = root;
|
||||||
contributorIds[sender] = _id;
|
contributorIds[root] = _id;
|
||||||
contributorsCount += 1;
|
contributorsCount += 1;
|
||||||
|
|
||||||
|
initialized();
|
||||||
}
|
}
|
||||||
|
|
||||||
function coreContributorsCount() view public returns (uint) {
|
function coreContributorsCount() view public returns (uint) {
|
||||||
@ -49,14 +44,14 @@ contract Contributors is Upgradeable {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateContributorAccount(uint id, address oldAccount, address newAccount) public onlyCoreOrOperator {
|
function updateContributorAccount(uint id, address oldAccount, address newAccount) public auth(MANAGE_CONTRIBUTORS_ROLE) {
|
||||||
contributorIds[oldAccount] = 0;
|
contributorIds[oldAccount] = 0;
|
||||||
contributorIds[newAccount] = id;
|
contributorIds[newAccount] = id;
|
||||||
contributors[id].account = newAccount;
|
contributors[id].account = newAccount;
|
||||||
ContributorAccountUpdated(id, oldAccount, newAccount);
|
ContributorAccountUpdated(id, oldAccount, newAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateContributorIpfsHash(uint id, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize) public onlyCoreOrOperator {
|
function updateContributorIpfsHash(uint id, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize) public isInitialized auth(MANAGE_CONTRIBUTORS_ROLE) {
|
||||||
Contributor storage c = contributors[id];
|
Contributor storage c = contributors[id];
|
||||||
bytes32 oldIpfsHash = c.ipfsHash;
|
bytes32 oldIpfsHash = c.ipfsHash;
|
||||||
c.ipfsHash = ipfsHash;
|
c.ipfsHash = ipfsHash;
|
||||||
@ -66,7 +61,7 @@ contract Contributors is Upgradeable {
|
|||||||
ContributorProfileUpdated(id, oldIpfsHash, c.ipfsHash);
|
ContributorProfileUpdated(id, oldIpfsHash, c.ipfsHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
function addContributor(address account, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize, bool isCore) public onlyCoreOrOperator {
|
function addContributor(address account, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize, bool isCore) public isInitialized auth(MANAGE_CONTRIBUTORS_ROLE) {
|
||||||
require(!addressExists(account));
|
require(!addressExists(account));
|
||||||
uint _id = contributorsCount + 1;
|
uint _id = contributorsCount + 1;
|
||||||
assert(!contributors[_id].exists); // this can not be acually
|
assert(!contributors[_id].exists); // this can not be acually
|
||||||
@ -80,7 +75,7 @@ contract Contributors is Upgradeable {
|
|||||||
contributorIds[account] = _id;
|
contributorIds[account] = _id;
|
||||||
|
|
||||||
contributorsCount += 1;
|
contributorsCount += 1;
|
||||||
ContributorAdded(_id, account);
|
emit ContributorAdded(_id, account);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isCore(uint id) view public returns (bool) {
|
function isCore(uint id) view public returns (bool) {
|
||||||
@ -112,7 +107,7 @@ contract Contributors is Upgradeable {
|
|||||||
return contributors[id];
|
return contributors[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getContributorById(uint _id) public view returns (uint id, address account, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize, bool isCore, uint balance, bool exists ) {
|
function getContributorById(uint _id) public view returns (uint id, address account, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize, bool isCore, bool exists ) {
|
||||||
id = _id;
|
id = _id;
|
||||||
Contributor storage c = contributors[_id];
|
Contributor storage c = contributors[_id];
|
||||||
account = c.account;
|
account = c.account;
|
||||||
@ -121,8 +116,9 @@ contract Contributors is Upgradeable {
|
|||||||
hashSize = c.hashSize;
|
hashSize = c.hashSize;
|
||||||
isCore = c.isCore;
|
isCore = c.isCore;
|
||||||
exists = c.exists;
|
exists = c.exists;
|
||||||
|
}
|
||||||
|
|
||||||
ERC20Basic token = ERC20Basic(registry.getProxyFor('Token'));
|
function canPerform(address _who, address _where, bytes32 _what, uint256[] _how) public view returns (bool) {
|
||||||
balance = token.balanceOf(account);
|
return addressExists(_who);
|
||||||
}
|
}
|
||||||
}
|
}
|
23
apps/contributor/contracts/misc/Migrations.sol
Normal file
23
apps/contributor/contracts/misc/Migrations.sol
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
pragma solidity ^0.4.4;
|
||||||
|
|
||||||
|
contract Migrations {
|
||||||
|
address public owner;
|
||||||
|
uint public last_completed_migration;
|
||||||
|
|
||||||
|
modifier restricted() {
|
||||||
|
if (msg.sender == owner) _;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Migrations() {
|
||||||
|
owner = msg.sender;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCompleted(uint completed) restricted {
|
||||||
|
last_completed_migration = completed;
|
||||||
|
}
|
||||||
|
|
||||||
|
function upgrade(address new_address) restricted {
|
||||||
|
Migrations upgraded = Migrations(new_address);
|
||||||
|
upgraded.setCompleted(last_completed_migration);
|
||||||
|
}
|
||||||
|
}
|
4
apps/contributor/manifest.json
Normal file
4
apps/contributor/manifest.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "Contributor",
|
||||||
|
"description": "Kredits Contributor app"
|
||||||
|
}
|
5
apps/contributor/migrations/1_initial_migration.js
Normal file
5
apps/contributor/migrations/1_initial_migration.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
var Migrations = artifacts.require('./Migrations.sol')
|
||||||
|
|
||||||
|
module.exports = function (deployer) {
|
||||||
|
deployer.deploy(Migrations)
|
||||||
|
}
|
5
apps/contributor/migrations/2_deploy_contracts.js
Normal file
5
apps/contributor/migrations/2_deploy_contracts.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
var Contributor = artifacts.require('Contributor.sol')
|
||||||
|
|
||||||
|
module.exports = function (deployer) {
|
||||||
|
deployer.deploy(Contributor)
|
||||||
|
}
|
13527
apps/contributor/package-lock.json
generated
Normal file
13527
apps/contributor/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
27
apps/contributor/package.json
Normal file
27
apps/contributor/package.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "kredits-contributor",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"dependencies": {
|
||||||
|
"@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": "",
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"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')
|
||||||
|
})
|
63
apps/contributor/truffle.js
Normal file
63
apps/contributor/truffle.js
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* https://github.com/aragon/aragonOS/blob/v4.0.0/truffle-config.js
|
||||||
|
*/
|
||||||
|
const homedir = require('homedir')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
const HDWalletProvider = require('truffle-hdwallet-provider')
|
||||||
|
const HDWalletProviderPrivkey = require('truffle-hdwallet-provider-privkey')
|
||||||
|
|
||||||
|
const DEFAULT_MNEMONIC = 'explain tackle mirror kit van hammer degree position ginger unfair soup bonus'
|
||||||
|
|
||||||
|
const defaultRPC = (network) =>
|
||||||
|
`https://${network}.infura.io`
|
||||||
|
|
||||||
|
const configFilePath = (filename) =>
|
||||||
|
path.join(homedir(), `.aragon/${filename}`)
|
||||||
|
|
||||||
|
const mnemonic = () => {
|
||||||
|
try {
|
||||||
|
return require(configFilePath('mnemonic.json')).mnemonic
|
||||||
|
} catch (e) {
|
||||||
|
return DEFAULT_MNEMONIC
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const settingsForNetwork = (network) => {
|
||||||
|
try {
|
||||||
|
return require(configFilePath(`${network}_key.json`))
|
||||||
|
} catch (e) {
|
||||||
|
return { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lazily loaded provider
|
||||||
|
const providerForNetwork = (network) => (
|
||||||
|
() => {
|
||||||
|
let { rpc, keys } = settingsForNetwork(network)
|
||||||
|
rpc = rpc || defaultRPC(network)
|
||||||
|
|
||||||
|
if (!keys || keys.length == 0) {
|
||||||
|
return new HDWalletProvider(mnemonic(), rpc)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HDWalletProviderPrivkey(keys, rpc)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
module.exports = {
|
||||||
|
networks: {
|
||||||
|
development: {
|
||||||
|
host: 'localhost',
|
||||||
|
port: 8545,
|
||||||
|
network_id: '*'
|
||||||
|
},
|
||||||
|
mainnet: {
|
||||||
|
network_id: 1,
|
||||||
|
provider: providerForNetwork('mainnet')
|
||||||
|
},
|
||||||
|
rinkeby: {
|
||||||
|
network_id: 4,
|
||||||
|
provider: providerForNetwork('rinkeby')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
162
apps/proposal/README.md
Normal file
162
apps/proposal/README.md
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
# Aragon React Boilerplate
|
||||||
|
|
||||||
|
> 🕵️ [Find more boilerplates using GitHub](https://github.com/search?q=topic:aragon-boilerplate) |
|
||||||
|
> ✨ [Official boilerplates](https://github.com/search?q=topic:aragon-boilerplate+org:aragon)
|
||||||
|
|
||||||
|
React boilerplate for Aragon applications.
|
||||||
|
|
||||||
|
This boilerplate also includes a fully working example app, complete with a background worker and a front-end in React (with Aragon UI).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```sh
|
||||||
|
aragon init app.aragonpm.eth react
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running your app
|
||||||
|
|
||||||
|
### Using HTTP
|
||||||
|
|
||||||
|
Running your app using HTTP will allow for a faster development process of your app's front-end, as it can be hot-reloaded without the need to execute `aragon run` every time a change is made.
|
||||||
|
|
||||||
|
- First start your app's development server running `npm run start:app`, and keep that process running. By default it will rebuild the app and reload the server when changes to the source are made.
|
||||||
|
|
||||||
|
- After that, you can run `npm run start:aragon:http` which will compile your app's contracts, publish the app locally and create a DAO. You will need to stop it and run it again after making changes to your smart contracts.
|
||||||
|
|
||||||
|
Changes to the app's background script (`app/script.js`) cannot be hot-reloaded, after making changes to the script, you will need to either restart the development server (`npm run start:app`) or rebuild the script `npm run build:script`.
|
||||||
|
|
||||||
|
### Using IPFS
|
||||||
|
|
||||||
|
Running your app using IPFS will mimic the production environment that will be used for running your app. `npm run start:aragon:ipfs` will run your app using IPFS. Whenever a change is made to any file in your front-end, a new version of the app needs to be published, so the command needs to be restarted.
|
||||||
|
|
||||||
|
## What's in the box?
|
||||||
|
|
||||||
|
### npm Scripts
|
||||||
|
|
||||||
|
- **start** or **start:aragon:ipfs**: Runs your app inside a DAO served from IPFS
|
||||||
|
- **start:aragon:http**: Runs your app inside a DAO served with HTTP (hot reloading)
|
||||||
|
- **start:app**: Starts a development server for your app
|
||||||
|
- **compile**: Compile the smart contracts
|
||||||
|
- **build**: Builds the front-end and background script
|
||||||
|
- **build:app**: Builds the front-end
|
||||||
|
- **build:script**: Builds the background script
|
||||||
|
- **test**: Runs tests for the contracts
|
||||||
|
- **publish:patch**: Release a patch version to aragonPM (only frontend/content changes allowed)
|
||||||
|
- **publish:minor**: Release a minor version to aragonPM (only frontend/content changes allowed)
|
||||||
|
- **publish:major**: Release a major version to aragonPM (frontend **and** contract changes)
|
||||||
|
- **versions**: Check the currently installed versions of the app
|
||||||
|
|
||||||
|
### Libraries
|
||||||
|
|
||||||
|
- [**@aragon/os**](https://github.com/aragon/aragonos): Aragon interfaces
|
||||||
|
- [**@aragon/client**](https://github.com/aragon/aragon.js/tree/master/packages/aragon-client): Wrapper for Aragon application RPC
|
||||||
|
- [**@aragon/ui**](https://github.com/aragon/aragon-ui): Aragon UI components (in React)
|
||||||
|
|
||||||
|
## Publish
|
||||||
|
|
||||||
|
This app has 3 environments defined in `arapp.json`:
|
||||||
|
|
||||||
|
| Environment | Network |
|
||||||
|
|--- |--- |
|
||||||
|
| default | localhost |
|
||||||
|
| staging | rinkeby |
|
||||||
|
| production | mainnet |
|
||||||
|
|
||||||
|
Prerequisites:
|
||||||
|
- ENS Registry address
|
||||||
|
|
||||||
|
Note: the `default` environment which points to `localhost` does not have an ENS Registry address specified because the `@aragon/cli` will default the value to `0xB9462EF3441346dBc6E49236Edbb0dF207db09B7` (the ENS Registry pre-deployed on the local development chain).
|
||||||
|
|
||||||
|
### Introduction to environments
|
||||||
|
|
||||||
|
Environments are defined in `arapp.json`, for example `staging` points to:
|
||||||
|
- an ENS registry (`0x314159265dd8dbb310642f98f50c066173c1259b`)
|
||||||
|
- an APM registry (`open.aragonpm.eth`)
|
||||||
|
- an APM repository (`app`)
|
||||||
|
- an Ethereum network (`rinkeby`)
|
||||||
|
- an Ethereum websockets provider (`wss://rinkeby.eth.aragon.network/ws` - to **read** from the blockchain)
|
||||||
|
|
||||||
|
The `rinkeby` network is further defined in `truffle.js`, and has:
|
||||||
|
- an Ethereum provider (to **write** to the blockchain):
|
||||||
|
- an address (`https://rinkeby.infura.io`)
|
||||||
|
- an Ethereum Account (`0xb4124cEB3451635DAcedd11767f004d8a28c6eE7`)
|
||||||
|
(which is the first account generated from the `DEFAULT_MNEMONIC` variable, to use a different account see [here](#Using-a-different-Ethereum-account))
|
||||||
|
|
||||||
|
### Major version: content + contract
|
||||||
|
|
||||||
|
Command:
|
||||||
|
```
|
||||||
|
npm run publish:major -- --environment staging
|
||||||
|
```
|
||||||
|
|
||||||
|
This will:
|
||||||
|
1. _build_ the app's frontend (the output lives in `dist`)
|
||||||
|
2. _compile_ the app's contract (the output lives in `build`)
|
||||||
|
3. publish the app to the **staging** environment.
|
||||||
|
|
||||||
|
Sample output:
|
||||||
|
```
|
||||||
|
> aragon apm publish major "--environment" "staging"
|
||||||
|
|
||||||
|
✔ Successfully published app.open.aragonpm.eth v1.0.0:
|
||||||
|
ℹ Contract address: 0xE636bcA5B95e94F749F63E322a04DB59362299F1
|
||||||
|
ℹ Content (ipfs): QmR695Wu5KrHNec7pRP3kPvwYihABDAyVYdX5D5vwLgxCn
|
||||||
|
ℹ Transaction hash: 0x3d752db29cc106e9ff98b260a90615921eb32471425a29ead8cbb830fb224d8
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: the contract location is defined in `arapp.json` under `path`.
|
||||||
|
Note: you can also deploy a major version with only frontend changes by passing `--only-content`.
|
||||||
|
|
||||||
|
### Minor/patch version: content only
|
||||||
|
|
||||||
|
Command:
|
||||||
|
```
|
||||||
|
npm run publish:patch -- --environment staging
|
||||||
|
```
|
||||||
|
|
||||||
|
This will:
|
||||||
|
1. _build_ the app's frontend (which lives in `dist`)
|
||||||
|
2. publish the app to the **staging** environment.
|
||||||
|
|
||||||
|
Sample output:
|
||||||
|
```
|
||||||
|
✔ Successfully published app.open.aragonpm.eth v1.1.1:
|
||||||
|
ℹ Contract address: 0xE636bcA5B95e94F749F63E322a04DB59362299F1
|
||||||
|
ℹ Content (ipfs): QmUYv9cjyNVxCyAJGK2YXjkbzh6u4iW2ak81Z9obdefM1q
|
||||||
|
ℹ Transaction hash: 0x57864d8efd8d439008621b494b19a3e8f876a8a46b38475f9626802f0a1403c2
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check published versions
|
||||||
|
|
||||||
|
Command:
|
||||||
|
```
|
||||||
|
npm run versions -- --environment staging
|
||||||
|
```
|
||||||
|
|
||||||
|
Sample output:
|
||||||
|
```
|
||||||
|
ℹ app.open.aragonpm.eth has 4 published versions
|
||||||
|
✔ 1.0.0: 0xE636bcA5B95e94F749F63E322a04DB59362299F1 ipfs:QmR695Wu5KrHNec7pRP3kPvwYihABDAyVYdX5D5vwLgxCn
|
||||||
|
✔ 1.1.0: 0xE636bcA5B95e94F749F63E322a04DB59362299F1 ipfs:QmSwjUZFpv2c2e9fLoxtgFrAsAmBN4DyQGJp4RcqQcW3z3
|
||||||
|
✔ 1.1.1: 0xE636bcA5B95e94F749F63E322a04DB59362299F1 ipfs:QmUYv9cjyNVxCyAJGK2YXjkbzh6u4iW2ak81Z9obdefM1q
|
||||||
|
✔ 2.0.0: 0x74CBbbC932d7C344FCd789Eba24BfD40e52980c9 ipfs:Qmadb3hzwLDKtb93fF367Vg1epkdsLZF4dhpapNYynjgZF
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using a different Ethereum account
|
||||||
|
|
||||||
|
To deploy from a different account, you can:
|
||||||
|
- define a `~/.aragon/mnemonic.json` file
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"mnemonic": "explain tackle mirror kit ..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
or
|
||||||
|
- define a `~/.aragon/${network_name}_key.json` file, for example: `~/.aragon/rinkeby_key.json`
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"keys": [
|
||||||
|
"a8a54b2d8197bc0b19bb8a084031be71835580a01e70a45a13babd16c9bc1563"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
43
apps/proposal/arapp.json
Normal file
43
apps/proposal/arapp.json
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"name": "Add proposal",
|
||||||
|
"id": "ADD_PROPOSAL_ROLE",
|
||||||
|
"params": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Vote proposals",
|
||||||
|
"id": "VOTE_PROPOSAL_ROLE",
|
||||||
|
"params": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Add contribution",
|
||||||
|
"id": "ADD_CONTRIBUTION_ROLE",
|
||||||
|
"params": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Manage contributors",
|
||||||
|
"id": "MANAGE_CONTRIBUTORS_ROLE",
|
||||||
|
"params": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"environments": {
|
||||||
|
"default": {
|
||||||
|
"network": "development",
|
||||||
|
"appName": "proposal.aragonpm.eth"
|
||||||
|
},
|
||||||
|
"staging": {
|
||||||
|
"registry": "0x98df287b6c145399aaa709692c8d308357bc085d",
|
||||||
|
"appName": "proposal.open.aragonpm.eth",
|
||||||
|
"wsRPC": "wss://rinkeby.eth.aragon.network/ws",
|
||||||
|
"network": "rinkeby"
|
||||||
|
},
|
||||||
|
"production": {
|
||||||
|
"registry": "0x314159265dd8dbb310642f98f50c066173c1259b",
|
||||||
|
"appName": "proposal.open.aragonpm.eth",
|
||||||
|
"wsRPC": "wss://mainnet.eth.aragon.network/ws",
|
||||||
|
"network": "mainnet"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path": "contracts/Proposal.sol"
|
||||||
|
}
|
132
apps/proposal/contracts/Proposal.sol
Normal file
132
apps/proposal/contracts/Proposal.sol
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
pragma solidity ^0.4.24;
|
||||||
|
|
||||||
|
import "@aragon/os/contracts/apps/AragonApp.sol";
|
||||||
|
import "@aragon/os/contracts/kernel/IKernel.sol";
|
||||||
|
|
||||||
|
interface IContributor {
|
||||||
|
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(uint256 amount, address contributor, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public;
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Proposal is AragonApp {
|
||||||
|
|
||||||
|
bytes32 public constant ADD_PROPOSAL_ROLE = keccak256("ADD_PROPOSAL_ROLE");
|
||||||
|
bytes32 public constant VOTE_PROPOSAL_ROLE = keccak256("VOTE_PROPOSAL_ROLE");
|
||||||
|
|
||||||
|
bytes32 public constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb;
|
||||||
|
bytes32 public constant CONTRIBUTOR_APP_ID = 0xe9140f1e39c8a1d04167c3b710688a3eecea2976f34735c8eb98956f4764635b;
|
||||||
|
bytes32 public constant CONTRIBUTION_APP_ID = 0x7fcf91283b719b30c2fa954ff0da021e1b91aed09d7aa13df5e8078a4a1007eb;
|
||||||
|
|
||||||
|
struct Proposal {
|
||||||
|
address creatorAccount;
|
||||||
|
uint contributorId;
|
||||||
|
uint votesCount;
|
||||||
|
uint votesNeeded;
|
||||||
|
uint256 amount;
|
||||||
|
bool executed;
|
||||||
|
bytes32 hashDigest;
|
||||||
|
uint8 hashFunction;
|
||||||
|
uint8 hashSize;
|
||||||
|
uint256[] voterIds;
|
||||||
|
mapping (uint256 => bool) votes;
|
||||||
|
bool exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
mapping(uint256 => Proposal) public proposals;
|
||||||
|
uint256 public proposalsCount;
|
||||||
|
|
||||||
|
event ProposalCreated(uint256 id, address creatorAccount, uint256 contributorId, uint256 amount);
|
||||||
|
|
||||||
|
event ProposalVoted(uint256 id, uint256 voterId, uint256 totalVotes);
|
||||||
|
event ProposalExecuted(uint256 id, uint256 contributorId, uint256 amount);
|
||||||
|
|
||||||
|
function initialize() public onlyInit {
|
||||||
|
initialized();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContributorContract() public view returns (address) {
|
||||||
|
return IKernel(kernel()).getApp(KERNEL_APP_ADDR_NAMESPACE, CONTRIBUTOR_APP_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContributionContract() public view returns (address) {
|
||||||
|
return IKernel(kernel()).getApp(KERNEL_APP_ADDR_NAMESPACE, CONTRIBUTION_APP_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
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.hashFunction = hashFunction;
|
||||||
|
p.hashSize = hashSize;
|
||||||
|
p.votesCount = 0;
|
||||||
|
p.votesNeeded = _votesNeeded;
|
||||||
|
p.exists = true;
|
||||||
|
|
||||||
|
proposalsCount++;
|
||||||
|
emit ProposalCreated(proposalId, msg.sender, p.contributorId, p.amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProposal(uint proposalId) public view returns (uint256 id, address creatorAccount, uint256 contributorId, uint256 votesCount, uint256 votesNeeded, uint256 amount, bool executed, bytes32 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.votesNeeded,
|
||||||
|
p.amount,
|
||||||
|
p.executed,
|
||||||
|
p.hashDigest,
|
||||||
|
p.hashFunction,
|
||||||
|
p.hashSize,
|
||||||
|
p.voterIds,
|
||||||
|
p.exists
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function vote(uint256 proposalId) public isInitialized auth(VOTE_PROPOSAL_ROLE) {
|
||||||
|
Proposal storage p = proposals[proposalId];
|
||||||
|
require(!p.executed, 'ALREADY_EXECUTED');
|
||||||
|
uint256 voterId = IContributor(getContributorContract()).getContributorIdByAddress(msg.sender);
|
||||||
|
require(p.votes[voterId] != true, 'ALREADY_VOTED');
|
||||||
|
p.voterIds.push(voterId);
|
||||||
|
p.votes[voterId] = true;
|
||||||
|
|
||||||
|
p.votesCount++;
|
||||||
|
if (p.votesCount >= p.votesNeeded) {
|
||||||
|
executeProposal(proposalId);
|
||||||
|
}
|
||||||
|
emit ProposalVoted(proposalId, voterId, p.votesCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function batchVote(uint256[] _proposalIds) public isInitialized auth(VOTE_PROPOSAL_ROLE) {
|
||||||
|
for (uint256 i = 0; i < _proposalIds.length; i++) {
|
||||||
|
vote(_proposalIds[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function executeProposal(uint proposalId) private {
|
||||||
|
Proposal storage p = proposals[proposalId];
|
||||||
|
require(!p.executed, 'ALREADY_EXECUTED');
|
||||||
|
require(p.votesCount >= p.votesNeeded, 'MISSING_VOTES');
|
||||||
|
|
||||||
|
address contributorAccount = IContributor(getContributorContract()).getContributorAddressById(p.contributorId);
|
||||||
|
IContribution(getContributionContract()).add(p.amount, contributorAccount, p.hashDigest, p.hashFunction, p.hashSize);
|
||||||
|
p.executed = true;
|
||||||
|
emit ProposalExecuted(proposalId, p.contributorId, p.amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
4
apps/proposal/manifest.json
Normal file
4
apps/proposal/manifest.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "Proposal",
|
||||||
|
"description": "Kredits proposal app"
|
||||||
|
}
|
5
apps/proposal/migrations/1_initial_migration.js
Normal file
5
apps/proposal/migrations/1_initial_migration.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
var Migrations = artifacts.require('./Migrations.sol')
|
||||||
|
|
||||||
|
module.exports = function (deployer) {
|
||||||
|
deployer.deploy(Migrations)
|
||||||
|
}
|
21093
apps/proposal/package-lock.json
generated
Normal file
21093
apps/proposal/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
28
apps/proposal/package.json
Normal file
28
apps/proposal/package.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": "kredits-proposal",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"dependencies": {
|
||||||
|
"@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": "",
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"keywords": []
|
||||||
|
}
|
63
apps/proposal/truffle.js
Normal file
63
apps/proposal/truffle.js
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* https://github.com/aragon/aragonOS/blob/v4.0.0/truffle-config.js
|
||||||
|
*/
|
||||||
|
const homedir = require('homedir')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
const HDWalletProvider = require('truffle-hdwallet-provider')
|
||||||
|
const HDWalletProviderPrivkey = require('truffle-hdwallet-provider-privkey')
|
||||||
|
|
||||||
|
const DEFAULT_MNEMONIC = 'explain tackle mirror kit van hammer degree position ginger unfair soup bonus'
|
||||||
|
|
||||||
|
const defaultRPC = (network) =>
|
||||||
|
`https://${network}.infura.io`
|
||||||
|
|
||||||
|
const configFilePath = (filename) =>
|
||||||
|
path.join(homedir(), `.aragon/${filename}`)
|
||||||
|
|
||||||
|
const mnemonic = () => {
|
||||||
|
try {
|
||||||
|
return require(configFilePath('mnemonic.json')).mnemonic
|
||||||
|
} catch (e) {
|
||||||
|
return DEFAULT_MNEMONIC
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const settingsForNetwork = (network) => {
|
||||||
|
try {
|
||||||
|
return require(configFilePath(`${network}_key.json`))
|
||||||
|
} catch (e) {
|
||||||
|
return { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lazily loaded provider
|
||||||
|
const providerForNetwork = (network) => (
|
||||||
|
() => {
|
||||||
|
let { rpc, keys } = settingsForNetwork(network)
|
||||||
|
rpc = rpc || defaultRPC(network)
|
||||||
|
|
||||||
|
if (!keys || keys.length == 0) {
|
||||||
|
return new HDWalletProvider(mnemonic(), rpc)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HDWalletProviderPrivkey(keys, rpc)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
module.exports = {
|
||||||
|
networks: {
|
||||||
|
development: {
|
||||||
|
host: 'localhost',
|
||||||
|
port: 8545,
|
||||||
|
network_id: '*'
|
||||||
|
},
|
||||||
|
mainnet: {
|
||||||
|
network_id: 1,
|
||||||
|
provider: providerForNetwork('mainnet')
|
||||||
|
},
|
||||||
|
rinkeby: {
|
||||||
|
network_id: 4,
|
||||||
|
provider: providerForNetwork('rinkeby')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
apps/token/.gitignore
vendored
Normal file
4
apps/token/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
node_modules
|
||||||
|
build
|
||||||
|
.cache
|
||||||
|
dist
|
14
apps/token/.ipfsignore
Normal file
14
apps/token/.ipfsignore
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Git files
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# Build files
|
||||||
|
.cache
|
||||||
|
node_modules
|
||||||
|
build
|
||||||
|
|
||||||
|
# Lock files
|
||||||
|
package-lock.json
|
||||||
|
yarn.lock
|
||||||
|
|
||||||
|
# Others
|
||||||
|
test
|
162
apps/token/README.md
Normal file
162
apps/token/README.md
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
# Aragon React Boilerplate
|
||||||
|
|
||||||
|
> 🕵️ [Find more boilerplates using GitHub](https://github.com/search?q=topic:aragon-boilerplate) |
|
||||||
|
> ✨ [Official boilerplates](https://github.com/search?q=topic:aragon-boilerplate+org:aragon)
|
||||||
|
|
||||||
|
React boilerplate for Aragon applications.
|
||||||
|
|
||||||
|
This boilerplate also includes a fully working example app, complete with a background worker and a front-end in React (with Aragon UI).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```sh
|
||||||
|
aragon init app.aragonpm.eth react
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running your app
|
||||||
|
|
||||||
|
### Using HTTP
|
||||||
|
|
||||||
|
Running your app using HTTP will allow for a faster development process of your app's front-end, as it can be hot-reloaded without the need to execute `aragon run` every time a change is made.
|
||||||
|
|
||||||
|
- First start your app's development server running `npm run start:app`, and keep that process running. By default it will rebuild the app and reload the server when changes to the source are made.
|
||||||
|
|
||||||
|
- After that, you can run `npm run start:aragon:http` which will compile your app's contracts, publish the app locally and create a DAO. You will need to stop it and run it again after making changes to your smart contracts.
|
||||||
|
|
||||||
|
Changes to the app's background script (`app/script.js`) cannot be hot-reloaded, after making changes to the script, you will need to either restart the development server (`npm run start:app`) or rebuild the script `npm run build:script`.
|
||||||
|
|
||||||
|
### Using IPFS
|
||||||
|
|
||||||
|
Running your app using IPFS will mimic the production environment that will be used for running your app. `npm run start:aragon:ipfs` will run your app using IPFS. Whenever a change is made to any file in your front-end, a new version of the app needs to be published, so the command needs to be restarted.
|
||||||
|
|
||||||
|
## What's in the box?
|
||||||
|
|
||||||
|
### npm Scripts
|
||||||
|
|
||||||
|
- **start** or **start:aragon:ipfs**: Runs your app inside a DAO served from IPFS
|
||||||
|
- **start:aragon:http**: Runs your app inside a DAO served with HTTP (hot reloading)
|
||||||
|
- **start:app**: Starts a development server for your app
|
||||||
|
- **compile**: Compile the smart contracts
|
||||||
|
- **build**: Builds the front-end and background script
|
||||||
|
- **build:app**: Builds the front-end
|
||||||
|
- **build:script**: Builds the background script
|
||||||
|
- **test**: Runs tests for the contracts
|
||||||
|
- **publish:patch**: Release a patch version to aragonPM (only frontend/content changes allowed)
|
||||||
|
- **publish:minor**: Release a minor version to aragonPM (only frontend/content changes allowed)
|
||||||
|
- **publish:major**: Release a major version to aragonPM (frontend **and** contract changes)
|
||||||
|
- **versions**: Check the currently installed versions of the app
|
||||||
|
|
||||||
|
### Libraries
|
||||||
|
|
||||||
|
- [**@aragon/os**](https://github.com/aragon/aragonos): Aragon interfaces
|
||||||
|
- [**@aragon/client**](https://github.com/aragon/aragon.js/tree/master/packages/aragon-client): Wrapper for Aragon application RPC
|
||||||
|
- [**@aragon/ui**](https://github.com/aragon/aragon-ui): Aragon UI components (in React)
|
||||||
|
|
||||||
|
## Publish
|
||||||
|
|
||||||
|
This app has 3 environments defined in `arapp.json`:
|
||||||
|
|
||||||
|
| Environment | Network |
|
||||||
|
|--- |--- |
|
||||||
|
| default | localhost |
|
||||||
|
| staging | rinkeby |
|
||||||
|
| production | mainnet |
|
||||||
|
|
||||||
|
Prerequisites:
|
||||||
|
- ENS Registry address
|
||||||
|
|
||||||
|
Note: the `default` environment which points to `localhost` does not have an ENS Registry address specified because the `@aragon/cli` will default the value to `0xB9462EF3441346dBc6E49236Edbb0dF207db09B7` (the ENS Registry pre-deployed on the local development chain).
|
||||||
|
|
||||||
|
### Introduction to environments
|
||||||
|
|
||||||
|
Environments are defined in `arapp.json`, for example `staging` points to:
|
||||||
|
- an ENS registry (`0x314159265dd8dbb310642f98f50c066173c1259b`)
|
||||||
|
- an APM registry (`open.aragonpm.eth`)
|
||||||
|
- an APM repository (`app`)
|
||||||
|
- an Ethereum network (`rinkeby`)
|
||||||
|
- an Ethereum websockets provider (`wss://rinkeby.eth.aragon.network/ws` - to **read** from the blockchain)
|
||||||
|
|
||||||
|
The `rinkeby` network is further defined in `truffle.js`, and has:
|
||||||
|
- an Ethereum provider (to **write** to the blockchain):
|
||||||
|
- an address (`https://rinkeby.infura.io`)
|
||||||
|
- an Ethereum Account (`0xb4124cEB3451635DAcedd11767f004d8a28c6eE7`)
|
||||||
|
(which is the first account generated from the `DEFAULT_MNEMONIC` variable, to use a different account see [here](#Using-a-different-Ethereum-account))
|
||||||
|
|
||||||
|
### Major version: content + contract
|
||||||
|
|
||||||
|
Command:
|
||||||
|
```
|
||||||
|
npm run publish:major -- --environment staging
|
||||||
|
```
|
||||||
|
|
||||||
|
This will:
|
||||||
|
1. _build_ the app's frontend (the output lives in `dist`)
|
||||||
|
2. _compile_ the app's contract (the output lives in `build`)
|
||||||
|
3. publish the app to the **staging** environment.
|
||||||
|
|
||||||
|
Sample output:
|
||||||
|
```
|
||||||
|
> aragon apm publish major "--environment" "staging"
|
||||||
|
|
||||||
|
✔ Successfully published app.open.aragonpm.eth v1.0.0:
|
||||||
|
ℹ Contract address: 0xE636bcA5B95e94F749F63E322a04DB59362299F1
|
||||||
|
ℹ Content (ipfs): QmR695Wu5KrHNec7pRP3kPvwYihABDAyVYdX5D5vwLgxCn
|
||||||
|
ℹ Transaction hash: 0x3d752db29cc106e9ff98b260a90615921eb32471425a29ead8cbb830fb224d8
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: the contract location is defined in `arapp.json` under `path`.
|
||||||
|
Note: you can also deploy a major version with only frontend changes by passing `--only-content`.
|
||||||
|
|
||||||
|
### Minor/patch version: content only
|
||||||
|
|
||||||
|
Command:
|
||||||
|
```
|
||||||
|
npm run publish:patch -- --environment staging
|
||||||
|
```
|
||||||
|
|
||||||
|
This will:
|
||||||
|
1. _build_ the app's frontend (which lives in `dist`)
|
||||||
|
2. publish the app to the **staging** environment.
|
||||||
|
|
||||||
|
Sample output:
|
||||||
|
```
|
||||||
|
✔ Successfully published app.open.aragonpm.eth v1.1.1:
|
||||||
|
ℹ Contract address: 0xE636bcA5B95e94F749F63E322a04DB59362299F1
|
||||||
|
ℹ Content (ipfs): QmUYv9cjyNVxCyAJGK2YXjkbzh6u4iW2ak81Z9obdefM1q
|
||||||
|
ℹ Transaction hash: 0x57864d8efd8d439008621b494b19a3e8f876a8a46b38475f9626802f0a1403c2
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check published versions
|
||||||
|
|
||||||
|
Command:
|
||||||
|
```
|
||||||
|
npm run versions -- --environment staging
|
||||||
|
```
|
||||||
|
|
||||||
|
Sample output:
|
||||||
|
```
|
||||||
|
ℹ app.open.aragonpm.eth has 4 published versions
|
||||||
|
✔ 1.0.0: 0xE636bcA5B95e94F749F63E322a04DB59362299F1 ipfs:QmR695Wu5KrHNec7pRP3kPvwYihABDAyVYdX5D5vwLgxCn
|
||||||
|
✔ 1.1.0: 0xE636bcA5B95e94F749F63E322a04DB59362299F1 ipfs:QmSwjUZFpv2c2e9fLoxtgFrAsAmBN4DyQGJp4RcqQcW3z3
|
||||||
|
✔ 1.1.1: 0xE636bcA5B95e94F749F63E322a04DB59362299F1 ipfs:QmUYv9cjyNVxCyAJGK2YXjkbzh6u4iW2ak81Z9obdefM1q
|
||||||
|
✔ 2.0.0: 0x74CBbbC932d7C344FCd789Eba24BfD40e52980c9 ipfs:Qmadb3hzwLDKtb93fF367Vg1epkdsLZF4dhpapNYynjgZF
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using a different Ethereum account
|
||||||
|
|
||||||
|
To deploy from a different account, you can:
|
||||||
|
- define a `~/.aragon/mnemonic.json` file
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"mnemonic": "explain tackle mirror kit ..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
or
|
||||||
|
- define a `~/.aragon/${network_name}_key.json` file, for example: `~/.aragon/rinkeby_key.json`
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"keys": [
|
||||||
|
"a8a54b2d8197bc0b19bb8a084031be71835580a01e70a45a13babd16c9bc1563"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
28
apps/token/arapp.json
Normal file
28
apps/token/arapp.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"name": "Mint token",
|
||||||
|
"id": "MINT_TOKEN_ROLE",
|
||||||
|
"params": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"environments": {
|
||||||
|
"default": {
|
||||||
|
"network": "development",
|
||||||
|
"appName": "token.aragonpm.eth"
|
||||||
|
},
|
||||||
|
"staging": {
|
||||||
|
"registry": "0x98df287b6c145399aaa709692c8d308357bc085d",
|
||||||
|
"appName": "token.open.aragonpm.eth",
|
||||||
|
"wsRPC": "wss://rinkeby.eth.aragon.network/ws",
|
||||||
|
"network": "rinkeby"
|
||||||
|
},
|
||||||
|
"production": {
|
||||||
|
"registry": "0x314159265dd8dbb310642f98f50c066173c1259b",
|
||||||
|
"appName": "token.open.aragonpm.eth",
|
||||||
|
"wsRPC": "wss://mainnet.eth.aragon.network/ws",
|
||||||
|
"network": "mainnet"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path": "contracts/Token.sol"
|
||||||
|
}
|
173
apps/token/contracts/ERC20Token.sol
Normal file
173
apps/token/contracts/ERC20Token.sol
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
pragma solidity ^0.4.24;
|
||||||
|
|
||||||
|
import "@aragon/os/contracts/lib/math/SafeMath.sol";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* beause ERC20.sol conflicts with the aragon ERC20.sol this is copied and modified from:
|
||||||
|
* https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/token/ERC20/ERC20.sol
|
||||||
|
* @title Standard ERC20 token
|
||||||
|
*
|
||||||
|
* @dev Implementation of the basic standard token.
|
||||||
|
* https://eips.ethereum.org/EIPS/eip-20
|
||||||
|
* Originally based on code by FirstBlood:
|
||||||
|
* https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
|
||||||
|
*
|
||||||
|
* This implementation emits additional Approval events, allowing applications to reconstruct the allowance status for
|
||||||
|
* all accounts just by listening to said events. Note that this isn't required by the specification, and other
|
||||||
|
* compliant implementations may not do it.
|
||||||
|
*/
|
||||||
|
contract ERC20Token {
|
||||||
|
using SafeMath for uint256;
|
||||||
|
|
||||||
|
mapping (address => uint256) public _balances;
|
||||||
|
|
||||||
|
mapping (address => mapping (address => uint256)) private _allowed;
|
||||||
|
|
||||||
|
uint256 public _totalSupply;
|
||||||
|
|
||||||
|
string public name;
|
||||||
|
string public symbol;
|
||||||
|
uint8 public decimals;
|
||||||
|
|
||||||
|
event Transfer(address indexed from, address indexed to, uint256 value);
|
||||||
|
|
||||||
|
event Approval(address indexed owner, address indexed spender, uint256 value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Total number of tokens in existence
|
||||||
|
*/
|
||||||
|
function totalSupply() public view returns (uint256) {
|
||||||
|
return _totalSupply;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Gets the balance of the specified address.
|
||||||
|
* @param owner The address to query the balance of.
|
||||||
|
* @return A uint256 representing the amount owned by the passed address.
|
||||||
|
*/
|
||||||
|
function balanceOf(address owner) public view returns (uint256) {
|
||||||
|
return _balances[owner];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Function to check the amount of tokens that an owner allowed to a spender.
|
||||||
|
* @param owner address The address which owns the funds.
|
||||||
|
* @param spender address The address which will spend the funds.
|
||||||
|
* @return A uint256 specifying the amount of tokens still available for the spender.
|
||||||
|
*/
|
||||||
|
function allowance(address owner, address spender) public view returns (uint256) {
|
||||||
|
return _allowed[owner][spender];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Transfer token to a specified address
|
||||||
|
* @param to The address to transfer to.
|
||||||
|
* @param value The amount to be transferred.
|
||||||
|
*/
|
||||||
|
function transfer(address to, uint256 value) public returns (bool) {
|
||||||
|
_transfer(msg.sender, to, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
|
||||||
|
* Beware that changing an allowance with this method brings the risk that someone may use both the old
|
||||||
|
* and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
|
||||||
|
* race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
|
||||||
|
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
|
||||||
|
* @param spender The address which will spend the funds.
|
||||||
|
* @param value The amount of tokens to be spent.
|
||||||
|
*/
|
||||||
|
function approve(address spender, uint256 value) public returns (bool) {
|
||||||
|
_approve(msg.sender, spender, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Transfer tokens from one address to another.
|
||||||
|
* Note that while this function emits an Approval event, this is not required as per the specification,
|
||||||
|
* and other compliant implementations may not emit the event.
|
||||||
|
* @param from address The address which you want to send tokens from
|
||||||
|
* @param to address The address which you want to transfer to
|
||||||
|
* @param value uint256 the amount of tokens to be transferred
|
||||||
|
*/
|
||||||
|
function transferFrom(address from, address to, uint256 value) public returns (bool) {
|
||||||
|
_transfer(from, to, value);
|
||||||
|
_approve(from, msg.sender, _allowed[from][msg.sender].sub(value));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Increase the amount of tokens that an owner allowed to a spender.
|
||||||
|
* approve should be called when _allowed[msg.sender][spender] == 0. To increment
|
||||||
|
* allowed value is better to use this function to avoid 2 calls (and wait until
|
||||||
|
* the first transaction is mined)
|
||||||
|
* From MonolithDAO Token.sol
|
||||||
|
* Emits an Approval event.
|
||||||
|
* @param spender The address which will spend the funds.
|
||||||
|
* @param addedValue The amount of tokens to increase the allowance by.
|
||||||
|
*/
|
||||||
|
function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
|
||||||
|
_approve(msg.sender, spender, _allowed[msg.sender][spender].add(addedValue));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Decrease the amount of tokens that an owner allowed to a spender.
|
||||||
|
* approve should be called when _allowed[msg.sender][spender] == 0. To decrement
|
||||||
|
* allowed value is better to use this function to avoid 2 calls (and wait until
|
||||||
|
* the first transaction is mined)
|
||||||
|
* From MonolithDAO Token.sol
|
||||||
|
* Emits an Approval event.
|
||||||
|
* @param spender The address which will spend the funds.
|
||||||
|
* @param subtractedValue The amount of tokens to decrease the allowance by.
|
||||||
|
*/
|
||||||
|
function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
|
||||||
|
_approve(msg.sender, spender, _allowed[msg.sender][spender].sub(subtractedValue));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Transfer token for a specified addresses
|
||||||
|
* @param from The address to transfer from.
|
||||||
|
* @param to The address to transfer to.
|
||||||
|
* @param value The amount to be transferred.
|
||||||
|
*/
|
||||||
|
function _transfer(address from, address to, uint256 value) internal {
|
||||||
|
require(to != address(0));
|
||||||
|
|
||||||
|
_balances[from] = _balances[from].sub(value);
|
||||||
|
_balances[to] = _balances[to].add(value);
|
||||||
|
emit Transfer(from, to, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Internal function that mints an amount of the token and assigns it to
|
||||||
|
* an account. This encapsulates the modification of balances such that the
|
||||||
|
* proper events are emitted.
|
||||||
|
* @param account The account that will receive the created tokens.
|
||||||
|
* @param value The amount that will be created.
|
||||||
|
*/
|
||||||
|
function _mint(address account, uint256 value) internal {
|
||||||
|
require(account != address(0), 'invalid address');
|
||||||
|
|
||||||
|
_totalSupply = _totalSupply.add(value);
|
||||||
|
_balances[account] = _balances[account].add(value);
|
||||||
|
emit Transfer(address(0), account, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Approve an address to spend another addresses' tokens.
|
||||||
|
* @param owner The address that owns the tokens.
|
||||||
|
* @param spender The address that will spend the tokens.
|
||||||
|
* @param value The number of tokens that can be spent.
|
||||||
|
*/
|
||||||
|
function _approve(address owner, address spender, uint256 value) internal {
|
||||||
|
require(spender != address(0));
|
||||||
|
require(owner != address(0));
|
||||||
|
|
||||||
|
_allowed[owner][spender] = value;
|
||||||
|
emit Approval(owner, spender, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
20
apps/token/contracts/Token.sol
Normal file
20
apps/token/contracts/Token.sol
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
pragma solidity ^0.4.24;
|
||||||
|
|
||||||
|
import "@aragon/os/contracts/apps/AragonApp.sol";
|
||||||
|
import "./ERC20Token.sol";
|
||||||
|
|
||||||
|
contract Token is ERC20Token, AragonApp {
|
||||||
|
bytes32 public constant MINT_TOKEN_ROLE = keccak256("MINT_TOKEN_ROLE");
|
||||||
|
|
||||||
|
event LogMint(address indexed recipient, uint256 amount, uint256 contributionId);
|
||||||
|
|
||||||
|
function initialize() public onlyInit {
|
||||||
|
initialized();
|
||||||
|
}
|
||||||
|
|
||||||
|
function mintFor(address contributorAccount, uint256 amount, uint256 contributionId) public isInitialized auth(MINT_TOKEN_ROLE) {
|
||||||
|
_mint(contributorAccount, amount);
|
||||||
|
emit LogMint(contributorAccount, amount, contributionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
23
apps/token/contracts/misc/Migrations.sol
Normal file
23
apps/token/contracts/misc/Migrations.sol
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
pragma solidity ^0.4.4;
|
||||||
|
|
||||||
|
contract Migrations {
|
||||||
|
address public owner;
|
||||||
|
uint public last_completed_migration;
|
||||||
|
|
||||||
|
modifier restricted() {
|
||||||
|
if (msg.sender == owner) _;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Migrations() {
|
||||||
|
owner = msg.sender;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCompleted(uint completed) restricted {
|
||||||
|
last_completed_migration = completed;
|
||||||
|
}
|
||||||
|
|
||||||
|
function upgrade(address new_address) restricted {
|
||||||
|
Migrations upgraded = Migrations(new_address);
|
||||||
|
upgraded.setCompleted(last_completed_migration);
|
||||||
|
}
|
||||||
|
}
|
4
apps/token/manifest.json
Normal file
4
apps/token/manifest.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "Token",
|
||||||
|
"description": "Kredits token app"
|
||||||
|
}
|
5
apps/token/migrations/1_initial_migration.js
Normal file
5
apps/token/migrations/1_initial_migration.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
var Migrations = artifacts.require('./Migrations.sol')
|
||||||
|
|
||||||
|
module.exports = function (deployer) {
|
||||||
|
deployer.deploy(Migrations)
|
||||||
|
}
|
5
apps/token/migrations/2_deploy_contracts.js
Normal file
5
apps/token/migrations/2_deploy_contracts.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
var Token = artifacts.require('Token.sol')
|
||||||
|
|
||||||
|
module.exports = function (deployer) {
|
||||||
|
deployer.deploy(Token)
|
||||||
|
}
|
13482
apps/token/package-lock.json
generated
Normal file
13482
apps/token/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
28
apps/token/package.json
Normal file
28
apps/token/package.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": "kredits-token",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"dependencies": {
|
||||||
|
"@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": "",
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"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')
|
||||||
|
})
|
63
apps/token/truffle.js
Normal file
63
apps/token/truffle.js
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* https://github.com/aragon/aragonOS/blob/v4.0.0/truffle-config.js
|
||||||
|
*/
|
||||||
|
const homedir = require('homedir')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
const HDWalletProvider = require('truffle-hdwallet-provider')
|
||||||
|
const HDWalletProviderPrivkey = require('truffle-hdwallet-provider-privkey')
|
||||||
|
|
||||||
|
const DEFAULT_MNEMONIC = 'explain tackle mirror kit van hammer degree position ginger unfair soup bonus'
|
||||||
|
|
||||||
|
const defaultRPC = (network) =>
|
||||||
|
`https://${network}.infura.io`
|
||||||
|
|
||||||
|
const configFilePath = (filename) =>
|
||||||
|
path.join(homedir(), `.aragon/${filename}`)
|
||||||
|
|
||||||
|
const mnemonic = () => {
|
||||||
|
try {
|
||||||
|
return require(configFilePath('mnemonic.json')).mnemonic
|
||||||
|
} catch (e) {
|
||||||
|
return DEFAULT_MNEMONIC
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const settingsForNetwork = (network) => {
|
||||||
|
try {
|
||||||
|
return require(configFilePath(`${network}_key.json`))
|
||||||
|
} catch (e) {
|
||||||
|
return { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lazily loaded provider
|
||||||
|
const providerForNetwork = (network) => (
|
||||||
|
() => {
|
||||||
|
let { rpc, keys } = settingsForNetwork(network)
|
||||||
|
rpc = rpc || defaultRPC(network)
|
||||||
|
|
||||||
|
if (!keys || keys.length == 0) {
|
||||||
|
return new HDWalletProvider(mnemonic(), rpc)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HDWalletProviderPrivkey(keys, rpc)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
module.exports = {
|
||||||
|
networks: {
|
||||||
|
development: {
|
||||||
|
host: 'localhost',
|
||||||
|
port: 8545,
|
||||||
|
network_id: '*'
|
||||||
|
},
|
||||||
|
mainnet: {
|
||||||
|
network_id: 1,
|
||||||
|
provider: providerForNetwork('mainnet')
|
||||||
|
},
|
||||||
|
rinkeby: {
|
||||||
|
network_id: 4,
|
||||||
|
provider: providerForNetwork('rinkeby')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
46
arapp.json
Normal file
46
arapp.json
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"name": "Add contributions",
|
||||||
|
"id": "ADD_CONTRIBUTION_ROLE",
|
||||||
|
"params": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Manage contributors",
|
||||||
|
"id": "MANAGE_CONTRIBUTORS_ROLE",
|
||||||
|
"params": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Mint token",
|
||||||
|
"id": "MINT_TOKEN_ROLE",
|
||||||
|
"params": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Add proposal",
|
||||||
|
"id": "ADD_PROPOSAL_ROLE",
|
||||||
|
"params": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Vote proposal",
|
||||||
|
"id": "VOTE_PROPOSAL_ROLE",
|
||||||
|
"params": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"environments": {
|
||||||
|
"rinkeby": {
|
||||||
|
"wsRPC": "wss://rinkeby.eth.aragon.network/ws",
|
||||||
|
"registry": "0x98Df287B6C145399Aaa709692c8D308357bC085D",
|
||||||
|
"appName": "dummy.open.aragonpm.eth",
|
||||||
|
"network": "rinkeby"
|
||||||
|
},
|
||||||
|
"rpc": {
|
||||||
|
"network": "rpc",
|
||||||
|
"appName": "dummy.aragonpm.eth"
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"network": "rpc",
|
||||||
|
"appName": "dummy.aragonpm.eth"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path": "contracts/misc/DummyApp.sol"
|
||||||
|
}
|
@ -1,126 +0,0 @@
|
|||||||
pragma solidity ^0.4.19;
|
|
||||||
|
|
||||||
import "zeppelin-solidity/contracts/token/ERC721/ERC721Token.sol";
|
|
||||||
import './upgradeable/Upgradeable.sol';
|
|
||||||
|
|
||||||
// ToDo: only load interfaces
|
|
||||||
import './Token.sol';
|
|
||||||
import './Contributors.sol';
|
|
||||||
|
|
||||||
contract Contribution is Upgradeable, ERC721Token {
|
|
||||||
|
|
||||||
struct ContributionData {
|
|
||||||
address contributor;
|
|
||||||
uint amount;
|
|
||||||
bool claimed;
|
|
||||||
bytes32 hashDigest;
|
|
||||||
uint8 hashFunction;
|
|
||||||
uint8 hashSize;
|
|
||||||
string tokenMetadataURL;
|
|
||||||
uint claimAfterBlock;
|
|
||||||
bool exists;
|
|
||||||
}
|
|
||||||
string internal name_;
|
|
||||||
string internal symbol_;
|
|
||||||
|
|
||||||
mapping(uint256 => address) contributionOwner;
|
|
||||||
mapping(address => uint256[]) ownedContributions;
|
|
||||||
|
|
||||||
mapping(uint256 => ContributionData) public contributions;
|
|
||||||
uint256 public contributionsCount;
|
|
||||||
|
|
||||||
event ContributionAdded(uint256 id, address indexed contributor, uint256 amount);
|
|
||||||
event ContributionClaimed(uint256 id, address indexed contributor, uint256 amount);
|
|
||||||
|
|
||||||
modifier coreOnly() {
|
|
||||||
require(contributorsContract().addressIsCore(msg.sender));
|
|
||||||
_;
|
|
||||||
}
|
|
||||||
modifier contributorOnly() {
|
|
||||||
require(contributorsContract().addressExists(msg.sender));
|
|
||||||
_;
|
|
||||||
}
|
|
||||||
|
|
||||||
function contributorsContract() view public returns (Contributors) {
|
|
||||||
return Contributors(registry.getProxyFor('Contributors'));
|
|
||||||
}
|
|
||||||
|
|
||||||
function tokenContract() view public returns (Token) {
|
|
||||||
return Token(registry.getProxyFor('Token'));
|
|
||||||
}
|
|
||||||
|
|
||||||
function name() external view returns (string) {
|
|
||||||
return name_;
|
|
||||||
}
|
|
||||||
|
|
||||||
function symbol() external view returns (string) {
|
|
||||||
return symbol_;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ownerOf(uint256 contributionId) public view returns (address) {
|
|
||||||
require(exists(contributionId));
|
|
||||||
return contributions[contributionId].contributor;
|
|
||||||
}
|
|
||||||
|
|
||||||
function balanceOf(address contributor) public view returns (uint) {
|
|
||||||
return ownedContributions[contributor].length;
|
|
||||||
}
|
|
||||||
|
|
||||||
function tokenOfOwnerByIndex(address contributor, uint index) public view returns (uint) {
|
|
||||||
return ownedContributions[contributor][index];
|
|
||||||
}
|
|
||||||
|
|
||||||
function tokenMetadata(uint contributionId) public view returns (string) {
|
|
||||||
return contributions[contributionId].tokenMetadataURL;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getContribution(uint contributionId) public view returns (uint256 id, address contributor, uint256 amount, bool claimed, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint claimAfterBlock, bool exists) {
|
|
||||||
id = contributionId;
|
|
||||||
ContributionData storage c = contributions[id];
|
|
||||||
return (
|
|
||||||
id,
|
|
||||||
c.contributor,
|
|
||||||
c.amount,
|
|
||||||
c.claimed,
|
|
||||||
c.hashDigest,
|
|
||||||
c.hashFunction,
|
|
||||||
c.hashSize,
|
|
||||||
c.claimAfterBlock,
|
|
||||||
c.exists
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function add(uint256 amount, address contributor, uint256 blocksToWait) public coreOnly {
|
|
||||||
uint contributionId = contributionsCount + 1;
|
|
||||||
ContributionData storage c = contributions[contributionId];
|
|
||||||
c.exists = true;
|
|
||||||
c.amount = amount;
|
|
||||||
c.claimed = false;
|
|
||||||
c.contributor = contributor;
|
|
||||||
c.claimAfterBlock = block.number + blocksToWait;
|
|
||||||
|
|
||||||
contributionsCount++;
|
|
||||||
|
|
||||||
contributionOwner[contributionId] = contributor;
|
|
||||||
ownedContributions[contributor].push(contributionId);
|
|
||||||
|
|
||||||
ContributionAdded(contributionId, contributor, amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
function claim(uint256 contributionId) public {
|
|
||||||
ContributionData storage c = contributions[contributionId];
|
|
||||||
require(c.exists);
|
|
||||||
require(!c.claimed);
|
|
||||||
require(block.number > c.claimAfterBlock);
|
|
||||||
c.claimed = true;
|
|
||||||
tokenContract().mintFor(c.contributor, c.amount, contributionId);
|
|
||||||
|
|
||||||
ContributionClaimed(contributionId, c.contributor, c.amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
function exists(uint256 contributionId) view public returns (bool) {
|
|
||||||
return contributions[contributionId].exists;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
85
contracts/KreditsKit.sol
Normal file
85
contracts/KreditsKit.sol
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
pragma solidity 0.4.24;
|
||||||
|
|
||||||
|
import "@aragon/os/contracts/apps/AragonApp.sol";
|
||||||
|
import "@aragon/os/contracts/kernel/Kernel.sol";
|
||||||
|
import "@aragon/os/contracts/acl/ACL.sol";
|
||||||
|
import "@aragon/os/contracts/acl/ACLSyntaxSugar.sol";
|
||||||
|
|
||||||
|
import "@aragon/kits-base/contracts/KitBase.sol";
|
||||||
|
import "./misc/APMNamehashOpen.sol";
|
||||||
|
|
||||||
|
import "../apps/contribution/contracts/Contribution.sol";
|
||||||
|
import "../apps/contributor/contracts/Contributor.sol";
|
||||||
|
import "../apps/token/contracts/Token.sol";
|
||||||
|
import "../apps/proposal/contracts/Proposal.sol";
|
||||||
|
|
||||||
|
contract KreditsKit is KitBase, APMNamehashOpen, ACLSyntaxSugar {
|
||||||
|
bytes32 public contributorAppId = apmNamehash("contributor"); // 0xe9140f1e39c8a1d04167c3b710688a3eecea2976f34735c8eb98956f4764635b
|
||||||
|
bytes32 public contributionAppId = apmNamehash("contribution"); // 0x7fcf91283b719b30c2fa954ff0da021e1b91aed09d7aa13df5e8078a4a1007eb
|
||||||
|
bytes32 public tokenAppId = apmNamehash("token"); // 0xe04a882e7a6adf5603207d545ea49aec17e6b936c4d9eae3d74dbe482264991a
|
||||||
|
bytes32 public proposalAppId = apmNamehash("proposal"); // 0xaf5fe5c3b0d9581ee88974bbc8699e6fa71efd1b321e44b2227103c9ef21dbdb
|
||||||
|
|
||||||
|
|
||||||
|
event DeployInstance(address dao);
|
||||||
|
event InstalledApp(address dao, address appProxy, bytes32 appId);
|
||||||
|
|
||||||
|
constructor (DAOFactory _fac, ENS _ens) public KitBase(_fac, _ens) {}
|
||||||
|
|
||||||
|
function newInstance() public returns (Kernel dao, ERCProxy proxy) {
|
||||||
|
address root = msg.sender;
|
||||||
|
dao = fac.newDAO(this);
|
||||||
|
ACL acl = ACL(dao.acl());
|
||||||
|
|
||||||
|
acl.createPermission(this, dao, dao.APP_MANAGER_ROLE(), this);
|
||||||
|
|
||||||
|
Contributor contributor = Contributor(_installApp(dao, contributorAppId));
|
||||||
|
contributor.initialize(root);
|
||||||
|
acl.createPermission(root, contributor, contributor.MANAGE_CONTRIBUTORS_ROLE(), root);
|
||||||
|
|
||||||
|
Token token = Token(_installApp(dao, tokenAppId));
|
||||||
|
token.initialize();
|
||||||
|
|
||||||
|
Contribution contribution = Contribution(_installApp(dao, contributionAppId));
|
||||||
|
contribution.initialize();
|
||||||
|
|
||||||
|
Proposal proposal = Proposal(_installApp(dao, proposalAppId));
|
||||||
|
proposal.initialize();
|
||||||
|
|
||||||
|
acl.createPermission(root, contribution, contribution.ADD_CONTRIBUTION_ROLE(), this);
|
||||||
|
acl.grantPermission(proposal, contribution, contribution.ADD_CONTRIBUTION_ROLE());
|
||||||
|
|
||||||
|
uint256[] memory params = new uint256[](1);
|
||||||
|
params[0] = uint256(203) << 248 | uint256(1) << 240 | uint240(contributor);
|
||||||
|
acl.grantPermissionP(root, contribution, contribution.ADD_CONTRIBUTION_ROLE(), params);
|
||||||
|
|
||||||
|
//acl.setPermissionManager(this, proposal, proposal.VOTE_PROPOSAL_ROLE();
|
||||||
|
acl.createPermission(root, proposal, proposal.VOTE_PROPOSAL_ROLE(), this);
|
||||||
|
acl.grantPermissionP(root, proposal, proposal.VOTE_PROPOSAL_ROLE(), params);
|
||||||
|
|
||||||
|
acl.createPermission(root, proposal, proposal.ADD_PROPOSAL_ROLE(), this);
|
||||||
|
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.createPermission(root, token, token.MINT_TOKEN_ROLE(), this);
|
||||||
|
acl.grantPermission(contribution, token, token.MINT_TOKEN_ROLE());
|
||||||
|
acl.setPermissionManager(root, token, token.MINT_TOKEN_ROLE());
|
||||||
|
|
||||||
|
|
||||||
|
cleanupDAOPermissions(dao, acl, root);
|
||||||
|
|
||||||
|
emit DeployInstance(dao);
|
||||||
|
//return dao;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _installApp(Kernel _dao, bytes32 _appId) internal returns (AragonApp) {
|
||||||
|
address baseAppAddress = latestVersionAppBase(_appId);
|
||||||
|
require(baseAppAddress != address(0), "App should be deployed");
|
||||||
|
AragonApp appProxy = AragonApp(_dao.newAppInstance(_appId, baseAppAddress, new bytes(0), true));
|
||||||
|
|
||||||
|
emit InstalledApp(_dao, appProxy, _appId);
|
||||||
|
return appProxy;
|
||||||
|
}
|
||||||
|
}
|
@ -1,133 +0,0 @@
|
|||||||
pragma solidity ^0.4.18;
|
|
||||||
|
|
||||||
// ToDo: only load interfaces
|
|
||||||
import './Token.sol';
|
|
||||||
import './Contributors.sol';
|
|
||||||
import './Contribution.sol';
|
|
||||||
|
|
||||||
contract Operator is Upgradeable {
|
|
||||||
|
|
||||||
struct Proposal {
|
|
||||||
address creatorAccount;
|
|
||||||
uint contributorId;
|
|
||||||
uint votesCount;
|
|
||||||
uint votesNeeded;
|
|
||||||
uint256 amount;
|
|
||||||
bool executed;
|
|
||||||
bytes32 ipfsHash;
|
|
||||||
uint8 hashFunction;
|
|
||||||
uint8 hashSize;
|
|
||||||
uint256[] voterIds;
|
|
||||||
mapping (uint256 => bool) votes;
|
|
||||||
bool exists;
|
|
||||||
}
|
|
||||||
|
|
||||||
mapping(uint256 => Proposal) public proposals;
|
|
||||||
uint256 public proposalsCount;
|
|
||||||
|
|
||||||
event ProposalCreated(uint256 id, address creatorAccount, uint256 contributorId, uint256 amount);
|
|
||||||
event ProposalVoted(uint256 id, uint256 voterId, uint256 totalVotes);
|
|
||||||
event ProposalExecuted(uint256 id, uint256 contributorId, uint256 amount);
|
|
||||||
|
|
||||||
modifier coreOnly() {
|
|
||||||
require(contributorsContract().addressIsCore(msg.sender));
|
|
||||||
_;
|
|
||||||
}
|
|
||||||
modifier contributorOnly() {
|
|
||||||
require(contributorsContract().addressExists(msg.sender));
|
|
||||||
_;
|
|
||||||
}
|
|
||||||
modifier noEther() {
|
|
||||||
require(msg.value == 0);
|
|
||||||
_;
|
|
||||||
}
|
|
||||||
|
|
||||||
function contributorsContract() view public returns (Contributors) {
|
|
||||||
return Contributors(registry.getProxyFor('Contributors'));
|
|
||||||
}
|
|
||||||
function tokenContract() view public returns (Token) {
|
|
||||||
return Token(registry.getProxyFor('Token'));
|
|
||||||
}
|
|
||||||
function contributionContract() view public returns (Contribution) {
|
|
||||||
return Contribution(registry.getProxyFor('Contribution'));
|
|
||||||
}
|
|
||||||
|
|
||||||
function contributorsCount() view public returns (uint) {
|
|
||||||
return contributorsContract().contributorsCount();
|
|
||||||
}
|
|
||||||
function coreContributorsCount() view public returns (uint) {
|
|
||||||
return contributorsContract().coreContributorsCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
function addProposal(uint contributorId, uint256 amount, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize) public {
|
|
||||||
require(contributorsContract().exists(contributorId));
|
|
||||||
|
|
||||||
uint256 proposalId = proposalsCount + 1;
|
|
||||||
uint256 _votesNeeded = contributorsContract().coreContributorsCount() / 100 * 75;
|
|
||||||
|
|
||||||
var p = proposals[proposalId];
|
|
||||||
p.creatorAccount = msg.sender;
|
|
||||||
p.contributorId = contributorId;
|
|
||||||
p.amount = amount;
|
|
||||||
p.ipfsHash = ipfsHash;
|
|
||||||
p.hashFunction = hashFunction;
|
|
||||||
p.hashSize = hashSize;
|
|
||||||
p.votesCount = 0;
|
|
||||||
p.votesNeeded = _votesNeeded;
|
|
||||||
p.exists = true;
|
|
||||||
|
|
||||||
proposalsCount++;
|
|
||||||
ProposalCreated(proposalId, msg.sender, p.contributorId, p.amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getProposal(uint proposalId) public view returns (uint256 id, address creatorAccount, uint256 contributorId, uint256 votesCount, uint256 votesNeeded, uint256 amount, bool executed, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize, uint256[] voterIds, bool exists) {
|
|
||||||
id = proposalId;
|
|
||||||
Proposal storage p = proposals[id];
|
|
||||||
return (
|
|
||||||
id,
|
|
||||||
p.creatorAccount,
|
|
||||||
p.contributorId,
|
|
||||||
p.votesCount,
|
|
||||||
p.votesNeeded,
|
|
||||||
p.amount,
|
|
||||||
p.executed,
|
|
||||||
p.ipfsHash,
|
|
||||||
p.hashFunction,
|
|
||||||
p.hashSize,
|
|
||||||
p.voterIds,
|
|
||||||
p.exists
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function vote(uint256 proposalId) public coreOnly {
|
|
||||||
var p = proposals[proposalId];
|
|
||||||
require(!p.executed);
|
|
||||||
uint256 voterId = contributorsContract().getContributorIdByAddress(msg.sender);
|
|
||||||
require(p.votes[voterId] != true);
|
|
||||||
p.voterIds.push(voterId);
|
|
||||||
p.votes[voterId] = true;
|
|
||||||
|
|
||||||
p.votesCount++;
|
|
||||||
if (p.votesCount >= p.votesNeeded) {
|
|
||||||
executeProposal(proposalId);
|
|
||||||
}
|
|
||||||
ProposalVoted(proposalId, voterId, p.votesCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
function batchVote(uint256[] _proposalIds) public coreOnly {
|
|
||||||
for (uint256 i = 0; i < _proposalIds.length; i++) {
|
|
||||||
vote(_proposalIds[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function executeProposal(uint proposalId) private {
|
|
||||||
var p = proposals[proposalId];
|
|
||||||
require(!p.executed);
|
|
||||||
require(p.votesCount >= p.votesNeeded);
|
|
||||||
address recipientAddress = contributorsContract().getContributorAddressById(p.contributorId);
|
|
||||||
contributionContract().add(p.amount, recipientAddress, 0);
|
|
||||||
p.executed = true;
|
|
||||||
ProposalExecuted(proposalId, p.contributorId, p.amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
pragma solidity ^0.4.18;
|
|
||||||
|
|
||||||
import 'zeppelin-solidity/contracts/token/ERC20/BasicToken.sol';
|
|
||||||
import './upgradeable/Upgradeable.sol';
|
|
||||||
|
|
||||||
contract Token is Upgradeable, BasicToken {
|
|
||||||
string public name;
|
|
||||||
string public symbol;
|
|
||||||
uint8 public decimals;
|
|
||||||
|
|
||||||
event LogMint(address indexed recipient, uint256 amount, uint256 proposalId);
|
|
||||||
|
|
||||||
function initialize(address sender) public payable {
|
|
||||||
require(msg.sender == address(registry));
|
|
||||||
name = 'Kredits';
|
|
||||||
symbol = 'K';
|
|
||||||
decimals = 18;
|
|
||||||
}
|
|
||||||
|
|
||||||
function mintFor(address contributorAccount, uint256 amount, uint proposalId) onlyRegistryContractFor('Contribution') public {
|
|
||||||
totalSupply_ = totalSupply_.add(amount);
|
|
||||||
balances[contributorAccount] = balances[contributorAccount].add(amount);
|
|
||||||
|
|
||||||
LogMint(contributorAccount, amount, proposalId);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
13
contracts/misc/APMNamehashOpen.sol
Normal file
13
contracts/misc/APMNamehashOpen.sol
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
pragma solidity 0.4.24;
|
||||||
|
|
||||||
|
import "@aragon/os/contracts/apm/APMNamehash.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract APMNamehashOpen is APMNamehash {
|
||||||
|
bytes32 public constant OPEN_TITLE = keccak256("open");
|
||||||
|
bytes32 public constant OPEN_APM_NODE = keccak256(abi.encodePacked(APM_NODE, OPEN_TITLE));
|
||||||
|
|
||||||
|
function apmNamehashOpen(string name) internal pure returns (bytes32) {
|
||||||
|
return keccak256(abi.encodePacked(OPEN_APM_NODE, keccak256(name)));
|
||||||
|
}
|
||||||
|
}
|
16
contracts/misc/DummyApp.sol
Normal file
16
contracts/misc/DummyApp.sol
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
pragma solidity 0.4.24;
|
||||||
|
|
||||||
|
import "@aragon/os/contracts/apps/AragonApp.sol";
|
||||||
|
|
||||||
|
|
||||||
|
// This is a "Dummy" app which's only purpose to exist is because
|
||||||
|
// Aragon's CLI still doesn't support running a Kit inside a project
|
||||||
|
// which isn't considered to be a "valid" Aragon project.
|
||||||
|
// It requires us to have an arrap.json file pointing to the contract
|
||||||
|
// and a manifest.json file which describes the front-end structure.
|
||||||
|
contract DummyApp is AragonApp {
|
||||||
|
function initialize() public onlyInit {
|
||||||
|
initialized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
pragma solidity ^0.4.17;
|
pragma solidity ^0.4.4;
|
||||||
|
|
||||||
contract Migrations {
|
contract Migrations {
|
||||||
address public owner;
|
address public owner;
|
||||||
@ -8,7 +8,7 @@ contract Migrations {
|
|||||||
if (msg.sender == owner) _;
|
if (msg.sender == owner) _;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Migrations() public {
|
function constructor() public {
|
||||||
owner = msg.sender;
|
owner = msg.sender;
|
||||||
}
|
}
|
||||||
|
|
@ -1,53 +0,0 @@
|
|||||||
pragma solidity ^0.4.18;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @title IRegistry
|
|
||||||
* @dev This contract represents the interface of a registry contract
|
|
||||||
*/
|
|
||||||
interface IRegistry {
|
|
||||||
/**
|
|
||||||
* @dev This event will be emitted every time a new proxy is created
|
|
||||||
* @param name of the contract, as specified in the registry
|
|
||||||
* @param proxy representing the address of the proxy created
|
|
||||||
*/
|
|
||||||
event ProxyCreated(string name, address proxy);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev This event will be emitted every time a new implementation is registered
|
|
||||||
* @param name of the contract, as specified in the registry
|
|
||||||
* @param version representing the version name of the registered implementation
|
|
||||||
* @param implementation representing the address of the registered implementation
|
|
||||||
*/
|
|
||||||
event VersionAdded(string name, uint version, address implementation);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev This event will be emitted every time a proxy is upgraded to a new version
|
|
||||||
* @param name of the contract, as specified in the registry
|
|
||||||
* @param version representing the version name of the registered implementation
|
|
||||||
*/
|
|
||||||
event ProxyImplementationUpgraded(string name, uint version);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Registers a new version with its implementation address
|
|
||||||
* @param name of the contract, as specified in the registry
|
|
||||||
* @param implementation representing the address of the new implementation to be registered
|
|
||||||
*/
|
|
||||||
function addVersion(string name, address implementation) public;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Tells the address of the implementation for a given version
|
|
||||||
* @param name of the contract, as specified in the registry
|
|
||||||
* @param version to query the implementation of
|
|
||||||
* @return address of the implementation registered for the given version
|
|
||||||
*/
|
|
||||||
function getVersion(string name, uint version) public view returns (address);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Tells the latest address of the implementation
|
|
||||||
* @param name of the contract, as specified in the registry
|
|
||||||
* @return address of the implementation registered for the latest version
|
|
||||||
*/
|
|
||||||
function getLatestVersion(string name) public view returns (address);
|
|
||||||
|
|
||||||
function getProxyFor(string name) public view returns (address);
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
pragma solidity ^0.4.18;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @title Proxy
|
|
||||||
* @dev Gives the possibility to delegate any call to a foreign implementation.
|
|
||||||
*/
|
|
||||||
contract Proxy {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Tells the address of the implementation where every call will be delegated.
|
|
||||||
* @return address of the implementation to which it will be delegated
|
|
||||||
*/
|
|
||||||
function implementation() public view returns (address);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Fallback function allowing to perform a delegatecall to the given implementation.
|
|
||||||
* This function will return whatever the implementation call returns
|
|
||||||
*/
|
|
||||||
function () payable public {
|
|
||||||
address _impl = implementation();
|
|
||||||
require(_impl != address(0));
|
|
||||||
bytes memory data = msg.data;
|
|
||||||
|
|
||||||
assembly {
|
|
||||||
let result := delegatecall(gas, _impl, add(data, 0x20), mload(data), 0, 0)
|
|
||||||
let size := returndatasize
|
|
||||||
|
|
||||||
let ptr := mload(0x40)
|
|
||||||
returndatacopy(ptr, 0, size)
|
|
||||||
|
|
||||||
switch result
|
|
||||||
case 0 { revert(ptr, size) }
|
|
||||||
default { return(ptr, size) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,86 +0,0 @@
|
|||||||
pragma solidity ^0.4.18;
|
|
||||||
|
|
||||||
import './IRegistry.sol';
|
|
||||||
import './Upgradeable.sol';
|
|
||||||
import './UpgradeabilityProxy.sol';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @title Registry
|
|
||||||
* @dev This contract works as a registry of versions, it holds the implementations for the registered versions.
|
|
||||||
*/
|
|
||||||
contract Registry is IRegistry {
|
|
||||||
// mapping of contract names to versions to implementation
|
|
||||||
// "Token" => "1.0.0" => "0x123"
|
|
||||||
mapping(bytes32 => mapping(uint => address)) public versions;
|
|
||||||
|
|
||||||
// current version for a certain contract
|
|
||||||
mapping(bytes32 => uint) public currentVersions;
|
|
||||||
|
|
||||||
// mapping of the contract names to the proxy addresses
|
|
||||||
mapping(bytes32 => address) public proxies;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Registers a new version with its implementation address
|
|
||||||
* @param name of the contract
|
|
||||||
* @param implementation representing the address of the new implementation to be registered
|
|
||||||
*/
|
|
||||||
function addVersion(string name, address implementation) public {
|
|
||||||
bytes32 key = keccak256(name);
|
|
||||||
currentVersions[key] = currentVersions[key] + 1;
|
|
||||||
uint version = currentVersions[key];
|
|
||||||
require(versions[key][version] == 0x0);
|
|
||||||
versions[key][version] = implementation;
|
|
||||||
VersionAdded(name, version, implementation);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Tells the address of the implementation for a given version
|
|
||||||
* @param name of the contract
|
|
||||||
* @param version to query the implementation of
|
|
||||||
* @return address of the implementation registered for the given version
|
|
||||||
*/
|
|
||||||
function getVersion(string name, uint version) public view returns (address) {
|
|
||||||
bytes32 key = keccak256(name);
|
|
||||||
return versions[key][version];
|
|
||||||
}
|
|
||||||
|
|
||||||
function getLatestVersion(string name) public view returns (address) {
|
|
||||||
bytes32 key = keccak256(name);
|
|
||||||
uint current = currentVersions[key];
|
|
||||||
return getVersion(name, current);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getProxyFor(string name) public view returns (address) {
|
|
||||||
bytes32 key = keccak256(name);
|
|
||||||
return proxies[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
function upgrade(string name, uint version) public {
|
|
||||||
bytes32 key = keccak256(name);
|
|
||||||
UpgradeabilityProxy(proxies[key]).upgradeTo(version);
|
|
||||||
ProxyImplementationUpgraded(name, version);
|
|
||||||
}
|
|
||||||
|
|
||||||
function upgradeToLatest(string name) public {
|
|
||||||
bytes32 key = keccak256(name);
|
|
||||||
uint current = currentVersions[key];
|
|
||||||
upgrade(name, current);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Creates an upgradeable proxy
|
|
||||||
* @param name of the contract
|
|
||||||
* @param version representing the first version to be set for the proxy
|
|
||||||
* @return address of the new proxy created
|
|
||||||
*/
|
|
||||||
function createProxy(string name, uint version) public payable returns (UpgradeabilityProxy) {
|
|
||||||
bytes32 key = keccak256(name);
|
|
||||||
require(proxies[key] == 0x0);
|
|
||||||
UpgradeabilityProxy proxy = new UpgradeabilityProxy(name, version);
|
|
||||||
proxies[key] = address(proxy);
|
|
||||||
Upgradeable(proxy).initialize.value(msg.value)(msg.sender);
|
|
||||||
ProxyCreated(name, proxy);
|
|
||||||
return proxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
pragma solidity ^0.4.18;
|
|
||||||
|
|
||||||
import './Proxy.sol';
|
|
||||||
import './IRegistry.sol';
|
|
||||||
import './UpgradeabilityStorage.sol';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @title UpgradeabilityProxy
|
|
||||||
* @dev This contract represents a proxy where the implementation address to which it will delegate can be upgraded
|
|
||||||
*/
|
|
||||||
contract UpgradeabilityProxy is Proxy, UpgradeabilityStorage {
|
|
||||||
|
|
||||||
function UpgradeabilityProxy(string _name, uint _version) public {
|
|
||||||
_proxiedContractName = _name;
|
|
||||||
registry = IRegistry(msg.sender);
|
|
||||||
upgradeTo(_version);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Upgrades the implementation to the requested version
|
|
||||||
* @param _version representing the version name of the new implementation to be set
|
|
||||||
*/
|
|
||||||
function upgradeTo(uint _version) public {
|
|
||||||
require(msg.sender == address(registry));
|
|
||||||
_implementation = registry.getVersion(_proxiedContractName, _version);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
pragma solidity ^0.4.18;
|
|
||||||
|
|
||||||
import './IRegistry.sol';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @title UpgradeabilityStorage
|
|
||||||
* @dev This contract holds all the necessary state variables to support the upgrade functionality
|
|
||||||
*/
|
|
||||||
contract UpgradeabilityStorage {
|
|
||||||
// Versions registry
|
|
||||||
IRegistry internal registry;
|
|
||||||
|
|
||||||
// Address of the current implementation
|
|
||||||
address internal _implementation;
|
|
||||||
|
|
||||||
// contract name
|
|
||||||
string public _proxiedContractName;
|
|
||||||
|
|
||||||
|
|
||||||
modifier requireRegistry() {
|
|
||||||
require(address(registry) != 0x0);
|
|
||||||
_;
|
|
||||||
}
|
|
||||||
modifier onlyRegistryContractFor(string name) {
|
|
||||||
require(address(registry) != 0x0);
|
|
||||||
require(msg.sender == registry.getProxyFor(name));
|
|
||||||
_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Tells the address of the current implementation
|
|
||||||
* @return address of the current implementation
|
|
||||||
*/
|
|
||||||
function implementation() public view returns (address) {
|
|
||||||
return _implementation;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
pragma solidity ^0.4.18;
|
|
||||||
|
|
||||||
import './UpgradeabilityStorage.sol';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @title Upgradeable
|
|
||||||
* @dev This contract holds all the minimum required functionality for a behavior to be upgradeable.
|
|
||||||
* This means, required state variables for owned upgradeability purpose and simple initialization validation.
|
|
||||||
*/
|
|
||||||
contract Upgradeable is UpgradeabilityStorage {
|
|
||||||
/**
|
|
||||||
* @dev Validates the caller is the versions registry.
|
|
||||||
* THIS FUNCTION SHOULD BE OVERRIDDEN CALLING SUPER
|
|
||||||
* @param sender representing the address deploying the initial behavior of the contract
|
|
||||||
*/
|
|
||||||
function initialize(address sender) public payable {
|
|
||||||
require(msg.sender == address(registry));
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because one or more lines are too long
1
lib/abis/Contributor.json
Normal file
1
lib/abis/Contributor.json
Normal file
File diff suppressed because one or more lines are too long
@ -1 +0,0 @@
|
|||||||
[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"contributors","outputs":[{"name":"account","type":"address"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"isCore","type":"bool"},{"name":"exists","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"implementation","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_proxiedContractName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"contributorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"contributorIds","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"oldIpfsHash","type":"bytes32"},{"indexed":false,"name":"newIpfsHash","type":"bytes32"}],"name":"ContributorProfileUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"oldAccount","type":"address"},{"indexed":false,"name":"newAccount","type":"address"}],"name":"ContributorAccountUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"account","type":"address"}],"name":"ContributorAdded","type":"event"},{"constant":false,"inputs":[{"name":"sender","type":"address"}],"name":"initialize","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"coreContributorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"id","type":"uint256"},{"name":"oldAccount","type":"address"},{"name":"newAccount","type":"address"}],"name":"updateContributorAccount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"id","type":"uint256"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"}],"name":"updateContributorIpfsHash","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"account","type":"address"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"isCore","type":"bool"}],"name":"addContributor","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"id","type":"uint256"}],"name":"isCore","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"id","type":"uint256"}],"name":"exists","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"addressIsCore","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"addressExists","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"getContributorIdByAddress","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"id","type":"uint256"}],"name":"getContributorAddressById","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_id","type":"uint256"}],"name":"getContributorById","outputs":[{"name":"id","type":"uint256"},{"name":"account","type":"address"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"isCore","type":"bool"},{"name":"balance","type":"uint256"},{"name":"exists","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}]
|
|
1
lib/abis/Kernel.json
Normal file
1
lib/abis/Kernel.json
Normal file
File diff suppressed because one or more lines are too long
@ -1 +0,0 @@
|
|||||||
[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"proposals","outputs":[{"name":"creatorAccount","type":"address"},{"name":"contributorId","type":"uint256"},{"name":"votesCount","type":"uint256"},{"name":"votesNeeded","type":"uint256"},{"name":"amount","type":"uint256"},{"name":"executed","type":"bool"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"exists","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proposalsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"implementation","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_proxiedContractName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"sender","type":"address"}],"name":"initialize","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"creatorAccount","type":"address"},{"indexed":false,"name":"contributorId","type":"uint256"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"ProposalCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"voterId","type":"uint256"},{"indexed":false,"name":"totalVotes","type":"uint256"}],"name":"ProposalVoted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"contributorId","type":"uint256"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"ProposalExecuted","type":"event"},{"constant":true,"inputs":[],"name":"contributorsContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"contributionContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"contributorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"coreContributorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"contributorId","type":"uint256"},{"name":"amount","type":"uint256"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"}],"name":"addProposal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"proposalId","type":"uint256"}],"name":"getProposal","outputs":[{"name":"id","type":"uint256"},{"name":"creatorAccount","type":"address"},{"name":"contributorId","type":"uint256"},{"name":"votesCount","type":"uint256"},{"name":"votesNeeded","type":"uint256"},{"name":"amount","type":"uint256"},{"name":"executed","type":"bool"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"voterIds","type":"uint256[]"},{"name":"exists","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"proposalId","type":"uint256"}],"name":"vote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_proposalIds","type":"uint256[]"}],"name":"batchVote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]
|
|
1
lib/abis/Proposal.json
Normal file
1
lib/abis/Proposal.json
Normal file
File diff suppressed because one or more lines are too long
@ -1 +0,0 @@
|
|||||||
[{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"currentVersions","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"proxies","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"},{"name":"","type":"uint256"}],"name":"versions","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"name","type":"string"},{"indexed":false,"name":"proxy","type":"address"}],"name":"ProxyCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"name","type":"string"},{"indexed":false,"name":"version","type":"uint256"},{"indexed":false,"name":"implementation","type":"address"}],"name":"VersionAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"name","type":"string"},{"indexed":false,"name":"version","type":"uint256"}],"name":"ProxyImplementationUpgraded","type":"event"},{"constant":false,"inputs":[{"name":"name","type":"string"},{"name":"implementation","type":"address"}],"name":"addVersion","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"name","type":"string"},{"name":"version","type":"uint256"}],"name":"getVersion","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"name","type":"string"}],"name":"getLatestVersion","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"name","type":"string"}],"name":"getProxyFor","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"name","type":"string"},{"name":"version","type":"uint256"}],"name":"upgrade","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"name","type":"string"}],"name":"upgradeToLatest","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"name","type":"string"},{"name":"version","type":"uint256"}],"name":"createProxy","outputs":[{"name":"","type":"address"}],"payable":true,"stateMutability":"payable","type":"function"}]
|
|
@ -1 +1 @@
|
|||||||
[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"implementation","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_proxiedContractName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"recipient","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"proposalId","type":"uint256"}],"name":"LogMint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"constant":false,"inputs":[{"name":"sender","type":"address"}],"name":"initialize","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"contributorAccount","type":"address"},{"name":"amount","type":"uint256"},{"name":"proposalId","type":"uint256"}],"name":"mintFor","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]
|
[{"constant":true,"inputs":[],"name":"hasInitialized","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_script","type":"bytes"}],"name":"getEVMScriptExecutor","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getRecoveryVault","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_foo","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"token","type":"address"}],"name":"allowRecoverability","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"appId","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"MINT_TOKEN_ROLE","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getInitializationBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"}],"name":"transferToVault","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_sender","type":"address"},{"name":"_role","type":"bytes32"},{"name":"_params","type":"uint256[]"}],"name":"canPerform","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getEVMScriptRegistry","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"kernel","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isPetrified","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"recipient","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"contributionId","type":"uint256"}],"name":"LogMint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"executor","type":"address"},{"indexed":false,"name":"script","type":"bytes"},{"indexed":false,"name":"input","type":"bytes"},{"indexed":false,"name":"returnData","type":"bytes"}],"name":"ScriptResult","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"vault","type":"address"},{"indexed":true,"name":"token","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"RecoverToVault","type":"event"},{"constant":false,"inputs":[],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"foo","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"mintFor","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]
|
@ -1 +0,0 @@
|
|||||||
{"42":"0x205fe1b3dac678b594c5f0535e7d158e38591f93","100":"0xa7fc9b1f678c41396b53904f94f50a42ff44d826"}
|
|
@ -1 +0,0 @@
|
|||||||
{"42":"0x9fd66ee78a5ebe86006f12b37ff59c63f9caa15b","100":"0x95d3bd7d136bb0b7ac9988097e964236f8a9976e"}
|
|
@ -1 +0,0 @@
|
|||||||
{"42":"0xc270e6ea4fe303df9f1a3d4a132ac425264082e7","100":"0x7458dea485d9d8301e3ce43e8a1ec1456be5ba83"}
|
|
@ -1 +0,0 @@
|
|||||||
{"42":"0xf71ccf7ab48044ef9ae0b5e6983dbd3266b78b36","100":"0x3fc29fbe40c2d0ca78c7e81342f00226650fe2ad"}
|
|
1
lib/addresses/dao.json
Normal file
1
lib/addresses/dao.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"23827572":"0xe4e0e7fe54d9189df29a80c07ab733fc9a212761","65047207":"0xdbe26ae4a434472a7474db60cc3e3c97a57bbf38"}
|
@ -46,6 +46,26 @@ class Contribution extends Base {
|
|||||||
return RSVP.all(contributions);
|
return RSVP.all(contributions);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addContribution(contributionAttr, callOptions = {}) {
|
||||||
|
let json = ContributionSerializer.serialize(contributionAttr);
|
||||||
|
// TODO: validate against schema
|
||||||
|
|
||||||
|
return this.ipfs
|
||||||
|
.add(json)
|
||||||
|
.then((ipfsHashAttr) => {
|
||||||
|
let contribution = [
|
||||||
|
contributionAttr.amount,
|
||||||
|
contributionAttr.contributorAccount,
|
||||||
|
ipfsHashAttr.hashDigest,
|
||||||
|
ipfsHashAttr.hashFunction,
|
||||||
|
ipfsHashAttr.hashSize,
|
||||||
|
];
|
||||||
|
|
||||||
|
console.log(contribution);
|
||||||
|
return this.functions.add(...contribution, callOptions);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Contribution;
|
module.exports = Contribution;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
Contributors: require('./contributor'),
|
Contributor: require('./contributor'),
|
||||||
Contribution: require('./contribution'),
|
Contribution: require('./contribution'),
|
||||||
Operator: require('./operator'),
|
Proposal: require('./proposal'),
|
||||||
Token: require('./token'),
|
Token: require('./token'),
|
||||||
Registry: require('./registry')
|
Kernel: require('./kernel')
|
||||||
};
|
};
|
||||||
|
21
lib/contracts/kernel.js
Normal file
21
lib/contracts/kernel.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
const Base = require('./base');
|
||||||
|
|
||||||
|
KERNEL_APP_ADDR_NAMESPACE = '0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb';
|
||||||
|
|
||||||
|
class Kernel extends Base {
|
||||||
|
|
||||||
|
getApp(appName) {
|
||||||
|
return this.functions.getApp(KERNEL_APP_ADDR_NAMESPACE, this.appNamehash(appName));
|
||||||
|
}
|
||||||
|
|
||||||
|
appNamehash(appName) {
|
||||||
|
return {
|
||||||
|
Contributor: '0xe9140f1e39c8a1d04167c3b710688a3eecea2976f34735c8eb98956f4764635b',
|
||||||
|
Contribution: '0x7fcf91283b719b30c2fa954ff0da021e1b91aed09d7aa13df5e8078a4a1007eb',
|
||||||
|
Token: '0xe04a882e7a6adf5603207d545ea49aec17e6b936c4d9eae3d74dbe482264991a',
|
||||||
|
Proposal: '0xaf5fe5c3b0d9581ee88974bbc8699e6fa71efd1b321e44b2227103c9ef21dbdb'
|
||||||
|
}[appName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Kernel;
|
@ -1,6 +0,0 @@
|
|||||||
const Base = require('./base');
|
|
||||||
|
|
||||||
class Registry extends Base {
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Registry;
|
|
@ -4,13 +4,19 @@ const RSVP = require('rsvp');
|
|||||||
const Preflight = require('./utils/preflight');
|
const Preflight = require('./utils/preflight');
|
||||||
|
|
||||||
const ABIS = {
|
const ABIS = {
|
||||||
Contributors: require('./abis/Contributors.json'),
|
Contributor: require('./abis/Contributor.json'),
|
||||||
Contribution: require('./abis/Contribution.json'),
|
Contribution: require('./abis/Contribution.json'),
|
||||||
Operator: require('./abis/Operator.json'),
|
Token: require('./abis/Token.json'),
|
||||||
Registry: require('./abis/Registry.json'),
|
Proposal: require('./abis/Proposal.json'),
|
||||||
Token: require('./abis/Token.json')
|
Kernel: require('./abis/Kernel.json')
|
||||||
};
|
};
|
||||||
const RegistryAddress = require('./addresses/Registry.json');
|
const APP_CONTRACTS = [
|
||||||
|
'Contributor',
|
||||||
|
'Contribution',
|
||||||
|
'Token',
|
||||||
|
'Proposal'
|
||||||
|
];
|
||||||
|
const DaoAddresses = require('./addresses/dao.json');
|
||||||
|
|
||||||
const Contracts = require('./contracts');
|
const Contracts = require('./contracts');
|
||||||
const IPFS = require('./utils/ipfs')
|
const IPFS = require('./utils/ipfs')
|
||||||
@ -28,18 +34,18 @@ class Kredits {
|
|||||||
|
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this.signer = signer;
|
this.signer = signer;
|
||||||
// by default we only need the registry address.
|
// by default we only need the DAO/Kernel address.
|
||||||
// the rest is loaded from there in the init() function
|
// the rest is loaded from there in the init() function
|
||||||
this.addresses = addresses || { Registry: RegistryAddress[this.provider.chainId.toString()] }; // chainID must be a string
|
this.addresses = addresses || { Kernel: DaoAddresses[this.provider.chainId.toString()] }; // chainID must be a string
|
||||||
this.abis = abis || ABIS;
|
this.abis = abis || ABIS;
|
||||||
this.ipfs = new IPFS(ipfsConfig);
|
this.ipfs = new IPFS(ipfsConfig);
|
||||||
this.contracts = {};
|
this.contracts = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
init(names) {
|
init(names) {
|
||||||
let contractsToLoad = names || Object.keys(ABIS);
|
let contractsToLoad = names || APP_CONTRACTS;
|
||||||
let addressPromises = contractsToLoad.map((contractName) => {
|
let addressPromises = contractsToLoad.map((contractName) => {
|
||||||
return this.Registry.functions.getProxyFor(contractName).then((address) => {
|
return this.Kernel.getApp(contractName).then((address) => {
|
||||||
this.addresses[contractName] = address;
|
this.addresses[contractName] = address;
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
throw new Error(`Failed to get address for ${contractName} from registry at ${this.Registry.contract.address}
|
throw new Error(`Failed to get address for ${contractName} from registry at ${this.Registry.contract.address}
|
||||||
@ -55,13 +61,13 @@ class Kredits {
|
|||||||
return new Kredits(provider, signer, { ipfsConfig: ipfsConfig }).init();
|
return new Kredits(provider, signer, { ipfsConfig: ipfsConfig }).init();
|
||||||
}
|
}
|
||||||
|
|
||||||
get Registry() {
|
get Kernel() {
|
||||||
return this.contractFor('registry');
|
return this.contractFor('Kernel');
|
||||||
}
|
}
|
||||||
|
|
||||||
get Contributor() {
|
get Contributor() {
|
||||||
// TODO: rename to contributor
|
// TODO: rename to contributor
|
||||||
return this.contractFor('contributors');
|
return this.contractFor('Contributor');
|
||||||
}
|
}
|
||||||
|
|
||||||
get Contributors() {
|
get Contributors() {
|
||||||
@ -69,16 +75,16 @@ class Kredits {
|
|||||||
return this.Contributor;
|
return this.Contributor;
|
||||||
}
|
}
|
||||||
|
|
||||||
get Operator() {
|
get Proposal() {
|
||||||
return this.contractFor('operator');
|
return this.contractFor('Proposal');
|
||||||
}
|
}
|
||||||
|
|
||||||
get Token() {
|
get Token() {
|
||||||
return this.contractFor('token');
|
return this.contractFor('Token');
|
||||||
}
|
}
|
||||||
|
|
||||||
get Contribution() {
|
get Contribution() {
|
||||||
return this.contractFor('contribution');
|
return this.contractFor('Contribution');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should be private
|
// Should be private
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
var Registry = artifacts.require('./upgradeable/Registry.sol');
|
|
||||||
|
|
||||||
module.exports = function(deployer) {
|
|
||||||
deployer.deploy(Registry);
|
|
||||||
};
|
|
@ -1,15 +0,0 @@
|
|||||||
var Registry = artifacts.require('./Registry.sol');
|
|
||||||
var Token = artifacts.require('./Token.sol');
|
|
||||||
|
|
||||||
module.exports = function(deployer) {
|
|
||||||
deployer.deploy(Token).then(function(token) {
|
|
||||||
console.log('Registry address: ', Registry.address);
|
|
||||||
console.log('Token address: ', Token.address);
|
|
||||||
Registry.deployed().then(function(registry) {
|
|
||||||
registry.addVersion('Token', Token.address);
|
|
||||||
registry.createProxy('Token', 1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
|||||||
var Registry = artifacts.require('./Registry.sol');
|
|
||||||
var Contributors = artifacts.require('./Contributors.sol');
|
|
||||||
|
|
||||||
module.exports = function(deployer) {
|
|
||||||
deployer.deploy(Contributors).then(function(contributors) {
|
|
||||||
console.log('Registry address: ', Registry.address);
|
|
||||||
console.log('Contributors address: ', Contributors.address);
|
|
||||||
Registry.deployed().then(function(registry) {
|
|
||||||
registry.addVersion('Contributors', Contributors.address);
|
|
||||||
registry.createProxy('Contributors', 1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
@ -1,13 +0,0 @@
|
|||||||
var Registry = artifacts.require('./Registry.sol');
|
|
||||||
var Operator = artifacts.require('./Operator.sol');
|
|
||||||
|
|
||||||
module.exports = function(deployer) {
|
|
||||||
deployer.deploy(Operator).then(function(operator) {
|
|
||||||
console.log('Registry address: ', Registry.address);
|
|
||||||
console.log('Operator address: ', Operator.address);
|
|
||||||
Registry.deployed().then(function(registry) {
|
|
||||||
registry.addVersion('Operator', Operator.address);
|
|
||||||
registry.createProxy('Operator', 1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
@ -1,13 +0,0 @@
|
|||||||
var Registry = artifacts.require('./Registry.sol');
|
|
||||||
var Contribution = artifacts.require('./Contribution.sol');
|
|
||||||
|
|
||||||
module.exports = function(deployer) {
|
|
||||||
deployer.deploy(Contribution).then(function(contribution) {
|
|
||||||
console.log('Registry address: ', Registry.address);
|
|
||||||
console.log('Contribution address: ', Contribution.address);
|
|
||||||
Registry.deployed().then(function(registry) {
|
|
||||||
registry.addVersion('Contribution', Contribution.address);
|
|
||||||
registry.createProxy('Contribution', 1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
14329
package-lock.json
generated
14329
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@ -7,7 +7,7 @@
|
|||||||
"test": "test"
|
"test": "test"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build-json": "truffle compile && node ./scripts/build-json.js",
|
"build-json": "aragon contracts compile --all && node ./scripts/build-json.js",
|
||||||
"reset": "truffle migrate --reset && npm run build-json",
|
"reset": "truffle migrate --reset && npm run build-json",
|
||||||
"bootstrap": "npm run reset && truffle exec scripts/seeds.js",
|
"bootstrap": "npm run reset && truffle exec scripts/seeds.js",
|
||||||
"ganache": "ganache-cli -p 7545 -i 100 --db=./.ganache-db -m kredits",
|
"ganache": "ganache-cli -p 7545 -i 100 --db=./.ganache-db -m kredits",
|
||||||
@ -25,11 +25,13 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/67P/truffle-kredits#readme",
|
"homepage": "https://github.com/67P/truffle-kredits#readme",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@aragon/cli": "^5.5.0",
|
||||||
|
"@aragon/kits-base": "^1.0.0",
|
||||||
|
"@aragon/os": "^4.1.0",
|
||||||
"async-each-series": "^1.1.0",
|
"async-each-series": "^1.1.0",
|
||||||
"ganache-cli": "^6.1.8",
|
"openzeppelin-solidity": "^2.2.0",
|
||||||
"promptly": "^3.0.3",
|
"promptly": "^3.0.3",
|
||||||
"truffle": "^4.1.14",
|
"solc": "^0.4.25"
|
||||||
"zeppelin-solidity": "^1.7.0"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ethers": "3.0.15",
|
"ethers": "3.0.15",
|
||||||
|
43
scripts/add-contribution.js
Normal file
43
scripts/add-contribution.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
const promptly = require('promptly');
|
||||||
|
|
||||||
|
const ethers = require('ethers');
|
||||||
|
const Kredits = require('../lib/kredits');
|
||||||
|
|
||||||
|
module.exports = async function(callback) {
|
||||||
|
const networkId = parseInt(web3.version.network);
|
||||||
|
const provider = new ethers.providers.Web3Provider(
|
||||||
|
web3.currentProvider, { chainId: networkId }
|
||||||
|
);
|
||||||
|
const kredits = await new Kredits(provider, provider.getSigner()).init();
|
||||||
|
|
||||||
|
console.log(`Using Contributions at: ${kredits.Contribution.contract.address}`);
|
||||||
|
|
||||||
|
let contributor = await promptly.prompt('Contributor (address or id): ');
|
||||||
|
let contributorId;
|
||||||
|
if (contributor.length < 5) {
|
||||||
|
contributorAccount = await kredits.Contributor.functions.getContributorAddressById(contributor);
|
||||||
|
} else {
|
||||||
|
contributorAccount = contributor;
|
||||||
|
}
|
||||||
|
console.log(`Creating a contribution for contributor Account #${contributorAccount}`);
|
||||||
|
|
||||||
|
let contributionAttributes = {
|
||||||
|
contributorAccount,
|
||||||
|
amount: await promptly.prompt('Amount: '),
|
||||||
|
description: await promptly.prompt('Description: '),
|
||||||
|
kind: await promptly.prompt('Kind: ', { default: 'dev' }),
|
||||||
|
url: await promptly.prompt('URL: ', { default: '' })
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("\nAdding contribution:");
|
||||||
|
console.log(contributionAttributes);
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
}
|
@ -1,4 +1,3 @@
|
|||||||
const Registry = artifacts.require('./Registry.sol');
|
|
||||||
const promptly = require('promptly');
|
const promptly = require('promptly');
|
||||||
|
|
||||||
const ethers = require('ethers');
|
const ethers = require('ethers');
|
||||||
@ -11,39 +10,35 @@ async function prompt(message, options) {
|
|||||||
return await promptly.prompt(message, options);
|
return await promptly.prompt(message, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = function(callback) {
|
module.exports = async function(callback) {
|
||||||
Registry.deployed().then(async (registry) => {
|
const networkId = parseInt(web3.version.network);
|
||||||
|
const provider = new ethers.providers.Web3Provider(
|
||||||
|
web3.currentProvider, { chainId: networkId }
|
||||||
|
);
|
||||||
|
const kredits = await new Kredits(provider, provider.getSigner()).init();
|
||||||
|
|
||||||
const networkId = parseInt(web3.version.network);
|
console.log(`Using contributors at: ${kredits.Contributor.contract.address}`);
|
||||||
const provider = new ethers.providers.Web3Provider(
|
|
||||||
web3.currentProvider, { chainId: networkId }
|
|
||||||
);
|
|
||||||
const kredits = await Kredits.setup(provider, provider.getSigner());
|
|
||||||
|
|
||||||
console.log(`Using contributors at: ${kredits.Contributor.contract.address}`);
|
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: await prompt('GitHub UID: '),
|
||||||
|
wiki_username: await prompt('Wiki username: '),
|
||||||
|
};
|
||||||
|
|
||||||
let contributorAttributes = {
|
console.log("\nAdding contributor:");
|
||||||
account: await prompt('Contributor address: ', {}),
|
console.log(contributorAttributes);
|
||||||
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: await prompt('GitHub UID: '),
|
|
||||||
wiki_username: await prompt('Wiki username: '),
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log("\nAdding contributor:");
|
|
||||||
console.log(contributorAttributes);
|
|
||||||
|
|
||||||
kredits.Contributor.add(contributorAttributes, { gasLimit: 250000 }).then((result) => {
|
|
||||||
console.log("\n\nResult:");
|
|
||||||
console.log(result);
|
|
||||||
callback();
|
|
||||||
}).catch((error) => {
|
|
||||||
console.log('Failed to create contributor');
|
|
||||||
callback(error);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
kredits.Contributor.add(contributorAttributes, { gasLimit: 250000 }).then((result) => {
|
||||||
|
console.log("\n\nResult:");
|
||||||
|
console.log(result);
|
||||||
|
callback();
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log('Failed to create contributor');
|
||||||
|
callback(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,46 +1,43 @@
|
|||||||
const Registry = artifacts.require('./Registry.sol');
|
|
||||||
const promptly = require('promptly');
|
const promptly = require('promptly');
|
||||||
|
|
||||||
const ethers = require('ethers');
|
const ethers = require('ethers');
|
||||||
const Kredits = require('../lib/kredits');
|
const Kredits = require('../lib/kredits');
|
||||||
|
|
||||||
module.exports = function(callback) {
|
module.exports = async function(callback) {
|
||||||
Registry.deployed().then(async (registry) => {
|
const networkId = parseInt(web3.version.network);
|
||||||
const networkId = parseInt(web3.version.network);
|
const provider = new ethers.providers.Web3Provider(
|
||||||
const provider = new ethers.providers.Web3Provider(
|
web3.currentProvider, { chainId: networkId }
|
||||||
web3.currentProvider, { chainId: networkId }
|
);
|
||||||
);
|
const kredits = await new Kredits(provider, provider.getSigner()).init();
|
||||||
const kredits = await Kredits.setup(provider, provider.getSigner());
|
|
||||||
|
|
||||||
console.log(`Using operator at: ${kredits.Operator.contract.address}`);
|
console.log(`Using Proposal at: ${kredits.Proposal.contract.address}`);
|
||||||
|
|
||||||
let contributor = await promptly.prompt('Contributor (address or id): ');
|
let contributor = await promptly.prompt('Contributor (address or id): ');
|
||||||
let contributorId;
|
let contributorId;
|
||||||
if (contributor.length < 5) {
|
if (contributor.length < 5) {
|
||||||
contributorId = contributor;
|
contributorId = contributor;
|
||||||
} else {
|
} else {
|
||||||
contributorId = await kredits.Contributor.functions.getContributorIdByAddress(contributor);
|
contributorId = await kredits.Contributor.functions.getContributorIdByAddress(contributor);
|
||||||
}
|
}
|
||||||
console.log(`Creating a proposal for contributor ID #${contributorId}`);
|
console.log(`Creating a proposal for contributor ID #${contributorId}`);
|
||||||
|
|
||||||
let contributionAttributes = {
|
let contributionAttributes = {
|
||||||
contributorId,
|
contributorId,
|
||||||
amount: await promptly.prompt('Amount: '),
|
amount: await promptly.prompt('Amount: '),
|
||||||
description: await promptly.prompt('Description: '),
|
description: await promptly.prompt('Description: '),
|
||||||
kind: await promptly.prompt('Kind: ', { default: 'dev' }),
|
kind: await promptly.prompt('Kind: ', { default: 'dev' }),
|
||||||
url: await promptly.prompt('URL: ', { default: '' })
|
url: await promptly.prompt('URL: ', { default: '' })
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("\nAdding proposal:");
|
console.log("\nAdding proposal:");
|
||||||
console.log(contributionAttributes);
|
console.log(contributionAttributes);
|
||||||
|
|
||||||
kredits.Operator.addProposal(contributionAttributes, { gasLimit: 300000 }).then((result) => {
|
kredits.Proposal.addProposal(contributionAttributes, { gasLimit: 300000 }).then((result) => {
|
||||||
console.log("\n\nResult:");
|
console.log("\n\nResult:");
|
||||||
console.log(result);
|
console.log(result);
|
||||||
callback();
|
callback();
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.log('Failed to create proposal');
|
console.log('Failed to create proposal');
|
||||||
console.log(error);
|
callback(error);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,12 @@ const path = require('path');
|
|||||||
const contractsPath = path.join(__dirname, '..', 'build', 'contracts');
|
const contractsPath = path.join(__dirname, '..', 'build', 'contracts');
|
||||||
const libPath = path.join(__dirname, '..', 'lib');
|
const libPath = path.join(__dirname, '..', 'lib');
|
||||||
const abisPath = path.join(libPath, 'abis');
|
const abisPath = path.join(libPath, 'abis');
|
||||||
const addressesPath = path.join(libPath, 'addresses');
|
|
||||||
|
|
||||||
const files = [
|
const files = [
|
||||||
'Contributors',
|
'Contributor',
|
||||||
'Contribution',
|
'Contribution',
|
||||||
'Operator',
|
'Kernel',
|
||||||
'Registry',
|
'Proposal',
|
||||||
'Token'
|
'Token'
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -18,17 +17,6 @@ files.forEach((fileName) => {
|
|||||||
let file = require(`${contractsPath}/${fileName}.json`);
|
let file = require(`${contractsPath}/${fileName}.json`);
|
||||||
let abiFile = path.join(abisPath, `${fileName}.json`);
|
let abiFile = path.join(abisPath, `${fileName}.json`);
|
||||||
fs.writeFileSync(abiFile, JSON.stringify(file.abi));
|
fs.writeFileSync(abiFile, JSON.stringify(file.abi));
|
||||||
|
|
||||||
if (fileName === 'Registry') {
|
|
||||||
let addresseFile = path.join(addressesPath, `${fileName}.json`);
|
|
||||||
let content = fs.readFileSync(addresseFile);
|
|
||||||
let addresses = Object.keys(file.networks)
|
|
||||||
.reduce((addresses, key) => {
|
|
||||||
addresses[key] = file.networks[key].address;
|
|
||||||
return addresses;
|
|
||||||
}, JSON.parse(content));
|
|
||||||
fs.writeFileSync(addresseFile, JSON.stringify(addresses));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("Don't forget to reaload the JSON files from your application; i.e. restart kredits-web");
|
console.log("Don't forget to reaload the JSON files from your application; i.e. restart kredits-web");
|
||||||
|
@ -5,14 +5,12 @@ const ethers = require('ethers');
|
|||||||
const Kredits = require('../lib/kredits');
|
const Kredits = require('../lib/kredits');
|
||||||
|
|
||||||
module.exports = function(callback) {
|
module.exports = function(callback) {
|
||||||
const Registry = artifacts.require('./Registry.sol');
|
const networkId = parseInt(web3.version.network);
|
||||||
Registry.deployed().then(async (registry) => {
|
const provider = new ethers.providers.Web3Provider(
|
||||||
const networkId = parseInt(web3.version.network);
|
web3.currentProvider, { chainId: networkId }
|
||||||
const provider = new ethers.providers.Web3Provider(
|
);
|
||||||
web3.currentProvider, { chainId: networkId }
|
|
||||||
);
|
|
||||||
const kredits = await Kredits.setup(provider, provider.getSigner());
|
|
||||||
|
|
||||||
|
new Kredits(provider, provider.getSigner()).init().then(async function(kredits) {
|
||||||
let contractName = await promptly.prompt('Contract Name: ');
|
let contractName = await promptly.prompt('Contract Name: ');
|
||||||
const contractWrapper = kredits[contractName];
|
const contractWrapper = kredits[contractName];
|
||||||
|
|
||||||
|
49
scripts/deploy-kit.js
Normal file
49
scripts/deploy-kit.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
const deployDAOFactory = require('@aragon/os/scripts/deploy-daofactory.js')
|
||||||
|
const KreditsKit = artifacts.require('KreditsKit')
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const libPath = path.join(__dirname, '..', 'lib');
|
||||||
|
const addressesPath = path.join(libPath, 'addresses');
|
||||||
|
|
||||||
|
const ensAddr = process.env.ENS
|
||||||
|
|
||||||
|
module.exports = async (callback) => {
|
||||||
|
if (!ensAddr) {
|
||||||
|
callback(new Error("ENS address not found in environment variable ENS"))
|
||||||
|
}
|
||||||
|
|
||||||
|
deployDAOFactory(null, { artifacts, verbose: false })
|
||||||
|
.catch(console.log)
|
||||||
|
.then((result) => {
|
||||||
|
const daoFactory = result.daoFactory
|
||||||
|
|
||||||
|
KreditsKit.new(daoFactory.address, ensAddr)
|
||||||
|
.catch(console.log)
|
||||||
|
.then((kreditsKit) => {
|
||||||
|
console.log(kreditsKit.address)
|
||||||
|
|
||||||
|
kreditsKit.newInstance().then((ret) => {
|
||||||
|
console.log(ret.logs);
|
||||||
|
const installedEvents = ret.logs.filter(log => log.event === 'InstalledApp').map(log => log.args)
|
||||||
|
const deployEvents = ret.logs.filter(log => log.event === 'DeployInstance').map(log => log.args)
|
||||||
|
|
||||||
|
if (deployEvents.length > 1) {
|
||||||
|
callback(new Error("More than one DAO was deployed. Something is wrong"))
|
||||||
|
}
|
||||||
|
const daoAddress = deployEvents[0].dao;
|
||||||
|
const networkId = parseInt(web3.version.network);
|
||||||
|
|
||||||
|
let addresseFile = path.join(addressesPath, `dao.json`);
|
||||||
|
let addresses = JSON.parse(fs.readFileSync(addresseFile));
|
||||||
|
|
||||||
|
addresses[networkId] = daoAddress;
|
||||||
|
fs.writeFileSync(addresseFile, JSON.stringify(addresses));
|
||||||
|
|
||||||
|
callback();
|
||||||
|
}).catch((e) => {
|
||||||
|
console.log(e);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
@ -1,18 +1,16 @@
|
|||||||
const REPL = require('repl');
|
const REPL = require('repl');
|
||||||
const promptly = require('promptly');
|
|
||||||
|
|
||||||
const ethers = require('ethers');
|
const ethers = require('ethers');
|
||||||
const Kredits = require('../lib/kredits');
|
const Kredits = require('../lib/kredits');
|
||||||
|
|
||||||
module.exports = function(callback) {
|
module.exports = function(callback) {
|
||||||
const Registry = artifacts.require('./Registry.sol');
|
const networkId = parseInt(web3.version.network);
|
||||||
Registry.deployed().then(async (registry) => {
|
const provider = new ethers.providers.Web3Provider(
|
||||||
const networkId = parseInt(web3.version.network);
|
web3.currentProvider, { chainId: networkId }
|
||||||
const provider = new ethers.providers.Web3Provider(
|
);
|
||||||
web3.currentProvider, { chainId: networkId }
|
|
||||||
);
|
new Kredits(provider, provider.getSigner()).init().then((kredits) => {
|
||||||
const kredits = await Kredits.setup(provider, provider.getSigner());
|
console.log(`Defined variables: kredits, web3`);
|
||||||
console.log(`defined variables: kredits, web3`);
|
|
||||||
let r = REPL.start();
|
let r = REPL.start();
|
||||||
r.context.kredits = kredits;
|
r.context.kredits = kredits;
|
||||||
r.context.web3 = web3;
|
r.context.web3 = web3;
|
||||||
|
73
truffle.js
73
truffle.js
@ -1,15 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* https://github.com/aragon/aragonOS/blob/v4.0.0/truffle-config.js
|
||||||
|
*/
|
||||||
|
const homedir = require('homedir')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
const HDWalletProvider = require('truffle-hdwallet-provider')
|
||||||
|
const HDWalletProviderPrivkey = require('truffle-hdwallet-provider-privkey')
|
||||||
|
|
||||||
|
const DEFAULT_MNEMONIC = 'explain tackle mirror kit van hammer degree position ginger unfair soup bonus'
|
||||||
|
|
||||||
|
const defaultRPC = (network) =>
|
||||||
|
`https://${network}.infura.io`
|
||||||
|
|
||||||
|
const configFilePath = (filename) =>
|
||||||
|
path.join(homedir(), `.aragon/${filename}`)
|
||||||
|
|
||||||
|
const mnemonic = () => {
|
||||||
|
try {
|
||||||
|
return require(configFilePath('mnemonic.json')).mnemonic
|
||||||
|
} catch (e) {
|
||||||
|
return DEFAULT_MNEMONIC
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const settingsForNetwork = (network) => {
|
||||||
|
try {
|
||||||
|
return require(configFilePath(`${network}_key.json`))
|
||||||
|
} catch (e) {
|
||||||
|
return { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lazily loaded provider
|
||||||
|
const providerForNetwork = (network) => (
|
||||||
|
() => {
|
||||||
|
let { rpc, keys } = settingsForNetwork(network)
|
||||||
|
rpc = rpc || defaultRPC(network)
|
||||||
|
|
||||||
|
if (!keys || keys.length == 0) {
|
||||||
|
return new HDWalletProvider(mnemonic(), rpc)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HDWalletProviderPrivkey(keys, rpc)
|
||||||
|
}
|
||||||
|
)
|
||||||
module.exports = {
|
module.exports = {
|
||||||
// See <http://truffleframework.com/docs/advanced/configuration>
|
|
||||||
networks: {
|
networks: {
|
||||||
development: {
|
rpc: {
|
||||||
host: "127.0.0.1",
|
host: 'localhost',
|
||||||
port: 7545,
|
|
||||||
network_id: "*" // Match any network id
|
|
||||||
},
|
|
||||||
kovan: {
|
|
||||||
host: "127.0.0.1",
|
|
||||||
port: 8545,
|
port: 8545,
|
||||||
network_id: "42"
|
network_id: '*'
|
||||||
|
},
|
||||||
|
development: {
|
||||||
|
host: 'localhost',
|
||||||
|
port: 8545,
|
||||||
|
network_id: '*'
|
||||||
|
},
|
||||||
|
mainnet: {
|
||||||
|
network_id: 1,
|
||||||
|
provider: providerForNetwork('mainnet')
|
||||||
|
},
|
||||||
|
rinkeby: {
|
||||||
|
network_id: 4,
|
||||||
|
provider: providerForNetwork('rinkeby')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user