From fddfea6145b123371fa102d908a972a256feca61 Mon Sep 17 00:00:00 2001 From: incrypto32 Date: Sun, 5 Jan 2025 21:58:50 +0530 Subject: [PATCH 01/21] Fix subgraphs without abi field failing to build --- packages/cli/src/compiler/index.ts | 58 ++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/packages/cli/src/compiler/index.ts b/packages/cli/src/compiler/index.ts index 6dbe4911d..143b5f7e3 100644 --- a/packages/cli/src/compiler/index.ts +++ b/packages/cli/src/compiler/index.ts @@ -508,6 +508,9 @@ export default class Compiler { `Failed to write compiled subgraph to ${displayDir}`, `Warnings while writing compiled subgraph to ${displayDir}`, async spinner => { + // Add debug log for initial subgraph state + compilerDebug('Initial subgraph state:', subgraph.toJS()); + // Copy schema and update its path subgraph = subgraph.updateIn(['schema', 'file'], schemaFile => { const schemaFilePath = path.resolve(this.sourceDir, schemaFile as string); @@ -518,32 +521,49 @@ export default class Compiler { return path.relative(this.options.outputDir, targetFile); }); + // Add debug log before processing data sources + compilerDebug('Processing dataSources:', subgraph.get('dataSources').toJS()); + // Copy data source files and update their paths subgraph = subgraph.update('dataSources', (dataSources: any[]) => dataSources.map(dataSource => { + // Add debug log for each data source + compilerDebug('Processing dataSource:', dataSource.toJS()); + let updatedDataSource = dataSource; if (this.protocol.hasABIs()) { - updatedDataSource = updatedDataSource - // Write data source ABIs to the output directory - .updateIn(['mapping', 'abis'], (abis: any[]) => - abis.map((abi: any) => - abi.update('file', (abiFile: string) => { - abiFile = path.resolve(this.sourceDir, abiFile); - const abiData = this.ABI.load(abi.get('name'), abiFile); - return path.relative( - this.options.outputDir, - this._writeSubgraphFile( - abiFile, - JSON.stringify(abiData.data.toJS(), null, 2), - this.sourceDir, - this.subgraphDir(this.options.outputDir, dataSource), - spinner, - ), - ); - }), - ), + // Add debug log for ABIs + compilerDebug( + 'Processing ABIs for dataSource:', + dataSource.getIn(['mapping', 'abis'])?.toJS() || 'undefined', + ); + + updatedDataSource = updatedDataSource.updateIn(['mapping', 'abis'], (abis: any[]) => { + compilerDebug('ABIs value:', Array.isArray(abis) ? abis : 'undefined'); + + if (!abis) { + compilerDebug('No ABIs found for dataSource'); + return immutable.List(); + } + + return abis.map((abi: any) => + abi.update('file', (abiFile: string) => { + abiFile = path.resolve(this.sourceDir, abiFile); + const abiData = this.ABI.load(abi.get('name'), abiFile); + return path.relative( + this.options.outputDir, + this._writeSubgraphFile( + abiFile, + JSON.stringify(abiData.data.toJS(), null, 2), + this.sourceDir, + this.subgraphDir(this.options.outputDir, dataSource), + spinner, + ), + ); + }), ); + }); } if (protocol.name == 'substreams' || protocol.name == 'substreams/triggers') { From 6b9f04b11dc0efd9e931aba755e52e8d084bcc80 Mon Sep 17 00:00:00 2001 From: incrypto32 Date: Thu, 23 Jan 2025 09:51:23 +0400 Subject: [PATCH 02/21] Fix graph init for composed subgraphs --- packages/cli/src/commands/init.ts | 47 ++++++++++++++++++--- packages/cli/src/scaffold/index.ts | 60 +++++++++++++++++++++------ packages/cli/src/utils.ts | 66 ++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+), 19 deletions(-) diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 731091be0..08f1d4232 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -23,7 +23,11 @@ import EthereumABI from '../protocols/ethereum/abi.js'; import Protocol, { ProtocolName } from '../protocols/index.js'; import { abiEvents } from '../scaffold/schema.js'; import Schema from '../schema.js'; -import { createIpfsClient, loadSubgraphSchemaFromIPFS } from '../utils.js'; +import { + createIpfsClient, + loadSubgraphSchemaFromIPFS, + validateSubgraphNetworkMatch, +} from '../utils.js'; import { validateContract } from '../validation/index.js'; import AddCommand from './add.js'; @@ -515,7 +519,7 @@ async function processInitForm( value: 'contract', }, { message: 'Substreams', name: 'substreams', value: 'substreams' }, - // { message: 'Subgraph', name: 'subgraph', value: 'subgraph' }, + { message: 'Subgraph', name: 'subgraph', value: 'subgraph' }, ].filter(({ name }) => name), }); @@ -596,9 +600,17 @@ async function processInitForm( isSubstreams || (!protocolInstance.hasContract() && !isComposedSubgraph), initial: initContract, - validate: async (value: string) => { + validate: async (value: string): Promise => { if (isComposedSubgraph) { - return value.startsWith('Qm') ? true : 'Subgraph deployment ID must start with Qm'; + if (!ipfsNode) { + return true; // Skip validation if no IPFS node is available + } + const ipfs = createIpfsClient(ipfsNode); + const { valid, error } = await validateSubgraphNetworkMatch(ipfs, value, network.id); + if (!valid) { + return error || 'Invalid subgraph network match'; + } + return true; } if (initFromExample !== undefined || !protocolInstance.hasContract()) { return true; @@ -731,7 +743,7 @@ async function processInitForm( isSubstreams || !!initAbiPath || isComposedSubgraph, - validate: async (value: string) => { + validate: async (value: string): Promise => { if ( initFromExample || abiFromApi || @@ -822,6 +834,22 @@ async function processInitForm( await promptManager.executeInteractive(); + // If loading from IPFS, validate network matches + if (ipfsNode && subgraphName.startsWith('Qm')) { + const ipfs = createIpfsClient(ipfsNode); + try { + const { valid, error } = await validateSubgraphNetworkMatch(ipfs, subgraphName, network.id); + if (!valid) { + throw new Error(error || 'Invalid subgraph network match'); + } + } catch (e) { + if (e instanceof Error) { + print.error(`Failed to validate subgraph network: ${e.message}`); + } + throw e; + } + } + return { abi: (abiFromApi || abiFromFile)!, protocolInstance, @@ -1188,8 +1216,9 @@ async function initSubgraphFromContract( } if ( - !protocolInstance.isComposedSubgraph() && + !isComposedSubgraph && protocolInstance.hasABIs() && + abi && // Add check for abi existence (abiEvents(abi).size === 0 || // @ts-expect-error TODO: the abiEvents result is expected to be a List, how's it an array? abiEvents(abi).length === 0) @@ -1204,6 +1233,12 @@ async function initSubgraphFromContract( `Failed to create subgraph scaffold`, `Warnings while creating subgraph scaffold`, async spinner => { + initDebugger('Generating scaffold with ABI:', abi); + initDebugger('ABI data:', abi?.data); + if (abi) { + initDebugger('ABI events:', abiEvents(abi)); + } + const scaffold = await generateScaffold( { protocolInstance, diff --git a/packages/cli/src/scaffold/index.ts b/packages/cli/src/scaffold/index.ts index 66a14cee0..d80c780c6 100644 --- a/packages/cli/src/scaffold/index.ts +++ b/packages/cli/src/scaffold/index.ts @@ -1,3 +1,4 @@ +import debugFactory from 'debug'; import fs from 'fs-extra'; import { strings } from 'gluegun'; import prettier from 'prettier'; @@ -11,6 +12,8 @@ import { generateEventIndexingHandlers } from './mapping.js'; import { abiEvents, generateEventType, generateExampleEntityType } from './schema.js'; import { generateTestsFiles } from './tests.js'; +const scaffoldDebugger = debugFactory('graph-cli:scaffold'); + const GRAPH_CLI_VERSION = process.env.GRAPH_CLI_TESTS ? // JSON.stringify should remove this key, we will install the local // graph-cli for the tests using `npm link` instead of fetching from npm. @@ -47,18 +50,34 @@ export default class Scaffold { spkgPath?: string; entities?: string[]; - constructor(options: ScaffoldOptions) { - this.protocol = options.protocol; - this.abi = options.abi; - this.indexEvents = options.indexEvents; - this.contract = options.contract; - this.network = options.network; - this.contractName = options.contractName; - this.subgraphName = options.subgraphName; - this.startBlock = options.startBlock; - this.node = options.node; - this.spkgPath = options.spkgPath; - this.entities = options.entities; + constructor({ + protocol, + abi, + contract, + network, + contractName, + startBlock, + subgraphName, + node, + spkgPath, + indexEvents, + entities, + }: ScaffoldOptions) { + this.protocol = protocol; + this.abi = abi; + this.contract = contract; + this.network = network; + this.contractName = contractName; + this.startBlock = startBlock; + this.subgraphName = subgraphName; + this.node = node; + this.spkgPath = spkgPath; + this.indexEvents = indexEvents; + this.entities = entities; + + scaffoldDebugger('Scaffold constructor called with ABI:', abi); + scaffoldDebugger('ABI data:', abi?.data); + scaffoldDebugger('ABI file:', abi?.file); } async generatePackageJson() { @@ -203,9 +222,24 @@ dataSources: } async generateABIs() { + scaffoldDebugger('Generating ABIs...'); + scaffoldDebugger('Protocol has ABIs:', this.protocol.hasABIs()); + scaffoldDebugger('ABI data:', this.abi?.data); + scaffoldDebugger('ABI file:', this.abi?.file); + + if (!this.protocol.hasABIs()) { + scaffoldDebugger('Protocol does not have ABIs, skipping ABI generation'); + return; + } + + if (!this.abi?.data) { + scaffoldDebugger('ABI data is undefined, skipping ABI generation'); + return; + } + return this.protocol.hasABIs() ? { - [`${this.contractName}.json`]: await prettier.format(JSON.stringify(this.abi?.data), { + [`${this.contractName}.json`]: await prettier.format(JSON.stringify(this.abi.data), { parser: 'json', }), } diff --git a/packages/cli/src/utils.ts b/packages/cli/src/utils.ts index 5b8223d20..4dcbb9d42 100644 --- a/packages/cli/src/utils.ts +++ b/packages/cli/src/utils.ts @@ -32,3 +32,69 @@ export async function loadSubgraphSchemaFromIPFS(ipfsClient: any, manifest: stri throw Error(`Failed to load schema from IPFS ${manifest}`); } } + +export async function loadManifestFromIPFS(ipfsClient: any, manifest: string) { + try { + const manifestBuffer = ipfsClient.cat(manifest); + let manifestFile = ''; + for await (const chunk of manifestBuffer) { + manifestFile += Buffer.from(chunk).toString('utf8'); + } + return manifestFile; + } catch (e) { + utilsDebug.extend('loadManifestFromIPFS')(`Failed to load manifest from IPFS ${manifest}`); + utilsDebug.extend('loadManifestFromIPFS')(e); + throw Error(`Failed to load manifest from IPFS ${manifest}`); + } +} + +/** + * Validates that the network of a source subgraph matches the target network + * @param ipfsClient IPFS client instance + * @param sourceSubgraphId IPFS hash of the source subgraph + * @param targetNetwork Network of the target subgraph being created + * @returns Object containing validation result and error message if any + */ +export async function validateSubgraphNetworkMatch( + ipfsClient: any, + sourceSubgraphId: string, + targetNetwork: string, +): Promise<{ valid: boolean; error?: string }> { + try { + const manifestFile = await loadManifestFromIPFS(ipfsClient, sourceSubgraphId); + const manifestYaml = yaml.load(manifestFile) as any; + + // Extract network from data sources + const dataSources = manifestYaml.dataSources || []; + const templates = manifestYaml.templates || []; + const allSources = [...dataSources, ...templates]; + + if (allSources.length === 0) { + return { valid: true }; // No data sources to validate + } + + // Get network from first data source + const sourceNetwork = allSources[0].network; + if (!sourceNetwork) { + return { valid: true }; // Network not specified in source, skip validation + } + + const normalizedSourceNetwork = sourceNetwork.toLowerCase(); + const normalizedTargetNetwork = targetNetwork.toLowerCase(); + + if (normalizedSourceNetwork !== normalizedTargetNetwork) { + return { + valid: false, + error: `Network mismatch: The source subgraph is indexing the '${sourceNetwork}' network, but you're creating a subgraph for '${targetNetwork}' network. When composing subgraphs, they must index the same network.`, + }; + } + + return { valid: true }; + } catch (e) { + utilsDebug.extend('validateSubgraphNetworkMatch')(`Failed to validate network match: ${e}`); + return { + valid: false, + error: e instanceof Error ? e.message : 'Failed to validate subgraph network', + }; + } +} From b065646a692a63311fca8de318e075d6e515bb7c Mon Sep 17 00:00:00 2001 From: incrypto32 Date: Thu, 23 Jan 2025 11:10:19 +0400 Subject: [PATCH 03/21] Add changeset --- .changeset/curly-buses-hang.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/curly-buses-hang.md diff --git a/.changeset/curly-buses-hang.md b/.changeset/curly-buses-hang.md new file mode 100644 index 000000000..ef7b1341e --- /dev/null +++ b/.changeset/curly-buses-hang.md @@ -0,0 +1,5 @@ +--- +'@graphprotocol/graph-cli': minor +--- + +Add support for subgraph datasource in `graph init` From 735b56572041ad2dd19b6e6630e88ff1abdc704a Mon Sep 17 00:00:00 2001 From: incrypto32 Date: Fri, 24 Jan 2025 15:38:41 +0400 Subject: [PATCH 04/21] Fix validation not working --- packages/cli/src/commands/init.ts | 34 +++++++++++++++---------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 08f1d4232..4c324a2fa 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -588,6 +588,19 @@ async function processInitForm( }, }); + promptManager.addStep({ + type: 'input', + name: 'ipfs', + message: `IPFS node to use for fetching subgraph manifest`, + initial: ipfsUrl, + skip: () => !isComposedSubgraph, + result: value => { + ipfsNode = value; + initDebugger.extend('processInitForm')('ipfs: %O', value); + return value; + }, + }); + promptManager.addStep({ type: 'input', name: 'source', @@ -603,7 +616,7 @@ async function processInitForm( validate: async (value: string): Promise => { if (isComposedSubgraph) { if (!ipfsNode) { - return true; // Skip validation if no IPFS node is available + return true; } const ipfs = createIpfsClient(ipfsNode); const { valid, error } = await validateSubgraphNetworkMatch(ipfs, value, network.id); @@ -691,19 +704,6 @@ async function processInitForm( }, }); - promptManager.addStep({ - type: 'input', - name: 'ipfs', - message: `IPFS node to use for fetching subgraph manifest`, - initial: ipfsUrl, - skip: () => !isComposedSubgraph, - result: value => { - ipfsNode = value; - initDebugger.extend('processInitForm')('ipfs: %O', value); - return value; - }, - }); - promptManager.addStep({ type: 'input', name: 'spkg', @@ -834,11 +834,11 @@ async function processInitForm( await promptManager.executeInteractive(); - // If loading from IPFS, validate network matches - if (ipfsNode && subgraphName.startsWith('Qm')) { + // Validate network matches if loading from IPFS + if (ipfsNode && source && source.startsWith('Qm')) { const ipfs = createIpfsClient(ipfsNode); try { - const { valid, error } = await validateSubgraphNetworkMatch(ipfs, subgraphName, network.id); + const { valid, error } = await validateSubgraphNetworkMatch(ipfs, source!, network.id); if (!valid) { throw new Error(error || 'Invalid subgraph network match'); } From 600280d88ce16f759b7c1a2c23ff6e54d419823e Mon Sep 17 00:00:00 2001 From: incrypto32 Date: Fri, 24 Jan 2025 15:38:52 +0400 Subject: [PATCH 05/21] Support declared calls in manifest --- packages/cli/src/protocols/subgraph/manifest.graphql | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cli/src/protocols/subgraph/manifest.graphql b/packages/cli/src/protocols/subgraph/manifest.graphql index f97621368..35731e2b7 100644 --- a/packages/cli/src/protocols/subgraph/manifest.graphql +++ b/packages/cli/src/protocols/subgraph/manifest.graphql @@ -63,6 +63,7 @@ type ContractABI { type EntityHandler { handler: String! entity: String! + calls: JSON } type Graft { From 3a75a7fd2231fb7cd2ed22c8020b1dd5539efa70 Mon Sep 17 00:00:00 2001 From: incrypto32 Date: Mon, 27 Jan 2025 16:14:32 +0400 Subject: [PATCH 06/21] Lint fix --- packages/cli/src/commands/init.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 4c324a2fa..864682774 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -835,7 +835,7 @@ async function processInitForm( await promptManager.executeInteractive(); // Validate network matches if loading from IPFS - if (ipfsNode && source && source.startsWith('Qm')) { + if (ipfsNode && source?.startsWith('Qm')) { const ipfs = createIpfsClient(ipfsNode); try { const { valid, error } = await validateSubgraphNetworkMatch(ipfs, source!, network.id); @@ -844,7 +844,7 @@ async function processInitForm( } } catch (e) { if (e instanceof Error) { - print.error(`Failed to validate subgraph network: ${e.message}`); + print.error(`Failed to validate subgraph network: ${e?.message}`); } throw e; } From a074696f74c600a6e258f82bd16e296c758a12fc Mon Sep 17 00:00:00 2001 From: incrypto32 Date: Mon, 3 Feb 2025 14:06:49 +0400 Subject: [PATCH 07/21] Address review comments --- packages/cli/src/commands/init.ts | 39 ++++++++++++++++--------------- packages/cli/src/utils.ts | 8 +------ 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 864682774..66b3cdf09 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -594,6 +594,17 @@ async function processInitForm( message: `IPFS node to use for fetching subgraph manifest`, initial: ipfsUrl, skip: () => !isComposedSubgraph, + validate: value => { + if (!value) { + return 'IPFS node URL cannot be empty'; + } + try { + new URL(value); + return true; + } catch { + return 'Please enter a valid URL'; + } + }, result: value => { ipfsNode = value; initDebugger.extend('processInitForm')('ipfs: %O', value); @@ -615,9 +626,6 @@ async function processInitForm( initial: initContract, validate: async (value: string): Promise => { if (isComposedSubgraph) { - if (!ipfsNode) { - return true; - } const ipfs = createIpfsClient(ipfsNode); const { valid, error } = await validateSubgraphNetworkMatch(ipfs, value, network.id); if (!valid) { @@ -834,22 +842,6 @@ async function processInitForm( await promptManager.executeInteractive(); - // Validate network matches if loading from IPFS - if (ipfsNode && source?.startsWith('Qm')) { - const ipfs = createIpfsClient(ipfsNode); - try { - const { valid, error } = await validateSubgraphNetworkMatch(ipfs, source!, network.id); - if (!valid) { - throw new Error(error || 'Invalid subgraph network match'); - } - } catch (e) { - if (e instanceof Error) { - print.error(`Failed to validate subgraph network: ${e?.message}`); - } - throw e; - } - } - return { abi: (abiFromApi || abiFromFile)!, protocolInstance, @@ -1207,10 +1199,19 @@ async function initSubgraphFromContract( }, }); + // Validate network match first + const { valid, error } = await validateSubgraphNetworkMatch(ipfsClient, source, network); + if (!valid) { + throw new Error(error || 'Invalid subgraph network match'); + } + const schemaString = await loadSubgraphSchemaFromIPFS(ipfsClient, source); const schema = await Schema.loadFromString(schemaString); entities = schema.getEntityNames(); } catch (e) { + if (e instanceof Error) { + print.error(`Failed to validate subgraph: ${e?.message}`); + } this.error(`Failed to load and parse subgraph schema: ${e.message}`, { exit: 1 }); } } diff --git a/packages/cli/src/utils.ts b/packages/cli/src/utils.ts index 4dcbb9d42..5dedbc683 100644 --- a/packages/cli/src/utils.ts +++ b/packages/cli/src/utils.ts @@ -75,14 +75,8 @@ export async function validateSubgraphNetworkMatch( // Get network from first data source const sourceNetwork = allSources[0].network; - if (!sourceNetwork) { - return { valid: true }; // Network not specified in source, skip validation - } - - const normalizedSourceNetwork = sourceNetwork.toLowerCase(); - const normalizedTargetNetwork = targetNetwork.toLowerCase(); - if (normalizedSourceNetwork !== normalizedTargetNetwork) { + if (sourceNetwork !== targetNetwork) { return { valid: false, error: `Network mismatch: The source subgraph is indexing the '${sourceNetwork}' network, but you're creating a subgraph for '${targetNetwork}' network. When composing subgraphs, they must index the same network.`, From ce245482a6f0325be1c441e7579b010907fea2fc Mon Sep 17 00:00:00 2001 From: incrypto32 Date: Mon, 3 Feb 2025 14:23:26 +0400 Subject: [PATCH 08/21] Dont allow adding new contracts when subgraph is a composed subgraph --- packages/cli/src/commands/init.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 66b3cdf09..5b2ea9430 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -1296,7 +1296,7 @@ async function initSubgraphFromContract( this.exit(1); } - while (addContract) { + while (addContract && !isComposedSubgraph) { addContract = await addAnotherContract .bind(this)({ protocolInstance, From b7b6bbf9927bb1319a4c774478995aa4139a6cc6 Mon Sep 17 00:00:00 2001 From: incrypto32 Date: Tue, 4 Feb 2025 11:17:30 +0400 Subject: [PATCH 09/21] Allow init of subgraph datasource subgraphs without the interactive mode --- packages/cli/src/commands/init.ts | 52 ++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 5b2ea9430..099f7029a 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -58,6 +58,10 @@ export default class InitCommand extends Command { summary: 'Graph node for which to initialize.', char: 'g', }), + 'from-source-subgraph': Flags.string({ + description: 'Creates a scaffold based on an existing contract.', + exclusive: ['from-example', 'from-contract'], + }), 'from-contract': Flags.string({ description: 'Creates a scaffold based on an existing contract.', exclusive: ['from-example'], @@ -92,7 +96,6 @@ export default class InitCommand extends Command { description: 'Block number to start indexing from.', // TODO: using a default sets the value and therefore requires --from-contract // default: '0', - dependsOn: ['from-contract'], }), abi: Flags.string({ @@ -128,6 +131,7 @@ export default class InitCommand extends Command { protocol, node: nodeFlag, 'from-contract': fromContract, + 'from-source-subgraph': fromSourceSubgraph, 'contract-name': contractName, 'from-example': fromExample, 'index-events': indexEvents, @@ -142,11 +146,16 @@ export default class InitCommand extends Command { initDebugger('Flags: %O', flags); + if (startBlock && !(fromContract || fromSourceSubgraph)) { + this.error('--start-block can only be used with --from-contract or --from-source-subgraph'); + } + if (skipGit) { this.warn( 'The --skip-git flag will be removed in the next major version. By default we will stop initializing a Git repository.', ); } + if ((fromContract || spkgPath) && !network && !fromExample) { this.error('--network is required when using --from-contract or --spkg'); } @@ -263,6 +272,47 @@ export default class InitCommand extends Command { return this.exit(0); } + // If all parameters are provided from the command-line, + // go straight to creating the subgraph from provided source subgraph + if (fromSourceSubgraph && protocol && subgraphName && directory && network && node) { + if (!protocolChoices.includes(protocol as ProtocolName)) { + this.error( + `Protocol '${protocol}' is not supported, choose from these options: ${protocolChoices.join( + ', ', + )}`, + { exit: 1 }, + ); + } + + if (protocol != 'subgraph') { + this.error('--protocol must be subgraph when using --from-source-subgraph'); + } + + const protocolInstance = new Protocol(protocol as ProtocolName); + + await initSubgraphFromContract.bind(this)( + { + protocolInstance, + abi, + directory, + source: fromSourceSubgraph!, + indexEvents, + network, + subgraphName, + contractName: contractName || DEFAULT_CONTRACT_NAME, + node, + startBlock, + spkgPath, + skipInstall, + skipGit, + ipfsUrl: ipfs, + }, + { commands, addContract: false }, + ); + // Exit with success + return this.exit(0); + } + if (fromExample) { const answers = await processFromExampleInitForm.bind(this)({ subgraphName, From 2ae4c2e5da110d1bb7d7f6bf59cd971e1d3a7781 Mon Sep 17 00:00:00 2001 From: incrypto32 Date: Tue, 4 Feb 2025 11:22:15 +0400 Subject: [PATCH 10/21] Reduce code duplication between subgraph datasource and normal data source --- packages/cli/src/commands/init.ts | 77 ++++++++++--------------------- 1 file changed, 24 insertions(+), 53 deletions(-) diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 099f7029a..174596d98 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -209,16 +209,15 @@ export default class InitCommand extends Command { let abi!: EthereumABI; // If all parameters are provided from the command-line, - // go straight to creating the subgraph from an existing contract - if ((fromContract || spkgPath) && protocol && subgraphName && directory && network && node) { - const registry = await loadRegistry(); - const contractService = new ContractService(registry); - const sourcifyContractInfo = await contractService.getFromSourcify( - EthereumABI, - network, - fromContract!, - ); - + // go straight to creating the subgraph from an existing contract or source subgraph + if ( + (fromContract || spkgPath || fromSourceSubgraph) && + protocol && + subgraphName && + directory && + network && + node + ) { if (!protocolChoices.includes(protocol as ProtocolName)) { this.error( `Protocol '${protocol}' is not supported, choose from these options: ${protocolChoices.join( @@ -228,9 +227,22 @@ export default class InitCommand extends Command { ); } + if (fromSourceSubgraph && protocol !== 'subgraph') { + this.error('--protocol must be subgraph when using --from-source-subgraph'); + } + const protocolInstance = new Protocol(protocol as ProtocolName); - if (protocolInstance.hasABIs()) { + // Only fetch contract info and ABI for non-source-subgraph cases + if (!fromSourceSubgraph && protocolInstance.hasABIs()) { + const registry = await loadRegistry(); + const contractService = new ContractService(registry); + const sourcifyContractInfo = await contractService.getFromSourcify( + EthereumABI, + network, + fromContract!, + ); + const ABI = protocolInstance.getABI(); if (abiPath) { try { @@ -254,48 +266,7 @@ export default class InitCommand extends Command { protocolInstance, abi, directory, - source: fromContract!, - indexEvents, - network, - subgraphName, - contractName: contractName || DEFAULT_CONTRACT_NAME, - node, - startBlock, - spkgPath, - skipInstall, - skipGit, - ipfsUrl: ipfs, - }, - { commands, addContract: false }, - ); - // Exit with success - return this.exit(0); - } - - // If all parameters are provided from the command-line, - // go straight to creating the subgraph from provided source subgraph - if (fromSourceSubgraph && protocol && subgraphName && directory && network && node) { - if (!protocolChoices.includes(protocol as ProtocolName)) { - this.error( - `Protocol '${protocol}' is not supported, choose from these options: ${protocolChoices.join( - ', ', - )}`, - { exit: 1 }, - ); - } - - if (protocol != 'subgraph') { - this.error('--protocol must be subgraph when using --from-source-subgraph'); - } - - const protocolInstance = new Protocol(protocol as ProtocolName); - - await initSubgraphFromContract.bind(this)( - { - protocolInstance, - abi, - directory, - source: fromSourceSubgraph!, + source: fromSourceSubgraph || fromContract!, indexEvents, network, subgraphName, From 9ed22cf96b4ad109d52b3b00e6d6783ca0809471 Mon Sep 17 00:00:00 2001 From: incrypto32 Date: Tue, 4 Feb 2025 11:23:33 +0400 Subject: [PATCH 11/21] prevent using --from-contract and --from-source-subgraph flags together --- packages/cli/src/commands/init.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 174596d98..43d43fff9 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -150,6 +150,10 @@ export default class InitCommand extends Command { this.error('--start-block can only be used with --from-contract or --from-source-subgraph'); } + if (fromContract && fromSourceSubgraph) { + this.error('Cannot use both --from-contract and --from-source-subgraph at the same time'); + } + if (skipGit) { this.warn( 'The --skip-git flag will be removed in the next major version. By default we will stop initializing a Git repository.', From 7683939381c6f3f5fddf2739cc76fa56a2e90317 Mon Sep 17 00:00:00 2001 From: incrypto32 Date: Tue, 4 Feb 2025 11:32:18 +0400 Subject: [PATCH 12/21] cli: validate protocol and source subgraph relationship --- packages/cli/src/commands/codegen.ts | 1 - packages/cli/src/commands/init.ts | 9 ++++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/commands/codegen.ts b/packages/cli/src/commands/codegen.ts index fe76b985d..75bbbe5d6 100644 --- a/packages/cli/src/commands/codegen.ts +++ b/packages/cli/src/commands/codegen.ts @@ -43,7 +43,6 @@ export default class CodegenCommand extends Command { summary: 'IPFS node to use for fetching subgraph data.', char: 'i', default: DEFAULT_IPFS_URL, - hidden: true, }), 'uncrashable-config': Flags.file({ summary: 'Directory for uncrashable config.', diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 43d43fff9..d32aa081b 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -117,7 +117,6 @@ export default class InitCommand extends Command { summary: 'IPFS node to use for fetching subgraph data.', char: 'i', default: DEFAULT_IPFS_URL, - hidden: true, }), }; @@ -237,6 +236,14 @@ export default class InitCommand extends Command { const protocolInstance = new Protocol(protocol as ProtocolName); + if (!fromSourceSubgraph && protocolInstance.isComposedSubgraph()) { + this.error('--protocol can only be subgraph when using --from-source-subgraph'); + } + + if (fromSourceSubgraph && !protocolInstance.isComposedSubgraph()) { + this.error('--protocol can only be subgraph when using --from-source-subgraph'); + } + // Only fetch contract info and ABI for non-source-subgraph cases if (!fromSourceSubgraph && protocolInstance.hasABIs()) { const registry = await loadRegistry(); From 40dbe072a6eeb5bb89c6db14fc7897b3386e1363 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 4 Feb 2025 07:32:48 +0000 Subject: [PATCH 13/21] chore(dependencies): updated changesets for modified dependencies --- .changeset/@graphprotocol_graph-cli-1920-dependencies.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/@graphprotocol_graph-cli-1920-dependencies.md diff --git a/.changeset/@graphprotocol_graph-cli-1920-dependencies.md b/.changeset/@graphprotocol_graph-cli-1920-dependencies.md new file mode 100644 index 000000000..5f845092a --- /dev/null +++ b/.changeset/@graphprotocol_graph-cli-1920-dependencies.md @@ -0,0 +1,6 @@ +--- +"@graphprotocol/graph-cli": patch +--- +dependencies updates: + - Updated dependency [`@oclif/core@4.2.6` ↗︎](https://www.npmjs.com/package/@oclif/core/v/4.2.6) (from `4.2.4`, in `dependencies`) + - Updated dependency [`semver@7.7.1` ↗︎](https://www.npmjs.com/package/semver/v/7.7.1) (from `7.6.3`, in `dependencies`) From ff2430ca2da8024b1ad996e5e6966466d68f2652 Mon Sep 17 00:00:00 2001 From: incrypto32 Date: Thu, 6 Feb 2025 09:31:57 +0400 Subject: [PATCH 14/21] change flag name for source subgraph --- packages/cli/src/commands/init.ts | 32 +++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index d32aa081b..8328a20ae 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -58,8 +58,8 @@ export default class InitCommand extends Command { summary: 'Graph node for which to initialize.', char: 'g', }), - 'from-source-subgraph': Flags.string({ - description: 'Creates a scaffold based on an existing contract.', + 'from-subgraph': Flags.string({ + description: 'Creates a scaffold based on an existing subgraph.', exclusive: ['from-example', 'from-contract'], }), 'from-contract': Flags.string({ @@ -130,7 +130,7 @@ export default class InitCommand extends Command { protocol, node: nodeFlag, 'from-contract': fromContract, - 'from-source-subgraph': fromSourceSubgraph, + 'from-subgraph': fromSubgraph, 'contract-name': contractName, 'from-example': fromExample, 'index-events': indexEvents, @@ -145,12 +145,12 @@ export default class InitCommand extends Command { initDebugger('Flags: %O', flags); - if (startBlock && !(fromContract || fromSourceSubgraph)) { - this.error('--start-block can only be used with --from-contract or --from-source-subgraph'); + if (startBlock && !(fromContract || fromSubgraph)) { + this.error('--start-block can only be used with --from-contract or --from-subgraph'); } - if (fromContract && fromSourceSubgraph) { - this.error('Cannot use both --from-contract and --from-source-subgraph at the same time'); + if (fromContract && fromSubgraph) { + this.error('Cannot use both --from-contract and --from-subgraph at the same time'); } if (skipGit) { @@ -214,7 +214,7 @@ export default class InitCommand extends Command { // If all parameters are provided from the command-line, // go straight to creating the subgraph from an existing contract or source subgraph if ( - (fromContract || spkgPath || fromSourceSubgraph) && + (fromContract || spkgPath || fromSubgraph) && protocol && subgraphName && directory && @@ -230,22 +230,22 @@ export default class InitCommand extends Command { ); } - if (fromSourceSubgraph && protocol !== 'subgraph') { - this.error('--protocol must be subgraph when using --from-source-subgraph'); + if (fromSubgraph && protocol !== 'subgraph') { + this.error('--protocol must be subgraph when using --from-subgraph'); } const protocolInstance = new Protocol(protocol as ProtocolName); - if (!fromSourceSubgraph && protocolInstance.isComposedSubgraph()) { - this.error('--protocol can only be subgraph when using --from-source-subgraph'); + if (!fromSubgraph && protocolInstance.isComposedSubgraph()) { + this.error('--protocol can only be subgraph when using --from-subgraph'); } - if (fromSourceSubgraph && !protocolInstance.isComposedSubgraph()) { - this.error('--protocol can only be subgraph when using --from-source-subgraph'); + if (fromSubgraph && !protocolInstance.isComposedSubgraph()) { + this.error('--protocol can only be subgraph when using --from-subgraph'); } // Only fetch contract info and ABI for non-source-subgraph cases - if (!fromSourceSubgraph && protocolInstance.hasABIs()) { + if (!fromSubgraph && protocolInstance.hasABIs()) { const registry = await loadRegistry(); const contractService = new ContractService(registry); const sourcifyContractInfo = await contractService.getFromSourcify( @@ -277,7 +277,7 @@ export default class InitCommand extends Command { protocolInstance, abi, directory, - source: fromSourceSubgraph || fromContract!, + source: fromSubgraph || fromContract!, indexEvents, network, subgraphName, From 08cfdd13c907d35337e75c0c58f3233a0797c497 Mon Sep 17 00:00:00 2001 From: incrypto32 Date: Thu, 6 Feb 2025 10:02:19 +0400 Subject: [PATCH 15/21] Refactor manifest validation util functions --- packages/cli/src/commands/init.ts | 7 +++-- packages/cli/src/utils.ts | 52 +++++++++++++------------------ 2 files changed, 27 insertions(+), 32 deletions(-) diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 8328a20ae..298fec07e 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -25,6 +25,7 @@ import { abiEvents } from '../scaffold/schema.js'; import Schema from '../schema.js'; import { createIpfsClient, + loadManifestYaml, loadSubgraphSchemaFromIPFS, validateSubgraphNetworkMatch, } from '../utils.js'; @@ -659,7 +660,8 @@ async function processInitForm( validate: async (value: string): Promise => { if (isComposedSubgraph) { const ipfs = createIpfsClient(ipfsNode); - const { valid, error } = await validateSubgraphNetworkMatch(ipfs, value, network.id); + const manifestYaml = await loadManifestYaml(ipfs, value); + const { valid, error } = validateSubgraphNetworkMatch(manifestYaml, network.id); if (!valid) { return error || 'Invalid subgraph network match'; } @@ -1232,7 +1234,8 @@ async function initSubgraphFromContract( }); // Validate network match first - const { valid, error } = await validateSubgraphNetworkMatch(ipfsClient, source, network); + const manifestYaml = await loadManifestYaml(ipfsClient, source); + const { valid, error } = validateSubgraphNetworkMatch(manifestYaml, network); if (!valid) { throw new Error(error || 'Invalid subgraph network match'); } diff --git a/packages/cli/src/utils.ts b/packages/cli/src/utils.ts index 5dedbc683..bff9d4202 100644 --- a/packages/cli/src/utils.ts +++ b/packages/cli/src/utils.ts @@ -48,47 +48,39 @@ export async function loadManifestFromIPFS(ipfsClient: any, manifest: string) { } } +export async function loadManifestYaml(ipfsClient: any, manifest: string): Promise { + const manifestFile = await loadManifestFromIPFS(ipfsClient, manifest); + return yaml.load(manifestFile) as any; +} + /** * Validates that the network of a source subgraph matches the target network - * @param ipfsClient IPFS client instance - * @param sourceSubgraphId IPFS hash of the source subgraph + * @param manifestYaml Parsed manifest YAML * @param targetNetwork Network of the target subgraph being created * @returns Object containing validation result and error message if any */ -export async function validateSubgraphNetworkMatch( - ipfsClient: any, - sourceSubgraphId: string, +export function validateSubgraphNetworkMatch( + manifestYaml: any, targetNetwork: string, -): Promise<{ valid: boolean; error?: string }> { - try { - const manifestFile = await loadManifestFromIPFS(ipfsClient, sourceSubgraphId); - const manifestYaml = yaml.load(manifestFile) as any; - - // Extract network from data sources - const dataSources = manifestYaml.dataSources || []; - const templates = manifestYaml.templates || []; - const allSources = [...dataSources, ...templates]; - - if (allSources.length === 0) { - return { valid: true }; // No data sources to validate - } +): { valid: boolean; error?: string } { + // Extract network from data sources + const dataSources = manifestYaml.dataSources || []; + const templates = manifestYaml.templates || []; + const allSources = [...dataSources, ...templates]; - // Get network from first data source - const sourceNetwork = allSources[0].network; + if (allSources.length === 0) { + return { valid: true }; // No data sources to validate + } - if (sourceNetwork !== targetNetwork) { - return { - valid: false, - error: `Network mismatch: The source subgraph is indexing the '${sourceNetwork}' network, but you're creating a subgraph for '${targetNetwork}' network. When composing subgraphs, they must index the same network.`, - }; - } + // Get network from first data source + const sourceNetwork = allSources[0].network; - return { valid: true }; - } catch (e) { - utilsDebug.extend('validateSubgraphNetworkMatch')(`Failed to validate network match: ${e}`); + if (sourceNetwork !== targetNetwork) { return { valid: false, - error: e instanceof Error ? e.message : 'Failed to validate subgraph network', + error: `Network mismatch: The source subgraph is indexing the '${sourceNetwork}' network, but you're creating a subgraph for '${targetNetwork}' network. When composing subgraphs, they must index the same network.`, }; } + + return { valid: true }; } From 808d02f80ed7c9f86d2fd637b8cc6f8b6b650ba6 Mon Sep 17 00:00:00 2001 From: incrypto32 Date: Thu, 6 Feb 2025 10:19:36 +0400 Subject: [PATCH 16/21] get start block from source manifest --- packages/cli/src/commands/init.ts | 4 ++++ packages/cli/src/utils.ts | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 298fec07e..54ef46aa8 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -25,6 +25,7 @@ import { abiEvents } from '../scaffold/schema.js'; import Schema from '../schema.js'; import { createIpfsClient, + getMinStartBlock, loadManifestYaml, loadSubgraphSchemaFromIPFS, validateSubgraphNetworkMatch, @@ -665,6 +666,7 @@ async function processInitForm( if (!valid) { return error || 'Invalid subgraph network match'; } + startBlock ||= getMinStartBlock(manifestYaml)?.toString(); return true; } if (initFromExample !== undefined || !protocolInstance.hasContract()) { @@ -715,6 +717,7 @@ async function processInitForm( } else { abiFromApi = initAbi; } + // If startBlock is not provided, try to fetch it from Etherscan API if (!initStartBlock) { startBlock = await retryWithPrompt(() => @@ -1240,6 +1243,7 @@ async function initSubgraphFromContract( throw new Error(error || 'Invalid subgraph network match'); } + startBlock ||= getMinStartBlock(manifestYaml)?.toString(); const schemaString = await loadSubgraphSchemaFromIPFS(ipfsClient, source); const schema = await Schema.loadFromString(schemaString); entities = schema.getEntityNames(); diff --git a/packages/cli/src/utils.ts b/packages/cli/src/utils.ts index bff9d4202..39d3689b9 100644 --- a/packages/cli/src/utils.ts +++ b/packages/cli/src/utils.ts @@ -84,3 +84,18 @@ export function validateSubgraphNetworkMatch( return { valid: true }; } + +/** + * Gets the minimum startBlock from all dataSources in the manifest + * @param manifestYaml Parsed manifest YAML + * @returns The minimum startBlock or undefined if no startBlock is found + */ +export function getMinStartBlock(manifestYaml: any): number | undefined { + const dataSources = manifestYaml.dataSources || []; + + const startBlocks = dataSources + .map((ds: { source?: { startBlock?: number } }) => ds.source?.startBlock) + .filter((block: unknown): block is number => typeof block === 'number'); + + return startBlocks.length > 0 ? Math.min(...startBlocks) : undefined; +} From e5ea2bdb011e3829f4609ce1d97b4fd0e1729ed9 Mon Sep 17 00:00:00 2001 From: incrypto32 Date: Thu, 6 Feb 2025 10:28:30 +0400 Subject: [PATCH 17/21] set fromSubgraph to be default value for graph init in interactive mode --- packages/cli/src/commands/init.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 54ef46aa8..814699143 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -232,12 +232,12 @@ export default class InitCommand extends Command { ); } - if (fromSubgraph && protocol !== 'subgraph') { + const protocolInstance = new Protocol(protocol as ProtocolName); + + if (fromSubgraph && protocolInstance.isComposedSubgraph()) { this.error('--protocol must be subgraph when using --from-subgraph'); } - const protocolInstance = new Protocol(protocol as ProtocolName); - if (!fromSubgraph && protocolInstance.isComposedSubgraph()) { this.error('--protocol can only be subgraph when using --from-subgraph'); } @@ -246,6 +246,10 @@ export default class InitCommand extends Command { this.error('--protocol can only be subgraph when using --from-subgraph'); } + if (fromContract && protocolInstance.isComposedSubgraph()) { + this.error('--protocol can only be subgraph when using --from-subgraph'); + } + // Only fetch contract info and ABI for non-source-subgraph cases if (!fromSubgraph && protocolInstance.hasABIs()) { const registry = await loadRegistry(); @@ -323,7 +327,7 @@ export default class InitCommand extends Command { abi, abiPath, directory, - source: fromContract, + source: fromContract || fromSubgraph, indexEvents, fromExample, subgraphName, From 7e95705b61319361e5d4d986bcd571912475248b Mon Sep 17 00:00:00 2001 From: YaroShkvorets Date: Thu, 6 Feb 2025 17:06:25 -0500 Subject: [PATCH 18/21] fix protocol flag validation --- packages/cli/src/commands/init.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 814699143..581958a50 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -234,20 +234,19 @@ export default class InitCommand extends Command { const protocolInstance = new Protocol(protocol as ProtocolName); - if (fromSubgraph && protocolInstance.isComposedSubgraph()) { - this.error('--protocol must be subgraph when using --from-subgraph'); - } - - if (!fromSubgraph && protocolInstance.isComposedSubgraph()) { + if (fromSubgraph && !protocolInstance.isComposedSubgraph()) { this.error('--protocol can only be subgraph when using --from-subgraph'); } - if (fromSubgraph && !protocolInstance.isComposedSubgraph()) { - this.error('--protocol can only be subgraph when using --from-subgraph'); + if ( + fromContract && + (protocolInstance.isComposedSubgraph() || protocolInstance.isSubstreams()) + ) { + this.error('--protocol cannot be subgraph or substreams when using --from-contract'); } - if (fromContract && protocolInstance.isComposedSubgraph()) { - this.error('--protocol can only be subgraph when using --from-subgraph'); + if (spkgPath && !protocolInstance.isSubstreams()) { + this.error('--protocol can only be substreams when using --spkg'); } // Only fetch contract info and ABI for non-source-subgraph cases From 2d6a7a9b98c01ab1c50c2937cff2c4e65d188d5b Mon Sep 17 00:00:00 2001 From: incrypto32 Date: Mon, 10 Feb 2025 11:58:47 +0400 Subject: [PATCH 19/21] Add init test for subgraphs --- .../tests/cli/__snapshots__/init.test.ts.snap | 34 +++++++++++++++++++ packages/cli/tests/cli/init.test.ts | 27 +++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/packages/cli/tests/cli/__snapshots__/init.test.ts.snap b/packages/cli/tests/cli/__snapshots__/init.test.ts.snap index a9fb74ba3..cfdb20b85 100644 --- a/packages/cli/tests/cli/__snapshots__/init.test.ts.snap +++ b/packages/cli/tests/cli/__snapshots__/init.test.ts.snap @@ -250,6 +250,40 @@ Make sure to visit the documentation on https://thegraph.com/docs/ for further i " `; +exports[`Init > From existing subgraph > From existing subgraph 1`] = ` +" › Warning: The --skip-git flag will be removed in the next major version. By + › default we will stop initializing a Git repository. +- Create subgraph scaffold + Generate subgraph +- Create subgraph scaffold + Write subgraph to directory +- Create subgraph scaffold +✔ Create subgraph scaffold +- Install dependencies with yarn +✔ Install dependencies with yarn +- Generate ABI and schema types with yarn codegen +✔ Generate ABI and schema types with yarn codegen +" +`; + +exports[`Init > From existing subgraph > From existing subgraph 2`] = `0`; + +exports[`Init > From existing subgraph > From existing subgraph 3`] = ` +" +Subgraph user/from-existing-subgraph created in subgraph + +Next steps: + + 1. Run \`graph auth\` to authenticate with your deploy key. + + 2. Type \`cd subgraph\` to enter the subgraph. + + 3. Run \`yarn deploy\` to deploy the subgraph. + +Make sure to visit the documentation on https://thegraph.com/docs/ for further information. +" +`; + exports[`Init > NEAR > From contract 1`] = ` " › Warning: The --skip-git flag will be removed in the next major version. By › default we will stop initializing a Git repository. diff --git a/packages/cli/tests/cli/init.test.ts b/packages/cli/tests/cli/init.test.ts index 392c0af66..ddda55612 100644 --- a/packages/cli/tests/cli/init.test.ts +++ b/packages/cli/tests/cli/init.test.ts @@ -241,5 +241,32 @@ describe( }, ); }); + + describe('From existing subgraph', () => { + const fromSubgraphBaseDir = path.join(baseDir, 'subgraph'); + + cliTest( + 'From existing subgraph', + [ + 'init', + '--skip-git', + '--from-subgraph', + 'QmSgvtjK6b5GmnSeboH9AMdVrK8YeVrmJ1ESHw3WhYKdDH', + '--network', + 'base', + '--protocol', + 'subgraph', + 'user/from-existing-subgraph', + path.join(fromSubgraphBaseDir, 'subgraph'), + ], + path.join('init', 'subgraph', 'subgraph'), + { + exitCode: 0, + timeout: 100_000, + cwd: fromSubgraphBaseDir, + deleteDir: true, + }, + ); + }); }, ); From dbaea17ad26cadc1adaaff578e4fcdf588367424 Mon Sep 17 00:00:00 2001 From: incrypto32 Date: Mon, 10 Feb 2025 12:01:25 +0400 Subject: [PATCH 20/21] Fix error message --- packages/cli/src/commands/init.ts | 3 --- packages/cli/tests/cli/init/subgraph/.gitkeep | 0 2 files changed, 3 deletions(-) create mode 100644 packages/cli/tests/cli/init/subgraph/.gitkeep diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 581958a50..6cb7bc5a3 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -1251,9 +1251,6 @@ async function initSubgraphFromContract( const schema = await Schema.loadFromString(schemaString); entities = schema.getEntityNames(); } catch (e) { - if (e instanceof Error) { - print.error(`Failed to validate subgraph: ${e?.message}`); - } this.error(`Failed to load and parse subgraph schema: ${e.message}`, { exit: 1 }); } } diff --git a/packages/cli/tests/cli/init/subgraph/.gitkeep b/packages/cli/tests/cli/init/subgraph/.gitkeep new file mode 100644 index 000000000..e69de29bb From 36cf7e3cdfff540411e03a7d6162f5bdf151caca Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 10 Feb 2025 08:28:24 +0000 Subject: [PATCH 21/21] chore(dependencies): updated changesets for modified dependencies --- .changeset/@graphprotocol_graph-cli-1920-dependencies.md | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .changeset/@graphprotocol_graph-cli-1920-dependencies.md diff --git a/.changeset/@graphprotocol_graph-cli-1920-dependencies.md b/.changeset/@graphprotocol_graph-cli-1920-dependencies.md deleted file mode 100644 index 5f845092a..000000000 --- a/.changeset/@graphprotocol_graph-cli-1920-dependencies.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@graphprotocol/graph-cli": patch ---- -dependencies updates: - - Updated dependency [`@oclif/core@4.2.6` ↗︎](https://www.npmjs.com/package/@oclif/core/v/4.2.6) (from `4.2.4`, in `dependencies`) - - Updated dependency [`semver@7.7.1` ↗︎](https://www.npmjs.com/package/semver/v/7.7.1) (from `7.6.3`, in `dependencies`)