diff --git a/package.json b/package.json index 3690731..66f9579 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "0.7.97", + "version": "0.7.98", "private": true, "type": "module", "scripts": { diff --git a/registry/eip155/autonomys-chronos.json b/registry/eip155/autonomys-chronos.json index 4a3c420..bec101f 100644 --- a/registry/eip155/autonomys-chronos.json +++ b/registry/eip155/autonomys-chronos.json @@ -21,7 +21,7 @@ "docsUrl": "https://docs.autonomys.xyz", "firehose": { "blockType": "sf.ethereum.type.v2.Block", - "evmExtendedModel": true, + "evmExtendedModel": false, "bufUrl": "https://buf.build/streamingfast/firehose-ethereum", "bytesEncoding": "hex", "firstStreamableBlock": { diff --git a/registry/eip155/autonomys.json b/registry/eip155/autonomys.json index 0f52dbf..2f60795 100644 --- a/registry/eip155/autonomys.json +++ b/registry/eip155/autonomys.json @@ -20,7 +20,7 @@ "docsUrl": "https://docs.autonomys.xyz", "firehose": { "blockType": "sf.ethereum.type.v2.Block", - "evmExtendedModel": true, + "evmExtendedModel": false, "bufUrl": "https://buf.build/streamingfast/firehose-ethereum", "bytesEncoding": "hex", "firstStreamableBlock": { diff --git a/registry/eip155/berachain-bepolia.json b/registry/eip155/berachain-bepolia.json index 3c01f44..a48a2b7 100644 --- a/registry/eip155/berachain-bepolia.json +++ b/registry/eip155/berachain-bepolia.json @@ -26,7 +26,7 @@ "docsUrl": "https://docs.berachain.com", "firehose": { "blockType": "sf.ethereum.type.v2.Block", - "evmExtendedModel": true, + "evmExtendedModel": false, "bufUrl": "https://buf.build/streamingfast/firehose-ethereum", "bytesEncoding": "hex", "firstStreamableBlock": { diff --git a/registry/eip155/berachain.json b/registry/eip155/berachain.json index 41dd080..86c3594 100644 --- a/registry/eip155/berachain.json +++ b/registry/eip155/berachain.json @@ -21,7 +21,7 @@ "docsUrl": "https://docs.berachain.com", "firehose": { "blockType": "sf.ethereum.type.v2.Block", - "evmExtendedModel": true, + "evmExtendedModel": false, "bufUrl": "https://buf.build/streamingfast/firehose-ethereum", "bytesEncoding": "hex", "firstStreamableBlock": { diff --git a/registry/eip155/fraxtal.json b/registry/eip155/fraxtal.json index 805c967..550474e 100644 --- a/registry/eip155/fraxtal.json +++ b/registry/eip155/fraxtal.json @@ -22,7 +22,7 @@ "docsUrl": "https://docs.frax.com/fraxtal", "firehose": { "blockType": "sf.ethereum.type.v2.Block", - "evmExtendedModel": true, + "evmExtendedModel": false, "bufUrl": "https://buf.build/streamingfast/firehose-ethereum", "bytesEncoding": "hex", "firstStreamableBlock": { diff --git a/registry/eip155/gravity-mainnet.json b/registry/eip155/gravity-mainnet.json index 1a4f201..d338a45 100644 --- a/registry/eip155/gravity-mainnet.json +++ b/registry/eip155/gravity-mainnet.json @@ -19,7 +19,7 @@ "docsUrl": "https://docs.gravity.xyz", "firehose": { "blockType": "sf.ethereum.type.v2.Block", - "evmExtendedModel": true, + "evmExtendedModel": false, "bufUrl": "https://buf.build/streamingfast/firehose-ethereum", "bytesEncoding": "hex", "firstStreamableBlock": { diff --git a/registry/eip155/gravity-testnet.json b/registry/eip155/gravity-testnet.json index 4214150..0df099f 100644 --- a/registry/eip155/gravity-testnet.json +++ b/registry/eip155/gravity-testnet.json @@ -22,7 +22,7 @@ "docsUrl": "https://docs.gravity.xyz", "firehose": { "blockType": "sf.ethereum.type.v2.Block", - "evmExtendedModel": true, + "evmExtendedModel": false, "bufUrl": "https://buf.build/streamingfast/firehose-ethereum", "bytesEncoding": "hex", "firstStreamableBlock": { diff --git a/registry/eip155/ink-sepolia.json b/registry/eip155/ink-sepolia.json index 3650ebe..2748e87 100644 --- a/registry/eip155/ink-sepolia.json +++ b/registry/eip155/ink-sepolia.json @@ -25,7 +25,7 @@ "docsUrl": "https://docs.inkonchain.com", "firehose": { "blockType": "sf.ethereum.type.v2.Block", - "evmExtendedModel": true, + "evmExtendedModel": false, "bufUrl": "https://buf.build/streamingfast/firehose-ethereum", "bytesEncoding": "hex", "firstStreamableBlock": { diff --git a/registry/eip155/ink.json b/registry/eip155/ink.json index 07fd800..d06fe71 100644 --- a/registry/eip155/ink.json +++ b/registry/eip155/ink.json @@ -17,14 +17,14 @@ "docsUrl": "https://docs.inkonchain.com", "firehose": { "blockType": "sf.ethereum.type.v2.Block", - "evmExtendedModel": true, + "evmExtendedModel": false, "bufUrl": "https://buf.build/streamingfast/firehose-ethereum", "bytesEncoding": "hex", "firstStreamableBlock": { "id": "0x23a2658170ba70d014ba0d0d2709f8fbfe2fa660cd868c5f282f991eecbe38ee", "height": 0 }, - "blockFeatures": ["extended"] + "blockFeatures": ["base"] }, "icon": { "web3Icons": { "name": "ink" } } } diff --git a/registry/eip155/megaeth-timothy.json b/registry/eip155/megaeth-timothy.json index ef5416e..849a406 100644 --- a/registry/eip155/megaeth-timothy.json +++ b/registry/eip155/megaeth-timothy.json @@ -23,7 +23,7 @@ "nativeToken": "ETH", "firehose": { "blockType": "sf.ethereum.type.v2.Block", - "evmExtendedModel": true, + "evmExtendedModel": false, "bufUrl": "https://buf.build/streamingfast/firehose-ethereum", "bytesEncoding": "hex", "firstStreamableBlock": { diff --git a/registry/eip155/megaeth.json b/registry/eip155/megaeth.json index a9292f4..48d3c53 100644 --- a/registry/eip155/megaeth.json +++ b/registry/eip155/megaeth.json @@ -25,7 +25,7 @@ "nativeToken": "ETH", "firehose": { "blockType": "sf.ethereum.type.v2.Block", - "evmExtendedModel": true, + "evmExtendedModel": false, "bufUrl": "https://buf.build/streamingfast/firehose-ethereum", "bytesEncoding": "hex", "firstStreamableBlock": { diff --git a/registry/eip155/monad.json b/registry/eip155/monad.json index d97659f..e5c4d2d 100644 --- a/registry/eip155/monad.json +++ b/registry/eip155/monad.json @@ -25,8 +25,8 @@ "bufUrl": "https://buf.build/streamingfast/firehose-ethereum", "bytesEncoding": "hex", "firstStreamableBlock": { - "id": "0x0c47353304f22b1c15706367d739b850cda80b5c87bbc335014fef3d88deaac9", - "height": 0 + "id": "0x76ac00ea25b983893d48fc0ddac36a9c4d605b31a5cfd440c2f9f3a06941152c", + "height": 1 }, "blockFeatures": ["base"] }, diff --git a/registry/eip155/peaq.json b/registry/eip155/peaq.json index 184a638..e0482ab 100644 --- a/registry/eip155/peaq.json +++ b/registry/eip155/peaq.json @@ -15,7 +15,7 @@ "docsUrl": "https://docs.peaq.network", "firehose": { "blockType": "sf.ethereum.type.v2.Block", - "evmExtendedModel": true, + "evmExtendedModel": false, "bufUrl": "https://buf.build/streamingfast/firehose-ethereum", "bytesEncoding": "hex", "firstStreamableBlock": { diff --git a/registry/eip155/polkadot-testnet.json b/registry/eip155/polkadot-testnet.json index 1683658..875cdf5 100644 --- a/registry/eip155/polkadot-testnet.json +++ b/registry/eip155/polkadot-testnet.json @@ -12,7 +12,7 @@ "docsUrl": "https://docs.polkadot.com", "firehose": { "blockType": "sf.ethereum.type.v2.Block", - "evmExtendedModel": true, + "evmExtendedModel": false, "bufUrl": "https://buf.build/streamingfast/firehose-ethereum", "bytesEncoding": "hex", "firstStreamableBlock": { diff --git a/registry/eip155/sei-atlantic.json b/registry/eip155/sei-atlantic.json index ca69c04..aca88f8 100644 --- a/registry/eip155/sei-atlantic.json +++ b/registry/eip155/sei-atlantic.json @@ -30,7 +30,7 @@ "id": "0xf9d3845df25b43b1c6926f3ceda6845c17f5624e12212fd8847d0ba01da1ab9e", "height": 0 }, - "blockFeatures": ["base"] + "blockFeatures": ["extended"] }, "icon": { "web3Icons": { "name": "sei-network" } } } diff --git a/registry/eip155/stable.json b/registry/eip155/stable.json index 3dee09f..83592ed 100644 --- a/registry/eip155/stable.json +++ b/registry/eip155/stable.json @@ -16,7 +16,7 @@ "nativeToken": "gUSDT", "firehose": { "blockType": "sf.ethereum.type.v2.Block", - "evmExtendedModel": true, + "evmExtendedModel": false, "bufUrl": "https://buf.build/streamingfast/firehose-ethereum", "bytesEncoding": "hex", "firstStreamableBlock": { diff --git a/registry/eip155/status-sepolia.json b/registry/eip155/status-sepolia.json index 28ca40e..bda022c 100644 --- a/registry/eip155/status-sepolia.json +++ b/registry/eip155/status-sepolia.json @@ -19,7 +19,7 @@ "docsUrl": "https://docs.status.network", "firehose": { "blockType": "sf.ethereum.type.v2.Block", - "evmExtendedModel": true, + "evmExtendedModel": false, "bufUrl": "https://buf.build/streamingfast/firehose-ethereum", "bytesEncoding": "hex", "firstStreamableBlock": { diff --git a/registry/eip155/swellchain-sepolia.json b/registry/eip155/swellchain-sepolia.json index 31b5498..d685b81 100644 --- a/registry/eip155/swellchain-sepolia.json +++ b/registry/eip155/swellchain-sepolia.json @@ -29,7 +29,7 @@ "docsUrl": "https://docs.swellnetwork.io", "firehose": { "blockType": "sf.ethereum.type.v2.Block", - "evmExtendedModel": true, + "evmExtendedModel": false, "bufUrl": "https://buf.build/streamingfast/firehose-ethereum", "bytesEncoding": "hex", "firstStreamableBlock": { diff --git a/registry/eip155/swellchain.json b/registry/eip155/swellchain.json index 6d693a4..391e18f 100644 --- a/registry/eip155/swellchain.json +++ b/registry/eip155/swellchain.json @@ -18,7 +18,7 @@ "docsUrl": "https://docs.swellnetwork.io", "firehose": { "blockType": "sf.ethereum.type.v2.Block", - "evmExtendedModel": true, + "evmExtendedModel": false, "bufUrl": "https://buf.build/streamingfast/firehose-ethereum", "bytesEncoding": "hex", "firstStreamableBlock": { diff --git a/registry/eip155/vana-moksha.json b/registry/eip155/vana-moksha.json index f5c9022..04e0c70 100644 --- a/registry/eip155/vana-moksha.json +++ b/registry/eip155/vana-moksha.json @@ -18,7 +18,7 @@ "docsUrl": "https://docs.vana.org", "firehose": { "blockType": "sf.ethereum.type.v2.Block", - "evmExtendedModel": true, + "evmExtendedModel": false, "bufUrl": "https://buf.build/streamingfast/firehose-ethereum", "bytesEncoding": "hex", "firstStreamableBlock": { diff --git a/registry/eip155/vana.json b/registry/eip155/vana.json index 21ba7ab..7725462 100644 --- a/registry/eip155/vana.json +++ b/registry/eip155/vana.json @@ -15,7 +15,7 @@ "docsUrl": "https://docs.vana.org", "firehose": { "blockType": "sf.ethereum.type.v2.Block", - "evmExtendedModel": true, + "evmExtendedModel": false, "bufUrl": "https://buf.build/streamingfast/firehose-ethereum", "bytesEncoding": "hex", "firstStreamableBlock": { diff --git a/registry/eip155/viction.json b/registry/eip155/viction.json index c11ddc6..039853d 100644 --- a/registry/eip155/viction.json +++ b/registry/eip155/viction.json @@ -14,7 +14,7 @@ "docsUrl": "https://docs.viction.xyz", "firehose": { "blockType": "sf.ethereum.type.v2.Block", - "evmExtendedModel": true, + "evmExtendedModel": false, "bufUrl": "https://buf.build/streamingfast/firehose-ethereum", "bytesEncoding": "hex", "firstStreamableBlock": { diff --git a/registry/eip155/zetachain.json b/registry/eip155/zetachain.json index f59b08a..27d963f 100644 --- a/registry/eip155/zetachain.json +++ b/registry/eip155/zetachain.json @@ -15,7 +15,7 @@ "docsUrl": "https://www.zetachain.com/docs", "firehose": { "blockType": "sf.ethereum.type.v2.Block", - "evmExtendedModel": true, + "evmExtendedModel": false, "bufUrl": "https://buf.build/streamingfast/firehose-ethereum", "bytesEncoding": "hex", "firstStreamableBlock": { diff --git a/src/validate_firehose.ts b/src/validate_firehose.ts index f97b582..5863c4a 100644 --- a/src/validate_firehose.ts +++ b/src/validate_firehose.ts @@ -10,146 +10,158 @@ const ERRORS: string[] = []; const WARNINGS: string[] = []; interface FirehoseInfo { - chainName: string; - chainNameAliases: string[]; - firstStreamableBlockNum?: string; - firstStreamableBlockId: string; - blockIdEncoding: - | "BLOCK_ID_ENCODING_HEX" - | "BLOCK_ID_ENCODING_BASE58" - | "BLOCK_ID_ENCODING_BASE64" - | "BLOCK_ID_ENCODING_0X_HEX"; - blockFeatures: ("base" | "extended" | "hybrid")[]; + chainName: string; + chainNameAliases: string[]; + firstStreamableBlockNum?: string; + firstStreamableBlockId: string; + blockIdEncoding: + | "BLOCK_ID_ENCODING_HEX" + | "BLOCK_ID_ENCODING_BASE58" + | "BLOCK_ID_ENCODING_BASE64" + | "BLOCK_ID_ENCODING_0X_HEX"; + blockFeatures: ("base" | "extended" | "hybrid")[]; } const ValidEncodingMap = { - BLOCK_ID_ENCODING_HEX: "hex", - BLOCK_ID_ENCODING_BASE58: "base58", - BLOCK_ID_ENCODING_BASE64: "base64", - BLOCK_ID_ENCODING_0X_HEX: "0xhex", + BLOCK_ID_ENCODING_HEX: "hex", + BLOCK_ID_ENCODING_BASE58: "base58", + BLOCK_ID_ENCODING_BASE64: "base64", + BLOCK_ID_ENCODING_0X_HEX: "0xhex", }; async function validateSingleEndpoint( - network: Network, - endpoint: string, + network: Network, + endpoint: string, ): Promise { - try { - const command = `grpcurl -H "X-Api-Key: ${process.env.SF_API_KEY}" ${endpoint} sf.firehose.v2.EndpointInfo/Info`; - const { stdout } = await execAsync(command); - const fh = JSON.parse(stdout) as FirehoseInfo; - - if ( - ValidEncodingMap[fh.blockIdEncoding] !== network.firehose?.bytesEncoding - ) { - const err = `\`${network.id}\` - endpoint \`${endpoint}\` has wrong \`bytesEncoding\`. Endpoint: \`${fh.blockIdEncoding}\` vs Registry: \`${network.firehose?.bytesEncoding}\``; - ERRORS.push(err); - console.error(err); - } - if ( - fh.blockFeatures?.includes("extended") && - !network.firehose?.evmExtendedModel - ) { - const err = `\`${network.id}\` - endpoint \`${endpoint}\` has inconsistent block model. Endpoint blockFeatures: \`extended\` vs Registry evmExtendedModel: \`false\``; - ERRORS.push(err); - console.error(err); - } - if ( - fh.blockFeatures?.includes("base") && - network.firehose?.evmExtendedModel - ) { - const err = `\`${network.id}\` - endpoint \`${endpoint}\` has potentially wrong block model. Endpoint blockFeatures: \`base\` vs Registry evmExtendedModel: \`true\``; - WARNINGS.push(err); - console.warn(err); - } - if ( - fh.blockFeatures?.includes("hybrid") && - !network.firehose?.evmExtendedModel - ) { - const err = `\`${network.id}\` - endpoint \`${endpoint}\` has inconsistent block model. Endpoint blockFeatures: \`hybrid\` vs Registry evmExtendedModel: \`false\``; - ERRORS.push(err); - console.error(err); - } - if ( - fh.blockFeatures?.sort().join(",") !== - network.firehose?.blockFeatures?.sort().join(",") - ) { - const err = `\`${network.id}\` - \`${endpoint}\` has inconsistent \`blockFeatures\`. Endpoint: ${fh.blockFeatures?.join(",")} vs Registry: ${network.firehose?.blockFeatures?.join(",")}`; - WARNINGS.push(err); - console.error(err); - } - if ( - !network.firehose?.firstStreamableBlock?.id?.includes( - fh.firstStreamableBlockId, - ) - ) { - const err = `\`${network.id}\` - \`${endpoint}\` has inconsistent \`firstStreamableBlockId\`. Endpoint: ${fh.firstStreamableBlockId} vs Registry: ${network.firehose?.firstStreamableBlock?.id}`; - if (["testnet", "devnet"].includes(network.networkType)) { - WARNINGS.push(err); - } else { - ERRORS.push(err); - } - console.error(err); - } - } catch (error) { - if (error instanceof Error && error.message.includes("Unimplemented")) { - WARNINGS.push( - `\`${network.id}\` - endpoint ${endpoint} does not expose firehose RPC`, - ); - return; - } - WARNINGS.push(`\`${network.id}\` - endpoint ${endpoint} is not accessible`); - } + try { + const command = `grpcurl -H "X-Api-Key: ${process.env.SF_API_KEY}" ${endpoint} sf.firehose.v2.EndpointInfo/Info`; + const { stdout } = await execAsync(command); + const fh = JSON.parse(stdout) as FirehoseInfo; + + if ( + ValidEncodingMap[fh.blockIdEncoding] !== network.firehose?.bytesEncoding + ) { + const err = `\`${network.id}\` - endpoint \`${endpoint}\` has wrong \`bytesEncoding\`. Endpoint: \`${fh.blockIdEncoding}\` vs Registry: \`${network.firehose?.bytesEncoding}\``; + ERRORS.push(err); + console.error(err); + } + if ( + fh.blockFeatures?.includes("extended") && + !network.firehose?.evmExtendedModel + ) { + const err = `\`${network.id}\` - endpoint \`${endpoint}\` has inconsistent block model. Endpoint blockFeatures: \`extended\` vs Registry evmExtendedModel: \`false\``; + ERRORS.push(err); + console.error(err); + } + if ( + fh.blockFeatures?.includes("base") && + network.firehose?.evmExtendedModel + ) { + const err = `\`${network.id}\` - endpoint \`${endpoint}\` has potentially wrong block model. Endpoint blockFeatures: \`base\` vs Registry evmExtendedModel: \`true\``; + WARNINGS.push(err); + console.warn(err); + } + if ( + fh.blockFeatures?.includes("hybrid") && + !network.firehose?.evmExtendedModel + ) { + const err = `\`${network.id}\` - endpoint \`${endpoint}\` has inconsistent block model. Endpoint blockFeatures: \`hybrid\` vs Registry evmExtendedModel: \`false\``; + ERRORS.push(err); + console.error(err); + } + if ( + fh.blockFeatures?.sort().join(",") !== + network.firehose?.blockFeatures?.sort().join(",") + ) { + const err = `\`${network.id}\` - \`${endpoint}\` has inconsistent \`blockFeatures\`. Endpoint: ${fh.blockFeatures?.join(",")} vs Registry: ${network.firehose?.blockFeatures?.join(",")}`; + WARNINGS.push(err); + console.error(err); + } + if ( + !network.firehose?.firstStreamableBlock?.id?.includes( + fh.firstStreamableBlockId, + ) + ) { + const err = `\`${network.id}\` - \`${endpoint}\` has inconsistent \`firstStreamableBlockId\` id. Endpoint: ${fh.firstStreamableBlockId} vs Registry: ${network.firehose?.firstStreamableBlock?.id}`; + if (["testnet", "devnet"].includes(network.networkType)) { + WARNINGS.push(err); + } else { + ERRORS.push(err); + } + console.error(err); + } + if ( + network.firehose?.firstStreamableBlock?.height !== + Number(fh.firstStreamableBlockNum ?? 0) + ) { + const err = `\`${network.id}\` - \`${endpoint}\` has inconsistent \`firstStreamableBlockId\` height. Endpoint: ${fh.firstStreamableBlockNum} vs Registry: ${network.firehose?.firstStreamableBlock?.height}`; + if (["testnet", "devnet"].includes(network.networkType)) { + WARNINGS.push(err); + } else { + ERRORS.push(err); + } + console.error(err); + } + } catch (error) { + if (error instanceof Error && error.message.includes("Unimplemented")) { + WARNINGS.push( + `\`${network.id}\` - endpoint ${endpoint} does not expose firehose RPC`, + ); + return; + } + WARNINGS.push(`\`${network.id}\` - endpoint ${endpoint} is not accessible`); + } } async function validateFirehoseEvmModel(networks: Network[]) { - if (!process.env.SF_API_KEY) { - console.error("Need SF_API_KEY to validate firehose endpoints"); - process.exit(1); - } - console.log("Validating firehose evm extended model "); - - const endpointsAndNetworks = networks.flatMap( - (network) => - network.services.firehose?.map( - (endpoint) => [endpoint, network] as [string, Network], - ) ?? [], - ); - - const BATCH = 20; - for (let i = 0; i < endpointsAndNetworks.length; i += BATCH) { - const chunk = endpointsAndNetworks.slice(i, i + BATCH); - await Promise.all( - chunk.map(([endpoint, network]) => - validateSingleEndpoint(network, endpoint), - ), - ); - } + if (!process.env.SF_API_KEY) { + console.error("Need SF_API_KEY to validate firehose endpoints"); + process.exit(1); + } + console.log("Validating firehose evm extended model "); + + const endpointsAndNetworks = networks.flatMap( + (network) => + network.services.firehose?.map( + (endpoint) => [endpoint, network] as [string, Network], + ) ?? [], + ); + + const BATCH = 20; + for (let i = 0; i < endpointsAndNetworks.length; i += BATCH) { + const chunk = endpointsAndNetworks.slice(i, i + BATCH); + await Promise.all( + chunk.map(([endpoint, network]) => + validateSingleEndpoint(network, endpoint), + ), + ); + } } export async function validateFirehose(networksPath: string) { - let networks = loadNetworks(networksPath); - console.log(`Loaded ${networks.length} networks`); + let networks = loadNetworks(networksPath); + console.log(`Loaded ${networks.length} networks`); - await validateFirehoseEvmModel(networks); + await validateFirehoseEvmModel(networks); - return { - errors: ERRORS.map((e) => `[firehose] ${e}`), - warnings: WARNINGS.map((e) => `[firehose] ${e}`), - }; + return { + errors: ERRORS.map((e) => `[firehose] ${e}`), + warnings: WARNINGS.map((e) => `[firehose] ${e}`), + }; } async function main() { - const [, , networksPath = "registry"] = process.argv; + const [, , networksPath = "registry"] = process.argv; - const { errors, warnings } = await validateFirehose(networksPath); + const { errors, warnings } = await validateFirehose(networksPath); - printErrorsAndWarnings(errors, warnings); - if (errors.length > 0) { - process.exit(1); - } + printErrorsAndWarnings(errors, warnings); + if (errors.length > 0) { + process.exit(1); + } } // Only run main() if this file is being run directly if (import.meta.main) { - await main(); + await main(); } diff --git a/src/validate_logic.ts b/src/validate_logic.ts index b012123..784da12 100644 --- a/src/validate_logic.ts +++ b/src/validate_logic.ts @@ -143,6 +143,14 @@ function validateEvmRules(networks: Network[]) { `\`${network.id}\` - EVM chain has firehose.evmExtendedModel=false but firehose.blockFeatures does not include "base"`, ); } + if ( + network.firehose.evmExtendedModel === true && + network.firehose.blockFeatures?.includes("base") + ) { + WARNINGS.push( + `\`${network.id}\` - EVM chain has firehose.evmExtendedModel=true but firehose.blockFeatures includes "base"`, + ); + } } if (network.graphNode?.protocol !== "ethereum") {