@graphprotocol/graph-cli 0.40.0 → 0.40.1-alpha-20230216174117-760a195
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/CHANGELOG.md +10 -0
- package/dist/bin.js +31 -3
- package/dist/command-helpers/network.js +16 -13
- package/dist/command-helpers/network.test.js +2 -2
- package/dist/command-helpers/spinner.js +4 -27
- package/dist/commands/add.js +48 -67
- package/dist/commands/auth.js +40 -102
- package/dist/commands/build.js +51 -68
- package/dist/commands/codegen.js +39 -61
- package/dist/commands/create.js +31 -55
- package/dist/commands/deploy.js +112 -174
- package/dist/commands/init.js +519 -512
- package/dist/commands/local.js +93 -103
- package/dist/commands/remove.js +32 -56
- package/dist/commands/test.js +81 -88
- package/dist/index.js +5 -0
- package/oclif.manifest.json +724 -0
- package/package.json +10 -3
- package/dist/cli.js +0 -26
- package/dist/command-helpers/gluegun.js +0 -48
package/dist/commands/init.js
CHANGED
|
@@ -29,357 +29,55 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
29
29
|
const fs_1 = __importDefault(require("fs"));
|
|
30
30
|
const os_1 = __importDefault(require("os"));
|
|
31
31
|
const path_1 = __importDefault(require("path"));
|
|
32
|
-
const
|
|
33
|
-
const
|
|
32
|
+
const core_1 = require("@oclif/core");
|
|
33
|
+
const gluegun_1 = require("gluegun");
|
|
34
34
|
const abi_1 = require("../command-helpers/abi");
|
|
35
35
|
const DataSourcesExtractor = __importStar(require("../command-helpers/data-sources"));
|
|
36
|
-
const gluegun_1 = require("../command-helpers/gluegun");
|
|
37
36
|
const network_1 = require("../command-helpers/network");
|
|
38
37
|
const node_1 = require("../command-helpers/node");
|
|
39
38
|
const scaffold_1 = require("../command-helpers/scaffold");
|
|
40
39
|
const spinner_1 = require("../command-helpers/spinner");
|
|
41
40
|
const studio_1 = require("../command-helpers/studio");
|
|
42
41
|
const subgraph_1 = require("../command-helpers/subgraph");
|
|
43
|
-
const debug_1 = __importDefault(require("../debug"));
|
|
44
42
|
const protocols_1 = __importDefault(require("../protocols"));
|
|
45
43
|
const schema_1 = require("../scaffold/schema");
|
|
46
44
|
const validation_1 = require("../validation");
|
|
47
|
-
const
|
|
45
|
+
const add_1 = __importDefault(require("./add"));
|
|
48
46
|
const protocolChoices = Array.from(protocols_1.default.availableProtocols().keys());
|
|
49
47
|
const availableNetworks = protocols_1.default.availableNetworks();
|
|
50
48
|
const DEFAULT_EXAMPLE_SUBGRAPH = 'ethereum/gravatar';
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
Selects the product for which to initialize
|
|
60
|
-
--studio Shortcut for --product subgraph-studio
|
|
61
|
-
-g, --node <node> Graph node for which to initialize
|
|
62
|
-
--allow-simple-name Use a subgraph name without a prefix (default: false)
|
|
63
|
-
-h, --help Show usage information
|
|
64
|
-
|
|
65
|
-
${chalk_1.default.dim('Choose mode with one of:')}
|
|
66
|
-
|
|
67
|
-
--from-contract <contract> Creates a scaffold based on an existing contract
|
|
68
|
-
--from-example [example] Creates a scaffold based on an example subgraph
|
|
69
|
-
|
|
70
|
-
${chalk_1.default.dim('Options for --from-contract:')}
|
|
71
|
-
|
|
72
|
-
--contract-name Name of the contract (default: Contract)
|
|
73
|
-
--index-events Index contract events as entities
|
|
74
|
-
--start-block Block number to start indexing from (default: 0)
|
|
75
|
-
|
|
76
|
-
${chalk_1.default.dim.underline('Ethereum:')}
|
|
77
|
-
|
|
78
|
-
--abi <path> Path to the contract ABI (default: download from Etherscan)
|
|
79
|
-
--network <${availableNetworks.get('ethereum').join('|')}>
|
|
80
|
-
Selects the network the contract is deployed to
|
|
81
|
-
|
|
82
|
-
${chalk_1.default.dim.underline('NEAR:')}
|
|
83
|
-
|
|
84
|
-
--network <${availableNetworks.get('near').join('|')}>
|
|
85
|
-
Selects the network the contract is deployed to
|
|
86
|
-
|
|
87
|
-
${chalk_1.default.dim.underline('Cosmos:')}
|
|
88
|
-
|
|
89
|
-
--network <${availableNetworks.get('cosmos').join('|')}>
|
|
90
|
-
Selects the network the contract is deployed to
|
|
91
|
-
`;
|
|
92
|
-
const processInitForm = async (toolbox, { protocol, product, studio, node, abi, allowSimpleName, directory, contract, indexEvents, fromExample, network, subgraphName, contractName, startBlock, }) => {
|
|
93
|
-
let abiFromEtherscan = undefined;
|
|
94
|
-
let abiFromFile = undefined;
|
|
95
|
-
let protocolInstance;
|
|
96
|
-
let ProtocolContract;
|
|
97
|
-
let ABI;
|
|
98
|
-
const questions = [
|
|
99
|
-
{
|
|
100
|
-
type: 'select',
|
|
101
|
-
name: 'protocol',
|
|
102
|
-
message: 'Protocol',
|
|
103
|
-
choices: protocolChoices,
|
|
104
|
-
skip: protocolChoices.includes(protocol),
|
|
105
|
-
result: (value) => {
|
|
106
|
-
protocol || (protocol = value);
|
|
107
|
-
protocolInstance = new protocols_1.default(protocol);
|
|
108
|
-
return protocol;
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
type: 'select',
|
|
113
|
-
name: 'product',
|
|
114
|
-
message: 'Product for which to initialize',
|
|
115
|
-
choices: ['subgraph-studio', 'hosted-service'],
|
|
116
|
-
skip: () => protocol === 'arweave' ||
|
|
117
|
-
protocol === 'cosmos' ||
|
|
118
|
-
protocol === 'near' ||
|
|
119
|
-
product === 'subgraph-studio' ||
|
|
120
|
-
product === 'hosted-service' ||
|
|
121
|
-
studio !== undefined ||
|
|
122
|
-
node !== undefined,
|
|
123
|
-
result: (value) => {
|
|
124
|
-
// For now we only support NEAR subgraphs in the Hosted Service
|
|
125
|
-
if (protocol === 'near') {
|
|
126
|
-
// Can be overwritten because the question will be skipped (product === undefined)
|
|
127
|
-
product = 'hosted-service';
|
|
128
|
-
return product;
|
|
129
|
-
}
|
|
130
|
-
if (value == 'subgraph-studio') {
|
|
131
|
-
allowSimpleName = true;
|
|
132
|
-
}
|
|
133
|
-
product = value;
|
|
134
|
-
return value;
|
|
135
|
-
},
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
type: 'input',
|
|
139
|
-
name: 'subgraphName',
|
|
140
|
-
message: () => (product == 'subgraph-studio' || studio ? 'Subgraph slug' : 'Subgraph name'),
|
|
141
|
-
initial: subgraphName,
|
|
142
|
-
validate: (name) => {
|
|
143
|
-
try {
|
|
144
|
-
(0, subgraph_1.validateSubgraphName)(name, { allowSimpleName });
|
|
145
|
-
return true;
|
|
146
|
-
}
|
|
147
|
-
catch (e) {
|
|
148
|
-
return `${e.message}
|
|
149
|
-
|
|
150
|
-
Examples:
|
|
151
|
-
|
|
152
|
-
$ graph init ${os_1.default.userInfo().username}/${name}
|
|
153
|
-
$ graph init ${name} --allow-simple-name`;
|
|
154
|
-
}
|
|
155
|
-
},
|
|
156
|
-
result: (value) => {
|
|
157
|
-
subgraphName = value;
|
|
158
|
-
return value;
|
|
159
|
-
},
|
|
160
|
-
},
|
|
161
|
-
{
|
|
162
|
-
type: 'input',
|
|
163
|
-
name: 'directory',
|
|
164
|
-
message: 'Directory to create the subgraph in',
|
|
165
|
-
initial: () => directory || (0, subgraph_1.getSubgraphBasename)(subgraphName),
|
|
166
|
-
validate: (value) => toolbox.filesystem.exists(value || directory || (0, subgraph_1.getSubgraphBasename)(subgraphName))
|
|
167
|
-
? 'Directory already exists'
|
|
168
|
-
: true,
|
|
169
|
-
},
|
|
170
|
-
{
|
|
171
|
-
type: 'select',
|
|
172
|
-
name: 'network',
|
|
173
|
-
message: () => `${protocolInstance.displayName()} network`,
|
|
174
|
-
choices: () => {
|
|
175
|
-
initDebug('Generating list of available networks for protocol "%s" (%M)', protocol, availableNetworks.get(protocol));
|
|
176
|
-
return (
|
|
177
|
-
// @ts-expect-error TODO: wait what?
|
|
178
|
-
availableNetworks
|
|
179
|
-
.get(protocol) // Get networks related to the chosen protocol.
|
|
180
|
-
// @ts-expect-error TODO: wait what?
|
|
181
|
-
.toArray()); // Needed because of gluegun. It can't even receive a JS iterable.
|
|
182
|
-
},
|
|
183
|
-
skip: fromExample !== undefined,
|
|
184
|
-
initial: network || 'mainnet',
|
|
185
|
-
result: (value) => {
|
|
186
|
-
network = value;
|
|
187
|
-
return value;
|
|
188
|
-
},
|
|
189
|
-
},
|
|
190
|
-
// TODO:
|
|
191
|
-
//
|
|
192
|
-
// protocols that don't support contract
|
|
193
|
-
// - arweave
|
|
194
|
-
// - cosmos
|
|
195
|
-
{
|
|
196
|
-
type: 'input',
|
|
197
|
-
name: 'contract',
|
|
198
|
-
message: () => {
|
|
199
|
-
ProtocolContract = protocolInstance.getContract();
|
|
200
|
-
return `Contract ${ProtocolContract.identifierName()}`;
|
|
201
|
-
},
|
|
202
|
-
skip: () => fromExample !== undefined || !protocolInstance.hasContract(),
|
|
203
|
-
initial: contract,
|
|
204
|
-
validate: async (value) => {
|
|
205
|
-
if (fromExample !== undefined || !protocolInstance.hasContract()) {
|
|
206
|
-
return true;
|
|
207
|
-
}
|
|
208
|
-
// Validate whether the contract is valid
|
|
209
|
-
const { valid, error } = (0, validation_1.validateContract)(value, ProtocolContract);
|
|
210
|
-
return valid ? true : error;
|
|
211
|
-
},
|
|
212
|
-
result: async (value) => {
|
|
213
|
-
if (fromExample !== undefined) {
|
|
214
|
-
return value;
|
|
215
|
-
}
|
|
216
|
-
ABI = protocolInstance.getABI();
|
|
217
|
-
// Try loading the ABI from Etherscan, if none was provided
|
|
218
|
-
if (protocolInstance.hasABIs() && !abi) {
|
|
219
|
-
try {
|
|
220
|
-
if (network === 'poa-core') {
|
|
221
|
-
// TODO: this variable is never used anywhere, what happens?
|
|
222
|
-
// abiFromBlockScout = await loadAbiFromBlockScout(ABI, network, value)
|
|
223
|
-
}
|
|
224
|
-
else {
|
|
225
|
-
abiFromEtherscan = await (0, abi_1.loadAbiFromEtherscan)(ABI, network, value);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
catch (e) {
|
|
229
|
-
// noop
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
// If startBlock is not set, try to load it.
|
|
233
|
-
if (!startBlock) {
|
|
234
|
-
try {
|
|
235
|
-
// Load startBlock for this contract
|
|
236
|
-
startBlock = Number(await (0, abi_2.loadStartBlockForContract)(network, value)).toString();
|
|
237
|
-
}
|
|
238
|
-
catch (error) {
|
|
239
|
-
// noop
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
return value;
|
|
243
|
-
},
|
|
244
|
-
},
|
|
245
|
-
{
|
|
246
|
-
type: 'input',
|
|
247
|
-
name: 'abi',
|
|
248
|
-
message: 'ABI file (path)',
|
|
249
|
-
initial: abi,
|
|
250
|
-
skip: () => !protocolInstance.hasABIs() || fromExample !== undefined || abiFromEtherscan !== undefined,
|
|
251
|
-
validate: async (value) => {
|
|
252
|
-
if (fromExample || abiFromEtherscan || !protocolInstance.hasABIs()) {
|
|
253
|
-
return true;
|
|
254
|
-
}
|
|
255
|
-
try {
|
|
256
|
-
abiFromFile = loadAbiFromFile(toolbox, ABI, value);
|
|
257
|
-
return true;
|
|
258
|
-
}
|
|
259
|
-
catch (e) {
|
|
260
|
-
return e.message;
|
|
261
|
-
}
|
|
262
|
-
},
|
|
263
|
-
},
|
|
264
|
-
{
|
|
265
|
-
type: 'input',
|
|
266
|
-
name: 'startBlock',
|
|
267
|
-
message: 'Start Block',
|
|
268
|
-
initial: () => startBlock || '0',
|
|
269
|
-
skip: () => fromExample !== undefined,
|
|
270
|
-
validate: (value) => parseInt(value) >= 0,
|
|
271
|
-
result: (value) => {
|
|
272
|
-
startBlock = value;
|
|
273
|
-
return value;
|
|
274
|
-
},
|
|
275
|
-
},
|
|
276
|
-
{
|
|
277
|
-
type: 'input',
|
|
278
|
-
name: 'contractName',
|
|
279
|
-
message: 'Contract Name',
|
|
280
|
-
initial: contractName || 'Contract',
|
|
281
|
-
skip: () => fromExample !== undefined || !protocolInstance.hasContract(),
|
|
282
|
-
validate: (value) => value && value.length > 0,
|
|
283
|
-
result: (value) => {
|
|
284
|
-
contractName = value;
|
|
285
|
-
return value;
|
|
286
|
-
},
|
|
287
|
-
},
|
|
288
|
-
{
|
|
289
|
-
type: 'confirm',
|
|
290
|
-
name: 'indexEvents',
|
|
291
|
-
message: 'Index contract events as entities',
|
|
292
|
-
initial: true,
|
|
293
|
-
skip: () => !!indexEvents,
|
|
294
|
-
result: (value) => {
|
|
295
|
-
indexEvents = value;
|
|
296
|
-
return value;
|
|
297
|
-
},
|
|
298
|
-
},
|
|
299
|
-
];
|
|
300
|
-
try {
|
|
301
|
-
const answers = await toolbox.prompt.ask(
|
|
302
|
-
// @ts-expect-error questions do somehow fit
|
|
303
|
-
questions);
|
|
304
|
-
return {
|
|
305
|
-
...answers,
|
|
306
|
-
abi: (abiFromEtherscan || abiFromFile),
|
|
307
|
-
protocolInstance,
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
catch (e) {
|
|
311
|
-
return undefined;
|
|
312
|
-
}
|
|
313
|
-
};
|
|
314
|
-
const loadAbiFromFile = (toolbox, ABI, filename) => {
|
|
315
|
-
const exists = toolbox.filesystem.exists(filename);
|
|
316
|
-
if (!exists) {
|
|
317
|
-
throw Error('File does not exist.');
|
|
318
|
-
}
|
|
319
|
-
else if (exists === 'dir') {
|
|
320
|
-
throw Error('Path points to a directory, not a file.');
|
|
321
|
-
}
|
|
322
|
-
else if (exists === 'other') {
|
|
323
|
-
throw Error('Not sure what this path points to.');
|
|
324
|
-
}
|
|
325
|
-
else {
|
|
326
|
-
return ABI.load('Contract', filename);
|
|
327
|
-
}
|
|
328
|
-
};
|
|
329
|
-
exports.default = {
|
|
330
|
-
description: 'Creates a new subgraph with basic scaffolding',
|
|
331
|
-
run: async (toolbox) => {
|
|
332
|
-
// Obtain tools
|
|
333
|
-
const { print, system } = toolbox;
|
|
334
|
-
// Read CLI parameters
|
|
335
|
-
let { protocol, product, studio, node, g, abi, allowSimpleName, fromContract, contractName, fromExample, h, help, indexEvents, network, startBlock, } = toolbox.parameters.options;
|
|
336
|
-
startBlock && (startBlock = Number(startBlock).toString());
|
|
337
|
-
node || (node = g);
|
|
338
|
-
({ node, allowSimpleName } = (0, node_1.chooseNodeUrl)({
|
|
49
|
+
class InitCommand extends core_1.Command {
|
|
50
|
+
async run() {
|
|
51
|
+
const { args: { subgraphName, directory }, flags: { protocol: protocolFlag, product, studio, node: nodeFlag, 'allow-simple-name': allowSimpleNameFlag, 'from-contract': fromContract, 'contract-name': contractName, 'from-example': fromExample, 'index-events': indexEvents, network, abi: abiPath, 'start-block': startBlock, }, } = await this.parse(InitCommand);
|
|
52
|
+
const protocol = protocolFlag;
|
|
53
|
+
if (!protocolChoices.includes(protocol)) {
|
|
54
|
+
this.error(`Invalid protocol "${protocol}" provided. Supported protocols are: ${protocolChoices.join(', ')}.`, { exit: 1 });
|
|
55
|
+
}
|
|
56
|
+
let { node, allowSimpleName } = (0, node_1.chooseNodeUrl)({
|
|
339
57
|
product,
|
|
340
58
|
studio,
|
|
341
|
-
node,
|
|
342
|
-
allowSimpleName,
|
|
343
|
-
})
|
|
59
|
+
node: nodeFlag,
|
|
60
|
+
allowSimpleName: allowSimpleNameFlag,
|
|
61
|
+
});
|
|
344
62
|
if (fromContract && fromExample) {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
let subgraphName, directory;
|
|
350
|
-
try {
|
|
351
|
-
[subgraphName, directory] = (0, gluegun_1.fixParameters)(toolbox.parameters, {
|
|
352
|
-
allowSimpleName,
|
|
353
|
-
help,
|
|
354
|
-
h,
|
|
355
|
-
indexEvents,
|
|
356
|
-
studio,
|
|
63
|
+
this.error('Only one of "--from-example" and "--from-contract" can be used at a time.', {
|
|
64
|
+
exit: 1,
|
|
357
65
|
});
|
|
358
66
|
}
|
|
359
|
-
catch (e) {
|
|
360
|
-
print.error(e.message);
|
|
361
|
-
process.exitCode = 1;
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
// Show help text if requested
|
|
365
|
-
if (help || h) {
|
|
366
|
-
print.info(HELP);
|
|
367
|
-
return;
|
|
368
|
-
}
|
|
369
67
|
// Detect git
|
|
370
|
-
const git = system.which('git');
|
|
371
|
-
if (git
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
68
|
+
const git = gluegun_1.system.which('git');
|
|
69
|
+
if (!git) {
|
|
70
|
+
this.error('Git was not found on your system. Please install "git" so it is in $PATH.', {
|
|
71
|
+
exit: 1,
|
|
72
|
+
});
|
|
375
73
|
}
|
|
376
74
|
// Detect Yarn and/or NPM
|
|
377
|
-
const yarn = system.which('yarn');
|
|
378
|
-
const npm = system.which('npm');
|
|
75
|
+
const yarn = gluegun_1.system.which('yarn');
|
|
76
|
+
const npm = gluegun_1.system.which('npm');
|
|
379
77
|
if (!yarn && !npm) {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
78
|
+
this.error(`Neither Yarn nor NPM were found on your system. Please install one of them.`, {
|
|
79
|
+
exit: 1,
|
|
80
|
+
});
|
|
383
81
|
}
|
|
384
82
|
const commands = {
|
|
385
83
|
link: yarn ? 'yarn link @graphprotocol/graph-cli' : 'npm link @graphprotocol/graph-cli',
|
|
@@ -390,27 +88,25 @@ exports.default = {
|
|
|
390
88
|
// If all parameters are provided from the command-line,
|
|
391
89
|
// go straight to creating the subgraph from the example
|
|
392
90
|
if (fromExample && subgraphName && directory) {
|
|
393
|
-
return await initSubgraphFromExample(
|
|
91
|
+
return await initSubgraphFromExample.bind(this)({ fromExample, allowSimpleName, directory, subgraphName, studio, product: product }, { commands });
|
|
394
92
|
}
|
|
93
|
+
// Will be assigned below if ethereum
|
|
94
|
+
let abi;
|
|
95
|
+
const protocolInstance = new protocols_1.default(protocol);
|
|
395
96
|
// If all parameters are provided from the command-line,
|
|
396
97
|
// go straight to creating the subgraph from an existing contract
|
|
397
98
|
if (fromContract && protocol && subgraphName && directory && network && node) {
|
|
398
99
|
if (!protocolChoices.includes(protocol)) {
|
|
399
|
-
|
|
400
|
-
process.exitCode = 1;
|
|
401
|
-
return;
|
|
100
|
+
this.error(`Protocol '${protocol}' is not supported, choose from these options: ${protocolChoices.join(', ')}`, { exit: 1 });
|
|
402
101
|
}
|
|
403
|
-
const protocolInstance = new protocols_1.default(protocol);
|
|
404
102
|
if (protocolInstance.hasABIs()) {
|
|
405
103
|
const ABI = protocolInstance.getABI();
|
|
406
|
-
if (
|
|
104
|
+
if (abiPath) {
|
|
407
105
|
try {
|
|
408
|
-
abi = loadAbiFromFile(
|
|
106
|
+
abi = loadAbiFromFile(ABI, abiPath);
|
|
409
107
|
}
|
|
410
108
|
catch (e) {
|
|
411
|
-
|
|
412
|
-
process.exitCode = 1;
|
|
413
|
-
return;
|
|
109
|
+
this.error(`Failed to load ABI: ${e.message}`, { exit: 1 });
|
|
414
110
|
}
|
|
415
111
|
}
|
|
416
112
|
else {
|
|
@@ -428,7 +124,7 @@ exports.default = {
|
|
|
428
124
|
}
|
|
429
125
|
}
|
|
430
126
|
}
|
|
431
|
-
return await initSubgraphFromContract(
|
|
127
|
+
return await initSubgraphFromContract.bind(this)({
|
|
432
128
|
protocolInstance,
|
|
433
129
|
abi,
|
|
434
130
|
allowSimpleName,
|
|
@@ -437,139 +133,473 @@ exports.default = {
|
|
|
437
133
|
indexEvents,
|
|
438
134
|
network,
|
|
439
135
|
subgraphName,
|
|
440
|
-
contractName,
|
|
136
|
+
contractName: contractName,
|
|
441
137
|
node,
|
|
442
138
|
studio,
|
|
443
|
-
product,
|
|
444
|
-
startBlock,
|
|
139
|
+
product: product,
|
|
140
|
+
startBlock: startBlock,
|
|
445
141
|
}, { commands, addContract: false });
|
|
446
142
|
}
|
|
447
|
-
// Otherwise, take the user through the interactive form
|
|
448
|
-
const inputs = await processInitForm(toolbox, {
|
|
449
|
-
protocol,
|
|
450
|
-
product,
|
|
451
|
-
studio,
|
|
452
|
-
node,
|
|
453
|
-
abi,
|
|
454
|
-
allowSimpleName,
|
|
455
|
-
directory,
|
|
456
|
-
contract: fromContract,
|
|
457
|
-
indexEvents,
|
|
458
|
-
fromExample,
|
|
459
|
-
network,
|
|
460
|
-
subgraphName,
|
|
461
|
-
contractName,
|
|
462
|
-
startBlock,
|
|
463
|
-
});
|
|
464
|
-
// Exit immediately when the form is cancelled
|
|
465
|
-
if (inputs === undefined) {
|
|
466
|
-
process.exit(1);
|
|
467
|
-
}
|
|
468
|
-
print.info('———');
|
|
469
143
|
if (fromExample) {
|
|
470
|
-
await initSubgraphFromExample(
|
|
144
|
+
await initSubgraphFromExample.bind(this)({
|
|
471
145
|
fromExample,
|
|
472
|
-
subgraphName
|
|
473
|
-
directory
|
|
474
|
-
studio
|
|
475
|
-
product:
|
|
146
|
+
subgraphName,
|
|
147
|
+
directory,
|
|
148
|
+
studio,
|
|
149
|
+
product: product,
|
|
476
150
|
}, { commands });
|
|
477
151
|
}
|
|
478
152
|
else {
|
|
479
153
|
({ node, allowSimpleName } = (0, node_1.chooseNodeUrl)({
|
|
480
|
-
product
|
|
154
|
+
product,
|
|
481
155
|
studio,
|
|
482
156
|
node,
|
|
483
157
|
allowSimpleName,
|
|
484
158
|
}));
|
|
485
|
-
await initSubgraphFromContract(
|
|
486
|
-
protocolInstance
|
|
159
|
+
await initSubgraphFromContract.bind(this)({
|
|
160
|
+
protocolInstance,
|
|
487
161
|
allowSimpleName,
|
|
488
|
-
subgraphName
|
|
489
|
-
directory
|
|
490
|
-
abi
|
|
491
|
-
network:
|
|
492
|
-
contract:
|
|
493
|
-
indexEvents
|
|
494
|
-
contractName:
|
|
495
|
-
node,
|
|
496
|
-
studio
|
|
497
|
-
product:
|
|
498
|
-
startBlock:
|
|
162
|
+
subgraphName,
|
|
163
|
+
directory,
|
|
164
|
+
abi,
|
|
165
|
+
network: network,
|
|
166
|
+
contract: fromContract,
|
|
167
|
+
indexEvents,
|
|
168
|
+
contractName: contractName,
|
|
169
|
+
node: node,
|
|
170
|
+
studio,
|
|
171
|
+
product: product,
|
|
172
|
+
startBlock: startBlock,
|
|
499
173
|
}, { commands, addContract: true });
|
|
500
174
|
}
|
|
501
|
-
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
exports.default = InitCommand;
|
|
178
|
+
InitCommand.description = 'Creates a new subgraph with basic scaffolding.';
|
|
179
|
+
InitCommand.args = {
|
|
180
|
+
subgraphName: core_1.Args.string({
|
|
181
|
+
required: true,
|
|
182
|
+
}),
|
|
183
|
+
directory: core_1.Args.string({
|
|
184
|
+
required: true,
|
|
185
|
+
}),
|
|
186
|
+
};
|
|
187
|
+
InitCommand.flags = {
|
|
188
|
+
protocol: core_1.Flags.string({
|
|
189
|
+
required: true,
|
|
190
|
+
options: protocolChoices,
|
|
191
|
+
}),
|
|
192
|
+
product: core_1.Flags.string({
|
|
193
|
+
summary: 'Selects the product for which to initialize.',
|
|
194
|
+
options: ['subgraph-studio', 'hosted-service'],
|
|
195
|
+
}),
|
|
196
|
+
studio: core_1.Flags.boolean({
|
|
197
|
+
summary: 'Shortcut for "--product subgraph-studio".',
|
|
198
|
+
exclusive: ['product'],
|
|
199
|
+
}),
|
|
200
|
+
node: core_1.Flags.string({
|
|
201
|
+
summary: 'Graph node for which to initialize.',
|
|
202
|
+
char: 'g',
|
|
203
|
+
}),
|
|
204
|
+
'allow-simple-name': core_1.Flags.boolean({
|
|
205
|
+
description: 'Use a subgraph name without a prefix.',
|
|
206
|
+
default: false,
|
|
207
|
+
}),
|
|
208
|
+
'from-contract': core_1.Flags.string({
|
|
209
|
+
description: 'Creates a scaffold based on an existing contract.',
|
|
210
|
+
exclusive: ['from-example'],
|
|
211
|
+
}),
|
|
212
|
+
'from-example': core_1.Flags.string({
|
|
213
|
+
description: 'Creates a scaffold based on an example subgraph.',
|
|
214
|
+
// TODO: using a default sets the value and therefore requires not to have --from-contract
|
|
215
|
+
// default: 'Contract',
|
|
216
|
+
exclusive: ['from-contract'],
|
|
217
|
+
}),
|
|
218
|
+
'contract-name': core_1.Flags.string({
|
|
219
|
+
helpGroup: 'Scaffold from contract',
|
|
220
|
+
description: 'Name of the contract.',
|
|
221
|
+
dependsOn: ['from-contract'],
|
|
222
|
+
}),
|
|
223
|
+
'index-events': core_1.Flags.boolean({
|
|
224
|
+
helpGroup: 'Scaffold from contract',
|
|
225
|
+
description: 'Index contract events as entities.',
|
|
226
|
+
dependsOn: ['from-contract'],
|
|
227
|
+
}),
|
|
228
|
+
'start-block': core_1.Flags.string({
|
|
229
|
+
helpGroup: 'Scaffold from contract',
|
|
230
|
+
description: 'Block number to start indexing from.',
|
|
231
|
+
// TODO: using a default sets the value and therefore requires --from-contract
|
|
232
|
+
// default: '0',
|
|
233
|
+
dependsOn: ['from-contract'],
|
|
234
|
+
}),
|
|
235
|
+
abi: core_1.Flags.string({
|
|
236
|
+
summary: 'Path to the contract ABI',
|
|
237
|
+
// TODO: using a default sets the value and therefore requires --from-contract
|
|
238
|
+
// default: '*Download from Etherscan*',
|
|
239
|
+
dependsOn: ['from-contract'],
|
|
240
|
+
}),
|
|
241
|
+
network: core_1.Flags.string({
|
|
242
|
+
summary: 'Network the contract is deployed to.',
|
|
243
|
+
dependsOn: ['from-contract'],
|
|
244
|
+
options: [
|
|
245
|
+
...availableNetworks.get('ethereum'),
|
|
246
|
+
...availableNetworks.get('near'),
|
|
247
|
+
...availableNetworks.get('cosmos'),
|
|
248
|
+
],
|
|
249
|
+
}),
|
|
250
|
+
};
|
|
251
|
+
// const processInitForm = async (
|
|
252
|
+
// toolbox: GluegunToolbox,
|
|
253
|
+
// {
|
|
254
|
+
// protocol,
|
|
255
|
+
// product,
|
|
256
|
+
// studio,
|
|
257
|
+
// node,
|
|
258
|
+
// abi,
|
|
259
|
+
// allowSimpleName,
|
|
260
|
+
// directory,
|
|
261
|
+
// contract,
|
|
262
|
+
// indexEvents,
|
|
263
|
+
// fromExample,
|
|
264
|
+
// network,
|
|
265
|
+
// subgraphName,
|
|
266
|
+
// contractName,
|
|
267
|
+
// startBlock,
|
|
268
|
+
// }: {
|
|
269
|
+
// protocol: ProtocolName;
|
|
270
|
+
// product: string;
|
|
271
|
+
// studio: string;
|
|
272
|
+
// node: string;
|
|
273
|
+
// abi: EthereumABI;
|
|
274
|
+
// allowSimpleName: boolean | undefined;
|
|
275
|
+
// directory: string;
|
|
276
|
+
// contract: string;
|
|
277
|
+
// indexEvents: boolean;
|
|
278
|
+
// fromExample: string | boolean;
|
|
279
|
+
// network: string;
|
|
280
|
+
// subgraphName: string;
|
|
281
|
+
// contractName: string;
|
|
282
|
+
// startBlock: string;
|
|
283
|
+
// },
|
|
284
|
+
// ): Promise<
|
|
285
|
+
// | {
|
|
286
|
+
// abi: EthereumABI;
|
|
287
|
+
// protocolInstance: Protocol;
|
|
288
|
+
// subgraphName: string;
|
|
289
|
+
// directory: string;
|
|
290
|
+
// studio: string;
|
|
291
|
+
// product: string;
|
|
292
|
+
// network: string;
|
|
293
|
+
// contract: string;
|
|
294
|
+
// indexEvents: boolean;
|
|
295
|
+
// contractName: string;
|
|
296
|
+
// startBlock: string;
|
|
297
|
+
// }
|
|
298
|
+
// | undefined
|
|
299
|
+
// > => {
|
|
300
|
+
// let abiFromEtherscan: EthereumABI | undefined = undefined;
|
|
301
|
+
// let abiFromFile = undefined;
|
|
302
|
+
// let protocolInstance!: Protocol;
|
|
303
|
+
// let ProtocolContract: ContractCtor;
|
|
304
|
+
// let ABI: typeof EthereumABI;
|
|
305
|
+
// const questions = [
|
|
306
|
+
// {
|
|
307
|
+
// type: 'select',
|
|
308
|
+
// name: 'protocol',
|
|
309
|
+
// message: 'Protocol',
|
|
310
|
+
// choices: protocolChoices,
|
|
311
|
+
// skip: protocolChoices.includes(protocol),
|
|
312
|
+
// result: (value: ProtocolName) => {
|
|
313
|
+
// // eslint-disable-next-line -- prettier has problems with ||=
|
|
314
|
+
// protocol = protocol || value;
|
|
315
|
+
// protocolInstance = new Protocol(protocol);
|
|
316
|
+
// return protocol;
|
|
317
|
+
// },
|
|
318
|
+
// },
|
|
319
|
+
// {
|
|
320
|
+
// type: 'select',
|
|
321
|
+
// name: 'product',
|
|
322
|
+
// message: 'Product for which to initialize',
|
|
323
|
+
// choices: ['subgraph-studio', 'hosted-service'],
|
|
324
|
+
// skip: () =>
|
|
325
|
+
// protocol === 'arweave' ||
|
|
326
|
+
// protocol === 'cosmos' ||
|
|
327
|
+
// protocol === 'near' ||
|
|
328
|
+
// product === 'subgraph-studio' ||
|
|
329
|
+
// product === 'hosted-service' ||
|
|
330
|
+
// studio !== undefined ||
|
|
331
|
+
// node !== undefined,
|
|
332
|
+
// result: (value: string | undefined) => {
|
|
333
|
+
// // For now we only support NEAR subgraphs in the Hosted Service
|
|
334
|
+
// if (protocol === 'near') {
|
|
335
|
+
// // Can be overwritten because the question will be skipped (product === undefined)
|
|
336
|
+
// product = 'hosted-service';
|
|
337
|
+
// return product;
|
|
338
|
+
// }
|
|
339
|
+
// if (value == 'subgraph-studio') {
|
|
340
|
+
// allowSimpleName = true;
|
|
341
|
+
// }
|
|
342
|
+
// product = value as any;
|
|
343
|
+
// return value;
|
|
344
|
+
// },
|
|
345
|
+
// },
|
|
346
|
+
// {
|
|
347
|
+
// type: 'input',
|
|
348
|
+
// name: 'subgraphName',
|
|
349
|
+
// message: () => (product == 'subgraph-studio' || studio ? 'Subgraph slug' : 'Subgraph name'),
|
|
350
|
+
// initial: subgraphName,
|
|
351
|
+
// validate: (name: string) => {
|
|
352
|
+
// try {
|
|
353
|
+
// validateSubgraphName(name, { allowSimpleName });
|
|
354
|
+
// return true;
|
|
355
|
+
// } catch (e) {
|
|
356
|
+
// return `${e.message}
|
|
357
|
+
// Examples:
|
|
358
|
+
// $ graph init ${os.userInfo().username}/${name}
|
|
359
|
+
// $ graph init ${name} --allow-simple-name`;
|
|
360
|
+
// }
|
|
361
|
+
// },
|
|
362
|
+
// result: (value: string) => {
|
|
363
|
+
// subgraphName = value;
|
|
364
|
+
// return value;
|
|
365
|
+
// },
|
|
366
|
+
// },
|
|
367
|
+
// {
|
|
368
|
+
// type: 'input',
|
|
369
|
+
// name: 'directory',
|
|
370
|
+
// message: 'Directory to create the subgraph in',
|
|
371
|
+
// initial: () => directory || getSubgraphBasename(subgraphName),
|
|
372
|
+
// validate: (value: string) =>
|
|
373
|
+
// toolbox.filesystem.exists(value || directory || getSubgraphBasename(subgraphName))
|
|
374
|
+
// ? 'Directory already exists'
|
|
375
|
+
// : true,
|
|
376
|
+
// },
|
|
377
|
+
// {
|
|
378
|
+
// type: 'select',
|
|
379
|
+
// name: 'network',
|
|
380
|
+
// message: () => `${protocolInstance.displayName()} network`,
|
|
381
|
+
// choices: () => {
|
|
382
|
+
// initDebug(
|
|
383
|
+
// 'Generating list of available networks for protocol "%s" (%M)',
|
|
384
|
+
// protocol,
|
|
385
|
+
// availableNetworks.get(protocol as any),
|
|
386
|
+
// );
|
|
387
|
+
// return (
|
|
388
|
+
// // @ts-expect-error TODO: wait what?
|
|
389
|
+
// availableNetworks
|
|
390
|
+
// .get(protocol) // Get networks related to the chosen protocol.
|
|
391
|
+
// // @ts-expect-error TODO: wait what?
|
|
392
|
+
// .toArray()
|
|
393
|
+
// ); // Needed because of gluegun. It can't even receive a JS iterable.
|
|
394
|
+
// },
|
|
395
|
+
// skip: fromExample !== undefined,
|
|
396
|
+
// initial: network || 'mainnet',
|
|
397
|
+
// result: (value: string) => {
|
|
398
|
+
// network = value;
|
|
399
|
+
// return value;
|
|
400
|
+
// },
|
|
401
|
+
// },
|
|
402
|
+
// // TODO:
|
|
403
|
+
// //
|
|
404
|
+
// // protocols that don't support contract
|
|
405
|
+
// // - arweave
|
|
406
|
+
// // - cosmos
|
|
407
|
+
// {
|
|
408
|
+
// type: 'input',
|
|
409
|
+
// name: 'contract',
|
|
410
|
+
// message: () => {
|
|
411
|
+
// ProtocolContract = protocolInstance.getContract()!;
|
|
412
|
+
// return `Contract ${ProtocolContract.identifierName()}`;
|
|
413
|
+
// },
|
|
414
|
+
// skip: () => fromExample !== undefined || !protocolInstance.hasContract(),
|
|
415
|
+
// initial: contract,
|
|
416
|
+
// validate: async (value: string) => {
|
|
417
|
+
// if (fromExample !== undefined || !protocolInstance.hasContract()) {
|
|
418
|
+
// return true;
|
|
419
|
+
// }
|
|
420
|
+
// // Validate whether the contract is valid
|
|
421
|
+
// const { valid, error } = validateContract(value, ProtocolContract);
|
|
422
|
+
// return valid ? true : error;
|
|
423
|
+
// },
|
|
424
|
+
// result: async (value: string) => {
|
|
425
|
+
// if (fromExample !== undefined) {
|
|
426
|
+
// return value;
|
|
427
|
+
// }
|
|
428
|
+
// ABI = protocolInstance.getABI();
|
|
429
|
+
// // Try loading the ABI from Etherscan, if none was provided
|
|
430
|
+
// if (protocolInstance.hasABIs() && !abi) {
|
|
431
|
+
// try {
|
|
432
|
+
// if (network === 'poa-core') {
|
|
433
|
+
// // TODO: this variable is never used anywhere, what happens?
|
|
434
|
+
// // abiFromBlockScout = await loadAbiFromBlockScout(ABI, network, value)
|
|
435
|
+
// } else {
|
|
436
|
+
// abiFromEtherscan = await loadAbiFromEtherscan(ABI, network!, value);
|
|
437
|
+
// }
|
|
438
|
+
// } catch (e) {
|
|
439
|
+
// // noop
|
|
440
|
+
// }
|
|
441
|
+
// }
|
|
442
|
+
// // If startBlock is not set, try to load it.
|
|
443
|
+
// if (!startBlock) {
|
|
444
|
+
// try {
|
|
445
|
+
// // Load startBlock for this contract
|
|
446
|
+
// startBlock = Number(await loadStartBlockForContract(network!, value)).toString();
|
|
447
|
+
// } catch (error) {
|
|
448
|
+
// // noop
|
|
449
|
+
// }
|
|
450
|
+
// }
|
|
451
|
+
// return value;
|
|
452
|
+
// },
|
|
453
|
+
// },
|
|
454
|
+
// {
|
|
455
|
+
// type: 'input',
|
|
456
|
+
// name: 'abi',
|
|
457
|
+
// message: 'ABI file (path)',
|
|
458
|
+
// initial: abi,
|
|
459
|
+
// skip: () =>
|
|
460
|
+
// !protocolInstance.hasABIs() || fromExample !== undefined || abiFromEtherscan !== undefined,
|
|
461
|
+
// validate: async (value: string) => {
|
|
462
|
+
// if (fromExample || abiFromEtherscan || !protocolInstance.hasABIs()) {
|
|
463
|
+
// return true;
|
|
464
|
+
// }
|
|
465
|
+
// try {
|
|
466
|
+
// abiFromFile = loadAbiFromFile(ABI, value);
|
|
467
|
+
// return true;
|
|
468
|
+
// } catch (e) {
|
|
469
|
+
// return e.message;
|
|
470
|
+
// }
|
|
471
|
+
// },
|
|
472
|
+
// },
|
|
473
|
+
// {
|
|
474
|
+
// type: 'input',
|
|
475
|
+
// name: 'startBlock',
|
|
476
|
+
// message: 'Start Block',
|
|
477
|
+
// initial: () => startBlock || '0',
|
|
478
|
+
// skip: () => fromExample !== undefined,
|
|
479
|
+
// validate: (value: string) => parseInt(value) >= 0,
|
|
480
|
+
// result: (value: string) => {
|
|
481
|
+
// startBlock = value;
|
|
482
|
+
// return value;
|
|
483
|
+
// },
|
|
484
|
+
// },
|
|
485
|
+
// {
|
|
486
|
+
// type: 'input',
|
|
487
|
+
// name: 'contractName',
|
|
488
|
+
// message: 'Contract Name',
|
|
489
|
+
// initial: contractName || 'Contract',
|
|
490
|
+
// skip: () => fromExample !== undefined || !protocolInstance.hasContract(),
|
|
491
|
+
// validate: (value: string) => value && value.length > 0,
|
|
492
|
+
// result: (value: string) => {
|
|
493
|
+
// contractName = value;
|
|
494
|
+
// return value;
|
|
495
|
+
// },
|
|
496
|
+
// },
|
|
497
|
+
// {
|
|
498
|
+
// type: 'confirm',
|
|
499
|
+
// name: 'indexEvents',
|
|
500
|
+
// message: 'Index contract events as entities',
|
|
501
|
+
// initial: true,
|
|
502
|
+
// skip: () => !!indexEvents,
|
|
503
|
+
// result: (value: boolean) => {
|
|
504
|
+
// indexEvents = value;
|
|
505
|
+
// return value;
|
|
506
|
+
// },
|
|
507
|
+
// },
|
|
508
|
+
// ];
|
|
509
|
+
// try {
|
|
510
|
+
// const answers = await toolbox.prompt.ask(
|
|
511
|
+
// // @ts-expect-error questions do somehow fit
|
|
512
|
+
// questions,
|
|
513
|
+
// );
|
|
514
|
+
// return {
|
|
515
|
+
// ...(answers as any), // necessary answers are here
|
|
516
|
+
// abi: (abiFromEtherscan || abiFromFile)!,
|
|
517
|
+
// protocolInstance,
|
|
518
|
+
// };
|
|
519
|
+
// } catch (e) {
|
|
520
|
+
// return undefined;
|
|
521
|
+
// }
|
|
522
|
+
// };
|
|
523
|
+
const loadAbiFromFile = (ABI, filename) => {
|
|
524
|
+
const exists = gluegun_1.filesystem.exists(filename);
|
|
525
|
+
if (!exists) {
|
|
526
|
+
throw Error('File does not exist.');
|
|
527
|
+
}
|
|
528
|
+
else if (exists === 'dir') {
|
|
529
|
+
throw Error('Path points to a directory, not a file.');
|
|
530
|
+
}
|
|
531
|
+
else if (exists === 'other') {
|
|
532
|
+
throw Error('Not sure what this path points to.');
|
|
533
|
+
}
|
|
534
|
+
else {
|
|
535
|
+
return ABI.load('Contract', filename);
|
|
536
|
+
}
|
|
502
537
|
};
|
|
503
|
-
|
|
538
|
+
function revalidateSubgraphName(subgraphName, { allowSimpleName }) {
|
|
504
539
|
// Fail if the subgraph name is invalid
|
|
505
540
|
try {
|
|
506
541
|
(0, subgraph_1.validateSubgraphName)(subgraphName, { allowSimpleName });
|
|
507
542
|
return true;
|
|
508
543
|
}
|
|
509
544
|
catch (e) {
|
|
510
|
-
|
|
545
|
+
this.error(`${e.message}
|
|
511
546
|
|
|
512
547
|
Examples:
|
|
513
548
|
|
|
514
549
|
$ graph init ${os_1.default.userInfo().username}/${subgraphName}
|
|
515
550
|
$ graph init ${subgraphName} --allow-simple-name`);
|
|
516
|
-
return false;
|
|
517
551
|
}
|
|
518
|
-
}
|
|
519
|
-
const initRepository = async (
|
|
552
|
+
}
|
|
553
|
+
const initRepository = async (directory) => await (0, spinner_1.withSpinner)(`Initialize subgraph repository`, `Failed to initialize subgraph repository`, `Warnings while initializing subgraph repository`, async () => {
|
|
520
554
|
// Remove .git dir in --from-example mode; in --from-contract, we're
|
|
521
555
|
// starting from an empty directory
|
|
522
556
|
const gitDir = path_1.default.join(directory, '.git');
|
|
523
|
-
if (
|
|
524
|
-
|
|
557
|
+
if (gluegun_1.filesystem.exists(gitDir)) {
|
|
558
|
+
gluegun_1.filesystem.remove(gitDir);
|
|
525
559
|
}
|
|
526
|
-
await
|
|
527
|
-
await
|
|
528
|
-
await
|
|
560
|
+
await gluegun_1.system.run('git init', { cwd: directory });
|
|
561
|
+
await gluegun_1.system.run('git add --all', { cwd: directory });
|
|
562
|
+
await gluegun_1.system.run('git commit -m "Initial commit"', {
|
|
529
563
|
cwd: directory,
|
|
530
564
|
});
|
|
531
565
|
return true;
|
|
532
566
|
});
|
|
533
|
-
const installDependencies = async (
|
|
567
|
+
const installDependencies = async (directory, commands) => await (0, spinner_1.withSpinner)(`Install dependencies with ${commands.install}`, `Failed to install dependencies`, `Warnings while installing dependencies`, async () => {
|
|
534
568
|
if (process.env.GRAPH_CLI_TESTS) {
|
|
535
|
-
await
|
|
569
|
+
await gluegun_1.system.run(commands.link, { cwd: directory });
|
|
536
570
|
}
|
|
537
|
-
await
|
|
571
|
+
await gluegun_1.system.run(commands.install, { cwd: directory });
|
|
538
572
|
return true;
|
|
539
573
|
});
|
|
540
|
-
const runCodegen = async (
|
|
541
|
-
await
|
|
574
|
+
const runCodegen = async (directory, codegenCommand) => await (0, spinner_1.withSpinner)(`Generate ABI and schema types with ${codegenCommand}`, `Failed to generate code from ABI and GraphQL schema`, `Warnings while generating code from ABI and GraphQL schema`, async () => {
|
|
575
|
+
await gluegun_1.system.run(codegenCommand, { cwd: directory });
|
|
542
576
|
return true;
|
|
543
577
|
});
|
|
544
|
-
|
|
545
|
-
const { print } = toolbox;
|
|
578
|
+
function printNextSteps({ subgraphName, directory }, { commands, }) {
|
|
546
579
|
const relativeDir = path_1.default.relative(process.cwd(), directory);
|
|
547
580
|
// Print instructions
|
|
548
|
-
|
|
549
|
-
Subgraph ${
|
|
581
|
+
this.log(`
|
|
582
|
+
Subgraph ${subgraphName} created in ${relativeDir}
|
|
550
583
|
`);
|
|
551
|
-
|
|
584
|
+
this.log(`Next steps:
|
|
552
585
|
|
|
553
|
-
1. Run
|
|
586
|
+
1. Run \`graph auth\` to authenticate with your deploy key.
|
|
554
587
|
|
|
555
|
-
2. Type
|
|
588
|
+
2. Type \`cd ${relativeDir}\` to enter the subgraph.
|
|
556
589
|
|
|
557
|
-
3. Run \`${
|
|
590
|
+
3. Run \`${commands.deploy}\` to deploy the subgraph.
|
|
558
591
|
|
|
559
592
|
Make sure to visit the documentation on https://thegraph.com/docs/ for further information.`);
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
const { filesystem, print, system } = toolbox;
|
|
593
|
+
}
|
|
594
|
+
async function initSubgraphFromExample({ fromExample, allowSimpleName, subgraphName, directory, studio, product, }, { commands, }) {
|
|
563
595
|
// Fail if the subgraph name is invalid
|
|
564
|
-
if (!revalidateSubgraphName(
|
|
596
|
+
if (!revalidateSubgraphName.bind(this)(subgraphName, { allowSimpleName })) {
|
|
565
597
|
process.exitCode = 1;
|
|
566
598
|
return;
|
|
567
599
|
}
|
|
568
600
|
// Fail if the output directory already exists
|
|
569
|
-
if (filesystem.exists(directory)) {
|
|
570
|
-
|
|
571
|
-
process.exitCode = 1;
|
|
572
|
-
return;
|
|
601
|
+
if (gluegun_1.filesystem.exists(directory)) {
|
|
602
|
+
this.error(`Directory or file "${directory}" already exists`, { exit: 1 });
|
|
573
603
|
}
|
|
574
604
|
// Clone the example subgraph repository
|
|
575
605
|
const cloned = await (0, spinner_1.withSpinner)(`Cloning example subgraph`, `Failed to clone example subgraph`, `Warnings while cloning example subgraph`, async () => {
|
|
@@ -577,24 +607,24 @@ const initSubgraphFromExample = async (toolbox, { fromExample, allowSimpleName,
|
|
|
577
607
|
const prefix = path_1.default.join(os_1.default.tmpdir(), 'example-subgraph-');
|
|
578
608
|
const tmpDir = fs_1.default.mkdtempSync(prefix);
|
|
579
609
|
try {
|
|
580
|
-
await system.run(`git clone http://github.com/graphprotocol/example-subgraphs ${tmpDir}`);
|
|
610
|
+
await gluegun_1.system.run(`git clone http://github.com/graphprotocol/example-subgraphs ${tmpDir}`);
|
|
581
611
|
// If an example is not specified, use the default one
|
|
582
612
|
if (fromExample === undefined || fromExample === true) {
|
|
583
613
|
fromExample = DEFAULT_EXAMPLE_SUBGRAPH;
|
|
584
614
|
}
|
|
585
615
|
const exampleSubgraphPath = path_1.default.join(tmpDir, String(fromExample));
|
|
586
|
-
if (!filesystem.exists(exampleSubgraphPath)) {
|
|
616
|
+
if (!gluegun_1.filesystem.exists(exampleSubgraphPath)) {
|
|
587
617
|
return { result: false, error: `Example not found: ${fromExample}` };
|
|
588
618
|
}
|
|
589
|
-
filesystem.copy(exampleSubgraphPath, directory);
|
|
619
|
+
gluegun_1.filesystem.copy(exampleSubgraphPath, directory);
|
|
590
620
|
return true;
|
|
591
621
|
}
|
|
592
622
|
finally {
|
|
593
|
-
filesystem.remove(tmpDir);
|
|
623
|
+
gluegun_1.filesystem.remove(tmpDir);
|
|
594
624
|
}
|
|
595
625
|
});
|
|
596
626
|
if (!cloned) {
|
|
597
|
-
|
|
627
|
+
this.exit(1);
|
|
598
628
|
return;
|
|
599
629
|
}
|
|
600
630
|
try {
|
|
@@ -606,21 +636,19 @@ const initSubgraphFromExample = async (toolbox, { fromExample, allowSimpleName,
|
|
|
606
636
|
}
|
|
607
637
|
}
|
|
608
638
|
catch (e) {
|
|
609
|
-
|
|
610
|
-
process.exitCode = 1;
|
|
611
|
-
return;
|
|
639
|
+
this.error(e.message, { exit: 1 });
|
|
612
640
|
}
|
|
613
|
-
const networkConf = await (0, network_1.initNetworksConfig)(
|
|
641
|
+
const networkConf = await (0, network_1.initNetworksConfig)(directory, 'address');
|
|
614
642
|
if (networkConf !== true) {
|
|
615
|
-
|
|
643
|
+
this.exit(1);
|
|
616
644
|
return;
|
|
617
645
|
}
|
|
618
646
|
// Update package.json to match the subgraph name
|
|
619
647
|
const prepared = await (0, spinner_1.withSpinner)(`Update subgraph name and commands in package.json`, `Failed to update subgraph name and commands in package.json`, `Warnings while updating subgraph name and commands in package.json`, async () => {
|
|
620
648
|
try {
|
|
621
649
|
// Load package.json
|
|
622
|
-
const pkgJsonFilename = filesystem.path(directory, 'package.json');
|
|
623
|
-
const pkgJson = await filesystem.read(pkgJsonFilename, 'json');
|
|
650
|
+
const pkgJsonFilename = gluegun_1.filesystem.path(directory, 'package.json');
|
|
651
|
+
const pkgJson = await gluegun_1.filesystem.read(pkgJsonFilename, 'json');
|
|
624
652
|
pkgJson.name = (0, subgraph_1.getSubgraphBasename)(subgraphName);
|
|
625
653
|
Object.keys(pkgJson.scripts).forEach(name => {
|
|
626
654
|
pkgJson.scripts[name] = pkgJson.scripts[name].replace('example', subgraphName);
|
|
@@ -632,60 +660,54 @@ const initSubgraphFromExample = async (toolbox, { fromExample, allowSimpleName,
|
|
|
632
660
|
delete pkgJson['devDependencies']['@graphprotocol/graph-cli'];
|
|
633
661
|
}
|
|
634
662
|
// Write package.json
|
|
635
|
-
filesystem.write(pkgJsonFilename, pkgJson, { jsonIndent: 2 });
|
|
663
|
+
gluegun_1.filesystem.write(pkgJsonFilename, pkgJson, { jsonIndent: 2 });
|
|
636
664
|
return true;
|
|
637
665
|
}
|
|
638
666
|
catch (e) {
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
return false;
|
|
667
|
+
gluegun_1.filesystem.remove(directory);
|
|
668
|
+
this.error(`Failed to preconfigure the subgraph: ${e}`);
|
|
642
669
|
}
|
|
643
670
|
});
|
|
644
671
|
if (!prepared) {
|
|
645
|
-
|
|
672
|
+
this.exit(1);
|
|
646
673
|
return;
|
|
647
674
|
}
|
|
648
675
|
// Initialize a fresh Git repository
|
|
649
|
-
const repo = await initRepository(
|
|
676
|
+
const repo = await initRepository(directory);
|
|
650
677
|
if (repo !== true) {
|
|
651
|
-
|
|
678
|
+
this.exit(1);
|
|
652
679
|
return;
|
|
653
680
|
}
|
|
654
681
|
// Install dependencies
|
|
655
|
-
const installed = await installDependencies(
|
|
682
|
+
const installed = await installDependencies(directory, commands);
|
|
656
683
|
if (installed !== true) {
|
|
657
|
-
|
|
684
|
+
this.exit(1);
|
|
658
685
|
return;
|
|
659
686
|
}
|
|
660
687
|
// Run code-generation
|
|
661
|
-
const codegen = await runCodegen(
|
|
688
|
+
const codegen = await runCodegen(directory, commands.codegen);
|
|
662
689
|
if (codegen !== true) {
|
|
663
|
-
|
|
690
|
+
this.exit(1);
|
|
664
691
|
return;
|
|
665
692
|
}
|
|
666
|
-
printNextSteps(
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
const { print } = toolbox;
|
|
693
|
+
printNextSteps.bind(this)({ subgraphName, directory }, { commands });
|
|
694
|
+
}
|
|
695
|
+
async function initSubgraphFromContract({ protocolInstance, allowSimpleName, subgraphName, directory, abi, network, contract, indexEvents, contractName, node, studio, product, startBlock, }, { commands, addContract, }) {
|
|
670
696
|
// Fail if the subgraph name is invalid
|
|
671
|
-
if (!revalidateSubgraphName(
|
|
672
|
-
|
|
697
|
+
if (!revalidateSubgraphName.bind(this)(subgraphName, { allowSimpleName })) {
|
|
698
|
+
this.exit(1);
|
|
673
699
|
return;
|
|
674
700
|
}
|
|
675
701
|
// Fail if the output directory already exists
|
|
676
|
-
if (
|
|
677
|
-
|
|
678
|
-
process.exitCode = 1;
|
|
679
|
-
return;
|
|
702
|
+
if (gluegun_1.filesystem.exists(directory)) {
|
|
703
|
+
this.error(`Directory or file "${directory}" already exists`, { exit: 1 });
|
|
680
704
|
}
|
|
681
705
|
if (protocolInstance.hasABIs() &&
|
|
682
706
|
((0, schema_1.abiEvents)(abi).size === 0 ||
|
|
683
707
|
// @ts-expect-error TODO: the abiEvents result is expected to be a List, how's it an array?
|
|
684
708
|
(0, schema_1.abiEvents)(abi).length === 0)) {
|
|
685
709
|
// Fail if the ABI does not contain any events
|
|
686
|
-
|
|
687
|
-
process.exitCode = 1;
|
|
688
|
-
return;
|
|
710
|
+
this.error(`ABI does not contain any events`, { exit: 1 });
|
|
689
711
|
}
|
|
690
712
|
// We can validate this before the scaffold because we receive
|
|
691
713
|
// the network from the form or via command line argument.
|
|
@@ -694,9 +716,7 @@ const initSubgraphFromContract = async (toolbox, { protocolInstance, allowSimple
|
|
|
694
716
|
(0, studio_1.validateStudioNetwork)({ studio, product, network });
|
|
695
717
|
}
|
|
696
718
|
catch (e) {
|
|
697
|
-
|
|
698
|
-
process.exitCode = 1;
|
|
699
|
-
return;
|
|
719
|
+
this.error(e, { exit: 1 });
|
|
700
720
|
}
|
|
701
721
|
// Scaffold subgraph
|
|
702
722
|
const scaffold = await (0, spinner_1.withSpinner)(`Create subgraph scaffold`, `Failed to create subgraph scaffold`, `Warnings while creating subgraph scaffold`, async (spinner) => {
|
|
@@ -720,102 +740,89 @@ const initSubgraphFromContract = async (toolbox, { protocolInstance, allowSimple
|
|
|
720
740
|
}
|
|
721
741
|
if (protocolInstance.hasContract()) {
|
|
722
742
|
const identifierName = protocolInstance.getContract().identifierName();
|
|
723
|
-
const networkConf = await (0, network_1.initNetworksConfig)(
|
|
743
|
+
const networkConf = await (0, network_1.initNetworksConfig)(directory, identifierName);
|
|
724
744
|
if (networkConf !== true) {
|
|
725
745
|
process.exitCode = 1;
|
|
726
746
|
return;
|
|
727
747
|
}
|
|
728
748
|
}
|
|
729
749
|
// Initialize a fresh Git repository
|
|
730
|
-
const repo = await initRepository(
|
|
750
|
+
const repo = await initRepository(directory);
|
|
731
751
|
if (repo !== true) {
|
|
732
|
-
|
|
752
|
+
this.exit(1);
|
|
733
753
|
return;
|
|
734
754
|
}
|
|
735
755
|
// Install dependencies
|
|
736
|
-
const installed = await installDependencies(
|
|
756
|
+
const installed = await installDependencies(directory, commands);
|
|
737
757
|
if (installed !== true) {
|
|
738
|
-
|
|
758
|
+
this.exit(1);
|
|
739
759
|
return;
|
|
740
760
|
}
|
|
741
761
|
// Run code-generation
|
|
742
|
-
const codegen = await runCodegen(
|
|
762
|
+
const codegen = await runCodegen(directory, commands.codegen);
|
|
743
763
|
if (codegen !== true) {
|
|
744
|
-
|
|
764
|
+
this.exit(1);
|
|
745
765
|
return;
|
|
746
766
|
}
|
|
747
767
|
while (addContract) {
|
|
748
|
-
addContract = await addAnotherContract(
|
|
749
|
-
}
|
|
750
|
-
printNextSteps(
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
const
|
|
768
|
+
addContract = await addAnotherContract.bind(this)({ protocolInstance, directory });
|
|
769
|
+
}
|
|
770
|
+
printNextSteps.bind(this)({ subgraphName, directory }, { commands });
|
|
771
|
+
}
|
|
772
|
+
async function addAnotherContract({ protocolInstance, directory, }) {
|
|
773
|
+
const addContractAnswer = await core_1.ux.prompt('Add another contract? (y/n)', {
|
|
774
|
+
required: true,
|
|
775
|
+
type: 'single',
|
|
776
|
+
});
|
|
777
|
+
const addContractConfirmation = addContractAnswer.toLowerCase() === 'y';
|
|
754
778
|
if (addContractConfirmation) {
|
|
755
779
|
let abiFromFile = false;
|
|
756
780
|
const ProtocolContract = protocolInstance.getContract();
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
{
|
|
779
|
-
type: 'input',
|
|
780
|
-
name: 'abi',
|
|
781
|
-
message: 'ABI file (path)',
|
|
782
|
-
skip: () => abiFromFile === false,
|
|
783
|
-
},
|
|
784
|
-
{
|
|
785
|
-
type: 'input',
|
|
786
|
-
name: 'contractName',
|
|
787
|
-
message: 'Contract Name',
|
|
788
|
-
initial: 'Contract',
|
|
789
|
-
validate: (value) => value && value.length > 0,
|
|
790
|
-
},
|
|
791
|
-
];
|
|
781
|
+
let contract = '';
|
|
782
|
+
for (;;) {
|
|
783
|
+
contract = await core_1.ux.prompt(`Contract ${ProtocolContract.identifierName()}`, {
|
|
784
|
+
required: true,
|
|
785
|
+
});
|
|
786
|
+
const { valid, error } = (0, validation_1.validateContract)(contract, ProtocolContract);
|
|
787
|
+
if (valid) {
|
|
788
|
+
break;
|
|
789
|
+
}
|
|
790
|
+
this.log(`✖ ${error}`);
|
|
791
|
+
}
|
|
792
|
+
const localAbi = await core_1.ux.prompt('Provide local ABI path? (y/n)', {
|
|
793
|
+
required: true,
|
|
794
|
+
type: 'single',
|
|
795
|
+
});
|
|
796
|
+
abiFromFile = localAbi.toLowerCase() === 'y';
|
|
797
|
+
let abiPath = '';
|
|
798
|
+
if (abiFromFile) {
|
|
799
|
+
abiPath = await core_1.ux.prompt('ABI file (path)', { required: true });
|
|
800
|
+
}
|
|
801
|
+
const contractName = await core_1.ux.prompt('Contract Name', { required: true, default: 'Contract' });
|
|
792
802
|
// Get the cwd before process.chdir in order to switch back in the end of command execution
|
|
793
803
|
const cwd = process.cwd();
|
|
794
804
|
try {
|
|
795
|
-
const { abi, contract, contractName } = await toolbox.prompt.ask(
|
|
796
|
-
// @ts-expect-error questions do somehow fit
|
|
797
|
-
questions);
|
|
798
805
|
if (fs_1.default.existsSync(directory)) {
|
|
799
806
|
process.chdir(directory);
|
|
800
807
|
}
|
|
801
808
|
const commandLine = ['add', contract, '--contract-name', contractName];
|
|
802
809
|
if (abiFromFile) {
|
|
803
|
-
if (
|
|
804
|
-
commandLine.push('--abi', path_1.default.normalize(
|
|
810
|
+
if (abiPath.includes(directory)) {
|
|
811
|
+
commandLine.push('--abi', path_1.default.normalize(abiPath.replace(directory, '')));
|
|
805
812
|
}
|
|
806
813
|
else {
|
|
807
|
-
commandLine.push('--abi',
|
|
814
|
+
commandLine.push('--abi', abiPath);
|
|
808
815
|
}
|
|
809
816
|
}
|
|
810
|
-
await
|
|
817
|
+
await add_1.default.run(commandLine);
|
|
811
818
|
}
|
|
812
819
|
catch (e) {
|
|
813
|
-
|
|
814
|
-
process.exit(1);
|
|
820
|
+
this.error(e);
|
|
815
821
|
}
|
|
816
822
|
finally {
|
|
823
|
+
// TODO: safer way of doing this?
|
|
817
824
|
process.chdir(cwd);
|
|
818
825
|
}
|
|
819
826
|
}
|
|
820
827
|
return addContractConfirmation;
|
|
821
|
-
}
|
|
828
|
+
}
|