refactor contrats with aragonos

This commit is contained in:
bumi 2019-03-22 18:15:16 +01:00
parent d687ff604e
commit 6c569239de
97 changed files with 85411 additions and 1155 deletions

2
.gitignore vendored
View File

@ -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
View File

@ -0,0 +1,4 @@
node_modules
build
.cache
dist

View 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
View 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"
]
}
```

View 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"
}

View 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;
}
}

View File

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

View File

@ -0,0 +1,4 @@
{
"name": "Contribution",
"description": "Kredits contribution app"
}

View File

@ -0,0 +1,5 @@
var Migrations = artifacts.require('./Migrations.sol')
module.exports = function (deployer) {
deployer.deploy(Migrations)
}

View 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

File diff suppressed because it is too large Load Diff

View 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": []
}

View File

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

View 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
View File

@ -0,0 +1,4 @@
node_modules
build
.cache
dist

View 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
View 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"
]
}
```

View 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"
}

View File

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

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

View File

@ -0,0 +1,4 @@
{
"name": "Contributor",
"description": "Kredits Contributor app"
}

View File

@ -0,0 +1,5 @@
var Migrations = artifacts.require('./Migrations.sol')
module.exports = function (deployer) {
deployer.deploy(Migrations)
}

View 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

File diff suppressed because it is too large Load Diff

View 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": []
}

View File

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

View 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
View 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
View 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"
}

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

View File

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

View 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

File diff suppressed because it is too large Load Diff

View 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
View 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
View File

@ -0,0 +1,4 @@
node_modules
build
.cache
dist

14
apps/token/.ipfsignore Normal file
View 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
View 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
View 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"
}

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

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

View 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
View File

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

View File

@ -0,0 +1,5 @@
var Migrations = artifacts.require('./Migrations.sol')
module.exports = function (deployer) {
deployer.deploy(Migrations)
}

View 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

File diff suppressed because it is too large Load Diff

28
apps/token/package.json Normal file
View 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
View 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
View 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
View 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"
}

View File

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

View File

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

View File

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

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

View 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();
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because one or more lines are too long

View File

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

File diff suppressed because one or more lines are too long

View File

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

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

@ -1 +0,0 @@
{"42":"0x205fe1b3dac678b594c5f0535e7d158e38591f93","100":"0xa7fc9b1f678c41396b53904f94f50a42ff44d826"}

View File

@ -1 +0,0 @@
{"42":"0x9fd66ee78a5ebe86006f12b37ff59c63f9caa15b","100":"0x95d3bd7d136bb0b7ac9988097e964236f8a9976e"}

View File

@ -1 +0,0 @@
{"42":"0xc270e6ea4fe303df9f1a3d4a132ac425264082e7","100":"0x7458dea485d9d8301e3ce43e8a1ec1456be5ba83"}

View File

@ -1 +0,0 @@
{"42":"0xf71ccf7ab48044ef9ae0b5e6983dbd3266b78b36","100":"0x3fc29fbe40c2d0ca78c7e81342f00226650fe2ad"}

1
lib/addresses/dao.json Normal file
View File

@ -0,0 +1 @@
{"23827572":"0xe4e0e7fe54d9189df29a80c07ab733fc9a212761","65047207":"0xdbe26ae4a434472a7474db60cc3e3c97a57bbf38"}

View File

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

View File

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

View File

@ -1,6 +0,0 @@
const Base = require('./base');
class Registry extends Base {
}
module.exports = Registry;

View File

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

View File

@ -1,5 +0,0 @@
var Registry = artifacts.require('./upgradeable/Registry.sol');
module.exports = function(deployer) {
deployer.deploy(Registry);
};

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -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",

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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