@graphprotocol/graph-cli 0.24.0-alpha.0 → 0.25.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/examples/basic-event-handlers/node_modules/@graphprotocol/graph-ts/README.md +83 -0
- package/examples/basic-event-handlers/node_modules/@graphprotocol/graph-ts/chain/ethereum.ts +2 -0
- package/examples/basic-event-handlers/node_modules/@graphprotocol/graph-ts/common/json.ts +2 -2
- package/examples/basic-event-handlers/node_modules/@graphprotocol/graph-ts/package.json +2 -7
- package/examples/basic-event-handlers/package.json +1 -1
- package/examples/basic-event-handlers/yarn.lock +4 -4
- package/examples/example-subgraph/package.json +1 -1
- package/examples/example-subgraph/yarn.lock +4 -4
- package/package.json +1 -1
- package/src/codegen/schema.js +9 -2
- package/src/codegen/types/index.js +8 -1
- package/src/command-helpers/scaffold.js +66 -0
- package/src/command-helpers/version.js +1 -1
- package/src/command-helpers/version.test.js +1 -1
- package/src/commands/init.js +128 -92
- package/src/protocols/ethereum/contract.js +22 -0
- package/src/protocols/ethereum/scaffold/manifest.js +32 -0
- package/src/protocols/ethereum/scaffold/mapping.js +76 -0
- package/src/protocols/ethereum/subgraph.js +0 -14
- package/src/protocols/index.js +88 -8
- package/src/protocols/near/contract.js +46 -0
- package/src/protocols/near/scaffold/manifest.js +16 -0
- package/src/protocols/near/scaffold/mapping.js +39 -0
- package/src/protocols/near/subgraph.js +1 -23
- package/src/{scaffold.test.js → scaffold/ethereum.test.js} +27 -19
- package/src/scaffold/index.js +149 -0
- package/src/scaffold/mapping.js +49 -0
- package/src/scaffold/near.test.js +89 -0
- package/src/scaffold/schema.js +62 -0
- package/src/subgraph.js +8 -0
- package/src/validation/contract.js +56 -0
- package/src/validation/index.js +2 -1
- package/src/validation/manifest.js +0 -29
- package/tests/cli/init/{abis → ethereum/abis}/Marketplace.json +0 -0
- package/tests/cli/init/{abis → ethereum/abis}/OverloadedElements.json +0 -0
- package/tests/cli/init/{abis → ethereum/abis}/SoloMargin.json +0 -0
- package/tests/cli/init/{from-contract-with-abi-and-structs.stderr → ethereum/from-contract-with-abi-and-structs.stderr} +1 -1
- package/tests/cli/init/{from-contract-with-abi-and-structs.stdout → ethereum/from-contract-with-abi-and-structs.stdout} +0 -0
- package/tests/cli/init/{from-contract-with-abi.stderr → ethereum/from-contract-with-abi.stderr} +1 -1
- package/tests/cli/init/{from-contract-with-abi.stdout → ethereum/from-contract-with-abi.stdout} +0 -0
- package/tests/cli/init/{from-contract-with-overloaded-elements.stderr → ethereum/from-contract-with-overloaded-elements.stderr} +1 -1
- package/tests/cli/init/{from-contract-with-overloaded-elements.stdout → ethereum/from-contract-with-overloaded-elements.stdout} +0 -0
- package/tests/cli/init/{from-contract.stderr → ethereum/from-contract.stderr} +1 -1
- package/tests/cli/init/{from-contract.stdout → ethereum/from-contract.stdout} +0 -0
- package/tests/cli/init/{from-example.stderr → ethereum/from-example.stderr} +0 -0
- package/tests/cli/init/{from-example.stdout → ethereum/from-example.stdout} +0 -0
- package/tests/cli/init/near/from-contract.stderr +12 -0
- package/tests/cli/init/near/from-contract.stdout +12 -0
- package/tests/cli/init.test.js +143 -112
- package/tests/cli/util.js +56 -41
- package/tests/cli/validation/near-is-valid/subgraph.yaml +1 -0
- package/src/scaffold.js +0 -364
package/src/commands/init.js
CHANGED
|
@@ -14,98 +14,107 @@ const { validateStudioNetwork } = require('../command-helpers/studio')
|
|
|
14
14
|
const { withSpinner, step } = require('../command-helpers/spinner')
|
|
15
15
|
const { fixParameters } = require('../command-helpers/gluegun')
|
|
16
16
|
const { chooseNodeUrl } = require('../command-helpers/node')
|
|
17
|
-
const {
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
'rinkeby',
|
|
25
|
-
'ropsten',
|
|
26
|
-
'goerli',
|
|
27
|
-
'poa-core',
|
|
28
|
-
'poa-sokol',
|
|
29
|
-
'xdai',
|
|
30
|
-
'matic',
|
|
31
|
-
'mumbai',
|
|
32
|
-
'fantom',
|
|
33
|
-
'bsc',
|
|
34
|
-
'chapel',
|
|
35
|
-
'clover',
|
|
36
|
-
'avalanche',
|
|
37
|
-
'fuji',
|
|
38
|
-
'celo',
|
|
39
|
-
'celo-alfajores',
|
|
40
|
-
'fuse',
|
|
41
|
-
'mbase',
|
|
42
|
-
'arbitrum-one',
|
|
43
|
-
'arbitrum-rinkeby',
|
|
44
|
-
'optimism',
|
|
45
|
-
'optimism-kovan'
|
|
46
|
-
]
|
|
17
|
+
const { generateScaffold, writeScaffold } = require('../command-helpers/scaffold')
|
|
18
|
+
const { abiEvents } = require('../scaffold/schema')
|
|
19
|
+
const { validateContract } = require('../validation')
|
|
20
|
+
const Protocol = require('../protocols')
|
|
21
|
+
|
|
22
|
+
const protocolChoices = Array.from(Protocol.availableProtocols().keys())
|
|
23
|
+
const availableNetworks = Protocol.availableNetworks()
|
|
47
24
|
|
|
48
25
|
const HELP = `
|
|
49
26
|
${chalk.bold('graph init')} [options] [subgraph-name] [directory]
|
|
50
27
|
|
|
51
28
|
${chalk.dim('Options:')}
|
|
52
29
|
|
|
30
|
+
--protocol <${protocolChoices.join('|')}>
|
|
53
31
|
--product <subgraph-studio|hosted-service>
|
|
54
|
-
|
|
55
|
-
--studio
|
|
56
|
-
-g, --node <node>
|
|
57
|
-
--allow-simple-name
|
|
58
|
-
-h, --help
|
|
32
|
+
Selects the product for which to initialize
|
|
33
|
+
--studio Shortcut for --product subgraph-studio
|
|
34
|
+
-g, --node <node> Graph node for which to initialize
|
|
35
|
+
--allow-simple-name Use a subgraph name without a prefix (default: false)
|
|
36
|
+
-h, --help Show usage information
|
|
59
37
|
|
|
60
38
|
${chalk.dim('Choose mode with one of:')}
|
|
61
39
|
|
|
62
|
-
--from-contract <
|
|
63
|
-
--from-example
|
|
40
|
+
--from-contract <contract> Creates a scaffold based on an existing contract
|
|
41
|
+
--from-example Creates a scaffold based on an example subgraph
|
|
64
42
|
|
|
65
43
|
${chalk.dim('Options for --from-contract:')}
|
|
66
44
|
|
|
67
|
-
--
|
|
68
|
-
--
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
45
|
+
--contract-name Name of the contract (default: Contract)
|
|
46
|
+
--index-events Index contract events as entities
|
|
47
|
+
|
|
48
|
+
${chalk.dim.underline('Ethereum:')}
|
|
49
|
+
|
|
50
|
+
--abi <path> Path to the contract ABI (default: download from Etherscan)
|
|
51
|
+
--network <${availableNetworks.get('ethereum').join('|')}>
|
|
52
|
+
Selects the network the contract is deployed to
|
|
53
|
+
|
|
54
|
+
${chalk.dim.underline('NEAR:')}
|
|
55
|
+
|
|
56
|
+
--network <${availableNetworks.get('near').join('|')}>
|
|
57
|
+
Selects the network the contract is deployed to
|
|
72
58
|
`
|
|
73
59
|
|
|
74
60
|
const processInitForm = async (
|
|
75
61
|
toolbox,
|
|
76
62
|
{
|
|
63
|
+
protocol,
|
|
77
64
|
product,
|
|
78
65
|
studio,
|
|
79
66
|
node,
|
|
80
67
|
abi,
|
|
81
68
|
allowSimpleName,
|
|
82
69
|
directory,
|
|
83
|
-
|
|
70
|
+
contract,
|
|
84
71
|
fromExample,
|
|
85
72
|
network,
|
|
86
73
|
subgraphName,
|
|
87
74
|
contractName
|
|
88
75
|
},
|
|
89
76
|
) => {
|
|
90
|
-
let addressPattern = /^(0x)?[0-9a-fA-F]{40}$/
|
|
91
|
-
|
|
92
77
|
let abiFromEtherscan = undefined
|
|
93
78
|
let abiFromFile = undefined
|
|
79
|
+
let protocolInstance
|
|
80
|
+
let ProtocolContract
|
|
81
|
+
let ABI
|
|
94
82
|
|
|
95
83
|
let questions = [
|
|
84
|
+
{
|
|
85
|
+
type: 'select',
|
|
86
|
+
name: 'protocol',
|
|
87
|
+
message: 'Protocol',
|
|
88
|
+
choices: protocolChoices,
|
|
89
|
+
skip: protocolChoices.includes(protocol),
|
|
90
|
+
result: value => {
|
|
91
|
+
protocol = protocol || value
|
|
92
|
+
protocolInstance = new Protocol(protocol)
|
|
93
|
+
return protocol
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
96
|
{
|
|
97
97
|
type: 'select',
|
|
98
98
|
name: 'product',
|
|
99
99
|
message: 'Product for which to initialize',
|
|
100
100
|
choices: ['subgraph-studio', 'hosted-service'],
|
|
101
|
-
skip:
|
|
101
|
+
skip: () =>
|
|
102
|
+
protocol === 'near' ||
|
|
102
103
|
product === 'subgraph-studio' ||
|
|
103
104
|
product === 'hosted-service' ||
|
|
104
105
|
studio !== undefined || node !== undefined,
|
|
105
106
|
result: value => {
|
|
107
|
+
// For now we only support NEAR subgraphs in the Hosted Service
|
|
108
|
+
if (protocol === 'near') {
|
|
109
|
+
// Can be overwritten because the question will be skipped (product === undefined)
|
|
110
|
+
product = 'hosted-service'
|
|
111
|
+
return product
|
|
112
|
+
}
|
|
113
|
+
|
|
106
114
|
if (value == 'subgraph-studio') {
|
|
107
115
|
allowSimpleName = true
|
|
108
116
|
}
|
|
117
|
+
|
|
109
118
|
product = value
|
|
110
119
|
return value
|
|
111
120
|
},
|
|
@@ -146,8 +155,11 @@ const processInitForm = async (
|
|
|
146
155
|
{
|
|
147
156
|
type: 'select',
|
|
148
157
|
name: 'network',
|
|
149
|
-
message:
|
|
150
|
-
choices:
|
|
158
|
+
message: () => `${protocolInstance.displayName()} network`,
|
|
159
|
+
choices: () =>
|
|
160
|
+
availableNetworks
|
|
161
|
+
.get(protocol) // Get networks related to the chosen protocol.
|
|
162
|
+
.toArray(), // Needed because of gluegun. It can't even receive a JS iterable.
|
|
151
163
|
skip: fromExample !== undefined,
|
|
152
164
|
initial: network || 'mainnet',
|
|
153
165
|
result: value => {
|
|
@@ -157,35 +169,39 @@ const processInitForm = async (
|
|
|
157
169
|
},
|
|
158
170
|
{
|
|
159
171
|
type: 'input',
|
|
160
|
-
name: '
|
|
161
|
-
message:
|
|
172
|
+
name: 'contract',
|
|
173
|
+
message: () => {
|
|
174
|
+
ProtocolContract = protocolInstance.getContract()
|
|
175
|
+
return `Contract ${ProtocolContract.identifierName()}`
|
|
176
|
+
},
|
|
162
177
|
skip: fromExample !== undefined,
|
|
163
|
-
initial:
|
|
178
|
+
initial: contract,
|
|
164
179
|
validate: async value => {
|
|
165
180
|
if (fromExample !== undefined) {
|
|
166
181
|
return true
|
|
167
182
|
}
|
|
168
183
|
|
|
169
|
-
// Validate whether the
|
|
170
|
-
|
|
171
|
-
return `Contract address "${value}" is invalid.
|
|
172
|
-
Must be 40 hexadecimal characters, with an optional '0x' prefix.`
|
|
173
|
-
}
|
|
184
|
+
// Validate whether the contract is valid
|
|
185
|
+
const { valid, error } = validateContract(value, ProtocolContract)
|
|
174
186
|
|
|
175
|
-
return
|
|
187
|
+
return valid
|
|
188
|
+
? true
|
|
189
|
+
: error
|
|
176
190
|
},
|
|
177
191
|
result: async value => {
|
|
178
192
|
if (fromExample !== undefined) {
|
|
179
193
|
return value
|
|
180
194
|
}
|
|
181
195
|
|
|
196
|
+
ABI = protocolInstance.getABI()
|
|
197
|
+
|
|
182
198
|
// Try loading the ABI from Etherscan, if none was provided
|
|
183
|
-
if (!abi) {
|
|
199
|
+
if (protocolInstance.hasABIs() && !abi) {
|
|
184
200
|
try {
|
|
185
201
|
if (network === 'poa-core') {
|
|
186
|
-
abiFromBlockScout = await loadAbiFromBlockScout(network, value)
|
|
202
|
+
abiFromBlockScout = await loadAbiFromBlockScout(ABI, network, value)
|
|
187
203
|
} else {
|
|
188
|
-
abiFromEtherscan = await loadAbiFromEtherscan(network, value)
|
|
204
|
+
abiFromEtherscan = await loadAbiFromEtherscan(ABI, network, value)
|
|
189
205
|
}
|
|
190
206
|
} catch (e) {}
|
|
191
207
|
}
|
|
@@ -197,14 +213,17 @@ const processInitForm = async (
|
|
|
197
213
|
name: 'abi',
|
|
198
214
|
message: 'ABI file (path)',
|
|
199
215
|
initial: abi,
|
|
200
|
-
skip: () =>
|
|
216
|
+
skip: () =>
|
|
217
|
+
!protocolInstance.hasABIs() ||
|
|
218
|
+
fromExample !== undefined ||
|
|
219
|
+
abiFromEtherscan !== undefined,
|
|
201
220
|
validate: async value => {
|
|
202
|
-
if (fromExample || abiFromEtherscan) {
|
|
221
|
+
if (fromExample || abiFromEtherscan || !protocolInstance.hasABIs()) {
|
|
203
222
|
return true
|
|
204
223
|
}
|
|
205
224
|
|
|
206
225
|
try {
|
|
207
|
-
abiFromFile = await loadAbiFromFile(value)
|
|
226
|
+
abiFromFile = await loadAbiFromFile(ABI, value)
|
|
208
227
|
return true
|
|
209
228
|
} catch (e) {
|
|
210
229
|
return e.message
|
|
@@ -227,13 +246,13 @@ const processInitForm = async (
|
|
|
227
246
|
|
|
228
247
|
try {
|
|
229
248
|
let answers = await toolbox.prompt.ask(questions)
|
|
230
|
-
return { ...answers, abi: abiFromEtherscan || abiFromFile }
|
|
249
|
+
return { ...answers, abi: abiFromEtherscan || abiFromFile, protocolInstance }
|
|
231
250
|
} catch (e) {
|
|
232
251
|
return undefined
|
|
233
252
|
}
|
|
234
253
|
}
|
|
235
254
|
|
|
236
|
-
const loadAbiFromBlockScout = async (network, address) =>
|
|
255
|
+
const loadAbiFromBlockScout = async (ABI, network, address) =>
|
|
237
256
|
await withSpinner(
|
|
238
257
|
`Fetching ABI from BlockScout`,
|
|
239
258
|
`Failed to fetch ABI from BlockScout`,
|
|
@@ -267,7 +286,7 @@ const getEtherscanLikeAPIUrl = (network) => {
|
|
|
267
286
|
}
|
|
268
287
|
}
|
|
269
288
|
|
|
270
|
-
const loadAbiFromEtherscan = async (network, address) =>
|
|
289
|
+
const loadAbiFromEtherscan = async (ABI, network, address) =>
|
|
271
290
|
await withSpinner(
|
|
272
291
|
`Fetching ABI from Etherscan`,
|
|
273
292
|
`Failed to fetch ABI from Etherscan`,
|
|
@@ -290,7 +309,7 @@ const loadAbiFromEtherscan = async (network, address) =>
|
|
|
290
309
|
},
|
|
291
310
|
)
|
|
292
311
|
|
|
293
|
-
const loadAbiFromFile = async filename => {
|
|
312
|
+
const loadAbiFromFile = async (ABI, filename) => {
|
|
294
313
|
let exists = await toolbox.filesystem.exists(filename)
|
|
295
314
|
|
|
296
315
|
if (!exists) {
|
|
@@ -315,6 +334,7 @@ module.exports = {
|
|
|
315
334
|
|
|
316
335
|
// Read CLI parameters
|
|
317
336
|
let {
|
|
337
|
+
protocol,
|
|
318
338
|
product,
|
|
319
339
|
studio,
|
|
320
340
|
node,
|
|
@@ -400,35 +420,47 @@ module.exports = {
|
|
|
400
420
|
|
|
401
421
|
// If all parameters are provided from the command-line,
|
|
402
422
|
// go straight to creating the subgraph from an existing contract
|
|
403
|
-
if (fromContract && subgraphName && directory && network && node) {
|
|
404
|
-
if (
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
423
|
+
if (fromContract && protocol && subgraphName && directory && network && node) {
|
|
424
|
+
if (!protocolChoices.includes(protocol)) {
|
|
425
|
+
print.error(`Protocol '${protocol}' is not supported, choose from these options: ${protocolChoices.join(', ')}`)
|
|
426
|
+
process.exitCode = 1
|
|
427
|
+
return
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const protocolInstance = new Protocol(protocol)
|
|
431
|
+
|
|
432
|
+
if (protocolInstance.hasABIs()) {
|
|
433
|
+
const ABI = protocolInstance.getABI()
|
|
434
|
+
if (abi) {
|
|
435
|
+
try {
|
|
436
|
+
abi = await loadAbiFromFile(ABI, abi)
|
|
437
|
+
} catch (e) {
|
|
438
|
+
print.error(`Failed to load ABI: ${e.message}`)
|
|
439
|
+
process.exitCode = 1
|
|
440
|
+
return
|
|
441
|
+
}
|
|
442
|
+
} else {
|
|
443
|
+
try {
|
|
444
|
+
if (network === 'poa-core') {
|
|
445
|
+
abi = await loadAbiFromBlockScout(ABI, network, fromContract)
|
|
446
|
+
} else {
|
|
447
|
+
abi = await loadAbiFromEtherscan(ABI, network, fromContract)
|
|
448
|
+
}
|
|
449
|
+
} catch (e) {
|
|
450
|
+
process.exitCode = 1
|
|
451
|
+
return
|
|
418
452
|
}
|
|
419
|
-
} catch (e) {
|
|
420
|
-
process.exitCode = 1
|
|
421
|
-
return
|
|
422
453
|
}
|
|
423
454
|
}
|
|
424
455
|
|
|
425
456
|
return await initSubgraphFromContract(
|
|
426
457
|
toolbox,
|
|
427
458
|
{
|
|
459
|
+
protocolInstance,
|
|
428
460
|
abi,
|
|
429
461
|
allowSimpleName,
|
|
430
462
|
directory,
|
|
431
|
-
|
|
463
|
+
contract: fromContract,
|
|
432
464
|
indexEvents,
|
|
433
465
|
network,
|
|
434
466
|
subgraphName,
|
|
@@ -443,13 +475,14 @@ module.exports = {
|
|
|
443
475
|
|
|
444
476
|
// Otherwise, take the user through the interactive form
|
|
445
477
|
let inputs = await processInitForm(toolbox, {
|
|
478
|
+
protocol,
|
|
446
479
|
product,
|
|
447
480
|
studio,
|
|
448
481
|
node,
|
|
449
482
|
abi,
|
|
450
483
|
allowSimpleName,
|
|
451
484
|
directory,
|
|
452
|
-
|
|
485
|
+
contract: fromContract,
|
|
453
486
|
fromExample,
|
|
454
487
|
network,
|
|
455
488
|
subgraphName,
|
|
@@ -484,12 +517,13 @@ module.exports = {
|
|
|
484
517
|
await initSubgraphFromContract(
|
|
485
518
|
toolbox,
|
|
486
519
|
{
|
|
520
|
+
protocolInstance: inputs.protocolInstance,
|
|
487
521
|
allowSimpleName,
|
|
488
522
|
subgraphName: inputs.subgraphName,
|
|
489
523
|
directory: inputs.directory,
|
|
490
524
|
abi: inputs.abi,
|
|
491
525
|
network: inputs.network,
|
|
492
|
-
|
|
526
|
+
contract: inputs.contract,
|
|
493
527
|
indexEvents,
|
|
494
528
|
contractName: inputs.contractName,
|
|
495
529
|
node,
|
|
@@ -713,12 +747,13 @@ const initSubgraphFromExample = async (
|
|
|
713
747
|
const initSubgraphFromContract = async (
|
|
714
748
|
toolbox,
|
|
715
749
|
{
|
|
750
|
+
protocolInstance,
|
|
716
751
|
allowSimpleName,
|
|
717
752
|
subgraphName,
|
|
718
753
|
directory,
|
|
719
754
|
abi,
|
|
720
755
|
network,
|
|
721
|
-
|
|
756
|
+
contract,
|
|
722
757
|
indexEvents,
|
|
723
758
|
contractName,
|
|
724
759
|
node,
|
|
@@ -742,7 +777,7 @@ const initSubgraphFromContract = async (
|
|
|
742
777
|
return
|
|
743
778
|
}
|
|
744
779
|
|
|
745
|
-
if (abiEvents(abi).length === 0) {
|
|
780
|
+
if (protocolInstance.hasABIs() && abiEvents(abi).length === 0) {
|
|
746
781
|
// Fail if the ABI does not contain any events
|
|
747
782
|
print.error(`ABI does not contain any events`)
|
|
748
783
|
process.exitCode = 1
|
|
@@ -760,7 +795,7 @@ const initSubgraphFromContract = async (
|
|
|
760
795
|
return
|
|
761
796
|
}
|
|
762
797
|
|
|
763
|
-
// Scaffold subgraph
|
|
798
|
+
// Scaffold subgraph
|
|
764
799
|
let scaffold = await withSpinner(
|
|
765
800
|
`Create subgraph scaffold`,
|
|
766
801
|
`Failed to create subgraph scaffold`,
|
|
@@ -768,10 +803,11 @@ const initSubgraphFromContract = async (
|
|
|
768
803
|
async spinner => {
|
|
769
804
|
let scaffold = await generateScaffold(
|
|
770
805
|
{
|
|
806
|
+
protocolInstance,
|
|
771
807
|
subgraphName,
|
|
772
808
|
abi,
|
|
773
809
|
network,
|
|
774
|
-
|
|
810
|
+
contract,
|
|
775
811
|
indexEvents,
|
|
776
812
|
contractName,
|
|
777
813
|
node,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module.exports = class EthereumContract {
|
|
2
|
+
static identifierName() {
|
|
3
|
+
return 'address'
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
constructor(address) {
|
|
7
|
+
this.address = address
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
validate() {
|
|
11
|
+
const pattern = /^(0x)?[0-9a-fA-F]{40}$/
|
|
12
|
+
|
|
13
|
+
const errorMessage = "Must be 40 hexadecimal characters, with an optional '0x' prefix."
|
|
14
|
+
|
|
15
|
+
const valid = pattern.test(this.address)
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
valid,
|
|
19
|
+
error: valid ? null : errorMessage,
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const { abiEvents } = require('../../../scaffold/schema')
|
|
2
|
+
const ABI = require('../abi')
|
|
3
|
+
|
|
4
|
+
const source = ({ contract, contractName }) => `
|
|
5
|
+
address: '${contract}'
|
|
6
|
+
abi: ${contractName}`
|
|
7
|
+
|
|
8
|
+
const mapping = ({ abi, contractName }) => `
|
|
9
|
+
kind: ethereum/events
|
|
10
|
+
apiVersion: 0.0.5
|
|
11
|
+
language: wasm/assemblyscript
|
|
12
|
+
entities:
|
|
13
|
+
${abiEvents(abi)
|
|
14
|
+
.map(event => `- ${event.get('_alias')}`)
|
|
15
|
+
.join('\n ')}
|
|
16
|
+
abis:
|
|
17
|
+
- name: ${contractName}
|
|
18
|
+
file: ./abis/${contractName}.json
|
|
19
|
+
eventHandlers:
|
|
20
|
+
${abiEvents(abi)
|
|
21
|
+
.map(
|
|
22
|
+
event => `
|
|
23
|
+
- event: ${ABI.eventSignature(event)}
|
|
24
|
+
handler: handle${event.get('_alias')}`,
|
|
25
|
+
)
|
|
26
|
+
.join('')}
|
|
27
|
+
file: ./src/mapping.ts`
|
|
28
|
+
|
|
29
|
+
module.exports = {
|
|
30
|
+
source,
|
|
31
|
+
mapping,
|
|
32
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const { generateEventFieldAssignments } = require('../../../scaffold/mapping')
|
|
2
|
+
|
|
3
|
+
const generatePlaceholderHandlers = ({ abi, events, contractName }) =>
|
|
4
|
+
`
|
|
5
|
+
import { BigInt } from '@graphprotocol/graph-ts'
|
|
6
|
+
import { ${contractName}, ${events.map(event => event._alias)} }
|
|
7
|
+
from '../generated/${contractName}/${contractName}'
|
|
8
|
+
import { ExampleEntity } from '../generated/schema'
|
|
9
|
+
|
|
10
|
+
${events
|
|
11
|
+
.map((event, index) =>
|
|
12
|
+
index === 0
|
|
13
|
+
? `
|
|
14
|
+
export function handle${event._alias}(event: ${event._alias}): void {
|
|
15
|
+
// Entities can be loaded from the store using a string ID; this ID
|
|
16
|
+
// needs to be unique across all entities of the same type
|
|
17
|
+
let entity = ExampleEntity.load(event.transaction.from.toHex())
|
|
18
|
+
|
|
19
|
+
// Entities only exist after they have been saved to the store;
|
|
20
|
+
// \`null\` checks allow to create entities on demand
|
|
21
|
+
if (!entity) {
|
|
22
|
+
entity = new ExampleEntity(event.transaction.from.toHex())
|
|
23
|
+
|
|
24
|
+
// Entity fields can be set using simple assignments
|
|
25
|
+
entity.count = BigInt.fromI32(0)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// BigInt and BigDecimal math are supported
|
|
29
|
+
entity.count = entity.count + BigInt.fromI32(1)
|
|
30
|
+
|
|
31
|
+
// Entity fields can be set based on event parameters
|
|
32
|
+
${generateEventFieldAssignments(event)
|
|
33
|
+
.slice(0, 2)
|
|
34
|
+
.join('\n')}
|
|
35
|
+
|
|
36
|
+
// Entities can be written to the store with \`.save()\`
|
|
37
|
+
entity.save()
|
|
38
|
+
|
|
39
|
+
// Note: If a handler doesn't require existing field values, it is faster
|
|
40
|
+
// _not_ to load the entity from the store. Instead, create it fresh with
|
|
41
|
+
// \`new Entity(...)\`, set the fields that should be updated and save the
|
|
42
|
+
// entity back to the store. Fields that were not set or unset remain
|
|
43
|
+
// unchanged, allowing for partial updates to be applied.
|
|
44
|
+
|
|
45
|
+
// It is also possible to access smart contracts from mappings. For
|
|
46
|
+
// example, the contract that has emitted the event can be connected to
|
|
47
|
+
// with:
|
|
48
|
+
//
|
|
49
|
+
// let contract = Contract.bind(event.address)
|
|
50
|
+
//
|
|
51
|
+
// The following functions can then be called on this contract to access
|
|
52
|
+
// state variables and other data:
|
|
53
|
+
//
|
|
54
|
+
// ${
|
|
55
|
+
abi
|
|
56
|
+
.codeGenerator()
|
|
57
|
+
.callableFunctions()
|
|
58
|
+
.isEmpty()
|
|
59
|
+
? 'None'
|
|
60
|
+
: abi
|
|
61
|
+
.codeGenerator()
|
|
62
|
+
.callableFunctions()
|
|
63
|
+
.map(fn => `- contract.${fn.get('name')}(...)`)
|
|
64
|
+
.join('\n// ')
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
`
|
|
68
|
+
: `
|
|
69
|
+
export function handle${event._alias}(event: ${event._alias}): void {}
|
|
70
|
+
`,
|
|
71
|
+
)
|
|
72
|
+
.join('\n')}`
|
|
73
|
+
|
|
74
|
+
module.exports = {
|
|
75
|
+
generatePlaceholderHandlers,
|
|
76
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const immutable = require('immutable')
|
|
2
2
|
const ABI = require('./abi')
|
|
3
3
|
const DataSourcesExtractor = require('../../command-helpers/data-sources')
|
|
4
|
-
const { validateContractValues } = require('../../validation')
|
|
5
4
|
|
|
6
5
|
module.exports = class EthereumSubgraph {
|
|
7
6
|
constructor(options = {}) {
|
|
@@ -12,7 +11,6 @@ module.exports = class EthereumSubgraph {
|
|
|
12
11
|
|
|
13
12
|
validateManifest() {
|
|
14
13
|
return this.validateAbis()
|
|
15
|
-
.concat(this.validateContractAddresses())
|
|
16
14
|
.concat(this.validateEvents())
|
|
17
15
|
.concat(this.validateCallFunctions())
|
|
18
16
|
}
|
|
@@ -72,18 +70,6 @@ ${abiNames
|
|
|
72
70
|
return nameErrors.concat(fileErrors)
|
|
73
71
|
}
|
|
74
72
|
|
|
75
|
-
validateContractAddresses() {
|
|
76
|
-
const ethereumAddressPattern = /^(0x)?[0-9a-fA-F]{40}$/
|
|
77
|
-
|
|
78
|
-
return validateContractValues(
|
|
79
|
-
this.manifest,
|
|
80
|
-
this.protocol,
|
|
81
|
-
'address',
|
|
82
|
-
address => ethereumAddressPattern.test(address),
|
|
83
|
-
"Must be 40 hexadecimal characters, with an optional '0x' prefix.",
|
|
84
|
-
)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
73
|
validateEvents() {
|
|
88
74
|
const dataSourcesAndTemplates = DataSourcesExtractor.fromManifest(this.manifest, this.protocol)
|
|
89
75
|
|