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