Skip to content

Commit 6362c90

Browse files
authored
Merge pull request #2 from ConsenSys/feat/actors
fromDAO: extract apps, distinguish apps from actors, fix name generation
2 parents 57e303d + 29ed6e8 commit 6362c90

File tree

5 files changed

+146
-74
lines changed

5 files changed

+146
-74
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "aragraph",
3-
"version": "0.0.4",
3+
"version": "0.0.5",
44
"description": "Visualize your Aragon DAO Templates",
55
"main": "index.js",
66
"bin": "./src/index.js",

src/aragraph.js

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@ const YAML = require('yaml');
1010
const fs = require('fs');
1111
const dao = require('./dao')
1212

13-
const ARAAPPS = {
14-
xvoting: "",
15-
}
16-
1713
function graphNormalizeEntity(e){
1814
return e.trim().replace(/[-\s\`\[\]]+/g, '_').replace(/(^_)|(_$)/g, "");
1915
}
@@ -59,22 +55,23 @@ class AraApp {
5955
delete options.ref
6056
delete options.type
6157

62-
6358
this.options = options;
64-
this.template = ARAAPPS[this.type];
65-
if(!this.template){
66-
this.template = `
59+
60+
this.template = `
6761
class %%ref%% {
68-
{abstract}${this.type}
62+
{abstract}%%type%%
6963
7064
%%note%%
7165
}
72-
`;
73-
}
66+
`;
67+
}
68+
setTemplate(t){
69+
this.template = t;
7470
}
7571
toString(){
7672
return this.template
7773
.replace("%%ref%%", graphNormalizeEntity(this.ref))
74+
.replace("%%type%%", graphNormalizeEntity(this.type))
7875
.replace("%%note%%", Object.entries(this.options).map(([k,v]) => `\t**${k}** ${v}`).join('\n'));
7976
}
8077
}
@@ -105,10 +102,12 @@ package %%ref%% {
105102
}
106103

107104
class AraGraph {
108-
constructor(){
105+
constructor(config){
109106
this.apps = {};
110107
this.tokens = {};
111108
this.permissionTuples = {};
109+
110+
this.config = config;
112111
}
113112

114113
addPermission(p){
@@ -127,6 +126,9 @@ class AraGraph {
127126
addApp(a){
128127
let app = new AraApp(a.ref, a.type, a);
129128
this.apps[app.ref] = app;
129+
let template = this.config.plantuml.applicationTemplates[app.type ? graphNormalizeEntity(app.type.toLowerCase()) : "__default__"];
130+
if(template)
131+
app.setTemplate(template)
130132
}
131133

132134
addToken(t){
@@ -152,16 +154,11 @@ class AraGraph {
152154
uml.push(`${graphNormalizeEntity(a.ref)} <|-- ${graphNormalizeEntity(a.options.token)}`)
153155
}
154156
}
155-
156157

157-
158-
159-
160158
uml.push(`' -- permissions --`);
161159
for(let p of Object.values(this.permissionTuples)){
162160
uml.push(p.toString())
163161
}
164-
165162

166163
uml.push("\n@enduml")
167164
return uml.join('\n');
@@ -178,7 +175,10 @@ class AragonPermissions {
178175
let config_default = {
179176
plantuml : {
180177
header: ["allowmixing", "skinparam handwritten true"],
181-
applicationTemplates: {}
178+
applicationTemplates: {
179+
_actor_: "actor %%ref%%",
180+
__default__: "class %%ref%% {\n {abstract}%%type%%\n ----\n %%note%%\n}"
181+
}
182182
}
183183
}
184184

@@ -195,8 +195,14 @@ class AragonPermissions {
195195
}
196196

197197
async fromDAO(daoAddress, chainId) {
198-
const permissions = await dao.getPermissions(daoAddress, chainId)
199-
this.data = {permissions: permissions, tokens: [], apps: [], actions: []}
198+
const remoteDao = new dao.RemoteDao(daoAddress, chainId)
199+
200+
await remoteDao.load()
201+
202+
const permissions = remoteDao.getPermissions()
203+
const apps = remoteDao.getApps()
204+
205+
this.data = {permissions: permissions, tokens: [], apps: apps, actions: []}
200206
return this;
201207
}
202208

@@ -251,7 +257,7 @@ class AragonPermissions {
251257

252258
uml(){
253259

254-
let g = new AraGraph();
260+
let g = new AraGraph(this.config);
255261

256262
for(let a of this.data.apps){
257263
g.addApp(a)

src/dao.js

Lines changed: 94 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
/**
2+
* @author github.com/tintinweb
3+
* @license MIT
4+
*
5+
* */
6+
'use strict'
17
const Web3 = require('web3')
28
const Aragon = require('@aragon/wrapper').default
39
const RxOp = require('rxjs/operators')
@@ -12,63 +18,104 @@ const ENS_REGISTRIES = {
1218
}
1319
const IPFS_GATEWAY_URL = 'https://ipfs.eth.aragon.network/ipfs'
1420

15-
async function getPermissions(daoAddress, chainId) {
16-
const dao = new Aragon(daoAddress, {
17-
provider: new Web3.providers.WebsocketProvider(ETH_PROVIDERS[chainId]),
18-
apm: {
19-
ipfs: {
20-
gateway: IPFS_GATEWAY_URL,
21+
class RemoteDao {
22+
23+
constructor(daoAddress, chainId){
24+
this.daoAddress = daoAddress
25+
this.chainId = chainId
26+
}
27+
28+
async load(){
29+
const dao = new Aragon(this.daoAddress, {
30+
provider: new Web3.providers.WebsocketProvider(ETH_PROVIDERS[this.chainId]),
31+
apm: {
32+
ipfs: {
33+
gateway: IPFS_GATEWAY_URL,
34+
},
35+
ensRegistryAddress: ENS_REGISTRIES[this.chainId],
2136
},
22-
ensRegistryAddress: ENS_REGISTRIES[chainId],
23-
},
24-
})
25-
// Temporarily replace console.info
26-
// to hide 'Redefining LocalForage driver: memoryStorageDriver' message
27-
const consoleInfo = console.info
28-
console.info = () => {}
29-
await dao.init()
30-
console.info = consoleInfo
37+
})
38+
// Temporarily replace console.info
39+
// to hide 'Redefining LocalForage driver: memoryStorageDriver' message
40+
const consoleInfo = console.info
41+
console.info = () => {}
42+
await dao.init()
43+
console.info = consoleInfo
44+
45+
this.apps = await dao.apps
46+
.pipe(RxOp.takeWhile((apps) => apps.length <= 1, true))
47+
.toPromise()
48+
49+
this.permissions = await dao.permissions
50+
.pipe(RxOp.takeWhile((permissions) => permissions.length <= 1, true))
51+
.toPromise()
52+
53+
return this
54+
}
3155

32-
// Get apps
33-
const apps = await dao.apps
34-
.pipe(RxOp.takeWhile((apps) => apps.length <= 1, true))
35-
.toPromise()
36-
const getAppName = (address) => {
37-
const app = apps.find((app) => {
38-
return address.toLowerCase() === app.proxyAddress
56+
getApps(){
57+
let apps = this.apps.map((app) => {
58+
return {ref: `${app.name} ${app.proxyAddress.toLowerCase().substring(0, 6)}`, type: app.name}
3959
})
40-
const name = app ? app.name : 'Unknown'
41-
return `${name} ${address.toLowerCase().substring(0, 6)}`
60+
let appNamesRefs = apps.map(app => app.ref)
61+
62+
for(let p of this.getPermissions()){
63+
if(appNamesRefs.indexOf(p.app) < 0){
64+
apps.push({ref: p.app, type: '__actor__'})
65+
appNamesRefs.push(p.app)
66+
}
67+
if(appNamesRefs.indexOf(p.grantee) < 0){
68+
apps.push({ref: p.grantee, type: '__actor__'})
69+
appNamesRefs.push(p.grantee)
70+
}
71+
if(appNamesRefs.indexOf(p.manager) < 0){
72+
apps.push({ref: p.manager, type: '__actor__'})
73+
appNamesRefs.push(p.manager)
74+
}
75+
}
76+
return apps
4277
}
4378

44-
// Get roles
45-
const allRoles = {}
46-
apps.forEach((app) => {
47-
app.roles.forEach((role) => {
48-
allRoles[role.bytes] = role.id
79+
getPermissions(){
80+
// Get apps
81+
const apps = this.apps
82+
const getAppName = (address) => {
83+
const app = apps.find((app) => {
84+
return address.toLowerCase() === app.proxyAddress.toLowerCase()
85+
})
86+
const name = app ? app.name : 'Unknown'
87+
return `${name} ${address.toLowerCase().substring(0, 6)}`
88+
}
89+
90+
// Get roles
91+
const allRoles = {}
92+
apps.forEach((app) => {
93+
app.roles.forEach((role) => {
94+
allRoles[role.bytes] = role.id
95+
})
4996
})
50-
})
5197

52-
// Get permissions
53-
const permissions = await dao.permissions
54-
.pipe(RxOp.takeWhile((permissions) => permissions.length <= 1, true))
55-
.toPromise()
98+
// Get permissions
99+
const permissions = this.permissions
56100

57-
const output = []
58-
for (const [app, roles] of Object.entries(permissions)) {
59-
for (const [roleHash, data] of Object.entries(roles)) {
60-
const roleId = allRoles[roleHash] || 'Unknown'
61-
data.allowedEntities.forEach((entity) => {
62-
output.push({
63-
app: getAppName(app),
64-
role: roleId,
65-
grantee: getAppName(entity),
66-
manager: getAppName(data.manager),
101+
const output = []
102+
for (const [app, roles] of Object.entries(permissions)) {
103+
for (const [roleHash, data] of Object.entries(roles)) {
104+
const roleId = allRoles[roleHash] || 'Unknown'
105+
data.allowedEntities.forEach((entity) => {
106+
output.push({
107+
app: getAppName(app),
108+
role: roleId,
109+
grantee: getAppName(entity),
110+
manager: getAppName(data.manager),
111+
})
67112
})
68-
})
113+
}
69114
}
115+
return output
70116
}
71-
return output
117+
118+
72119
}
73120

74-
module.exports.getPermissions = getPermissions
121+
module.exports.RemoteDao = RemoteDao

src/index.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ const argv = require('yargs') // eslint-disable-line
1616
default: 1,
1717
type: 'number',
1818
})
19+
.option('t', {
20+
alias: 'default-config',
21+
default: false,
22+
type: 'boolean',
23+
})
1924
.demandCommand(1)
2025
.alias('c', 'config')
2126
.describe('c', 'path to configuration file')
@@ -29,15 +34,17 @@ let config = null;
2934

3035
if(argv.config && argv.config.endsWith('.json')){
3136
config = JSON.parse(fs.readFileSync(argv.config));
37+
} else if (argv.defaultConfig) {
38+
config = JSON.parse(fs.readFileSync(`${__dirname}/../templates/config.json`));
3239
}
3340

3441
argv._.forEach(inp => {
3542
if(inp.endsWith(".yaml")){
36-
console.log(new AragonPermissions().fromYaml(inp).uml());
43+
console.log(new AragonPermissions(config).fromYaml(inp).uml());
3744
} else if(inp.endsWith(".md")){
38-
console.log(new AragonPermissions().fromMarkdownTable(inp).uml())
45+
console.log(new AragonPermissions(config).fromMarkdownTable(inp).uml())
3946
} else if(inp.startsWith('0x')){
40-
new AragonPermissions().fromDAO(inp, argv.chainId).then((aragaph) => {
47+
new AragonPermissions(config).fromDAO(inp, argv.chainId).then((aragaph) => {
4148
console.log(aragaph.uml())
4249
process.exit(0)
4350
})

templates/config.json

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,19 @@
22
"plantuml" : {
33
"header": ["allowmixing", "skinparam handwritten true"],
44
"applicationTemplates": {
5-
"agent": ""
5+
"_actor_": "actor %%ref%%",
6+
"__default__": "class %%ref%% {\n {abstract}%%type%%\n ----\n %%note%%\n}",
7+
"acl": "class %%ref%% {\n {abstract}%%type%%\n ----\n +{static}CREATE_PERMISSIONS\n ----\n %%note%%\n}",
8+
"kernel": "class %%ref%% {\n {abstract}%%type%%\n ----\n +{static}APP_MANAGER\n ----\n %%note%%\n}",
9+
"evm_script_registry": "class %%ref%% {\n {abstract}%%type%%\n ----\n +{static}REGISTRY_ADD_EXECUTOR\n +{static}REGISTRY_MANAGER\n ----\n %%note%%\n}",
10+
"agent": "class %%ref%% {\n {abstract}%%type%%\n ----\n +{static}EXECUTE\n +{static}SAFE_EXECUTE_ROLE\n +{static}ADD_PROTECTED_TOKEN_ROLE\n +{static}REMOVE_PROTECTED_TOKEN_ROLE\n +{static}RUN_SCRIPT\n +{static}ADD_PRESIGNED_HASH\n +{static}DESIGNATE_SIGNER\n ----\n %%note%%\n}",
11+
"finance": "class %%ref%% {\n {abstract}%%type%%\n ----\n +{static}CREATE_PAYMENTS\n +{static}CHANGE_PERIOD\n +{static}CHANGE_BUDGETS\n +{static}EXECUTE_PAYMENTS\n +{static}MANAGE_PAYMENTS\n ----\n %%note%%\n}",
12+
"voting": "class %%ref%% {\n {abstract}%%type%%\n ----\n +{static}CREATE_VOTES\n +{static}MODIFY_SUPPORT\n +{static}MODIFY_QUORUM\n ----\n %%note%%\n}",
13+
"token_manager": "class %%ref%% {\n {abstract}%%type%%\n ----\n +{static}MINT\n +{static}ISSUE\n +{static}ASSIGN\n +{static}REVOKE_VESTINGS\n +{static}BURN\n ----\n %%note%%\n}",
14+
"vault": "class %%ref%% {\n {abstract}%%type%%\n ----\n+{static}TRANSFER\n ----\n %%note%%\n}"
615
}
716
}
8-
}
17+
}
18+
19+
20+

0 commit comments

Comments
 (0)