@@ -23,7 +23,13 @@ import EthereumABI from '../protocols/ethereum/abi.js';
2323import Protocol , { ProtocolName } from '../protocols/index.js' ;
2424import { abiEvents } from '../scaffold/schema.js' ;
2525import Schema from '../schema.js' ;
26- import { createIpfsClient , loadSubgraphSchemaFromIPFS } from '../utils.js' ;
26+ import {
27+ createIpfsClient ,
28+ getMinStartBlock ,
29+ loadManifestYaml ,
30+ loadSubgraphSchemaFromIPFS ,
31+ validateSubgraphNetworkMatch ,
32+ } from '../utils.js' ;
2733import { validateContract } from '../validation/index.js' ;
2834import AddCommand from './add.js' ;
2935
@@ -54,6 +60,10 @@ export default class InitCommand extends Command {
5460 summary : 'Graph node for which to initialize.' ,
5561 char : 'g' ,
5662 } ) ,
63+ 'from-subgraph' : Flags . string ( {
64+ description : 'Creates a scaffold based on an existing subgraph.' ,
65+ exclusive : [ 'from-example' , 'from-contract' ] ,
66+ } ) ,
5767 'from-contract' : Flags . string ( {
5868 description : 'Creates a scaffold based on an existing contract.' ,
5969 exclusive : [ 'from-example' ] ,
@@ -88,7 +98,6 @@ export default class InitCommand extends Command {
8898 description : 'Block number to start indexing from.' ,
8999 // TODO: using a default sets the value and therefore requires --from-contract
90100 // default: '0',
91- dependsOn : [ 'from-contract' ] ,
92101 } ) ,
93102
94103 abi : Flags . string ( {
@@ -110,7 +119,6 @@ export default class InitCommand extends Command {
110119 summary : 'IPFS node to use for fetching subgraph data.' ,
111120 char : 'i' ,
112121 default : DEFAULT_IPFS_URL ,
113- hidden : true ,
114122 } ) ,
115123 } ;
116124
@@ -127,6 +135,7 @@ export default class InitCommand extends Command {
127135 protocol,
128136 node : nodeFlag ,
129137 'from-contract' : fromContract ,
138+ 'from-subgraph' : fromSubgraph ,
130139 'contract-name' : contractName ,
131140 'from-example' : fromExample ,
132141 'index-events' : indexEvents ,
@@ -141,11 +150,20 @@ export default class InitCommand extends Command {
141150
142151 initDebugger ( 'Flags: %O' , flags ) ;
143152
153+ if ( startBlock && ! ( fromContract || fromSubgraph ) ) {
154+ this . error ( '--start-block can only be used with --from-contract or --from-subgraph' ) ;
155+ }
156+
157+ if ( fromContract && fromSubgraph ) {
158+ this . error ( 'Cannot use both --from-contract and --from-subgraph at the same time' ) ;
159+ }
160+
144161 if ( skipGit ) {
145162 this . warn (
146163 'The --skip-git flag will be removed in the next major version. By default we will stop initializing a Git repository.' ,
147164 ) ;
148165 }
166+
149167 if ( ( fromContract || spkgPath ) && ! network && ! fromExample ) {
150168 this . error ( '--network is required when using --from-contract or --spkg' ) ;
151169 }
@@ -199,16 +217,15 @@ export default class InitCommand extends Command {
199217 let abi ! : EthereumABI ;
200218
201219 // If all parameters are provided from the command-line,
202- // go straight to creating the subgraph from an existing contract
203- if ( ( fromContract || spkgPath ) && protocol && subgraphName && directory && network && node ) {
204- const registry = await loadRegistry ( ) ;
205- const contractService = new ContractService ( registry ) ;
206- const sourcifyContractInfo = await contractService . getFromSourcify (
207- EthereumABI ,
208- network ,
209- fromContract ! ,
210- ) ;
211-
220+ // go straight to creating the subgraph from an existing contract or source subgraph
221+ if (
222+ ( fromContract || spkgPath || fromSubgraph ) &&
223+ protocol &&
224+ subgraphName &&
225+ directory &&
226+ network &&
227+ node
228+ ) {
212229 if ( ! protocolChoices . includes ( protocol as ProtocolName ) ) {
213230 this . error (
214231 `Protocol '${ protocol } ' is not supported, choose from these options: ${ protocolChoices . join (
@@ -220,7 +237,31 @@ export default class InitCommand extends Command {
220237
221238 const protocolInstance = new Protocol ( protocol as ProtocolName ) ;
222239
223- if ( protocolInstance . hasABIs ( ) ) {
240+ if ( fromSubgraph && ! protocolInstance . isComposedSubgraph ( ) ) {
241+ this . error ( '--protocol can only be subgraph when using --from-subgraph' ) ;
242+ }
243+
244+ if (
245+ fromContract &&
246+ ( protocolInstance . isComposedSubgraph ( ) || protocolInstance . isSubstreams ( ) )
247+ ) {
248+ this . error ( '--protocol cannot be subgraph or substreams when using --from-contract' ) ;
249+ }
250+
251+ if ( spkgPath && ! protocolInstance . isSubstreams ( ) ) {
252+ this . error ( '--protocol can only be substreams when using --spkg' ) ;
253+ }
254+
255+ // Only fetch contract info and ABI for non-source-subgraph cases
256+ if ( ! fromSubgraph && protocolInstance . hasABIs ( ) ) {
257+ const registry = await loadRegistry ( ) ;
258+ const contractService = new ContractService ( registry ) ;
259+ const sourcifyContractInfo = await contractService . getFromSourcify (
260+ EthereumABI ,
261+ network ,
262+ fromContract ! ,
263+ ) ;
264+
224265 const ABI = protocolInstance . getABI ( ) ;
225266 if ( abiPath ) {
226267 try {
@@ -244,7 +285,7 @@ export default class InitCommand extends Command {
244285 protocolInstance,
245286 abi,
246287 directory,
247- source : fromContract ! ,
288+ source : fromSubgraph || fromContract ! ,
248289 indexEvents,
249290 network,
250291 subgraphName,
@@ -288,7 +329,7 @@ export default class InitCommand extends Command {
288329 abi,
289330 abiPath,
290331 directory,
291- source : fromContract ,
332+ source : fromContract || fromSubgraph ,
292333 indexEvents,
293334 fromExample,
294335 subgraphName,
@@ -534,7 +575,7 @@ async function processInitForm(
534575 value : 'contract' ,
535576 } ,
536577 { message : 'Substreams' , name : 'substreams' , value : 'substreams' } ,
537- // { message: 'Subgraph', name: 'subgraph', value: 'subgraph' },
578+ { message : 'Subgraph' , name : 'subgraph' , value : 'subgraph' } ,
538579 ] . filter ( ( { name } ) => name ) ,
539580 } ) ;
540581
@@ -604,6 +645,30 @@ async function processInitForm(
604645 } ,
605646 } ) ;
606647
648+ promptManager . addStep ( {
649+ type : 'input' ,
650+ name : 'ipfs' ,
651+ message : `IPFS node to use for fetching subgraph manifest` ,
652+ initial : ipfsUrl ,
653+ skip : ( ) => ! isComposedSubgraph ,
654+ validate : value => {
655+ if ( ! value ) {
656+ return 'IPFS node URL cannot be empty' ;
657+ }
658+ try {
659+ new URL ( value ) ;
660+ return true ;
661+ } catch {
662+ return 'Please enter a valid URL' ;
663+ }
664+ } ,
665+ result : value => {
666+ ipfsNode = value ;
667+ initDebugger . extend ( 'processInitForm' ) ( 'ipfs: %O' , value ) ;
668+ return value ;
669+ } ,
670+ } ) ;
671+
607672 promptManager . addStep ( {
608673 type : 'input' ,
609674 name : 'source' ,
@@ -616,9 +681,16 @@ async function processInitForm(
616681 isSubstreams ||
617682 ( ! protocolInstance . hasContract ( ) && ! isComposedSubgraph ) ,
618683 initial : initContract ,
619- validate : async ( value : string ) => {
684+ validate : async ( value : string ) : Promise < string | boolean > => {
620685 if ( isComposedSubgraph ) {
621- return value . startsWith ( 'Qm' ) ? true : 'Subgraph deployment ID must start with Qm' ;
686+ const ipfs = createIpfsClient ( ipfsNode ) ;
687+ const manifestYaml = await loadManifestYaml ( ipfs , value ) ;
688+ const { valid, error } = validateSubgraphNetworkMatch ( manifestYaml , network . id ) ;
689+ if ( ! valid ) {
690+ return error || 'Invalid subgraph network match' ;
691+ }
692+ startBlock ||= getMinStartBlock ( manifestYaml ) ?. toString ( ) ;
693+ return true ;
622694 }
623695 if ( initFromExample !== undefined || ! protocolInstance . hasContract ( ) ) {
624696 return true ;
@@ -668,6 +740,7 @@ async function processInitForm(
668740 } else {
669741 abiFromApi = initAbi ;
670742 }
743+
671744 // If startBlock is not provided, try to fetch it from Etherscan API
672745 if ( ! initStartBlock ) {
673746 startBlock = await retryWithPrompt ( ( ) =>
@@ -699,19 +772,6 @@ async function processInitForm(
699772 } ,
700773 } ) ;
701774
702- promptManager . addStep ( {
703- type : 'input' ,
704- name : 'ipfs' ,
705- message : `IPFS node to use for fetching subgraph manifest` ,
706- initial : ipfsUrl ,
707- skip : ( ) => ! isComposedSubgraph ,
708- result : value => {
709- ipfsNode = value ;
710- initDebugger . extend ( 'processInitForm' ) ( 'ipfs: %O' , value ) ;
711- return value ;
712- } ,
713- } ) ;
714-
715775 promptManager . addStep ( {
716776 type : 'input' ,
717777 name : 'spkg' ,
@@ -751,7 +811,7 @@ async function processInitForm(
751811 isSubstreams ||
752812 ! ! initAbiPath ||
753813 isComposedSubgraph ,
754- validate : async ( value : string ) => {
814+ validate : async ( value : string ) : Promise < string | boolean > => {
755815 if (
756816 initFromExample ||
757817 abiFromApi ||
@@ -1199,6 +1259,14 @@ async function initSubgraphFromContract(
11991259 } ,
12001260 } ) ;
12011261
1262+ // Validate network match first
1263+ const manifestYaml = await loadManifestYaml ( ipfsClient , source ) ;
1264+ const { valid, error } = validateSubgraphNetworkMatch ( manifestYaml , network ) ;
1265+ if ( ! valid ) {
1266+ throw new Error ( error || 'Invalid subgraph network match' ) ;
1267+ }
1268+
1269+ startBlock ||= getMinStartBlock ( manifestYaml ) ?. toString ( ) ;
12021270 const schemaString = await loadSubgraphSchemaFromIPFS ( ipfsClient , source ) ;
12031271 const schema = await Schema . loadFromString ( schemaString ) ;
12041272 entities = schema . getEntityNames ( ) ;
@@ -1208,8 +1276,9 @@ async function initSubgraphFromContract(
12081276 }
12091277
12101278 if (
1211- ! protocolInstance . isComposedSubgraph ( ) &&
1279+ ! isComposedSubgraph &&
12121280 protocolInstance . hasABIs ( ) &&
1281+ abi && // Add check for abi existence
12131282 ( abiEvents ( abi ) . size === 0 ||
12141283 // @ts -expect-error TODO: the abiEvents result is expected to be a List, how's it an array?
12151284 abiEvents ( abi ) . length === 0 )
@@ -1224,6 +1293,12 @@ async function initSubgraphFromContract(
12241293 `Failed to create subgraph scaffold` ,
12251294 `Warnings while creating subgraph scaffold` ,
12261295 async spinner => {
1296+ initDebugger ( 'Generating scaffold with ABI:' , abi ) ;
1297+ initDebugger ( 'ABI data:' , abi ?. data ) ;
1298+ if ( abi ) {
1299+ initDebugger ( 'ABI events:' , abiEvents ( abi ) ) ;
1300+ }
1301+
12271302 const scaffold = await generateScaffold (
12281303 {
12291304 protocolInstance,
@@ -1280,7 +1355,7 @@ async function initSubgraphFromContract(
12801355 this . exit ( 1 ) ;
12811356 }
12821357
1283- while ( addContract ) {
1358+ while ( addContract && ! isComposedSubgraph ) {
12841359 addContract = await addAnotherContract
12851360 . bind ( this ) ( {
12861361 protocolInstance,
0 commit comments