refactor contrats with aragonos

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

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

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