@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.
@@ -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 chalk_1 = __importDefault(require("chalk"));
33
- const graphCli = __importStar(require("../cli"));
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 abi_2 = require("./../command-helpers/abi");
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
- const initDebug = (0, debug_1.default)('graph-cli:init');
52
- const HELP = `
53
- ${chalk_1.default.bold('graph init')} [options] [subgraph-name] [directory]
54
-
55
- ${chalk_1.default.dim('Options:')}
56
-
57
- --protocol <${protocolChoices.join('|')}>
58
- --product <subgraph-studio|hosted-service>
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
- print.error(`Only one of --from-example and --from-contract can be used at a time.`);
346
- process.exitCode = 1;
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 === null) {
372
- print.error(`Git was not found on your system. Please install 'git' so it is in $PATH.`);
373
- process.exitCode = 1;
374
- return;
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
- print.error(`Neither Yarn nor NPM were found on your system. Please install one of them.`);
381
- process.exitCode = 1;
382
- return;
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(toolbox, { fromExample, allowSimpleName, directory, subgraphName, studio, product }, { commands });
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
- print.error(`Protocol '${protocol}' is not supported, choose from these options: ${protocolChoices.join(', ')}`);
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 (abi) {
104
+ if (abiPath) {
407
105
  try {
408
- abi = loadAbiFromFile(toolbox, ABI, abi);
106
+ abi = loadAbiFromFile(ABI, abiPath);
409
107
  }
410
108
  catch (e) {
411
- print.error(`Failed to load ABI: ${e.message}`);
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(toolbox, {
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(toolbox, {
144
+ await initSubgraphFromExample.bind(this)({
471
145
  fromExample,
472
- subgraphName: inputs.subgraphName,
473
- directory: inputs.directory,
474
- studio: inputs.studio,
475
- product: inputs.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: inputs.product,
154
+ product,
481
155
  studio,
482
156
  node,
483
157
  allowSimpleName,
484
158
  }));
485
- await initSubgraphFromContract(toolbox, {
486
- protocolInstance: inputs.protocolInstance,
159
+ await initSubgraphFromContract.bind(this)({
160
+ protocolInstance,
487
161
  allowSimpleName,
488
- subgraphName: inputs.subgraphName,
489
- directory: inputs.directory,
490
- abi: inputs.abi,
491
- network: inputs.network,
492
- contract: inputs.contract,
493
- indexEvents: inputs.indexEvents,
494
- contractName: inputs.contractName,
495
- node,
496
- studio: inputs.studio,
497
- product: inputs.product,
498
- startBlock: inputs.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
- const revalidateSubgraphName = async (toolbox, subgraphName, { allowSimpleName }) => {
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
- toolbox.print.error(`${e.message}
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 (toolbox, directory) => await (0, spinner_1.withSpinner)(`Initialize subgraph repository`, `Failed to initialize subgraph repository`, `Warnings while initializing subgraph repository`, 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 (toolbox.filesystem.exists(gitDir)) {
524
- toolbox.filesystem.remove(gitDir);
557
+ if (gluegun_1.filesystem.exists(gitDir)) {
558
+ gluegun_1.filesystem.remove(gitDir);
525
559
  }
526
- await toolbox.system.run('git init', { cwd: directory });
527
- await toolbox.system.run('git add --all', { cwd: directory });
528
- await toolbox.system.run('git commit -m "Initial commit"', {
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 (toolbox, directory, commands) => await (0, spinner_1.withSpinner)(`Install dependencies with ${toolbox.print.colors.muted(commands.install)}`, `Failed to install dependencies`, `Warnings while installing dependencies`, 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 toolbox.system.run(commands.link, { cwd: directory });
569
+ await gluegun_1.system.run(commands.link, { cwd: directory });
536
570
  }
537
- await toolbox.system.run(commands.install, { cwd: directory });
571
+ await gluegun_1.system.run(commands.install, { cwd: directory });
538
572
  return true;
539
573
  });
540
- const runCodegen = async (toolbox, directory, codegenCommand) => await (0, spinner_1.withSpinner)(`Generate ABI and schema types with ${toolbox.print.colors.muted(codegenCommand)}`, `Failed to generate code from ABI and GraphQL schema`, `Warnings while generating code from ABI and GraphQL schema`, async () => {
541
- await toolbox.system.run(codegenCommand, { cwd: directory });
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
- const printNextSteps = (toolbox, { subgraphName, directory }, { commands, }) => {
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
- print.success(`
549
- Subgraph ${print.colors.blue(subgraphName)} created in ${print.colors.blue(relativeDir)}
581
+ this.log(`
582
+ Subgraph ${subgraphName} created in ${relativeDir}
550
583
  `);
551
- print.info(`Next steps:
584
+ this.log(`Next steps:
552
585
 
553
- 1. Run \`${print.colors.muted('graph auth')}\` to authenticate with your deploy key.
586
+ 1. Run \`graph auth\` to authenticate with your deploy key.
554
587
 
555
- 2. Type \`${print.colors.muted(`cd ${relativeDir}`)}\` to enter the subgraph.
588
+ 2. Type \`cd ${relativeDir}\` to enter the subgraph.
556
589
 
557
- 3. Run \`${print.colors.muted(commands.deploy)}\` to deploy the subgraph.
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
- const initSubgraphFromExample = async (toolbox, { fromExample, allowSimpleName, subgraphName, directory, studio, product, }, { commands, }) => {
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(toolbox, subgraphName, { allowSimpleName: !!allowSimpleName })) {
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
- print.error(`Directory or file "${directory}" already exists`);
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
- process.exitCode = 1;
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
- print.error(e.message);
610
- process.exitCode = 1;
611
- return;
639
+ this.error(e.message, { exit: 1 });
612
640
  }
613
- const networkConf = await (0, network_1.initNetworksConfig)(toolbox, directory, 'address');
641
+ const networkConf = await (0, network_1.initNetworksConfig)(directory, 'address');
614
642
  if (networkConf !== true) {
615
- process.exitCode = 1;
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
- print.error(`Failed to preconfigure the subgraph: ${e}`);
640
- filesystem.remove(directory);
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
- process.exitCode = 1;
672
+ this.exit(1);
646
673
  return;
647
674
  }
648
675
  // Initialize a fresh Git repository
649
- const repo = await initRepository(toolbox, directory);
676
+ const repo = await initRepository(directory);
650
677
  if (repo !== true) {
651
- process.exitCode = 1;
678
+ this.exit(1);
652
679
  return;
653
680
  }
654
681
  // Install dependencies
655
- const installed = await installDependencies(toolbox, directory, commands);
682
+ const installed = await installDependencies(directory, commands);
656
683
  if (installed !== true) {
657
- process.exitCode = 1;
684
+ this.exit(1);
658
685
  return;
659
686
  }
660
687
  // Run code-generation
661
- const codegen = await runCodegen(toolbox, directory, commands.codegen);
688
+ const codegen = await runCodegen(directory, commands.codegen);
662
689
  if (codegen !== true) {
663
- process.exitCode = 1;
690
+ this.exit(1);
664
691
  return;
665
692
  }
666
- printNextSteps(toolbox, { subgraphName, directory }, { commands });
667
- };
668
- const initSubgraphFromContract = async (toolbox, { protocolInstance, allowSimpleName, subgraphName, directory, abi, network, contract, indexEvents, contractName, node, studio, product, startBlock, }, { commands, addContract, }) => {
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(toolbox, subgraphName, { allowSimpleName })) {
672
- process.exitCode = 1;
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 (toolbox.filesystem.exists(directory)) {
677
- print.error(`Directory or file "${directory}" already exists`);
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
- print.error(`ABI does not contain any events`);
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
- print.error(e.message);
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)(toolbox, directory, identifierName);
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(toolbox, directory);
750
+ const repo = await initRepository(directory);
731
751
  if (repo !== true) {
732
- process.exitCode = 1;
752
+ this.exit(1);
733
753
  return;
734
754
  }
735
755
  // Install dependencies
736
- const installed = await installDependencies(toolbox, directory, commands);
756
+ const installed = await installDependencies(directory, commands);
737
757
  if (installed !== true) {
738
- process.exitCode = 1;
758
+ this.exit(1);
739
759
  return;
740
760
  }
741
761
  // Run code-generation
742
- const codegen = await runCodegen(toolbox, directory, commands.codegen);
762
+ const codegen = await runCodegen(directory, commands.codegen);
743
763
  if (codegen !== true) {
744
- process.exitCode = 1;
764
+ this.exit(1);
745
765
  return;
746
766
  }
747
767
  while (addContract) {
748
- addContract = await addAnotherContract(toolbox, { protocolInstance, directory });
749
- }
750
- printNextSteps(toolbox, { subgraphName, directory }, { commands });
751
- };
752
- const addAnotherContract = async (toolbox, { protocolInstance, directory }) => {
753
- const addContractConfirmation = await toolbox.prompt.confirm('Add another contract?');
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
- const questions = [
758
- {
759
- type: 'input',
760
- name: 'contract',
761
- message: () => `Contract ${ProtocolContract.identifierName()}`,
762
- validate: async (value) => {
763
- // Validate whether the contract is valid
764
- const { valid, error } = (0, validation_1.validateContract)(value, ProtocolContract);
765
- return valid ? true : error;
766
- },
767
- },
768
- {
769
- type: 'select',
770
- name: 'localAbi',
771
- message: 'Provide local ABI path?',
772
- choices: ['yes', 'no'],
773
- result: (value) => {
774
- abiFromFile = value === 'yes' ? true : false;
775
- return abiFromFile;
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 (abi.includes(directory)) {
804
- commandLine.push('--abi', path_1.default.normalize(abi.replace(directory, '')));
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', abi);
814
+ commandLine.push('--abi', abiPath);
808
815
  }
809
816
  }
810
- await graphCli.run(commandLine);
817
+ await add_1.default.run(commandLine);
811
818
  }
812
819
  catch (e) {
813
- toolbox.print.error(e);
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
+ }