refactor contrats with aragonos
This commit is contained in:
162
apps/proposal/README.md
Normal file
162
apps/proposal/README.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# Aragon React Boilerplate
|
||||
|
||||
> 🕵️ [Find more boilerplates using GitHub](https://github.com/search?q=topic:aragon-boilerplate) |
|
||||
> ✨ [Official boilerplates](https://github.com/search?q=topic:aragon-boilerplate+org:aragon)
|
||||
|
||||
React boilerplate for Aragon applications.
|
||||
|
||||
This boilerplate also includes a fully working example app, complete with a background worker and a front-end in React (with Aragon UI).
|
||||
|
||||
## Usage
|
||||
|
||||
```sh
|
||||
aragon init app.aragonpm.eth react
|
||||
```
|
||||
|
||||
## Running your app
|
||||
|
||||
### Using HTTP
|
||||
|
||||
Running your app using HTTP will allow for a faster development process of your app's front-end, as it can be hot-reloaded without the need to execute `aragon run` every time a change is made.
|
||||
|
||||
- First start your app's development server running `npm run start:app`, and keep that process running. By default it will rebuild the app and reload the server when changes to the source are made.
|
||||
|
||||
- After that, you can run `npm run start:aragon:http` which will compile your app's contracts, publish the app locally and create a DAO. You will need to stop it and run it again after making changes to your smart contracts.
|
||||
|
||||
Changes to the app's background script (`app/script.js`) cannot be hot-reloaded, after making changes to the script, you will need to either restart the development server (`npm run start:app`) or rebuild the script `npm run build:script`.
|
||||
|
||||
### Using IPFS
|
||||
|
||||
Running your app using IPFS will mimic the production environment that will be used for running your app. `npm run start:aragon:ipfs` will run your app using IPFS. Whenever a change is made to any file in your front-end, a new version of the app needs to be published, so the command needs to be restarted.
|
||||
|
||||
## What's in the box?
|
||||
|
||||
### npm Scripts
|
||||
|
||||
- **start** or **start:aragon:ipfs**: Runs your app inside a DAO served from IPFS
|
||||
- **start:aragon:http**: Runs your app inside a DAO served with HTTP (hot reloading)
|
||||
- **start:app**: Starts a development server for your app
|
||||
- **compile**: Compile the smart contracts
|
||||
- **build**: Builds the front-end and background script
|
||||
- **build:app**: Builds the front-end
|
||||
- **build:script**: Builds the background script
|
||||
- **test**: Runs tests for the contracts
|
||||
- **publish:patch**: Release a patch version to aragonPM (only frontend/content changes allowed)
|
||||
- **publish:minor**: Release a minor version to aragonPM (only frontend/content changes allowed)
|
||||
- **publish:major**: Release a major version to aragonPM (frontend **and** contract changes)
|
||||
- **versions**: Check the currently installed versions of the app
|
||||
|
||||
### Libraries
|
||||
|
||||
- [**@aragon/os**](https://github.com/aragon/aragonos): Aragon interfaces
|
||||
- [**@aragon/client**](https://github.com/aragon/aragon.js/tree/master/packages/aragon-client): Wrapper for Aragon application RPC
|
||||
- [**@aragon/ui**](https://github.com/aragon/aragon-ui): Aragon UI components (in React)
|
||||
|
||||
## Publish
|
||||
|
||||
This app has 3 environments defined in `arapp.json`:
|
||||
|
||||
| Environment | Network |
|
||||
|--- |--- |
|
||||
| default | localhost |
|
||||
| staging | rinkeby |
|
||||
| production | mainnet |
|
||||
|
||||
Prerequisites:
|
||||
- ENS Registry address
|
||||
|
||||
Note: the `default` environment which points to `localhost` does not have an ENS Registry address specified because the `@aragon/cli` will default the value to `0xB9462EF3441346dBc6E49236Edbb0dF207db09B7` (the ENS Registry pre-deployed on the local development chain).
|
||||
|
||||
### Introduction to environments
|
||||
|
||||
Environments are defined in `arapp.json`, for example `staging` points to:
|
||||
- an ENS registry (`0x314159265dd8dbb310642f98f50c066173c1259b`)
|
||||
- an APM registry (`open.aragonpm.eth`)
|
||||
- an APM repository (`app`)
|
||||
- an Ethereum network (`rinkeby`)
|
||||
- an Ethereum websockets provider (`wss://rinkeby.eth.aragon.network/ws` - to **read** from the blockchain)
|
||||
|
||||
The `rinkeby` network is further defined in `truffle.js`, and has:
|
||||
- an Ethereum provider (to **write** to the blockchain):
|
||||
- an address (`https://rinkeby.infura.io`)
|
||||
- an Ethereum Account (`0xb4124cEB3451635DAcedd11767f004d8a28c6eE7`)
|
||||
(which is the first account generated from the `DEFAULT_MNEMONIC` variable, to use a different account see [here](#Using-a-different-Ethereum-account))
|
||||
|
||||
### Major version: content + contract
|
||||
|
||||
Command:
|
||||
```
|
||||
npm run publish:major -- --environment staging
|
||||
```
|
||||
|
||||
This will:
|
||||
1. _build_ the app's frontend (the output lives in `dist`)
|
||||
2. _compile_ the app's contract (the output lives in `build`)
|
||||
3. publish the app to the **staging** environment.
|
||||
|
||||
Sample output:
|
||||
```
|
||||
> aragon apm publish major "--environment" "staging"
|
||||
|
||||
✔ Successfully published app.open.aragonpm.eth v1.0.0:
|
||||
ℹ Contract address: 0xE636bcA5B95e94F749F63E322a04DB59362299F1
|
||||
ℹ Content (ipfs): QmR695Wu5KrHNec7pRP3kPvwYihABDAyVYdX5D5vwLgxCn
|
||||
ℹ Transaction hash: 0x3d752db29cc106e9ff98b260a90615921eb32471425a29ead8cbb830fb224d8
|
||||
```
|
||||
|
||||
Note: the contract location is defined in `arapp.json` under `path`.
|
||||
Note: you can also deploy a major version with only frontend changes by passing `--only-content`.
|
||||
|
||||
### Minor/patch version: content only
|
||||
|
||||
Command:
|
||||
```
|
||||
npm run publish:patch -- --environment staging
|
||||
```
|
||||
|
||||
This will:
|
||||
1. _build_ the app's frontend (which lives in `dist`)
|
||||
2. publish the app to the **staging** environment.
|
||||
|
||||
Sample output:
|
||||
```
|
||||
✔ Successfully published app.open.aragonpm.eth v1.1.1:
|
||||
ℹ Contract address: 0xE636bcA5B95e94F749F63E322a04DB59362299F1
|
||||
ℹ Content (ipfs): QmUYv9cjyNVxCyAJGK2YXjkbzh6u4iW2ak81Z9obdefM1q
|
||||
ℹ Transaction hash: 0x57864d8efd8d439008621b494b19a3e8f876a8a46b38475f9626802f0a1403c2
|
||||
```
|
||||
|
||||
### Check published versions
|
||||
|
||||
Command:
|
||||
```
|
||||
npm run versions -- --environment staging
|
||||
```
|
||||
|
||||
Sample output:
|
||||
```
|
||||
ℹ app.open.aragonpm.eth has 4 published versions
|
||||
✔ 1.0.0: 0xE636bcA5B95e94F749F63E322a04DB59362299F1 ipfs:QmR695Wu5KrHNec7pRP3kPvwYihABDAyVYdX5D5vwLgxCn
|
||||
✔ 1.1.0: 0xE636bcA5B95e94F749F63E322a04DB59362299F1 ipfs:QmSwjUZFpv2c2e9fLoxtgFrAsAmBN4DyQGJp4RcqQcW3z3
|
||||
✔ 1.1.1: 0xE636bcA5B95e94F749F63E322a04DB59362299F1 ipfs:QmUYv9cjyNVxCyAJGK2YXjkbzh6u4iW2ak81Z9obdefM1q
|
||||
✔ 2.0.0: 0x74CBbbC932d7C344FCd789Eba24BfD40e52980c9 ipfs:Qmadb3hzwLDKtb93fF367Vg1epkdsLZF4dhpapNYynjgZF
|
||||
```
|
||||
|
||||
### Using a different Ethereum account
|
||||
|
||||
To deploy from a different account, you can:
|
||||
- define a `~/.aragon/mnemonic.json` file
|
||||
```
|
||||
{
|
||||
"mnemonic": "explain tackle mirror kit ..."
|
||||
}
|
||||
```
|
||||
or
|
||||
- define a `~/.aragon/${network_name}_key.json` file, for example: `~/.aragon/rinkeby_key.json`
|
||||
```
|
||||
{
|
||||
"keys": [
|
||||
"a8a54b2d8197bc0b19bb8a084031be71835580a01e70a45a13babd16c9bc1563"
|
||||
]
|
||||
}
|
||||
```
|
||||
43
apps/proposal/arapp.json
Normal file
43
apps/proposal/arapp.json
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"roles": [
|
||||
{
|
||||
"name": "Add proposal",
|
||||
"id": "ADD_PROPOSAL_ROLE",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "Vote proposals",
|
||||
"id": "VOTE_PROPOSAL_ROLE",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "Add contribution",
|
||||
"id": "ADD_CONTRIBUTION_ROLE",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "Manage contributors",
|
||||
"id": "MANAGE_CONTRIBUTORS_ROLE",
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
"environments": {
|
||||
"default": {
|
||||
"network": "development",
|
||||
"appName": "proposal.aragonpm.eth"
|
||||
},
|
||||
"staging": {
|
||||
"registry": "0x98df287b6c145399aaa709692c8d308357bc085d",
|
||||
"appName": "proposal.open.aragonpm.eth",
|
||||
"wsRPC": "wss://rinkeby.eth.aragon.network/ws",
|
||||
"network": "rinkeby"
|
||||
},
|
||||
"production": {
|
||||
"registry": "0x314159265dd8dbb310642f98f50c066173c1259b",
|
||||
"appName": "proposal.open.aragonpm.eth",
|
||||
"wsRPC": "wss://mainnet.eth.aragon.network/ws",
|
||||
"network": "mainnet"
|
||||
}
|
||||
},
|
||||
"path": "contracts/Proposal.sol"
|
||||
}
|
||||
132
apps/proposal/contracts/Proposal.sol
Normal file
132
apps/proposal/contracts/Proposal.sol
Normal file
@@ -0,0 +1,132 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
import "@aragon/os/contracts/apps/AragonApp.sol";
|
||||
import "@aragon/os/contracts/kernel/IKernel.sol";
|
||||
|
||||
interface IContributor {
|
||||
function getContributorAddressById(uint256 contributorId) public view returns (address);
|
||||
function getContributorIdByAddress(address contributorAccount) public view returns (uint256);
|
||||
function exists(uint256 contributorId) public view returns (bool);
|
||||
}
|
||||
|
||||
interface IContribution {
|
||||
function add(uint256 amount, address contributor, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public;
|
||||
}
|
||||
|
||||
contract Proposal is AragonApp {
|
||||
|
||||
bytes32 public constant ADD_PROPOSAL_ROLE = keccak256("ADD_PROPOSAL_ROLE");
|
||||
bytes32 public constant VOTE_PROPOSAL_ROLE = keccak256("VOTE_PROPOSAL_ROLE");
|
||||
|
||||
bytes32 public constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb;
|
||||
bytes32 public constant CONTRIBUTOR_APP_ID = 0xe9140f1e39c8a1d04167c3b710688a3eecea2976f34735c8eb98956f4764635b;
|
||||
bytes32 public constant CONTRIBUTION_APP_ID = 0x7fcf91283b719b30c2fa954ff0da021e1b91aed09d7aa13df5e8078a4a1007eb;
|
||||
|
||||
struct Proposal {
|
||||
address creatorAccount;
|
||||
uint contributorId;
|
||||
uint votesCount;
|
||||
uint votesNeeded;
|
||||
uint256 amount;
|
||||
bool executed;
|
||||
bytes32 hashDigest;
|
||||
uint8 hashFunction;
|
||||
uint8 hashSize;
|
||||
uint256[] voterIds;
|
||||
mapping (uint256 => bool) votes;
|
||||
bool exists;
|
||||
}
|
||||
|
||||
mapping(uint256 => Proposal) public proposals;
|
||||
uint256 public proposalsCount;
|
||||
|
||||
event ProposalCreated(uint256 id, address creatorAccount, uint256 contributorId, uint256 amount);
|
||||
|
||||
event ProposalVoted(uint256 id, uint256 voterId, uint256 totalVotes);
|
||||
event ProposalExecuted(uint256 id, uint256 contributorId, uint256 amount);
|
||||
|
||||
function initialize() public onlyInit {
|
||||
initialized();
|
||||
}
|
||||
|
||||
function getContributorContract() public view returns (address) {
|
||||
return IKernel(kernel()).getApp(KERNEL_APP_ADDR_NAMESPACE, CONTRIBUTOR_APP_ID);
|
||||
}
|
||||
|
||||
function getContributionContract() public view returns (address) {
|
||||
return IKernel(kernel()).getApp(KERNEL_APP_ADDR_NAMESPACE, CONTRIBUTION_APP_ID);
|
||||
}
|
||||
|
||||
function addProposal(uint contributorId, uint256 amount, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(ADD_PROPOSAL_ROLE) {
|
||||
require(IContributor(getContributorContract()).exists(contributorId), 'CONTRIBUTOR_NOT_FOUND');
|
||||
|
||||
uint256 proposalId = proposalsCount + 1;
|
||||
uint256 _votesNeeded = 1; //contributorsContract().coreContributorsCount() / 100 * 75;
|
||||
|
||||
Proposal storage p = proposals[proposalId];
|
||||
p.creatorAccount = msg.sender;
|
||||
p.contributorId = contributorId;
|
||||
p.amount = amount;
|
||||
p.hashDigest = hashDigest;
|
||||
p.hashFunction = hashFunction;
|
||||
p.hashSize = hashSize;
|
||||
p.votesCount = 0;
|
||||
p.votesNeeded = _votesNeeded;
|
||||
p.exists = true;
|
||||
|
||||
proposalsCount++;
|
||||
emit ProposalCreated(proposalId, msg.sender, p.contributorId, p.amount);
|
||||
}
|
||||
|
||||
function getProposal(uint proposalId) public view returns (uint256 id, address creatorAccount, uint256 contributorId, uint256 votesCount, uint256 votesNeeded, uint256 amount, bool executed, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint256[] voterIds, bool exists) {
|
||||
id = proposalId;
|
||||
Proposal storage p = proposals[id];
|
||||
return (
|
||||
id,
|
||||
p.creatorAccount,
|
||||
p.contributorId,
|
||||
p.votesCount,
|
||||
p.votesNeeded,
|
||||
p.amount,
|
||||
p.executed,
|
||||
p.hashDigest,
|
||||
p.hashFunction,
|
||||
p.hashSize,
|
||||
p.voterIds,
|
||||
p.exists
|
||||
);
|
||||
}
|
||||
|
||||
function vote(uint256 proposalId) public isInitialized auth(VOTE_PROPOSAL_ROLE) {
|
||||
Proposal storage p = proposals[proposalId];
|
||||
require(!p.executed, 'ALREADY_EXECUTED');
|
||||
uint256 voterId = IContributor(getContributorContract()).getContributorIdByAddress(msg.sender);
|
||||
require(p.votes[voterId] != true, 'ALREADY_VOTED');
|
||||
p.voterIds.push(voterId);
|
||||
p.votes[voterId] = true;
|
||||
|
||||
p.votesCount++;
|
||||
if (p.votesCount >= p.votesNeeded) {
|
||||
executeProposal(proposalId);
|
||||
}
|
||||
emit ProposalVoted(proposalId, voterId, p.votesCount);
|
||||
}
|
||||
|
||||
function batchVote(uint256[] _proposalIds) public isInitialized auth(VOTE_PROPOSAL_ROLE) {
|
||||
for (uint256 i = 0; i < _proposalIds.length; i++) {
|
||||
vote(_proposalIds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function executeProposal(uint proposalId) private {
|
||||
Proposal storage p = proposals[proposalId];
|
||||
require(!p.executed, 'ALREADY_EXECUTED');
|
||||
require(p.votesCount >= p.votesNeeded, 'MISSING_VOTES');
|
||||
|
||||
address contributorAccount = IContributor(getContributorContract()).getContributorAddressById(p.contributorId);
|
||||
IContribution(getContributionContract()).add(p.amount, contributorAccount, p.hashDigest, p.hashFunction, p.hashSize);
|
||||
p.executed = true;
|
||||
emit ProposalExecuted(proposalId, p.contributorId, p.amount);
|
||||
}
|
||||
|
||||
}
|
||||
4
apps/proposal/manifest.json
Normal file
4
apps/proposal/manifest.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "Proposal",
|
||||
"description": "Kredits proposal app"
|
||||
}
|
||||
5
apps/proposal/migrations/1_initial_migration.js
Normal file
5
apps/proposal/migrations/1_initial_migration.js
Normal file
@@ -0,0 +1,5 @@
|
||||
var Migrations = artifacts.require('./Migrations.sol')
|
||||
|
||||
module.exports = function (deployer) {
|
||||
deployer.deploy(Migrations)
|
||||
}
|
||||
21093
apps/proposal/package-lock.json
generated
Normal file
21093
apps/proposal/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
28
apps/proposal/package.json
Normal file
28
apps/proposal/package.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "kredits-proposal",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"@aragon/os": "^4.1.0",
|
||||
"@aragon/cli": "^5.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
},
|
||||
"scripts": {
|
||||
"start": "npm run start:aragon:ipfs",
|
||||
"start:aragon:ipfs": "aragon run",
|
||||
"start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist",
|
||||
"start:app": "",
|
||||
"test": "aragon contracts test",
|
||||
"compile": "aragon contracts compile",
|
||||
"sync-assets": "",
|
||||
"build:app": "",
|
||||
"build:script": "",
|
||||
"build": "",
|
||||
"publish:patch": "aragon apm publish patch",
|
||||
"publish:minor": "aragon apm publish minor",
|
||||
"publish:major": "aragon apm publish major",
|
||||
"versions": "aragon apm versions"
|
||||
},
|
||||
"keywords": []
|
||||
}
|
||||
63
apps/proposal/truffle.js
Normal file
63
apps/proposal/truffle.js
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* https://github.com/aragon/aragonOS/blob/v4.0.0/truffle-config.js
|
||||
*/
|
||||
const homedir = require('homedir')
|
||||
const path = require('path')
|
||||
|
||||
const HDWalletProvider = require('truffle-hdwallet-provider')
|
||||
const HDWalletProviderPrivkey = require('truffle-hdwallet-provider-privkey')
|
||||
|
||||
const DEFAULT_MNEMONIC = 'explain tackle mirror kit van hammer degree position ginger unfair soup bonus'
|
||||
|
||||
const defaultRPC = (network) =>
|
||||
`https://${network}.infura.io`
|
||||
|
||||
const configFilePath = (filename) =>
|
||||
path.join(homedir(), `.aragon/${filename}`)
|
||||
|
||||
const mnemonic = () => {
|
||||
try {
|
||||
return require(configFilePath('mnemonic.json')).mnemonic
|
||||
} catch (e) {
|
||||
return DEFAULT_MNEMONIC
|
||||
}
|
||||
}
|
||||
|
||||
const settingsForNetwork = (network) => {
|
||||
try {
|
||||
return require(configFilePath(`${network}_key.json`))
|
||||
} catch (e) {
|
||||
return { }
|
||||
}
|
||||
}
|
||||
|
||||
// Lazily loaded provider
|
||||
const providerForNetwork = (network) => (
|
||||
() => {
|
||||
let { rpc, keys } = settingsForNetwork(network)
|
||||
rpc = rpc || defaultRPC(network)
|
||||
|
||||
if (!keys || keys.length == 0) {
|
||||
return new HDWalletProvider(mnemonic(), rpc)
|
||||
}
|
||||
|
||||
return new HDWalletProviderPrivkey(keys, rpc)
|
||||
}
|
||||
)
|
||||
module.exports = {
|
||||
networks: {
|
||||
development: {
|
||||
host: 'localhost',
|
||||
port: 8545,
|
||||
network_id: '*'
|
||||
},
|
||||
mainnet: {
|
||||
network_id: 1,
|
||||
provider: providerForNetwork('mainnet')
|
||||
},
|
||||
rinkeby: {
|
||||
network_id: 4,
|
||||
provider: providerForNetwork('rinkeby')
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user