Skip to content

Commit 2d9ebaa

Browse files
committedMay 6, 2019
Initial creation from create-aragon-app
0 parents  commit 2d9ebaa

24 files changed

+825
-0
lines changed
 

‎.eslintignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
node_modules
2+
app/node_modules
3+
build
4+
.cache
5+
dist
6+
coverage

‎.eslintrc.json

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"env": {
3+
"browser": true,
4+
"es6": true
5+
},
6+
"extends": [
7+
"standard",
8+
"standard-react",
9+
"prettier",
10+
"prettier/react"
11+
],
12+
"parser": "babel-eslint",
13+
"parserOptions": {
14+
"ecmaVersion": 6,
15+
"ecmaFeatures": {
16+
"jsx": true
17+
},
18+
"sourceType": "module"
19+
},
20+
"plugins": ["prettier", "react"],
21+
"rules": {
22+
"valid-jsdoc": "error",
23+
"react/prop-types": 0,
24+
"prettier/prettier": [
25+
"error",
26+
{
27+
"singleQuote": true,
28+
"semi": false,
29+
"trailingComma": "es5",
30+
"bracketSpacing": true,
31+
"jsxBracketSameLine": false
32+
}
33+
],
34+
"linebreak-style": ["error", "unix"]
35+
}
36+
}

‎.gitignore

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
node_modules
2+
build
3+
.cache
4+
dist
5+
ipfs.cmd
6+
package-lock.json
7+
coverage.json
8+
coverage

‎.ipfsignore

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Git files
2+
.gitignore
3+
4+
# Build files
5+
.cache
6+
node_modules
7+
build
8+
coverage
9+
10+
# Lock files
11+
package-lock.json
12+
yarn.lock
13+
14+
# Others
15+
test

‎.solcover.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module.exports = {
2+
norpc: true,
3+
copyPackages: [],
4+
skipFiles: [
5+
'test',
6+
]
7+
}

‎.soliumignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
contracts/Migrations.sol

‎.soliumrc.json

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"extends": "solium:all",
3+
"plugins": [
4+
"security"
5+
],
6+
"rules": {
7+
"imports-on-top": ["error"],
8+
"variable-declarations": ["error"],
9+
"array-declarations": ["error"],
10+
"operator-whitespace": ["error"],
11+
"lbrace": ["error"],
12+
"mixedcase": ["warning"],
13+
"camelcase": ["error"],
14+
"uppercase": ["warning"],
15+
"no-empty-blocks": ["error"],
16+
"no-unused-vars": ["error"],
17+
"quotes": ["error"],
18+
"indentation": ["error"],
19+
"whitespace": ["error"],
20+
"deprecated-suicide": ["error"],
21+
"arg-overflow": ["error", 8],
22+
"pragma-on-top": ["error"],
23+
"security/enforce-explicit-visibility": ["error"],
24+
"consequent": 0,
25+
"error-reason": ["warning"],
26+
"function-order": [
27+
"error",
28+
{
29+
"ignore": {
30+
"functions": ["initialize"]
31+
}
32+
}
33+
]
34+
}
35+
}

‎README.md

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Aragon React Boilerplate
2+
3+
> 🕵️ [Find more boilerplates using GitHub](https://github.com/search?q=topic:aragon-boilerplate) |
4+
> [Official boilerplates](https://github.com/search?q=topic:aragon-boilerplate+org:aragon)
5+
6+
React boilerplate for Aragon applications.
7+
8+
This boilerplate includes a fully working example app, complete with a background worker and a front-end in React (with Aragon UI). Also comes with a DAO Template which will allow for using your app to interact with other Aragon apps like the Voting app. You can read more about DAO Template [here](https://hack.aragon.org/docs/templates-intro).
9+
10+
## Usage
11+
12+
To setup use the command `create-aragon-app`:
13+
14+
```sh
15+
npx create-aragon-app <app-name> react
16+
```
17+
18+
## Make the template work with your app
19+
20+
- Edit the roles defined in the template to configure your DAO as you want!
21+
22+
## Run the template
23+
24+
```sh
25+
npx aragon run --template Template --template-init @ARAGON_ENS
26+
```
27+
28+
## Running your app
29+
30+
### Using HTTP
31+
32+
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.
33+
34+
- 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.
35+
36+
- After that, you can run `npm run start:http` or `npm run start:http:template` 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.
37+
38+
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`.
39+
40+
### Using IPFS
41+
42+
Running your app using IPFS will mimic the production environment that will be used for running your app. `npm run start: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.
43+
44+
## What's in this boilerplate?
45+
46+
### npm Scripts
47+
48+
- **start** or **start:ipfs**: Runs your app inside a DAO served from IPFS
49+
- **start:http**: Runs your app inside a DAO served with HTTP (hot reloading)
50+
- **start:ipfs:template**: Creates a DAO with the [Template](https://github.com/aragon/aragon-react-boilerplate/blob/master/contracts/Template.sol) and serves the app from IPFS
51+
- **start:http:template**: Creates a DAO with the [Template](https://github.com/aragon/aragon-react-boilerplate/blob/master/contracts/Template.sol) and serves the app with HTTP (hot reloading)
52+
- **prepare**: Installs dependencies of the front-end
53+
- **start:app**: Starts a development server for your app
54+
- **compile**: Compiles the smart contracts
55+
- **build**: Builds the front-end and background script
56+
- **test**: Runs tests for the contracts
57+
- **publish:patch**: Releases a patch version to aragonPM (only frontend/content changes allowed)
58+
- **publish:minor**: Releases a minor version to aragonPM (only frontend/content changes allowed)
59+
- **publish:major**: Releases a major version to aragonPM (frontend **and** contract changes)
60+
- **versions**: Checks the currently installed versions of the app
61+
- **lint**: Checks the app and the contracts for linting errors
62+
- **lint:fix**: Fixes the lint errors that can be resolved automatically
63+
- **coverage**: Runs the tests for the contracts and creates a report
64+
65+
### Libraries
66+
67+
- [**@aragon/os**](https://github.com/aragon/aragonos): Aragon interfaces
68+
- [**@aragon/api**](https://github.com/aragon/aragon.js/tree/master/packages/aragon-api): Wrapper for Aragon application RPC
69+
- [**@aragon/ui**](https://github.com/aragon/aragon-ui): Aragon UI components (in React)
70+
71+
## What you can do with this boilerplate?
72+
73+
### Publish
74+
75+
You can publish you app on [aragonPM](https://hack.aragon.org/docs/apm). See how in our [publish guide](https://hack.aragon.org/docs/guides-publish).
76+
77+
> **Note**<br>
78+
> The [Template](https://github.com/aragon/aragon-react-boilerplate/blob/master/contracts/Template.sol) will not be published.
79+
80+
### Using a different Ethereum account
81+
82+
You can use a different account to interact with you app. [Check the documentation](https://hack.aragon.org/docs/guides-faq#set-a-private-key).
83+
84+
### Propagate content
85+
86+
You can propagate the content of your app on IPFS. Learn more in our [troubleshooting guide](https://hack.aragon.org/docs/guides-faq#propagating-your-content-hash-through-ipfs) or use the `aragon ipfs propagate` command:
87+
88+
```
89+
npx aragon ipfs propagate <cid>
90+
```
91+
92+
Where `cid` is your content id hash (this will be displayed after publishing).

‎app/.babelrc

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"presets": [
3+
[
4+
"@babel/preset-env",
5+
{
6+
"modules": false,
7+
"targets": {
8+
"browsers": [
9+
"> 1%",
10+
"last 3 versions",
11+
"ie >= 9",
12+
"ios >= 8",
13+
"android >= 4.2"
14+
]
15+
},
16+
"useBuiltIns": false
17+
}
18+
]
19+
],
20+
"plugins": [
21+
"@babel/plugin-proposal-class-properties",
22+
[
23+
"styled-components",
24+
{
25+
"displayName": true
26+
}
27+
]
28+
]
29+
}

‎app/index.html

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>Aragon App</title>
5+
</head>
6+
<body>
7+
<div id="root"></div>
8+
<script src="src/index.js"></script>
9+
</body>
10+
</html>

‎app/package.json

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"name": "home-frontend",
3+
"version": "1.0.0",
4+
"main": "src/index.js",
5+
"dependencies": {
6+
"@aragon/api": "^1.0.0-beta.1",
7+
"@aragon/api-react": "^1.0.0-beta.1",
8+
"@aragon/ui": "^0.32.0",
9+
"@babel/polyfill": "^7.2.5",
10+
"react": "^16.8.4",
11+
"react-dom": "^16.8.4",
12+
"rxjs": "^6.2.1",
13+
"styled-components": "^4.1.3"
14+
},
15+
"devDependencies": {
16+
"@babel/core": "^7.3.4",
17+
"@babel/plugin-proposal-class-properties": "^7.3.4",
18+
"@babel/preset-env": "^7.3.4",
19+
"parcel-bundler": "^1.12.3"
20+
},
21+
"scripts": {
22+
"build": "npm run sync-assets && npm run build:app && npm run build:script",
23+
"build:app": "parcel build index.html -d ../dist/ --public-url \".\" --no-cache",
24+
"build:script": "parcel build src/script.js --out-dir ../dist/ --no-cache",
25+
"watch:script": "parcel watch src/script.js --out-dir ../dist/ --no-hmr",
26+
"devserver": "parcel serve index.html -p 8001 --out-dir ../dist/ --no-cache",
27+
"start": "npm run sync-assets && npm run build:script -- --no-minify && npm run devserver",
28+
"sync-assets": "copy-aragon-ui-assets ../dist"
29+
}
30+
}

‎app/src/App.js

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import React from 'react'
2+
import { useAragonApi } from '@aragon/api-react'
3+
import { Main, Button } from '@aragon/ui'
4+
import styled from 'styled-components'
5+
6+
function App() {
7+
const { api, appState } = useAragonApi()
8+
const { count, syncing } = appState
9+
return (
10+
<Main>
11+
<BaseLayout>
12+
{syncing && <Syncing />}
13+
<Count>Count: {count}</Count>
14+
<Buttons>
15+
<Button mode="secondary" onClick={() => api.decrement(1)}>
16+
Decrement
17+
</Button>
18+
<Button mode="secondary" onClick={() => api.increment(1)}>
19+
Increment
20+
</Button>
21+
</Buttons>
22+
</BaseLayout>
23+
</Main>
24+
)
25+
}
26+
27+
const BaseLayout = styled.div`
28+
display: flex;
29+
align-items: center;
30+
justify-content: center;
31+
height: 100vh;
32+
flex-direction: column;
33+
`
34+
35+
const Count = styled.h1`
36+
font-size: 30px;
37+
`
38+
39+
const Buttons = styled.div`
40+
display: grid;
41+
grid-auto-flow: column;
42+
grid-gap: 40px;
43+
margin-top: 20px;
44+
`
45+
46+
const Syncing = styled.div.attrs({ children: 'Syncing…' })`
47+
position: absolute;
48+
top: 15px;
49+
right: 20px;
50+
`
51+
52+
export default App

‎app/src/index.js

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import React from 'react'
2+
import ReactDOM from 'react-dom'
3+
import { AragonApi } from '@aragon/api-react'
4+
import App from './App'
5+
6+
const reducer = state => {
7+
if (state === null) {
8+
return { count: 0, syncing: true }
9+
}
10+
return state
11+
}
12+
13+
ReactDOM.render(
14+
<AragonApi reducer={reducer}>
15+
<App />
16+
</AragonApi>,
17+
document.getElementById('root')
18+
)

‎app/src/script.js

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import '@babel/polyfill'
2+
import { of } from 'rxjs'
3+
import AragonApi from '@aragon/api'
4+
5+
const INITIALIZATION_TRIGGER = Symbol('INITIALIZATION_TRIGGER')
6+
7+
const api = new AragonApi()
8+
9+
api.store(
10+
async (state, event) => {
11+
let newState
12+
13+
switch (event.event) {
14+
case INITIALIZATION_TRIGGER:
15+
newState = { count: await getValue() }
16+
break
17+
case 'Increment':
18+
newState = { count: await getValue() }
19+
break
20+
case 'Decrement':
21+
newState = { count: await getValue() }
22+
break
23+
default:
24+
newState = state
25+
}
26+
27+
return newState
28+
},
29+
[
30+
// Always initialize the store with our own home-made event
31+
of({ event: INITIALIZATION_TRIGGER }),
32+
]
33+
)
34+
35+
async function getValue() {
36+
return parseInt(await api.call('value').toPromise(), 10)
37+
}

‎arapp.json

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"roles": [
3+
{
4+
"name": "Increment the counter",
5+
"id": "INCREMENT_ROLE",
6+
"params": []
7+
},
8+
{
9+
"name": "Decrement the counter",
10+
"id": "DECREMENT_ROLE",
11+
"params": []
12+
}
13+
],
14+
"environments": {
15+
"default": {
16+
"network": "development",
17+
"appName": "home.aragonpm.eth"
18+
},
19+
"staging": {
20+
"registry": "0x98df287b6c145399aaa709692c8d308357bc085d",
21+
"appName": "home.open.aragonpm.eth",
22+
"wsRPC": "wss://rinkeby.eth.aragon.network/ws",
23+
"network": "rinkeby"
24+
},
25+
"production": {
26+
"registry": "0x314159265dd8dbb310642f98f50c066173c1259b",
27+
"appName": "home.open.aragonpm.eth",
28+
"wsRPC": "wss://mainnet.eth.aragon.network/ws",
29+
"network": "mainnet"
30+
}
31+
},
32+
"path": "contracts/CounterApp.sol"
33+
}

‎contracts/CounterApp.sol

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
pragma solidity ^0.4.24;
2+
3+
import "@aragon/os/contracts/apps/AragonApp.sol";
4+
import "@aragon/os/contracts/lib/math/SafeMath.sol";
5+
6+
7+
contract CounterApp is AragonApp {
8+
using SafeMath for uint256;
9+
10+
/// Events
11+
event Increment(address indexed entity, uint256 step);
12+
event Decrement(address indexed entity, uint256 step);
13+
14+
/// State
15+
uint256 public value;
16+
17+
/// ACL
18+
bytes32 constant public INCREMENT_ROLE = keccak256("INCREMENT_ROLE");
19+
bytes32 constant public DECREMENT_ROLE = keccak256("DECREMENT_ROLE");
20+
21+
function initialize() public onlyInit {
22+
initialized();
23+
}
24+
25+
/**
26+
* @notice Increment the counter by `step`
27+
* @param step Amount to increment by
28+
*/
29+
function increment(uint256 step) external auth(INCREMENT_ROLE) {
30+
value = value.add(step);
31+
emit Increment(msg.sender, step);
32+
}
33+
34+
/**
35+
* @notice Decrement the counter by `step`
36+
* @param step Amount to decrement by
37+
*/
38+
function decrement(uint256 step) external auth(DECREMENT_ROLE) {
39+
value = value.sub(step);
40+
emit Decrement(msg.sender, step);
41+
}
42+
}

‎contracts/Template.sol

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* SPDX-License-Identitifer: GPL-3.0-or-later
3+
*
4+
* This file requires contract dependencies which are licensed as
5+
* GPL-3.0-or-later, forcing it to also be licensed as such.
6+
*
7+
* This is the only file in your project that requires this license and
8+
* you are free to choose a different license for the rest of the project.
9+
*/
10+
11+
pragma solidity 0.4.24;
12+
13+
import "@aragon/os/contracts/factory/DAOFactory.sol";
14+
import "@aragon/os/contracts/apm/Repo.sol";
15+
import "@aragon/os/contracts/lib/ens/ENS.sol";
16+
import "@aragon/os/contracts/lib/ens/PublicResolver.sol";
17+
import "@aragon/os/contracts/apm/APMNamehash.sol";
18+
19+
import "@aragon/apps-voting/contracts/Voting.sol";
20+
import "@aragon/apps-token-manager/contracts/TokenManager.sol";
21+
import "@aragon/apps-shared-minime/contracts/MiniMeToken.sol";
22+
23+
import "./CounterApp.sol";
24+
25+
26+
contract TemplateBase is APMNamehash {
27+
ENS public ens;
28+
DAOFactory public fac;
29+
30+
event DeployInstance(address dao);
31+
event InstalledApp(address appProxy, bytes32 appId);
32+
33+
constructor(DAOFactory _fac, ENS _ens) public {
34+
ens = _ens;
35+
36+
// If no factory is passed, get it from on-chain bare-kit
37+
if (address(_fac) == address(0)) {
38+
bytes32 bareKit = apmNamehash("bare-kit");
39+
fac = TemplateBase(latestVersionAppBase(bareKit)).fac();
40+
} else {
41+
fac = _fac;
42+
}
43+
}
44+
45+
function latestVersionAppBase(bytes32 appId) public view returns (address base) {
46+
Repo repo = Repo(PublicResolver(ens.resolver(appId)).addr(appId));
47+
(,base,) = repo.getLatest();
48+
49+
return base;
50+
}
51+
}
52+
53+
54+
contract Template is TemplateBase {
55+
MiniMeTokenFactory tokenFactory;
56+
57+
uint64 constant PCT = 10 ** 16;
58+
address constant ANY_ENTITY = address(-1);
59+
60+
constructor(ENS ens) TemplateBase(DAOFactory(0), ens) public {
61+
tokenFactory = new MiniMeTokenFactory();
62+
}
63+
64+
function newInstance() public {
65+
Kernel dao = fac.newDAO(this);
66+
ACL acl = ACL(dao.acl());
67+
acl.createPermission(this, dao, dao.APP_MANAGER_ROLE(), this);
68+
69+
address root = msg.sender;
70+
bytes32 appId = apmNamehash("home");
71+
bytes32 votingAppId = apmNamehash("voting");
72+
bytes32 tokenManagerAppId = apmNamehash("token-manager");
73+
74+
CounterApp app = CounterApp(dao.newAppInstance(appId, latestVersionAppBase(appId)));
75+
Voting voting = Voting(dao.newAppInstance(votingAppId, latestVersionAppBase(votingAppId)));
76+
TokenManager tokenManager = TokenManager(dao.newAppInstance(tokenManagerAppId, latestVersionAppBase(tokenManagerAppId)));
77+
78+
MiniMeToken token = tokenFactory.createCloneToken(MiniMeToken(0), 0, "App token", 0, "APP", true);
79+
token.changeController(tokenManager);
80+
81+
app.initialize();
82+
tokenManager.initialize(token, true, 0);
83+
// Initialize apps
84+
voting.initialize(token, 50 * PCT, 20 * PCT, 1 days);
85+
86+
acl.createPermission(this, tokenManager, tokenManager.MINT_ROLE(), this);
87+
tokenManager.mint(root, 1); // Give one token to root
88+
89+
acl.createPermission(ANY_ENTITY, voting, voting.CREATE_VOTES_ROLE(), root);
90+
91+
acl.createPermission(voting, app, app.INCREMENT_ROLE(), voting);
92+
acl.createPermission(ANY_ENTITY, app, app.DECREMENT_ROLE(), root);
93+
acl.grantPermission(voting, tokenManager, tokenManager.MINT_ROLE());
94+
95+
// Clean up permissions
96+
acl.grantPermission(root, dao, dao.APP_MANAGER_ROLE());
97+
acl.revokePermission(this, dao, dao.APP_MANAGER_ROLE());
98+
acl.setPermissionManager(root, dao, dao.APP_MANAGER_ROLE());
99+
100+
acl.grantPermission(root, acl, acl.CREATE_PERMISSIONS_ROLE());
101+
acl.revokePermission(this, acl, acl.CREATE_PERMISSIONS_ROLE());
102+
acl.setPermissionManager(root, acl, acl.CREATE_PERMISSIONS_ROLE());
103+
104+
emit DeployInstance(dao);
105+
}
106+
}

‎contracts/misc/Migrations.sol

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
pragma solidity ^0.4.4;
2+
3+
4+
contract Migrations {
5+
address public owner;
6+
uint public lastCompletedMigration;
7+
8+
modifier restricted() {
9+
if (msg.sender == owner)
10+
_;
11+
}
12+
13+
constructor() public {
14+
owner = msg.sender;
15+
}
16+
17+
function setCompleted(uint completed) public restricted {
18+
lastCompletedMigration = completed;
19+
}
20+
21+
function upgrade(address newAddress) public restricted {
22+
Migrations upgraded = Migrations(newAddress);
23+
upgraded.setCompleted(lastCompletedMigration);
24+
}
25+
}

‎manifest.json

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"name": "Counter",
3+
"description": "An application for Aragon",
4+
"icons": [{
5+
"src": "/dist/images/icon.png",
6+
"sizes": "22x22"
7+
}],
8+
"start_url": "/dist/index.html",
9+
"script": "/dist/script.js"
10+
}

‎migrations/1_initial_migration.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/* global artifacts */
2+
var Migrations = artifacts.require('./Migrations.sol')
3+
4+
module.exports = function(deployer) {
5+
deployer.deploy(Migrations)
6+
}

‎migrations/2_deploy_contracts.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/* global artifacts */
2+
var CounterApp = artifacts.require('CounterApp.sol')
3+
4+
module.exports = function(deployer) {
5+
deployer.deploy(CounterApp)
6+
}

‎package.json

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"name": "home",
3+
"version": "1.0.0",
4+
"description": "",
5+
"dependencies": {
6+
"@aragon/os": "^4.0.1",
7+
"@aragon/apps-shared-minime": "^1.0.0",
8+
"@aragon/apps-token-manager": "2.0.0",
9+
"@aragon/apps-voting": "2.0.0"
10+
},
11+
"devDependencies": {
12+
"@aragon/cli": "^5.4.0",
13+
"@aragon/test-helpers": "^1.0.1",
14+
"babel-eslint": "^10.0.1",
15+
"cross-env": "^5.2.0",
16+
"eslint": "^5.13.0",
17+
"eslint-config-prettier": "^4.0.0",
18+
"eslint-config-standard": "^12.0.0",
19+
"eslint-config-standard-react": "^7.0.2",
20+
"eslint-plugin-import": "^2.16.0",
21+
"eslint-plugin-node": "^8.0.1",
22+
"eslint-plugin-prettier": "^3.0.1",
23+
"eslint-plugin-promise": "^4.0.1",
24+
"eslint-plugin-react": "^7.12.4",
25+
"eslint-plugin-standard": "^4.0.0",
26+
"ethlint": "^1.2.3",
27+
"prettier": "^1.16.4",
28+
"solidity-coverage": "^0.5.11"
29+
},
30+
"scripts": {
31+
"start": "npm run start:ipfs",
32+
"start:ipfs": "aragon run",
33+
"start:http": "aragon run --http localhost:8001 --http-served-from ./dist",
34+
"start:ipfs:template": "npm run start:ipfs -- --template Template --template-init @ARAGON_ENS",
35+
"start:http:template": "npm run start:http -- --template Template --template-init @ARAGON_ENS",
36+
"prepare": "cd app && npm install && cd ..",
37+
"start:app": "cd app && npm start && cd ..",
38+
"test": "cross-env TRUFFLE_TEST=true npm run ganache-cli:test",
39+
"compile": "aragon contracts compile",
40+
"build": "cd app && npm run build && cd ..",
41+
"publish:patch": "aragon apm publish patch",
42+
"publish:minor": "aragon apm publish minor",
43+
"publish:major": "aragon apm publish major",
44+
"versions": "aragon apm versions",
45+
"lint": "eslint . & solium --dir ./contracts",
46+
"lint:fix": "eslint . --fix & solium --dir ./contracts --fix",
47+
"coverage": "cross-env SOLIDITY_COVERAGE=true npm run ganache-cli:test",
48+
"ganache-cli:test": "sh ./node_modules/@aragon/test-helpers/ganache-cli.sh"
49+
}
50+
}

‎test/app.js

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/* global artifacts contract before beforeEach it assert */
2+
const { assertRevert } = require('@aragon/test-helpers/assertThrow')
3+
4+
const CounterApp = artifacts.require('CounterApp.sol')
5+
const DAOFactory = artifacts.require(
6+
'@aragon/core/contracts/factory/DAOFactory'
7+
)
8+
const EVMScriptRegistryFactory = artifacts.require(
9+
'@aragon/core/contracts/factory/EVMScriptRegistryFactory'
10+
)
11+
const ACL = artifacts.require('@aragon/core/contracts/acl/ACL')
12+
const Kernel = artifacts.require('@aragon/core/contracts/kernel/Kernel')
13+
14+
const getContract = name => artifacts.require(name)
15+
16+
const ANY_ADDRESS = '0xffffffffffffffffffffffffffffffffffffffff'
17+
18+
contract('CounterApp', accounts => {
19+
let APP_MANAGER_ROLE, INCREMENT_ROLE, DECREMENT_ROLE
20+
let daoFact, appBase, app
21+
22+
const firstAccount = accounts[0]
23+
const secondAccount = accounts[1]
24+
25+
before(async () => {
26+
const kernelBase = await getContract('Kernel').new(true) // petrify immediately
27+
const aclBase = await getContract('ACL').new()
28+
const regFact = await EVMScriptRegistryFactory.new()
29+
daoFact = await DAOFactory.new(
30+
kernelBase.address,
31+
aclBase.address,
32+
regFact.address
33+
)
34+
appBase = await CounterApp.new()
35+
36+
// Setup constants
37+
APP_MANAGER_ROLE = await kernelBase.APP_MANAGER_ROLE()
38+
INCREMENT_ROLE = await appBase.INCREMENT_ROLE()
39+
DECREMENT_ROLE = await appBase.DECREMENT_ROLE()
40+
})
41+
42+
beforeEach(async () => {
43+
const daoReceipt = await daoFact.newDAO(firstAccount)
44+
const dao = Kernel.at(
45+
daoReceipt.logs.filter(l => l.event === 'DeployDAO')[0].args.dao
46+
)
47+
const acl = ACL.at(await dao.acl())
48+
49+
await acl.createPermission(
50+
firstAccount,
51+
dao.address,
52+
APP_MANAGER_ROLE,
53+
firstAccount,
54+
{
55+
from: firstAccount,
56+
}
57+
)
58+
59+
const receipt = await dao.newAppInstance(
60+
'0x1234',
61+
appBase.address,
62+
'0x',
63+
false,
64+
{ from: firstAccount }
65+
)
66+
67+
app = CounterApp.at(
68+
receipt.logs.filter(l => l.event === 'NewAppProxy')[0].args.proxy
69+
)
70+
71+
await acl.createPermission(
72+
ANY_ADDRESS,
73+
app.address,
74+
INCREMENT_ROLE,
75+
firstAccount,
76+
{
77+
from: firstAccount,
78+
}
79+
)
80+
await acl.createPermission(
81+
ANY_ADDRESS,
82+
app.address,
83+
DECREMENT_ROLE,
84+
firstAccount,
85+
{
86+
from: firstAccount,
87+
}
88+
)
89+
})
90+
91+
it('should be incremented by any address', async () => {
92+
app.initialize()
93+
await app.increment(1, { from: secondAccount })
94+
assert.equal(await app.value(), 1)
95+
})
96+
97+
it('should not be decremented if already 0', async () => {
98+
app.initialize()
99+
return assertRevert(async () => {
100+
return app.decrement(1)
101+
})
102+
})
103+
})

‎truffle.js

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
* https://github.com/aragon/aragonOS/blob/v4.0.0/truffle-config.js
3+
*/
4+
const homedir = require('homedir')
5+
const path = require('path')
6+
7+
const HDWalletProvider = require('truffle-hdwallet-provider')
8+
const HDWalletProviderPrivkey = require('truffle-hdwallet-provider-privkey')
9+
10+
const DEFAULT_MNEMONIC =
11+
'explain tackle mirror kit van hammer degree position ginger unfair soup bonus'
12+
13+
const defaultRPC = network => `https://${network}.infura.io`
14+
15+
const configFilePath = filename => path.join(homedir(), `.aragon/${filename}`)
16+
17+
const mnemonic = () => {
18+
try {
19+
return require(configFilePath('mnemonic.json')).mnemonic
20+
} catch (e) {
21+
return DEFAULT_MNEMONIC
22+
}
23+
}
24+
25+
const settingsForNetwork = network => {
26+
try {
27+
return require(configFilePath(`${network}_key.json`))
28+
} catch (e) {
29+
return {}
30+
}
31+
}
32+
33+
// Lazily loaded provider
34+
const providerForNetwork = network => () => {
35+
let { rpc, keys } = settingsForNetwork(network)
36+
rpc = rpc || defaultRPC(network)
37+
38+
if (!keys || keys.length === 0) {
39+
return new HDWalletProvider(mnemonic(), rpc)
40+
}
41+
42+
return new HDWalletProviderPrivkey(keys, rpc)
43+
}
44+
module.exports = {
45+
networks: {
46+
development: {
47+
host: 'localhost',
48+
port: 8545,
49+
network_id: '*',
50+
},
51+
mainnet: {
52+
network_id: 1,
53+
provider: providerForNetwork('mainnet'),
54+
},
55+
rinkeby: {
56+
network_id: 4,
57+
provider: providerForNetwork('rinkeby'),
58+
},
59+
coverage: {
60+
host: 'localhost',
61+
network_id: '*',
62+
port: 8555,
63+
gas: 0xffffffffff,
64+
gasPrice: 0x01,
65+
},
66+
},
67+
}

0 commit comments

Comments
 (0)
Please sign in to comment.