Skip to content

Commit e2a6d9b

Browse files
dOrgJelliorenyodfat
authored andcommitted
DAOTracker Support (#380)
* init * testing next * tests * remove brainstorming doc * fix * fix * fix * fixes * fix * :face_palm: * fix * fix * timeout * found it * fix * checking correct things * see if dao tracker is even a thing * fix datasource.yaml * .toLowerCase() * fix * fix * wait longer? * feedback changes * updates based on feedback * fixes * change to 31 so tests pass * fixed * fix lint * new DAOtracker * revert * fix? * fix * ci pls work * maybe this? * DaoCreator only * Hardcoded Blacklist * revert * fix lint
1 parent 16e492e commit e2a6d9b

20 files changed

+740
-32
lines changed

README.md

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ In order to add support for a new contract follow these steps:
7272
list of entities that are written by the the mapping.
7373
3. [`eventHandlers`](https://github.com/graphprotocol/graph-node/blob/master/docs/subgraph-manifest.md#1522-eventhandler) -
7474
map of solidity event signatures to event handlers in mapping code.
75-
4. [`templates`]([https://](https://github.com/graphprotocol/graph-node/blob/master/docs/subgraph-manifest.md#17-dynamicdatasource)) - list of datasource mappings that are created by the mapping.
7675
4. `test/integration/<contract name>.spec.ts`
7776

7877
3. Add your contract to `ops/mappings.json`. Under the JSON object for the network your contract is located at, under the `"mappings"` JSON array, add the following.
@@ -107,6 +106,24 @@ In order to add support for a new contract follow these steps:
107106

108107
To index a DAO please follow the instructions here: [https://github.com/daostack/subgraph/blob/master/documentations/Deployment.md#indexing-a-new-dao](https://github.com/daostack/subgraph/blob/master/documentations/Deployment.md#indexing-a-new-dao)
109108

109+
## Add a new datasource template
110+
111+
Datasource templates allow you to index blockchain data from addresses the subgraph finds out about at runtime. This is used to dynamically index newly deployed DAOs. To add a new contract ABI that can be used as a template within your mappings, modify the `ops/templates.json` file like so:
112+
113+
```json
114+
{
115+
"templates": [
116+
...,
117+
{
118+
"name": "<contract name as appears in `abis/arcVersion` folder>",
119+
"mapping": "<name of the `src/mappings/...` folder to be used with this contract>",
120+
"start_arcVersion": "<contract arc version under which the abi is located in the `abis` folder>",
121+
"end_arcVersion": "(optional) <contract arc version under which the abi is located in the `abis` folder> if not given, all future versions of this `name`'s contract ABI will be added as a template for this mapping"
122+
}
123+
]
124+
}
125+
```
126+
110127
## Deploy Subgraph
111128

112129
To deploy the subgraph, please follow the instructions below:
@@ -148,3 +165,16 @@ The docker images are available as:
148165

149166
`daostack/subgraph-postgres:${network}-${migration-version}-${subgraph-version}`
150167
`daostack/subgraph-ipfs:${network}-${migration-version}-${subgraph-version}`
168+
169+
## Blacklist a malicious DAO
170+
Add the DAO's Avatar address to the `ops/blacklist.json` file in the proper network array. For example, blacklisting `0xF7074b67B4B7830694a6f58Df06375F00365d2c2` on mainnet would look like:
171+
```json
172+
{
173+
"private": [],
174+
"kovan": [],
175+
"rinkeby": [],
176+
"mainnet": [
177+
"0xF7074b67B4B7830694a6f58Df06375F00365d2c2"
178+
]
179+
}
180+
```

docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ services:
1818
ipfs: 'ipfs:5001'
1919
ethereum: 'private:http://ganache:8545'
2020
GRAPH_IPFS_TIMEOUT: '2'
21-
GRAPH_MAX_IPFS_FILE_BYTES: '120000'
21+
GRAPH_MAX_IPFS_FILE_BYTES: '200000'
2222
GRAPH_GRAPHQL_MAX_FIRST: '1000'
2323
ipfs:
2424
build: docker/ipfs

ops/blacklist.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"private": [],
3+
"kovan": [],
4+
"rinkeby": [],
5+
"mainnet": []
6+
}

ops/generate-abis.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
const fs = require("fs-extra");
2-
const path = require("path");
32

43
/**
54
* Fetch all abis from @daostack/migration into the `abis` folder.
65
*/
76
async function generateAbis() {
8-
fs.copy("node_modules/@daostack/migration/abis", "abis");
7+
fs.copySync("node_modules/@daostack/migration/abis", `${__dirname}/../abis`);
98
}
109

1110
if (require.main === module) {

ops/generate-contractsinfo.js

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
const fs = require("fs");
2-
const yaml = require("js-yaml");
32
const { migrationFileLocation: defaultMigrationFileLocation, network } = require("./settings");
3+
const { forEachTemplate } = require("./utils");
44
const path = require("path");
5-
const currentDir = path.resolve(`${__dirname}`)
5+
const currentDir = path.resolve(`${__dirname}`);
66

77
/**
88
* Generate a `src/contractinfo.js` file from `migration.json`
@@ -20,8 +20,8 @@ async function generateContractInfo(opts={}) {
2020
const migration = JSON.parse(fs.readFileSync(require.resolve(opts.migrationFile), "utf-8"));
2121

2222
let versions = migration[network].base
23-
let buffer = "import { setContractInfo } from './utils';\n";
24-
buffer += "// this code was generated automatically . please not edit it -:)\n";
23+
let buffer = "import {\n setBlacklistedDAO,\n setContractInfo,\n setTemplateInfo,\n} from './utils';\n";
24+
buffer += "\n// this code was generated automatically . please not edit it -:)\n";
2525
buffer += "/* tslint:disable:max-line-length */\n";
2626

2727
buffer += "export function setContractsInfo(): void {\n";
@@ -53,6 +53,26 @@ async function generateContractInfo(opts={}) {
5353
});
5454
buffer += "}\n";
5555

56+
buffer += "\nexport function setTemplatesInfo(): void {\n";
57+
58+
forEachTemplate((name, mapping, arcVersion) => {
59+
const templateName = arcVersion.replace(/\.|-/g, '_');
60+
buffer += ` setTemplateInfo('${name}', '${arcVersion}', '${name}_${templateName}');\n`;
61+
});
62+
63+
buffer += "}\n";
64+
65+
const blacklist = require("./blacklist.json")[network];
66+
67+
buffer += "\nexport function setBlacklistedDAOs(): void {\n";
68+
69+
blacklist.forEach(function(avatar) {
70+
buffer += ` setBlacklistedDAO('${avatar.toLowerCase()}');\n`;
71+
});
72+
73+
buffer += " return;\n";
74+
buffer += "}\n";
75+
5676
fs.writeFileSync(
5777
`${currentDir}/../src/contractsInfo.ts`,
5878
buffer,

ops/generate-subgraph.js

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const path = require("path")
33
const yaml = require("js-yaml");
44
const { migrationFileLocation: defaultMigrationFileLocation,
55
network ,startBlock} = require("./settings");
6+
const { versionToNum, forEachTemplate } = require("./utils");
67
const mappings = require("./mappings.json")[network].mappings;
78
const { subgraphLocation: defaultSubgraphLocation } = require('./graph-cli')
89

@@ -38,10 +39,13 @@ async function generateSubgraph(opts={}) {
3839
throw Error(`The following contracts are missing addresses: ${missing.toString()}`);
3940
}
4041

42+
const templates = buildTemplates();
43+
4144
const subgraph = {
4245
specVersion: "0.0.1",
4346
schema: { file: "./schema.graphql" },
44-
dataSources
47+
dataSources,
48+
templates
4549
};
4650

4751
fs.writeFileSync(
@@ -55,19 +59,17 @@ function combineFragments(fragments, isTemplate, addresses, missingAddresses) {
5559
let ids = [];
5660
return fragments.map(mapping => {
5761
const contract = mapping.name;
62+
const version = mapping.arcVersion;
5863
const fragment = `${__dirname}/../src/mappings/${mapping.mapping}/datasource.yaml`;
59-
var abis, entities, eventHandlers, templates, file, yamlLoad, abi;
64+
var abis, entities, eventHandlers, file, yamlLoad, abi;
6065

6166
if (fs.existsSync(fragment)) {
6267
yamlLoad = yaml.safeLoad(fs.readFileSync(fragment, "utf-8"));
6368
file = `${__dirname}/../src/mappings/${mapping.mapping}/mapping.ts`;
6469
eventHandlers = yamlLoad.eventHandlers;
6570
entities = yamlLoad.entities;
66-
templates = yamlLoad.templates;
6771
abis = (yamlLoad.abis || [contract]).map(contractName => {
68-
const version = mapping.arcVersion;
69-
const strlen = version.length
70-
const versionNum = Number(version.slice(strlen - 2, strlen));
72+
const versionNum = versionToNum(version);
7173

7274
if ((versionNum < 24) && (contractName === "UGenericScheme")) {
7375
return {
@@ -95,17 +97,17 @@ function combineFragments(fragments, isTemplate, addresses, missingAddresses) {
9597
if (mapping.dao === 'address') {
9698
contractAddress = mapping.address;
9799
} else if (mapping.dao === 'organs') {
98-
contractAddress = addresses[network].test[mapping.arcVersion][mapping.dao][mapping.contractName];
100+
contractAddress = addresses[network].test[version][mapping.dao][mapping.contractName];
99101
} else {
100-
contractAddress = addresses[network][mapping.dao][mapping.arcVersion][mapping.contractName];
102+
contractAddress = addresses[network][mapping.dao][version][mapping.contractName];
101103
}
102104

103105
// If the contract isn't predeployed
104106
if (!contractAddress) {
105107
// Keep track of contracts that're missing addresses.
106108
// These contracts should have addresses because they're not
107109
// templated contracts aren't used as templates
108-
const daoContract = `${network}-${mapping.arcVersion}-${mapping.contractName}-${mapping.dao}`;
110+
const daoContract = `${network}-${version}-${mapping.contractName}-${mapping.dao}`;
109111
if (missingAddresses[daoContract] === undefined) {
110112
missingAddresses[daoContract] = true;
111113
}
@@ -131,6 +133,12 @@ function combineFragments(fragments, isTemplate, addresses, missingAddresses) {
131133
return;
132134
}
133135

136+
if (isTemplate) {
137+
// convert name to be version specific
138+
const classVersion = version.replace(/\.|-/g, '_');
139+
name = `${name}_${classVersion}`
140+
}
141+
134142
var result = {
135143
kind: 'ethereum/contract',
136144
name: `${name}`,
@@ -147,21 +155,31 @@ function combineFragments(fragments, isTemplate, addresses, missingAddresses) {
147155
}
148156
};
149157

150-
if (templates && templates.length) {
151-
result.templates = combineFragments(
152-
templates.map(template => ({
153-
name: template,
154-
mapping: template,
155-
arcVersion: mapping.arcVersion
156-
})),
157-
true, addresses, missingAddresses
158-
);
159-
}
160-
161158
return result;
162159
});
163160
}
164161

162+
function buildTemplates() {
163+
let results = [];
164+
165+
forEachTemplate((name, mapping, arcVersion) => {
166+
results.push(
167+
...combineFragments(
168+
[{
169+
name,
170+
mapping,
171+
arcVersion
172+
}],
173+
true,
174+
undefined,
175+
undefined
176+
)
177+
);
178+
});
179+
180+
return results;
181+
}
182+
165183
if (require.main === module) {
166184
generateSubgraph().catch(err => {
167185
console.log(err);

ops/mappings.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,13 @@
610610
"dao": "base",
611611
"mapping": "Redeemer",
612612
"arcVersion": "0.0.1-rc.32"
613+
},
614+
{
615+
"name": "DAOTracker",
616+
"contractName": "DAOTracker",
617+
"dao": "base",
618+
"mapping": "DAOTracker",
619+
"arcVersion": "0.0.1-rc.32"
613620
}
614621
]
615622
},
@@ -824,6 +831,13 @@
824831
"dao": "base",
825832
"mapping": "GenesisProtocol",
826833
"arcVersion": "0.0.1-rc.24"
834+
},
835+
{
836+
"name": "DAOTracker",
837+
"contractName": "DAOTracker",
838+
"dao": "base",
839+
"mapping": "DAOTracker",
840+
"arcVersion": "0.0.1-rc.32"
827841
}
828842
]
829843
},
@@ -969,6 +983,13 @@
969983
"dao": "base",
970984
"mapping": "DAORegistry",
971985
"arcVersion": "0.0.1-rc.30"
986+
},
987+
{
988+
"name": "DAOTracker",
989+
"contractName": "DAOTracker",
990+
"dao": "base",
991+
"mapping": "DAOTracker",
992+
"arcVersion": "0.0.1-rc.32"
972993
}
973994
]
974995
}

ops/templates.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"templates": [
3+
{
4+
"name": "Avatar",
5+
"mapping": "Avatar",
6+
"start_arcVersion": "0.0.1-rc.16"
7+
},
8+
{
9+
"name": "Controller",
10+
"mapping": "Controller",
11+
"start_arcVersion": "0.0.1-rc.16"
12+
},
13+
{
14+
"name": "DAOToken",
15+
"mapping": "DAOToken",
16+
"start_arcVersion": "0.0.1-rc.16"
17+
},
18+
{
19+
"name": "Reputation",
20+
"mapping": "Reputation",
21+
"start_arcVersion": "0.0.1-rc.16"
22+
}
23+
]
24+
}

ops/utils.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const fs = require("fs");
2+
3+
function versionToNum(version) {
4+
const strlen = version.length;
5+
return Number(version.slice(strlen - 2, strlen));
6+
}
7+
8+
function forEachTemplate(callback) {
9+
const templates = require("./templates.json").templates;
10+
const abiDirectories = fs.readdirSync(`${__dirname}/../abis`, { withFileTypes: true })
11+
.filter(dirent => dirent.isDirectory())
12+
.map(dirent => dirent.name);
13+
14+
for (var template of templates) {
15+
const { name, mapping, start_arcVersion, end_arcVersion } = template;
16+
const startNum = versionToNum(start_arcVersion);
17+
const endNum = end_arcVersion ? versionToNum(end_arcVersion) : undefined;
18+
19+
// for each ABI directory, if it falls within start & end
20+
for (var abiDirectory of abiDirectories) {
21+
const arcVersion = abiDirectory;
22+
const arcNum = versionToNum(arcVersion);
23+
24+
if (arcNum >= startNum && (endNum === undefined || arcNum <= endNum)) {
25+
callback(name, mapping, arcVersion);
26+
}
27+
}
28+
}
29+
}
30+
31+
module.exports = {
32+
versionToNum,
33+
forEachTemplate
34+
}

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/domain/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Address, BigDecimal, BigInt, ByteArray, Bytes, crypto, Entity, store, Value} from '@graphprotocol/graph-ts';
2-
import { setContractsInfo } from '../contractsInfo';
2+
import { setContractsInfo, setTemplatesInfo } from '../contractsInfo';
33
import {
44
NewContributionProposal,
55
ProposalExecuted,
@@ -208,6 +208,7 @@ export function handleRegisterScheme(avatar: Address,
208208
);
209209
if (isFirstRegister == null) {
210210
setContractsInfo();
211+
setTemplatesInfo();
211212
let dao = daoModule.insertNewDAO(avatar, nativeTokenAddress , nativeReputationAddress);
212213
insertToken(hexToAddress(dao.nativeToken), avatar.toHex());
213214
insertReputation(

src/domain/schema.graphql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,11 @@ type ContractInfo @entity {
199199
address: Bytes!
200200
}
201201

202+
type TemplateInfo @entity {
203+
id: ID!
204+
templateName: String!
205+
}
206+
202207
type Event @entity {
203208
id: ID!
204209
type: EventType!
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
abis:
2+
- DAOTracker
3+
entities:
4+
- DAOTrackerContract
5+
- BlacklistedDAO
6+
eventHandlers:
7+
- event: TrackDAO(indexed address,address,address,address,address,string)
8+
handler: handleTrackDAO

0 commit comments

Comments
 (0)