diff --git a/README.md b/README.md index a833a12..e6416bf 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ Options: -v, --validators Number of validator nodes to generate. (default: 4) -a, --allocations Path to a genesis allocations JSON file. (default: none) --abi-directory Directory containing ABI JSON files to publish as ConfigMaps. + --subgraph-hash-file Path to a file containing the subgraph IPFS hash. -o, --outputType Output target (screen, file, kubernetes). (default: "screen") --static-node-port P2P port used for static-nodes enode URIs. (default: 30303) --static-node-discovery-port Discovery port used for static-nodes enode URIs. (default: 30303) diff --git a/bun.lock b/bun.lock index c69e235..25846b5 100644 --- a/bun.lock +++ b/bun.lock @@ -8,6 +8,7 @@ "@kubernetes/client-node": "1.3.0", "commander": "14.0.1", "lefthook": "1.13.1", + "multiformats": "12.1.3", "ox": "0.9.6", "viem": "2.37.7", "yaml": "2.8.1", @@ -402,6 +403,8 @@ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "multiformats": ["multiformats@12.1.3", "", {}, "sha512-eajQ/ZH7qXZQR2AgtfpmSMizQzmyYVmCql7pdhldPuYQi4atACekbJaQplk6dWyIi10jCaFnd6pqvcEFXjbaJw=="], + "mute-stream": ["mute-stream@2.0.0", "", {}, "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA=="], "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], diff --git a/package.json b/package.json index ac1f1f2..fb7dd7e 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "@inquirer/prompts": "7.8.6", "@kubernetes/client-node": "1.3.0", "commander": "14.0.1", + "multiformats": "12.1.3", "lefthook": "1.13.1", "ox": "0.9.6", "viem": "2.37.7", diff --git a/src/cli/commands/bootstrap/bootstrap.command.test.ts b/src/cli/commands/bootstrap/bootstrap.command.test.ts index c528421..c3f28be 100644 --- a/src/cli/commands/bootstrap/bootstrap.command.test.ts +++ b/src/cli/commands/bootstrap/bootstrap.command.test.ts @@ -23,6 +23,7 @@ const { genesisConfigMapName: DEFAULT_GENESIS_CONFIGMAP_NAME, staticNodesConfigMapName: DEFAULT_STATIC_NODES_CONFIGMAP_NAME, faucetArtifactPrefix: DEFAULT_FAUCET_PREFIX, + subgraphConfigMapName: DEFAULT_SUBGRAPH_CONFIGMAP_NAME, } = ARTIFACT_DEFAULTS; const UNCOMPRESSED_PUBLIC_KEY_PREFIX = "04"; const UNCOMPRESSED_PUBLIC_KEY_LENGTH = 130; @@ -35,6 +36,8 @@ const PUBLIC_KEY_REPEAT = 64; const FIRST_VALIDATOR_INDEX = 1; const SECOND_VALIDATOR_INDEX = 2; const FAUCET_INDEX = VALIDATOR_RETURN + 1; +const SAMPLE_SUBGRAPH_HASH = + "bafybeigdyrztzd4gufq2bdsd6we3jh7uzulnd2ipkyli5sto6f5j6rlude"; const createFactoryStub = () => { let counter = 0; return { @@ -204,6 +207,7 @@ describe("CLI command bootstrap", () => { loadAbisPath = path; return Promise.resolve([]); }, + loadSubgraphHash: () => Promise.resolve(SAMPLE_SUBGRAPH_HASH), outputResult: async (type, payload) => { outputInvocation = { type, payload }; await realOutputResult(type, payload); @@ -242,6 +246,7 @@ describe("CLI command bootstrap", () => { validatorPrefix: DEFAULT_POD_PREFIX, genesisConfigMapName: DEFAULT_GENESIS_CONFIGMAP_NAME, staticNodesConfigMapName: DEFAULT_STATIC_NODES_CONFIGMAP_NAME, + subgraphConfigMapName: DEFAULT_SUBGRAPH_CONFIGMAP_NAME, }); }); @@ -278,6 +283,7 @@ describe("CLI command bootstrap", () => { capturedAbiPath = path; return Promise.resolve(abiArtifacts); }, + loadSubgraphHash: () => Promise.resolve(SAMPLE_SUBGRAPH_HASH), outputResult: (_type, payload) => { capturedPayload = payload; return Promise.resolve(); @@ -296,6 +302,56 @@ describe("CLI command bootstrap", () => { expect(capturedPayload?.abiArtifacts).toEqual(abiArtifacts); }); + test("runBootstrap loads subgraph hash from environment", async () => { + const factory = createFactoryStub(); + let capturedPath: string | undefined; + let capturedPayload: OutputPayload | undefined; + const originalEnv = Bun.env.SUBGRAPH_HASH_FILE; + + const deps: BootstrapDependencies = { + factory, + promptForCount: () => Promise.resolve(EXPECTED_DEFAULT_VALIDATOR), + promptForGenesis: async (_service, { faucetAddress }) => ({ + algorithm: ALGORITHM.QBFT, + config: { + chainId: 1, + faucetWalletAddress: faucetAddress, + gasLimit: "0x1", + secondsPerBlock: 2, + }, + genesis: { config: {}, extraData: "0x" } as any, + }), + promptForText: passthroughTextPrompt, + service: {} as any, + loadAllocations: () => + Promise.resolve({} satisfies Record), + loadAbis: () => Promise.resolve([]), + loadSubgraphHash: (path: string) => { + capturedPath = path; + return Promise.resolve(SAMPLE_SUBGRAPH_HASH); + }, + outputResult: (_type, payload) => { + capturedPayload = payload; + return Promise.resolve(); + }, + }; + + Bun.env.SUBGRAPH_HASH_FILE = " /tmp/subgraph.txt "; + + try { + await runBootstrap({ acceptDefaults: true }, deps); + } finally { + if (originalEnv === undefined) { + Bun.env.SUBGRAPH_HASH_FILE = undefined; + } else { + Bun.env.SUBGRAPH_HASH_FILE = originalEnv; + } + } + + expect(capturedPath).toBe("/tmp/subgraph.txt"); + expect(capturedPayload?.subgraphHash).toBe(SAMPLE_SUBGRAPH_HASH); + }); + test("createCliCommand wires metadata", () => { const command = createCliCommand(); expect(command.name()).toBe("network-bootstrapper"); @@ -337,6 +393,7 @@ describe("CLI command bootstrap", () => { loadAllocations: () => Promise.resolve({} satisfies Record), loadAbis: () => Promise.resolve([]), + loadSubgraphHash: () => Promise.resolve(SAMPLE_SUBGRAPH_HASH), outputResult: async (type, payload) => { await realOutputResult(type, payload); }, @@ -405,6 +462,7 @@ describe("CLI command bootstrap", () => { loadAllocations: () => Promise.resolve({} satisfies Record), loadAbis: () => Promise.resolve([]), + loadSubgraphHash: () => Promise.resolve(SAMPLE_SUBGRAPH_HASH), outputResult: (_type, payload) => { capturedPayload = payload; return Promise.resolve(); @@ -457,6 +515,7 @@ describe("CLI command bootstrap", () => { validatorPrefix: "custom-validator", genesisConfigMapName: "custom-genesis", staticNodesConfigMapName: "custom-static-nodes", + subgraphConfigMapName: DEFAULT_SUBGRAPH_CONFIGMAP_NAME, }); }); @@ -487,6 +546,7 @@ describe("CLI command bootstrap", () => { loadAllocations: () => Promise.resolve({} satisfies Record), loadAbis: () => Promise.resolve([]), + loadSubgraphHash: () => Promise.resolve(SAMPLE_SUBGRAPH_HASH), outputResult: (_type, payload) => { capturedPayload = payload; return Promise.resolve(); @@ -556,6 +616,7 @@ describe("CLI command bootstrap", () => { loadAllocations: () => Promise.resolve({} satisfies Record), loadAbis: () => Promise.resolve([]), + loadSubgraphHash: () => Promise.resolve(SAMPLE_SUBGRAPH_HASH), outputResult: async () => { // no-op for test }, @@ -639,6 +700,7 @@ describe("CLI command bootstrap", () => { loadAllocations: () => Promise.resolve({} satisfies Record), loadAbis: () => Promise.resolve([]), + loadSubgraphHash: () => Promise.resolve(SAMPLE_SUBGRAPH_HASH), outputResult: (type) => { capturedOutputType = type; return Promise.resolve(); @@ -730,6 +792,7 @@ describe("CLI command bootstrap", () => { loadAbisInvoked = true; return Promise.resolve([]); }, + loadSubgraphHash: () => Promise.resolve(SAMPLE_SUBGRAPH_HASH), outputResult: (_type, payload) => { expect(payload.validators).toHaveLength(EXPECTED_DEFAULT_VALIDATOR); expect(payload.artifactNames).toEqual({ @@ -737,6 +800,7 @@ describe("CLI command bootstrap", () => { validatorPrefix: DEFAULT_POD_PREFIX, genesisConfigMapName: DEFAULT_GENESIS_CONFIGMAP_NAME, staticNodesConfigMapName: DEFAULT_STATIC_NODES_CONFIGMAP_NAME, + subgraphConfigMapName: DEFAULT_SUBGRAPH_CONFIGMAP_NAME, }); return Promise.resolve(); }, diff --git a/src/cli/commands/bootstrap/bootstrap.command.ts b/src/cli/commands/bootstrap/bootstrap.command.ts index 46ce25d..6c6ef5a 100644 --- a/src/cli/commands/bootstrap/bootstrap.command.ts +++ b/src/cli/commands/bootstrap/bootstrap.command.ts @@ -25,6 +25,7 @@ import { promptForCount, promptForText, } from "./bootstrap.prompt-helpers.ts"; +import { loadSubgraphHash } from "./bootstrap.subgraph.ts"; type CliOptions = { allocations?: string; @@ -48,6 +49,7 @@ type CliOptions = { genesisConfigmapName?: string; staticNodesConfigmapName?: string; faucetArtifactPrefix?: string; + subgraphHashFile?: string; }; type BootstrapDependencies = { @@ -58,6 +60,7 @@ type BootstrapDependencies = { service: BesuGenesisService; loadAllocations: typeof loadAllocations; loadAbis: typeof loadAbis; + loadSubgraphHash: typeof loadSubgraphHash; outputResult: (type: OutputType, payload: OutputPayload) => Promise; }; @@ -69,6 +72,7 @@ const { genesisConfigMapName: DEFAULT_GENESIS_CONFIGMAP_NAME, staticNodesConfigMapName: DEFAULT_STATIC_NODES_CONFIGMAP_NAME, faucetArtifactPrefix: DEFAULT_FAUCET_ARTIFACT_PREFIX, + subgraphConfigMapName: DEFAULT_SUBGRAPH_CONFIGMAP_NAME, } = ARTIFACT_DEFAULTS; const OUTPUT_CHOICES: OutputType[] = ["screen", "file", "kubernetes"]; const LEADING_DOT_REGEX = /^\./u; @@ -303,6 +307,7 @@ const runBootstrap = async ( genesisConfigmapName: genesisConfigmapNameOption, staticNodesConfigmapName: staticNodesConfigmapNameOption, faucetArtifactPrefix: faucetArtifactPrefixOption, + subgraphHashFile: subgraphHashFileOption, } = options; const resolveCount = ( @@ -399,6 +404,22 @@ const runBootstrap = async ( ? await deps.loadAbis(trimmedAbiDirectory) : []; + const envSubgraphHashFile = Bun.env.SUBGRAPH_HASH_FILE?.trim(); + const providedSubgraphHashFile = + subgraphHashFileOption === undefined + ? undefined + : subgraphHashFileOption.trim(); + let subgraphHashPath: string | undefined; + if (providedSubgraphHashFile && providedSubgraphHashFile.length > 0) { + subgraphHashPath = providedSubgraphHashFile; + } else if (envSubgraphHashFile && envSubgraphHashFile.length > 0) { + subgraphHashPath = envSubgraphHashFile; + } + + const subgraphHash = subgraphHashPath + ? await deps.loadSubgraphHash(subgraphHashPath) + : undefined; + const { genesis } = await deps.promptForGenesis(deps.service, { faucetAddress, allocations: allocationOverrides, @@ -425,8 +446,10 @@ const runBootstrap = async ( validatorPrefix: staticNodePodPrefix, genesisConfigMapName, staticNodesConfigMapName, + subgraphConfigMapName: DEFAULT_SUBGRAPH_CONFIGMAP_NAME, }, abiArtifacts, + subgraphHash, }; await deps.outputResult(outputType ?? "screen", payload); @@ -441,6 +464,7 @@ const defaultDependencies: BootstrapDependencies = { service: new BesuGenesisService(), loadAllocations, loadAbis, + loadSubgraphHash, outputResult: defaultOutputResult, }; /* c8 ignore end */ @@ -487,6 +511,11 @@ const createCliCommand = ( "Directory containing ABI JSON files to publish as ConfigMaps.", (value: string) => stripSurroundingQuotes(value) ) + .option( + "--subgraph-hash-file ", + "Path to a file containing the subgraph IPFS hash.", + (value: string) => stripSurroundingQuotes(value) + ) .option( "-o, --outputType ", `Output target (${OUTPUT_CHOICES.join(", ")}).`, @@ -601,6 +630,10 @@ const createCliCommand = ( normalizedOptions.abiDirectory === undefined ? undefined : stripSurroundingQuotes(normalizedOptions.abiDirectory), + subgraphHashFile: + normalizedOptions.subgraphHashFile === undefined + ? undefined + : stripSurroundingQuotes(normalizedOptions.subgraphHashFile), }; for (const { key, sanitize } of TEXT_OPTION_DESCRIPTORS) { @@ -628,6 +661,12 @@ const createCliCommand = ( trimmed.length === 0 ? undefined : trimmed; } + if (sanitizedOptions.subgraphHashFile) { + const trimmed = sanitizedOptions.subgraphHashFile.trim(); + sanitizedOptions.subgraphHashFile = + trimmed.length === 0 ? undefined : trimmed; + } + await runBootstrap(sanitizedOptions, deps); }); diff --git a/src/cli/commands/bootstrap/bootstrap.output.test.ts b/src/cli/commands/bootstrap/bootstrap.output.test.ts index 0d841a9..912c209 100644 --- a/src/cli/commands/bootstrap/bootstrap.output.test.ts +++ b/src/cli/commands/bootstrap/bootstrap.output.test.ts @@ -25,6 +25,7 @@ import { printGenesis, printGroup, } from "./bootstrap.output.ts"; +import { SUBGRAPH_HASH_KEY } from "./bootstrap.subgraph.ts"; let output = ""; let originalWrite: typeof process.stdout.write; @@ -97,7 +98,9 @@ const SAMPLE_ABI_ARTIFACTS = [ contents: `${JSON.stringify({ contractName: "Sample" }, null, 2)}\n`, }, ] as const; -const BASE_CONFIGMAP_COUNT = 8; +const SAMPLE_SUBGRAPH_HASH = + "bafybeigdyrztzd4gufq2bdsd6we3jh7uzulnd2ipkyli5sto6f5j6rlude"; +const BASE_CONFIGMAP_COUNT = 9; const EXPECTED_CONFIGMAP_COUNT = BASE_CONFIGMAP_COUNT + SAMPLE_ABI_ARTIFACTS.length; const EXPECTED_SECRET_COUNT = 2; @@ -285,8 +288,10 @@ const samplePayload: OutputPayload = { validatorPrefix: DEFAULT_POD_PREFIX, genesisConfigMapName: DEFAULT_GENESIS_CONFIGMAP_NAME, staticNodesConfigMapName: DEFAULT_STATIC_NODES_CONFIGMAP_NAME, + subgraphConfigMapName: ARTIFACT_DEFAULTS.subgraphConfigMapName, }, abiArtifacts: SAMPLE_ABI_ARTIFACTS, + subgraphHash: SAMPLE_SUBGRAPH_HASH, }; describe("outputResult", () => { @@ -327,6 +332,7 @@ describe("outputResult", () => { "abi-sample.json", "besu-genesis.json", "besu-static-nodes.json", + "besu-subgraph.json", ].sort() ); @@ -342,6 +348,14 @@ describe("outputResult", () => { ); expect(JSON.parse(staticNodesContent)).toEqual(samplePayload.staticNodes); + const subgraphConfig = await readFile( + join(targetDirPath, "besu-subgraph.json"), + "utf8" + ); + expect(JSON.parse(subgraphConfig)).toEqual({ + [SUBGRAPH_HASH_KEY]: SAMPLE_SUBGRAPH_HASH, + }); + await rm("out", { recursive: true, force: true }); }); @@ -436,6 +450,7 @@ describe("outputResult", () => { expect(mapNames).toContain("besu-faucet-address"); expect(mapNames).toContain("besu-faucet-pubkey"); expect(mapNames).toContain("besu-static-nodes"); + expect(mapNames).toContain("besu-subgraph"); expect(mapNames).toContain( toAllocationConfigMapName(allocationTarget.address) ); @@ -508,6 +523,13 @@ describe("outputResult", () => { expect(abiConfig?.data?.[SAMPLE_ABI_ARTIFACTS[0]?.fileName ?? ""]).toBe( SAMPLE_ABI_ARTIFACTS[0]?.contents ); + const subgraphConfig = createdConfigMaps.find( + (entry) => entry.name === "besu-subgraph" + ); + expect(subgraphConfig?.immutable).toBe(true); + expect(subgraphConfig?.data?.[SUBGRAPH_HASH_KEY]).toBe( + SAMPLE_SUBGRAPH_HASH + ); } finally { (KubeConfig.prototype as any).loadFromCluster = originalLoad; (KubeConfig.prototype as any).makeApiClient = originalMake; @@ -526,6 +548,7 @@ describe("outputResult", () => { validatorPrefix: "custom-validator", genesisConfigMapName: "custom-genesis", staticNodesConfigMapName: "custom-static", + subgraphConfigMapName: "custom-subgraph", }, staticNodes: [ staticNodeUri( @@ -550,6 +573,7 @@ describe("outputResult", () => { "custom-validator-0-pubkey", "custom-faucet-address", "custom-faucet-pubkey", + "custom-subgraph", toAllocationConfigMapName(allocationTarget.address), SAMPLE_ABI_ARTIFACTS[0]?.configMapName ?? "", ]; diff --git a/src/cli/commands/bootstrap/bootstrap.output.ts b/src/cli/commands/bootstrap/bootstrap.output.ts index 042a9b3..8966e62 100644 --- a/src/cli/commands/bootstrap/bootstrap.output.ts +++ b/src/cli/commands/bootstrap/bootstrap.output.ts @@ -21,6 +21,7 @@ import { } from "../../integrations/kubernetes/kubernetes.client.ts"; import type { AbiArtifact } from "./bootstrap.abis.ts"; import { accent, label, muted } from "./bootstrap.colors.ts"; +import { SUBGRAPH_HASH_KEY } from "./bootstrap.subgraph.ts"; type IndexedNode = GeneratedNodeKey & { index: number }; @@ -31,6 +32,7 @@ type ArtifactNames = { validatorPrefix: string; genesisConfigMapName: string; staticNodesConfigMapName: string; + subgraphConfigMapName: string; }; type OutputPayload = { @@ -40,6 +42,7 @@ type OutputPayload = { staticNodes: readonly string[]; artifactNames: ArtifactNames; abiArtifacts: readonly AbiArtifact[]; + subgraphHash?: string; }; type ConfigMapSpec = ConfigMapEntrySpec; @@ -226,6 +229,18 @@ const outputToFile = async (payload: OutputPayload): Promise => { }, ]; + if (payload.subgraphHash) { + fileEntries.push({ + path: join(directory, `${artifactNames.subgraphConfigMapName}.json`), + description: `${artifactNames.subgraphConfigMapName}.json`, + contents: `${JSON.stringify( + { [SUBGRAPH_HASH_KEY]: payload.subgraphHash }, + null, + 2 + )}\n`, + }); + } + for (const entry of fileEntries) { logNonScreenStep(`Writing ${entry.description}`); } @@ -276,6 +291,15 @@ const outputToKubernetes = async (payload: OutputPayload): Promise => { ...createAbiConfigSpecs(payload.abiArtifacts), ...allocationSpecs, ]; + if (payload.subgraphHash) { + configMapSpecs.push({ + name: artifactNames.subgraphConfigMapName, + key: SUBGRAPH_HASH_KEY, + value: payload.subgraphHash, + immutable: true, + onConflict: "skip", + }); + } const secretSpecs = [ ...allSpecs.filter((spec) => spec.key === "privateKey"), ...createFaucetSecretSpecs(payload.faucet, artifactNames.faucetPrefix), diff --git a/src/cli/commands/bootstrap/bootstrap.subgraph.test.ts b/src/cli/commands/bootstrap/bootstrap.subgraph.test.ts new file mode 100644 index 0000000..95a0225 --- /dev/null +++ b/src/cli/commands/bootstrap/bootstrap.subgraph.test.ts @@ -0,0 +1,53 @@ +import { afterEach, describe, expect, test } from "bun:test"; +import { mkdir, rm } from "node:fs/promises"; +import { join } from "node:path"; + +import { loadSubgraphHash } from "./bootstrap.subgraph.ts"; + +const TMP_DIR = join(process.cwd(), "tmp-subgraph-tests"); +const writeTempFile = async ( + name: string, + contents: string +): Promise => { + await mkdir(TMP_DIR, { recursive: true }); + const path = join(TMP_DIR, name); + await Bun.write(path, contents); + return path; +}; + +afterEach(async () => { + await rm(TMP_DIR, { recursive: true, force: true }); +}); + +describe("loadSubgraphHash", () => { + test("reads and validates CID values", async () => { + const cid = "bafybeigdyrztzd4gufq2bdsd6we3jh7uzulnd2ipkyli5sto6f5j6rlude"; + const path = await writeTempFile("hash.txt", `${cid}\n`); + + const loaded = await loadSubgraphHash(path); + + expect(loaded).toBe(cid); + }); + + test("throws when file does not exist", async () => { + await expect( + loadSubgraphHash(join(TMP_DIR, "missing.txt")) + ).rejects.toThrow("Subgraph hash file not found"); + }); + + test("throws when file is empty", async () => { + const path = await writeTempFile("empty.txt", " \n\t "); + + await expect(loadSubgraphHash(path)).rejects.toThrow( + "Subgraph hash file is empty." + ); + }); + + test("throws when contents are not a valid CID", async () => { + const path = await writeTempFile("invalid.txt", "not-a-cid"); + + await expect(loadSubgraphHash(path)).rejects.toThrow( + "Subgraph hash is not a valid IPFS hash" + ); + }); +}); diff --git a/src/cli/commands/bootstrap/bootstrap.subgraph.ts b/src/cli/commands/bootstrap/bootstrap.subgraph.ts new file mode 100644 index 0000000..09dbc06 --- /dev/null +++ b/src/cli/commands/bootstrap/bootstrap.subgraph.ts @@ -0,0 +1,32 @@ +import { CID } from "multiformats/cid"; + +const SUBGRAPH_HASH_KEY = "SUBGRAPH_HASH" as const; + +const loadSubgraphHash = async (path: string): Promise => { + const trimmedPath = path.trim(); + if (trimmedPath.length === 0) { + throw new Error("Subgraph hash file path must be provided."); + } + + const file = Bun.file(trimmedPath); + if (!(await file.exists())) { + throw new Error(`Subgraph hash file not found at ${path}`); + } + + const contents = (await file.text()).trim(); + if (contents.length === 0) { + throw new Error("Subgraph hash file is empty."); + } + + try { + CID.parse(contents); + } catch (error) { + throw new Error( + `Subgraph hash is not a valid IPFS hash: ${(error as Error).message}` + ); + } + + return contents; +}; + +export { loadSubgraphHash, SUBGRAPH_HASH_KEY }; diff --git a/src/constants/artifact-defaults.ts b/src/constants/artifact-defaults.ts index ca7f632..37016ec 100644 --- a/src/constants/artifact-defaults.ts +++ b/src/constants/artifact-defaults.ts @@ -4,6 +4,7 @@ const ARTIFACT_DEFAULTS = { genesisConfigMapName: "besu-genesis", staticNodesConfigMapName: "besu-static-nodes", faucetArtifactPrefix: "besu-faucet", + subgraphConfigMapName: "besu-subgraph", } as const; export { ARTIFACT_DEFAULTS };