From e95e58d8bb7c3a57e4d9467a8fc69a5418fbde0c Mon Sep 17 00:00:00 2001 From: stanlou Date: Fri, 27 Mar 2026 15:38:29 +0100 Subject: [PATCH 1/3] feat(cli): add scripts.env file support --- package-lock.json | 1 + packages/cli/package.json | 1 + packages/cli/src/commands/lightnet/faucet.ts | 3 +- .../cli/src/commands/lightnet/initialize.ts | 2 +- .../cli/src/commands/settlement/deploy.ts | 2 +- .../src/commands/settlement/tokenDeploy.ts | 2 +- packages/cli/src/scripts/bridge/deposit.ts | 15 ++-- packages/cli/src/scripts/bridge/redeem.ts | 15 ++-- .../src/scripts/settlement/deploy-token.ts | 37 +++++---- packages/cli/src/scripts/settlement/deploy.ts | 14 ++-- packages/cli/src/utils/loadEnv.ts | 14 +++- packages/cli/src/utils/modules.ts | 80 +++++++++++++++++++ 12 files changed, 142 insertions(+), 44 deletions(-) create mode 100644 packages/cli/src/utils/modules.ts diff --git a/package-lock.json b/package-lock.json index 1d57189ac..190c5394c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29004,6 +29004,7 @@ "@proto-kit/indexer": "*", "@proto-kit/library": "*", "@proto-kit/module": "*", + "@proto-kit/persistance": "*", "@proto-kit/protocol": "*", "@proto-kit/sdk": "*", "@proto-kit/sequencer": "*", diff --git a/packages/cli/package.json b/packages/cli/package.json index 3f30a7a27..caee8ca91 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -41,6 +41,7 @@ "@proto-kit/sequencer": "*", "@proto-kit/stack": "*", "@proto-kit/indexer": "*", + "@proto-kit/persistance": "*", "o1js": "^2.10.0", "tsyringe": "^4.10.0" }, diff --git a/packages/cli/src/commands/lightnet/faucet.ts b/packages/cli/src/commands/lightnet/faucet.ts index b4f3cb38c..57c28ffc7 100644 --- a/packages/cli/src/commands/lightnet/faucet.ts +++ b/packages/cli/src/commands/lightnet/faucet.ts @@ -11,7 +11,8 @@ interface FaucetArgs { export const faucetCommand: CommandModule<{}, FaucetArgs> = { command: "faucet ", - describe: "Send MINA to an account from the lightnet faucet", + describe: + "Send MINA to an account from the lightnet faucet\n\nRequires: MINA_NODE_GRAPHQL_HOST, MINA_NODE_GRAPHQL_PORT, MINA_ARCHIVE_GRAPHQL_HOST, MINA_ARCHIVE_GRAPHQL_PORT, MINA_ACCOUNT_MANAGER_HOST, MINA_ACCOUNT_MANAGER_PORT", builder: (yarg) => addEnvironmentOptions( yarg.positional("publicKey", { diff --git a/packages/cli/src/commands/lightnet/initialize.ts b/packages/cli/src/commands/lightnet/initialize.ts index 3adfd8578..ab1a9d39e 100644 --- a/packages/cli/src/commands/lightnet/initialize.ts +++ b/packages/cli/src/commands/lightnet/initialize.ts @@ -11,7 +11,7 @@ interface InitializeArgs { export const initializeCommand: CommandModule<{}, InitializeArgs> = { command: "initialize", describe: - "Initialize lightnet: wait for network, fund accounts, and deploy settlement\n\nRequires: MINA_NODE_GRAPHQL_HOST, MINA_NODE_GRAPHQL_PORT, MINA_ARCHIVE_GRAPHQL_HOST, MINA_ARCHIVE_GRAPHQL_PORT, MINA_ACCOUNT_MANAGER_HOST, MINA_ACCOUNT_MANAGER_PORT, PROTOKIT_SETTLEMENT_CONTRACT_PRIVATE_KEY, PROTOKIT_DISPATCHER_CONTRACT_PRIVATE_KEY, PROTOKIT_MINA_BRIDGE_CONTRACT_PRIVATE_KEY", + "Initialize lightnet: wait for network, fund accounts, and deploy settlement\n\nRequires: MINA_NODE_GRAPHQL_HOST, MINA_NODE_GRAPHQL_PORT, MINA_ARCHIVE_GRAPHQL_HOST, MINA_ARCHIVE_GRAPHQL_PORT, MINA_ACCOUNT_MANAGER_HOST, MINA_ACCOUNT_MANAGER_PORT, PROTOKIT_SEQUENCER_PUBLIC_KEY, TEST_ACCOUNT_1_PUBLIC_KEY, PROTOKIT_SETTLEMENT_CONTRACT_PUBLIC_KEY, PROTOKIT_DISPATCHER_CONTRACT_PUBLIC_KEY", builder: (yarg) => addEnvironmentOptions(yarg), handler: async (args) => { try { diff --git a/packages/cli/src/commands/settlement/deploy.ts b/packages/cli/src/commands/settlement/deploy.ts index 11ba65f54..972895c87 100644 --- a/packages/cli/src/commands/settlement/deploy.ts +++ b/packages/cli/src/commands/settlement/deploy.ts @@ -11,7 +11,7 @@ interface DeployArgs { export const deployCommand: CommandModule<{}, DeployArgs> = { command: "deploy", describe: - "Deploy settlement contracts\n\nRequires: PROTOKIT_SETTLEMENT_CONTRACT_PRIVATE_KEY, PROTOKIT_DISPATCHER_CONTRACT_PRIVATE_KEY, PROTOKIT_MINA_BRIDGE_CONTRACT_PRIVATE_KEY", + "Deploy settlement contracts\n\nRequires: PROTOKIT_SETTLEMENT_CONTRACT_PUBLIC_KEY, PROTOKIT_DISPATCHER_CONTRACT_PUBLIC_KEY", builder: (yarg) => addEnvironmentOptions(yarg), handler: async (args) => { try { diff --git a/packages/cli/src/commands/settlement/tokenDeploy.ts b/packages/cli/src/commands/settlement/tokenDeploy.ts index b9277509d..38af039d1 100644 --- a/packages/cli/src/commands/settlement/tokenDeploy.ts +++ b/packages/cli/src/commands/settlement/tokenDeploy.ts @@ -16,7 +16,7 @@ export const tokenDeployCommand: CommandModule<{}, TokenDeployArgs> = { command: "token-deploy [mintAmount]", describe: - "Deploy custom fungible token for settlement\n\nRequires: PROTOKIT_SETTLEMENT_CONTRACT_PRIVATE_KEY, PROTOKIT_DISPATCHER_CONTRACT_PRIVATE_KEY, PROTOKIT_CUSTOM_TOKEN_PRIVATE_KEY, PROTOKIT_CUSTOM_TOKEN_ADMIN_PRIVATE_KEY, PROTOKIT_CUSTOM_TOKEN_BRIDGE_PRIVATE_KEY", + "Deploy custom fungible token for settlement\n\nRequires: REDIS_HOST, REDIS_PORT, REDIS_PASSWORD, DATABASE_URL, PROTOKIT_SETTLEMENT_CONTRACT_PRIVATE_KEY, PROTOKIT_DISPATCHER_CONTRACT_PRIVATE_KEY", builder: (yarg) => addEnvironmentOptions( yarg diff --git a/packages/cli/src/scripts/bridge/deposit.ts b/packages/cli/src/scripts/bridge/deposit.ts index 5272d5de2..50b471313 100644 --- a/packages/cli/src/scripts/bridge/deposit.ts +++ b/packages/cli/src/scripts/bridge/deposit.ts @@ -1,4 +1,5 @@ import { DispatchSmartContract } from "@proto-kit/protocol"; +import { InMemoryDatabase } from "@proto-kit/sequencer"; import { loadEnvironmentVariables, @@ -25,6 +26,9 @@ export default async function ( ); } loadEnvironmentVariables(options); + const { scriptModules, scriptModulesConfig } = + await import("../../utils/modules"); + const { BridgingModule, MinaTransactionSender, @@ -34,7 +38,6 @@ export default async function ( } = await import("@proto-kit/sequencer"); const { Runtime } = await import("@proto-kit/module"); const { Protocol } = await import("@proto-kit/protocol"); - const { DefaultConfigs, DefaultModules } = await import("@proto-kit/stack"); const { AccountUpdate, fetchAccount, @@ -87,8 +90,8 @@ export default async function ( ...protocol.settlementModules, }), Sequencer: Sequencer.from({ - ...DefaultModules.inMemoryDatabase(), - ...DefaultModules.settlementScript(), + Database: InMemoryDatabase, + ...scriptModules, }), }); @@ -99,10 +102,8 @@ export default async function ( ...protocol.settlementModulesConfig, }, Sequencer: { - ...DefaultConfigs.inMemoryDatabase(), - ...DefaultConfigs.settlementScript({ - preset: "development", - }), + Database: {}, + ...scriptModulesConfig, }, }); diff --git a/packages/cli/src/scripts/bridge/redeem.ts b/packages/cli/src/scripts/bridge/redeem.ts index efa950d39..4595f481a 100644 --- a/packages/cli/src/scripts/bridge/redeem.ts +++ b/packages/cli/src/scripts/bridge/redeem.ts @@ -1,3 +1,5 @@ +import { InMemoryDatabase } from "@proto-kit/sequencer"; + import { loadEnvironmentVariables, getRequiredEnv, @@ -22,6 +24,8 @@ export default async function ( ); } loadEnvironmentVariables(options); + const { scriptModules, scriptModulesConfig } = + await import("../../utils/modules"); const { BridgingModule, @@ -42,7 +46,6 @@ export default async function ( UInt64, } = await import("o1js"); const { FungibleToken } = await import("mina-fungible-token"); - const { DefaultConfigs, DefaultModules } = await import("@proto-kit/stack"); const { runtime, protocol } = await loadUserModules(); const tokenId = Field(bridgeArgs.tokenId); const toPrivateKey = PrivateKey.fromBase58( @@ -72,8 +75,8 @@ export default async function ( ...protocol.settlementModules, }), Sequencer: Sequencer.from({ - ...DefaultModules.inMemoryDatabase(), - ...DefaultModules.settlementScript(), + Database: InMemoryDatabase, + ...scriptModules, }), }); @@ -84,10 +87,8 @@ export default async function ( ...protocol.settlementModulesConfig, }, Sequencer: { - ...DefaultConfigs.inMemoryDatabase(), - ...DefaultConfigs.settlementScript({ - preset: "development", - }), + Database: {}, + ...scriptModulesConfig, }, }); diff --git a/packages/cli/src/scripts/settlement/deploy-token.ts b/packages/cli/src/scripts/settlement/deploy-token.ts index 419435901..78145b63a 100644 --- a/packages/cli/src/scripts/settlement/deploy-token.ts +++ b/packages/cli/src/scripts/settlement/deploy-token.ts @@ -1,10 +1,15 @@ /* eslint-disable no-inner-declarations */ import { DispatchSmartContract } from "@proto-kit/protocol"; +import { PrismaRedisDatabase } from "@proto-kit/persistance"; import "reflect-metadata"; import { container } from "tsyringe"; -import { loadEnvironmentVariables, LoadEnvOptions } from "../../utils/loadEnv"; +import { + getRequiredEnv, + loadEnvironmentVariables, + LoadEnvOptions, +} from "../../utils/loadEnv"; import { loadUserModules } from "../../utils/loadUserModules"; export interface TokenDeployArgs { @@ -25,6 +30,8 @@ export default async function ( ); } loadEnvironmentVariables(options); + const { scriptModules, scriptModulesConfig } = + await import("../../utils/modules"); const { Runtime } = await import("@proto-kit/module"); const { Protocol } = await import("@proto-kit/protocol"); @@ -51,7 +58,6 @@ export default async function ( } = await import("o1js"); const { FungibleToken, FungibleTokenAdmin } = await import("mina-fungible-token"); - const { DefaultConfigs, DefaultModules } = await import("@proto-kit/stack"); const { runtime, protocol } = await loadUserModules(); const appChain = AppChain.from({ @@ -61,8 +67,8 @@ export default async function ( ...protocol.settlementModules, }), Sequencer: Sequencer.from({ - ...DefaultModules.prismaRedisDatabase(), - ...DefaultModules.settlementScript(), + Database: PrismaRedisDatabase, + ...scriptModules, }), }); @@ -73,15 +79,18 @@ export default async function ( ...protocol.settlementModulesConfig, }, Sequencer: { - ...DefaultConfigs.prismaRedisDatabase({ - preset: "development", - overrides: { - pruneOnStartup: false, + Database: { + redis: { + host: getRequiredEnv("REDIS_HOST"), + port: Number(getRequiredEnv("REDIS_PORT")), + password: getRequiredEnv("REDIS_PASSWORD"), }, - }), - ...DefaultConfigs.settlementScript({ - preset: "development", - }), + prisma: { + connection: getRequiredEnv("DATABASE_URL"), + }, + pruneOnStartup: false, + }, + ...scriptModulesConfig, }, }); @@ -256,10 +265,10 @@ export default async function ( Provable.log("Deployed and initialized settlement contracts", { settlement: PrivateKey.fromBase58( - process.env.PROTOKIT_SETTLEMENT_CONTRACT_PRIVATE_KEY! + getRequiredEnv("PROTOKIT_SETTLEMENT_CONTRACT_PRIVATE_KEY") ).toPublicKey(), dispatcher: PrivateKey.fromBase58( - process.env.PROTOKIT_DISPATCHER_CONTRACT_PRIVATE_KEY! + getRequiredEnv("PROTOKIT_DISPATCHER_CONTRACT_PRIVATE_KEY") ).toPublicKey(), }); diff --git a/packages/cli/src/scripts/settlement/deploy.ts b/packages/cli/src/scripts/settlement/deploy.ts index 2d206fb16..c4a3c63e5 100644 --- a/packages/cli/src/scripts/settlement/deploy.ts +++ b/packages/cli/src/scripts/settlement/deploy.ts @@ -1,6 +1,5 @@ import "reflect-metadata"; import { container } from "tsyringe"; -import type { Environment } from "@proto-kit/stack"; import { loadEnvironmentVariables, @@ -17,8 +16,10 @@ export default async function (options: LoadEnvOptions) { const { AppChain, Sequencer, SettlementModule, InMemoryDatabase } = await import("@proto-kit/sequencer"); - const { DefaultModules, DefaultConfigs } = await import("@proto-kit/stack"); loadEnvironmentVariables(options); + const { scriptModules, scriptModulesConfig } = + await import("../../utils/modules"); + const { runtime, protocol } = await loadUserModules(); const appChain = AppChain.from({ Runtime: Runtime.from(runtime.modules), @@ -28,7 +29,7 @@ export default async function (options: LoadEnvOptions) { }), Sequencer: Sequencer.from({ Database: InMemoryDatabase, - ...DefaultModules.settlementScript(), + ...scriptModules, }), }); @@ -39,11 +40,8 @@ export default async function (options: LoadEnvOptions) { ...protocol.settlementModulesConfig, }, Sequencer: { - ...DefaultConfigs.inMemoryDatabase(), - ...DefaultConfigs.settlementScript({ - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - preset: options.env as Environment, - }), + Database: {}, + ...scriptModulesConfig, SettlementModule: { addresses: undefined, }, diff --git a/packages/cli/src/utils/loadEnv.ts b/packages/cli/src/utils/loadEnv.ts index a79453e5e..8806749e1 100644 --- a/packages/cli/src/utils/loadEnv.ts +++ b/packages/cli/src/utils/loadEnv.ts @@ -13,12 +13,18 @@ export type LoadEnvOptions = { export function loadEnvironmentVariables(options: LoadEnvOptions) { const cwd = process.cwd(); + + const scriptsEnvPath = path.join( + resolveChainPath(), + `./src/core/environments/${options.env}/scripts.env` + ); + const defaultEnvPath = path.join( + resolveChainPath(), + `./src/core/environments/${options.env}/.env` + ); const env = options.envPath ?? - path.join( - resolveChainPath(), - `./src/core/environments/${options.env}/.env` - ); + (fs.existsSync(scriptsEnvPath) ? scriptsEnvPath : defaultEnvPath); const envPath = path.isAbsolute(env) ? env : path.join(cwd, env); if (fs.existsSync(envPath)) { dotenv.config({ path: envPath }); diff --git a/packages/cli/src/utils/modules.ts b/packages/cli/src/utils/modules.ts new file mode 100644 index 000000000..7be304f0c --- /dev/null +++ b/packages/cli/src/utils/modules.ts @@ -0,0 +1,80 @@ +import { + PrivateMempool, + SequencerStartupModule, + WorkerModule, + VanillaTaskWorkerModules, + MinaBaseLayer, + ConstantFeeStrategy, + BatchProducerModule, + SettlementModule, + LocalTaskQueue, + InMemoryMinaSigner, + BridgingModule, +} from "@proto-kit/sequencer"; +import { PrivateKey } from "o1js"; + +import { getRequiredEnv } from "./loadEnv"; + +export const scriptModules = { + BaseLayer: MinaBaseLayer, + FeeStrategy: ConstantFeeStrategy, + BatchProducerModule, + SettlementModule, + SettlementSigner: InMemoryMinaSigner, + BridgingModule, + Mempool: PrivateMempool, + TaskQueue: LocalTaskQueue, + WorkerModule: WorkerModule.from(VanillaTaskWorkerModules.allTasks()), + SequencerStartupModule, +}; + +export const scriptModulesConfig = { + BaseLayer: { + network: { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + type: getRequiredEnv("MINA_NETWORK") as "local" | "lightnet" | "remote", + graphql: `${getRequiredEnv("MINA_NODE_GRAPHQL_HOST")}:${getRequiredEnv("MINA_NODE_GRAPHQL_PORT")}/graphql`, + archive: `${getRequiredEnv("MINA_ARCHIVE_GRAPHQL_HOST")}:${getRequiredEnv("MINA_ARCHIVE_GRAPHQL_PORT")}`, + accountManager: `${getRequiredEnv("MINA_ACCOUNT_MANAGER_HOST")}:${getRequiredEnv("MINA_ACCOUNT_MANAGER_PORT")}`, + }, + }, + SettlementModule: { + addresses: { + SettlementContract: PrivateKey.fromBase58( + getRequiredEnv("PROTOKIT_SETTLEMENT_CONTRACT_PRIVATE_KEY") + ).toPublicKey(), + }, + }, + BridgingModule: { + addresses: { + DispatchContract: PrivateKey.fromBase58( + getRequiredEnv("PROTOKIT_DISPATCHER_CONTRACT_PRIVATE_KEY") + ).toPublicKey(), + }, + }, + SettlementSigner: { + feepayer: PrivateKey.fromBase58( + getRequiredEnv("PROTOKIT_SEQUENCER_PRIVATE_KEY") + ), + contractKeys: [ + PrivateKey.fromBase58( + getRequiredEnv("PROTOKIT_SETTLEMENT_CONTRACT_PRIVATE_KEY") + ), + PrivateKey.fromBase58( + getRequiredEnv("PROTOKIT_DISPATCHER_CONTRACT_PRIVATE_KEY") + ), + PrivateKey.fromBase58( + getRequiredEnv("PROTOKIT_MINA_BRIDGE_CONTRACT_PRIVATE_KEY") + ), + ], + }, + FeeStrategy: {}, + BatchProducerModule: {}, + WorkerModule: VanillaTaskWorkerModules.defaultConfig(), + SequencerStartupModule: {}, + TaskQueue: { + simulatedDuration: 0, + }, + LocalTaskWorker: VanillaTaskWorkerModules.defaultConfig(), + Mempool: {}, +}; From 78e48c9ea1355e1d9a1b91911872fbc3caa91d0e Mon Sep 17 00:00:00 2001 From: stanlou Date: Wed, 1 Apr 2026 04:32:25 +0100 Subject: [PATCH 2/3] refactor: adapt wizard --- packages/cli/src/commands/wizard.ts | 2 +- .../generate.ts} | 18 +- packages/cli/src/utils/create-environment.ts | 442 ---------- packages/cli/src/utils/wizard.ts | 811 ++++++++++++++++++ 4 files changed, 820 insertions(+), 453 deletions(-) rename packages/cli/src/scripts/{env/create-environment.ts => wizard/generate.ts} (88%) delete mode 100644 packages/cli/src/utils/create-environment.ts create mode 100644 packages/cli/src/utils/wizard.ts diff --git a/packages/cli/src/commands/wizard.ts b/packages/cli/src/commands/wizard.ts index 61d66090f..a0ff98047 100644 --- a/packages/cli/src/commands/wizard.ts +++ b/packages/cli/src/commands/wizard.ts @@ -7,7 +7,7 @@ export const wizardCommand: CommandModule<{}> = { handler: async () => { try { const { default: createEnvironment } = - await import("../scripts/env/create-environment"); + await import("../scripts/wizard/generate"); await createEnvironment(); process.exit(0); } catch (error) { diff --git a/packages/cli/src/scripts/env/create-environment.ts b/packages/cli/src/scripts/wizard/generate.ts similarity index 88% rename from packages/cli/src/scripts/env/create-environment.ts rename to packages/cli/src/scripts/wizard/generate.ts index 22821366d..5fa4c90b4 100644 --- a/packages/cli/src/scripts/env/create-environment.ts +++ b/packages/cli/src/scripts/wizard/generate.ts @@ -12,7 +12,7 @@ import { generateWorkerConfig, icons, promptUser, -} from "../../utils/create-environment"; +} from "../../utils/wizard"; import { resolveChainPath } from "../../utils/pathResolver"; export default async function () { @@ -46,24 +46,22 @@ export default async function () { fs.writeFileSync(chainConfigPath, chainConfig); if (answers.includeIndexer) { - const indexerConfig = generateIndexerConfig(answers); + const indexerConfig = generateIndexerConfig(); if (indexerConfig) { fs.writeFileSync(indexerConfigPath, indexerConfig); } } - if (answers.includeProcessor && answers.includeIndexer) { - const processorConfig = generateProcessorConfig(answers); + if (answers.includeProcessor) { + const processorConfig = generateProcessorConfig(); if (processorConfig) { fs.writeFileSync(processorConfigPath, processorConfig); } } - if (answers.preset !== "inmemory") { - const workerConfig = generateWorkerConfig(answers); - if (workerConfig) { - fs.writeFileSync(workerConfigPath, workerConfig); - } + const workerConfig = generateWorkerConfig(answers); + if (workerConfig) { + fs.writeFileSync(workerConfigPath, workerConfig); } console.log( @@ -89,7 +87,7 @@ export default async function () { if (answers.includeProcessor && answers.includeIndexer) { console.log(` ${green(icons.checkmark)} processor.config.ts`); } - if (answers.preset !== "inmemory") { + if (workerConfig) { console.log(` ${green(icons.checkmark)} worker.config.ts`); } diff --git a/packages/cli/src/utils/create-environment.ts b/packages/cli/src/utils/create-environment.ts deleted file mode 100644 index f95eecb80..000000000 --- a/packages/cli/src/utils/create-environment.ts +++ /dev/null @@ -1,442 +0,0 @@ -import * as fs from "fs"; -import * as path from "path"; - -import inquirer from "inquirer"; -import figuresLib from "@inquirer/figures"; -import { cyan, green, blue, gray, bold } from "kleur/colors"; - -import { resolveChainPath } from "./pathResolver"; - -export const icons = { - checkmark: figuresLib.tick, - cross: figuresLib.cross, - arrow: figuresLib.pointerSmall, - circle: figuresLib.bullet, - square: figuresLib.square, -}; - -export type PresetType = "inmemory" | "development" | "sovereign"; - -export interface WizardAnswers { - environmentName: string; - preset: PresetType; - includeIndexer: boolean; - includeProcessor: boolean; - includeMetrics: boolean; - settlementEnabled: boolean; -} - -export const PRESET_ENV_NAMES: Record = { - inmemory: "inmemory", - development: "development", - sovereign: "sovereign", -}; - -export const PRESET_DESCRIPTIONS: Record = { - inmemory: "Fast testing and development environment", - development: "Local development environment", - sovereign: "Production-ready environment", -}; - -export const PRESET_LABELS: Record = { - inmemory: "In-Memory", - development: "Development", - sovereign: "Sovereign", -}; - -export function printHeader(): void { - console.log(bold(cyan(" ╔════════════════════════════════════════╗"))); - console.log(bold(cyan(" ║ 🚀 Proto-Kit Environment Wizard ║"))); - console.log(bold(cyan(" ╚════════════════════════════════════════╝"))); - console.log(""); -} - -export function printSection(title: string): void { - const section = `${icons.square} ${title}`; - console.log(`\n${bold(blue(section))}`); - console.log(gray("-".repeat(50))); - console.log(""); -} - -export async function selectPreset(): Promise { - const presetTypes: PresetType[] = ["inmemory", "development", "sovereign"]; - const answer = await inquirer.prompt<{ preset: PresetType }>([ - { - type: "list", - name: "preset", - message: "Select Environment Preset", - choices: presetTypes.map((type) => { - const label = PRESET_LABELS[type]; - const description = PRESET_DESCRIPTIONS[type]; - return { - name: `${label} - ${description}`, - value: type, - }; - }), - }, - ]); - - return answer.preset; -} - -export async function selectModules(preset: PresetType): Promise<{ - includeIndexer: boolean; - includeProcessor: boolean; - includeMetrics: boolean; - settlementEnabled: boolean; -}> { - const isInMemory = preset === "inmemory"; - - const answers = await inquirer.prompt<{ - includeIndexer: boolean; - includeProcessor: boolean; - includeMetrics: boolean; - settlementEnabled: boolean; - }>([ - { - type: "confirm", - name: "includeIndexer", - message: "Include Indexer Module?", - default: false, - when: !isInMemory, - }, - { - type: "confirm", - name: "includeProcessor", - message: "Include Processor Module? (requires Indexer)", - default: false, - when: (ans: WizardAnswers) => ans.includeIndexer === true, - }, - { - type: "confirm", - name: "includeMetrics", - message: "Include OpenTelemetry Metrics?", - default: false, - }, - { - type: "confirm", - name: "settlementEnabled", - message: "Enable Settlement Module?", - default: false, - }, - ]); - if (isInMemory && answers.includeIndexer === false) { - answers.includeIndexer = false; - } - if (answers.includeProcessor === false) { - answers.includeProcessor = false; - } - - return answers; -} - -export async function promptUser(): Promise { - printHeader(); - - printSection("Environment Configuration"); - - const answers = await inquirer.prompt<{ environmentName: string }>([ - { - type: "input", - name: "environmentName", - message: "Environment name (e.g 'production')", - validate: (input: string) => { - if (!input.trim()) { - return "Environment name is required"; - } - return true; - }, - }, - ]); - - const environmentName = (answers.environmentName ?? "").trim(); - const confirmationMessage = `${icons.checkmark} Environment: ${environmentName}`; - console.log(`${green(confirmationMessage)}\n`); - - const preset = await selectPreset(); - - printSection("Configure Modules"); - const modules = await selectModules(preset); - - return { - environmentName, - preset, - ...modules, - }; -} - -export function generateChainConfig(answers: WizardAnswers): string { - const presetEnv = PRESET_ENV_NAMES[answers.preset]; - const isInMemory = answers.preset === "inmemory"; - - const moduleParts: string[] = []; - - if (answers.includeMetrics) { - moduleParts.push(" ...DefaultModules.metrics(),"); - } - if (isInMemory) { - moduleParts.push(" ...DefaultModules.inMemoryDatabase(),"); - } else { - moduleParts.push(" ...DefaultModules.prismaRedisDatabase(),"); - } - moduleParts.push( - ` ...DefaultModules.core({ settlementEnabled: ${answers.settlementEnabled} }),` - ); - if (isInMemory) { - moduleParts.push(" ...DefaultModules.localTaskQueue(),"); - } else { - moduleParts.push(" ...DefaultModules.redisTaskQueue(),"); - } - if (answers.includeIndexer) { - moduleParts.push(" ...DefaultModules.sequencerIndexer(),"); - } - const modulesString = moduleParts.join("\n"); - const configParts: string[] = []; - const coreConfig = ` ...DefaultConfigs.core({ settlementEnabled: ${answers.settlementEnabled}, preset: "${presetEnv}" }),`; - configParts.push(coreConfig); - if (answers.includeIndexer) { - configParts.push(" ...DefaultConfigs.sequencerIndexer(),"); - } - if (answers.includeMetrics) { - configParts.push( - ` ...DefaultConfigs.metrics({ preset: "${presetEnv}" }),` - ); - } - if (isInMemory) { - configParts.push(" ...DefaultConfigs.localTaskQueue(),"); - configParts.push(" ...DefaultConfigs.inMemoryDatabase(),"); - } else { - configParts.push( - ` ...DefaultConfigs.redisTaskQueue({ - preset: "${presetEnv}", - overrides: { - redisDb: 1, - }, - }),` - ); - configParts.push( - ` ...DefaultConfigs.prismaRedisDatabase({ - preset: "${presetEnv}", - }),` - ); - } - const configString = configParts.join("\n"); - return `import { Runtime } from "@proto-kit/module"; -import { Protocol } from "@proto-kit/protocol"; -import { AppChain, Sequencer } from "@proto-kit/sequencer"; -import runtime from "../../../runtime"; -import * as protocol from "../../../protocol"; - -import { Arguments } from "../../../start"; -import { Startable } from "@proto-kit/common"; -import { DefaultConfigs, DefaultModules } from "@proto-kit/stack"; - -const settlementEnabled = process.env.PROTOKIT_SETTLEMENT_ENABLED! === "true"; - -const appChain = AppChain.from({ - Runtime: Runtime.from(runtime.modules), - Protocol: Protocol.from({ - ...protocol.modules, - ...(settlementEnabled ? protocol.settlementModules : {}), - }), - Sequencer: Sequencer.from({ - // ordering of the modules matters due to dependency resolution -${modulesString} - }), - ...DefaultModules.appChainBase(), -}); - -export default async (args: Arguments): Promise => { - appChain.configurePartial({ - Runtime: runtime.config, - Protocol: { - ...protocol.config, - ...(settlementEnabled ? protocol.settlementModulesConfig : {}), - }, - Sequencer: { -${configString} - }, - ...DefaultConfigs.appChainBase(), - }); - - return appChain; -};`; -} - -export function generateIndexerConfig(answers: WizardAnswers): string { - if (!answers.includeIndexer) { - return ""; - } - - const presetEnv = PRESET_ENV_NAMES[answers.preset]; - - return `import { Indexer } from "@proto-kit/indexer"; -import { Arguments } from "../../../start"; -import { Startable } from "@proto-kit/common"; -import { DefaultConfigs, DefaultModules } from "@proto-kit/stack"; - -const indexer = Indexer.from({ - ...DefaultModules.indexer(), -}); - -export default async (args: Arguments): Promise => { - indexer.configurePartial({ - ...DefaultConfigs.indexer({ - preset: "${presetEnv}", - overrides: { - pruneOnStartup: args.pruneOnStartup, - redisDb: 1, - }, - }), - }); - - return indexer; -};`; -} - -export function generateProcessorConfig(answers: WizardAnswers): string { - if (!answers.includeProcessor || !answers.includeIndexer) { - return ""; - } - - const presetEnv = PRESET_ENV_NAMES[answers.preset]; - - return `import { DatabasePruneModule, Processor } from "@proto-kit/processor"; -import { databaseModule } from "../../processor"; -import { Arguments } from "../../../start"; -import { Startable } from "@proto-kit/common"; -import { DefaultConfigs, DefaultModules } from "@proto-kit/stack"; - -import { handlers } from "../../processor/handlers"; -import { resolvers } from "../../processor/api/resolvers"; - -const processor = Processor.from({ - Database: databaseModule, - DatabasePruneModule: DatabasePruneModule, - ...DefaultModules.processor(resolvers, handlers), -}); - -export default async (args: Arguments): Promise => { - processor.configurePartial({ - ...DefaultConfigs.processor({ - preset: "${presetEnv}", - }), - Database: {}, - DatabasePruneModule: { - pruneOnStartup: args.pruneOnStartup, - }, - }); - - return processor; -};`; -} - -export function generateWorkerConfig(answers: WizardAnswers): string { - if (answers.preset === "inmemory") { - return ""; - } - - const presetEnv = PRESET_ENV_NAMES[answers.preset]; - const taskWorkerImports = answers.settlementEnabled - ? "" - : " WorkerModule, VanillaTaskWorkerModules"; - const withoutSettlementTask = answers.settlementEnabled - ? "" - : `WorkerModule: WorkerModule.from( - VanillaTaskWorkerModules.withoutSettlement() - ), - `; - return `import { Runtime } from "@proto-kit/module"; -import { Protocol } from "@proto-kit/protocol"; -import { Sequencer, AppChain, ${taskWorkerImports} } from "@proto-kit/sequencer"; -import runtime from "../../../runtime"; -import * as protocol from "../../../protocol"; -import { Arguments } from "../../../start"; - -import { log, Startable } from "@proto-kit/common"; -import { DefaultConfigs, DefaultModules } from "@proto-kit/stack"; - -const settlementEnabled = process.env.PROTOKIT_SETTLEMENT_ENABLED! === "true"; - -const appChain = AppChain.from({ - Runtime: Runtime.from(runtime.modules), - Protocol: Protocol.from({ - ...protocol.modules, - ...(settlementEnabled ? protocol.settlementModules : {}), - }), - Sequencer: Sequencer.from({ - ...DefaultModules.worker(), - ${withoutSettlementTask} - }), -}); - -export default async (args: Arguments): Promise => { - appChain.configurePartial({ - Runtime: runtime.config, - Protocol: { - ...protocol.config, - ...(settlementEnabled ? protocol.settlementModulesConfig : {}), - }, - Sequencer: DefaultConfigs.worker({ - preset: "${presetEnv}", - overrides: { - redisDb: 1, - }, - }), - }); - - log.setLevel("DEBUG"); - - return appChain; -};`; -} - -export function copyAndUpdateEnvFile( - answers: WizardAnswers, - envDir: string -): boolean { - const presetEnvPath = path.join( - resolveChainPath(true), - "src", - "core", - "environments", - answers.preset, - ".env" - ); - - if (!fs.existsSync(presetEnvPath)) { - console.warn(`Could not find .env file at ${presetEnvPath}`); - return false; - } - - try { - let envContent = fs.readFileSync(presetEnvPath, "utf-8"); - - if (envContent.includes("PROTOKIT_ENV_FOLDER=")) { - envContent = envContent.replace( - /PROTOKIT_ENV_FOLDER=.*/g, - `PROTOKIT_ENV_FOLDER=${answers.environmentName}` - ); - } else { - const envFolder = `PROTOKIT_ENV_FOLDER=${answers.environmentName}`; - envContent = `${envFolder}\n${envContent}`; - } - - if (envContent.includes("PROTOKIT_SETTLEMENT_ENABLED=")) { - envContent = envContent.replace( - /PROTOKIT_SETTLEMENT_ENABLED=.*/g, - `PROTOKIT_SETTLEMENT_ENABLED=${answers.settlementEnabled}` - ); - } else { - envContent += `\nPROTOKIT_SETTLEMENT_ENABLED=${answers.settlementEnabled}\n`; - } - - const envFilePath = path.join(envDir, ".env"); - fs.writeFileSync(envFilePath, envContent); - - return true; - } catch (error) { - console.error(`Error copying .env file: ${error}}`); - return false; - } -} diff --git a/packages/cli/src/utils/wizard.ts b/packages/cli/src/utils/wizard.ts new file mode 100644 index 000000000..eee2bc74e --- /dev/null +++ b/packages/cli/src/utils/wizard.ts @@ -0,0 +1,811 @@ +/* eslint-disable @typescript-eslint/quotes */ +import * as fs from "fs"; +import * as path from "path"; + +import inquirer from "inquirer"; +import figuresLib from "@inquirer/figures"; +import { cyan, green, blue, gray, bold } from "kleur/colors"; + +import { resolveChainPath } from "./pathResolver"; + +export const icons = { + checkmark: figuresLib.tick, + cross: figuresLib.cross, + arrow: figuresLib.pointerSmall, + circle: figuresLib.bullet, + square: figuresLib.square, +}; + +export type PresetType = "inmemory" | "development" | "sovereign"; +export type DatabaseType = "inmemory" | "prisma-redis"; +export type WorkerType = "local" | "remote"; + +export interface WizardAnswers { + environmentName: string; + preset: PresetType; + database: DatabaseType; + worker: WorkerType; + settlement: boolean; + bridging: boolean; + includeIndexer: boolean; + includeProcessor: boolean; + includeMetrics: boolean; +} + +const PRESETS: Record = { + inmemory: { label: "In-Memory", desc: "Fast testing (no persistence)" }, + development: { label: "Development", desc: "Local dev with Prisma + Redis" }, + sovereign: { label: "Sovereign", desc: "Production-ready" }, +}; + +const printHeader = () => { + console.log(bold(cyan(" ╔════════════════════════════════════════╗"))); + console.log(bold(cyan(" ║ 🚀 Proto-Kit Environment Wizard ║"))); + console.log(bold(cyan(" ╚════════════════════════════════════════╝\n"))); +}; + +const printSection = (title: string) => { + const sectionTitle = `${icons.square} ${title}`; + console.log(`\n${bold(blue(sectionTitle))}`); + console.log(gray("-".repeat(50)), "\n"); +}; + +export async function promptUser(): Promise { + printHeader(); + printSection("Environment Configuration"); + + const { environmentName } = await inquirer.prompt<{ + environmentName: string; + }>([ + { + type: "input", + name: "environmentName", + message: "Environment name (e.g 'production')", + validate: (i: string) => + i.trim() ? true : "Environment name is required", + }, + ]); + const trimmedName = environmentName.trim(); + console.log(green(`${icons.checkmark} Environment: ${trimmedName}\n`)); + + printSection("Select Preset"); + const { preset } = await inquirer.prompt<{ preset: PresetType }>([ + { + type: "list", + name: "preset", + message: "Select Preset (.env template)", + choices: Object.entries(PRESETS).map(([key, env]) => ({ + name: `${env.label} - ${env.desc}`, + value: key, + })), + }, + ]); + console.log(green(`${icons.checkmark} Preset: ${PRESETS[preset].label}\n`)); + + printSection("Infrastructure Configuration"); + const { database } = await inquirer.prompt<{ database: DatabaseType }>([ + { + type: "list", + name: "database", + message: "Select Database", + default: preset === "inmemory" ? "inmemory" : "prisma-redis", + choices: [ + { name: "In-Memory - No persistence", value: "inmemory" }, + { name: "Prisma + Redis - Production-ready", value: "prisma-redis" }, + ], + }, + ]); + + let worker: WorkerType = "local"; + let settlement = false; + let bridging = false; + let includeIndexer = false; + let includeProcessor = false; + let includeMetrics = false; + + if (database === "prisma-redis") { + const workerPrompt = await inquirer.prompt<{ worker: WorkerType }>([ + { + type: "list", + name: "worker", + message: "Worker Type", + choices: [ + { name: "Local - Local process", value: "local" }, + { name: "Remote - BullQueue + worker process", value: "remote" }, + ], + }, + ]); + worker = workerPrompt.worker; + + printSection("Settlement Configuration"); + const settlementPrompt = await inquirer.prompt<{ settlement: boolean }>([ + { + type: "confirm", + name: "settlement", + message: "Enable Settlement?", + default: false, + }, + ]); + settlement = settlementPrompt.settlement; + if (settlement) { + const bridgingPrompt = await inquirer.prompt<{ bridging: boolean }>([ + { + type: "confirm", + name: "bridging", + message: "Enable Bridging?", + default: true, + }, + ]); + bridging = bridgingPrompt.bridging; + } + + printSection("Additional Modules"); + const modulesPrompt = await inquirer.prompt<{ + includeIndexer: boolean; + includeProcessor: boolean; + includeMetrics: boolean; + }>([ + { + type: "confirm", + name: "includeIndexer", + message: "Include Indexer?", + default: false, + when: () => worker === "remote", + }, + { + type: "confirm", + name: "includeProcessor", + message: "Include Processor?", + default: false, + when: (answer) => answer.includeIndexer, + }, + { + type: "confirm", + name: "includeMetrics", + message: "Include Metrics?", + default: false, + }, + ]); + includeIndexer = modulesPrompt.includeIndexer ?? false; + includeProcessor = modulesPrompt.includeProcessor ?? false; + includeMetrics = modulesPrompt.includeMetrics ?? false; + } + + return { + environmentName: trimmedName, + preset, + database, + worker, + settlement, + bridging, + includeIndexer, + includeProcessor, + includeMetrics, + }; +} + +function formatImportStatement(imports: string[], packageName: string): string { + if (imports.length <= 2) { + return `import { ${imports.join(", ")} } from "${packageName}";`; + } + + return `import { + ${imports.join(",\n ")}, +} from "${packageName}";`; +} + +function buildSequencerImports(answer: WizardAnswers): string[] { + const imports = [ + "AppChain", + "Sequencer", + "PrivateMempool", + "TimedBlockTrigger", + ]; + + if (answer.database === "inmemory") { + imports.push("InMemoryDatabase"); + } + + if (answer.worker === "local") { + imports.push("LocalTaskQueue", "WorkerModule", "VanillaTaskWorkerModules"); + } + + imports.push( + answer.settlement ? "SequencerCoreModule" : "LocalSequencerCoreModule" + ); + + if (answer.settlement) { + imports.push( + "MinaBaseLayer", + "ConstantFeeStrategy", + "SettlementModule", + "InMemoryMinaSigner" + ); + if (answer.bridging) { + imports.push("BridgingModule"); + } + } + + return imports; +} + +function buildImportStatements(answer: WizardAnswers): string[] { + const seqImports = buildSequencerImports(answer); + const imports = [ + `import { Runtime } from "@proto-kit/module";`, + `import { Protocol } from "@proto-kit/protocol";`, + formatImportStatement(seqImports, "@proto-kit/sequencer"), + `import { VanillaGraphqlModules, GraphqlSequencerModule } from "@proto-kit/api";`, + ]; + + if (answer.database === "prisma-redis") { + imports.push( + `import { PrismaRedisDatabase } from "@proto-kit/persistance";` + ); + } + if (answer.worker === "remote") { + imports.push(`import { BullQueue } from "@proto-kit/deployment";`); + } + if (answer.includeIndexer) { + imports.push(`import { IndexerNotifier } from "@proto-kit/indexer";`); + } + + imports.push( + formatImportStatement( + [ + "BlockStorageNetworkStateModule", + "InMemoryTransactionSender", + "StateServiceQueryModule", + ], + "@proto-kit/sdk" + ), + `import { Startable } from "@proto-kit/common";`, + `import runtime from "../../../runtime";`, + `import * as protocol from "../../../protocol";` + ); + + if (answer.database === "prisma-redis") { + imports.push(`import { Arguments } from "../../../start";`); + } + if (answer.settlement) { + imports.push(`import { PrivateKey } from "o1js";`); + imports.push( + `import { + buildCustomTokenConfig, + buildSettlementTokenConfig, +} from "@proto-kit/stack";` + ); + if (!answer.bridging) { + imports.push( + `import { SettlementContractModule } from "@proto-kit/protocol";` + ); + } + } + if (answer.includeMetrics) { + imports.push( + `import { + metricsSequencerModules, + metricsSequencerModulesConfig, +} from "../../sequencer";` + ); + } + + return imports; +} + +function buildSequencerModules(answer: WizardAnswers): string[] { + const modules: string[] = []; + + if (answer.includeMetrics) { + modules.push("...metricsSequencerModules,"); + } + + modules.push( + answer.database === "inmemory" + ? "Database: InMemoryDatabase," + : "Database: PrismaRedisDatabase," + ); + + if (answer.worker === "remote") { + modules.push("TaskQueue: BullQueue,"); + } else { + const tasks = answer.settlement ? "allTasks()" : "withoutSettlement()"; + modules.push( + "TaskQueue: LocalTaskQueue,", + `WorkerModule: WorkerModule.from( + VanillaTaskWorkerModules.${tasks} + ),` + ); + } + + modules.push( + "Graphql: GraphqlSequencerModule.from(VanillaGraphqlModules.with({})),", + "Mempool: PrivateMempool,", + "BlockTrigger: TimedBlockTrigger," + ); + + modules.push( + answer.settlement ? "SequencerCoreModule," : "LocalSequencerCoreModule," + ); + + if (answer.settlement) { + modules.push( + "BaseLayer: MinaBaseLayer,", + "FeeStrategy: ConstantFeeStrategy,", + "SettlementModule,", + "SettlementSigner: InMemoryMinaSigner," + ); + if (answer.bridging) { + modules.push("BridgingModule,"); + } + } + if (answer.includeIndexer) { + modules.push("IndexerNotifier,"); + } + + return modules; +} + +function buildDatabaseConfig(answer: WizardAnswers): string[] { + const config: string[] = []; + + if (answer.database === "inmemory") { + config.push("Database: {},"); + } else { + config.push( + `Database: { + redis: { + host: process.env.REDIS_HOST!, + port: Number(process.env.REDIS_PORT!), + password: process.env.REDIS_PASSWORD, + }, + prisma: { connection: process.env.DATABASE_URL! }, + pruneOnStartup: args.pruneOnStartup, + },` + ); + } + + return config; +} + +function buildWorkerConfig(answer: WizardAnswers): string[] { + const config: string[] = []; + + if (answer.worker === "remote") { + config.push( + `TaskQueue: { + redis: { + host: process.env.REDIS_HOST!, + port: Number(process.env.REDIS_PORT!), + password: process.env.REDIS_PASSWORD, + db: 1, + }, + },` + ); + } else { + config.push( + "TaskQueue: {},", + "WorkerModule: VanillaTaskWorkerModules.defaultConfig()," + ); + } + + return config; +} + +function buildBlockTriggerConfig(answer: WizardAnswers): string { + const interval = answer.database === "inmemory" ? 5000 : 30000; + + if (answer.settlement) { + return `BlockTrigger: { + blockInterval: ${interval}, + produceEmptyBlocks: true, + settlementInterval: 60000, + settlementTokenConfig: buildSettlementTokenConfig( + process.env.PROTOKIT_MINA_BRIDGE_CONTRACT_PRIVATE_KEY!, + buildCustomTokenConfig( + process.env.PROTOKIT_CUSTOM_TOKEN_PRIVATE_KEY, + process.env.PROTOKIT_CUSTOM_TOKEN_BRIDGE_PRIVATE_KEY + ) + ), + },`; + } + + return `BlockTrigger: { + blockInterval: ${interval}, + produceEmptyBlocks: true, + settlementTokenConfig: {}, + },`; +} + +function buildCoreModuleConfig(answer: WizardAnswers): string { + if (answer.settlement) { + return `SequencerCoreModule: { + BlockProducerModule: {}, + BatchProducerModule: {}, + SequencerStartupModule: {}, + },`; + } + + return `LocalSequencerCoreModule: { + SequencerStartupModule: {}, + BlockProducerModule: {}, + },`; +} + +function buildSettlementConfig(answer: WizardAnswers): string[] { + const config: string[] = []; + + if (!answer.settlement) return config; + + config.push( + `BaseLayer: { + network: { + type: + (process.env.MINA_NETWORK as "local" | "lightnet" | "remote") ?? + "lightnet", + graphql: \`\${process.env.MINA_NODE_GRAPHQL_HOST}:\${process.env.MINA_NODE_GRAPHQL_PORT}/graphql\`, + archive: \`\${process.env.MINA_ARCHIVE_GRAPHQL_HOST}:\${process.env.MINA_ARCHIVE_GRAPHQL_PORT}\`, + accountManager: \`\${process.env.MINA_ACCOUNT_MANAGER_HOST}:\${process.env.MINA_ACCOUNT_MANAGER_PORT}\`, + }, + },`, + "FeeStrategy: {},", + `SettlementModule: { + addresses: { + SettlementContract: PrivateKey.fromBase58( + process.env.PROTOKIT_SETTLEMENT_CONTRACT_PRIVATE_KEY! + ).toPublicKey(), + }, + },`, + `SettlementSigner: { + feepayer: PrivateKey.fromBase58( + process.env.PROTOKIT_SEQUENCER_PRIVATE_KEY! + ), + contractKeys: [ + PrivateKey.fromBase58( + process.env.PROTOKIT_SETTLEMENT_CONTRACT_PRIVATE_KEY! + ), + PrivateKey.fromBase58( + process.env.PROTOKIT_DISPATCHER_CONTRACT_PRIVATE_KEY! + ), + PrivateKey.fromBase58( + process.env.PROTOKIT_MINA_BRIDGE_CONTRACT_PRIVATE_KEY! + ), + ], + },` + ); + + if (answer.bridging) { + config.push( + `BridgingModule: { + addresses: { + DispatchContract: PrivateKey.fromBase58( + process.env.PROTOKIT_DISPATCHER_CONTRACT_PRIVATE_KEY! + ).toPublicKey(), + }, + },` + ); + } + + return config; +} + +function buildSequencerConfig(answer: WizardAnswers): string[] { + const config: string[] = []; + + if (answer.includeMetrics) { + config.push("...metricsSequencerModulesConfig,"); + } + + config.push(...buildDatabaseConfig(answer)); + config.push(...buildWorkerConfig(answer)); + config.push( + `Graphql: { + ...VanillaGraphqlModules.defaultConfig(), + containerConfig: { + port: Number(process.env.PROTOKIT_GRAPHQL_PORT), + host: process.env.PROTOKIT_GRAPHQL_HOST!, + graphiql: true, + }, + },` + ); + config.push("Mempool: {},"); + config.push(buildBlockTriggerConfig(answer)); + config.push(buildCoreModuleConfig(answer)); + config.push(...buildSettlementConfig(answer)); + + if (answer.includeIndexer) { + config.push("IndexerNotifier: {},"); + } + + return config; +} + +function buildProtocolConfig(answer: WizardAnswers): string { + if (!answer.settlement) { + return "protocol.modules"; + } + + if (answer.settlement && !answer.bridging) { + return `{ + ...protocol.modules, + SettlementContractModule: SettlementContractModule.from( + SettlementContractModule.settlementOnly() + ), + }`; + } + + return `{ + ...protocol.modules, + ...protocol.settlementModules, + }`; +} + +function buildProtocolConfigData(answer: WizardAnswers): string { + if (!answer.settlement) { + return "protocol.config"; + } + + if (answer.settlement && !answer.bridging) { + return `{ + ...protocol.config, + SettlementContractModule: { + SettlementContract: {}, + }, + }`; + } + + return "{ ...protocol.config, ...protocol.settlementModulesConfig }"; +} + +export function generateChainConfig(answer: WizardAnswers): string { + const imports = buildImportStatements(answer); + const modules = buildSequencerModules(answer); + const config = buildSequencerConfig(answer); + const protocolMods = buildProtocolConfig(answer); + const protocolCfg = buildProtocolConfigData(answer); + const hasArgument = answer.database === "prisma-redis"; + + return `${imports.join("\n")} + +const appChain = AppChain.from({ + Runtime: Runtime.from(runtime.modules), + Protocol: Protocol.from(${protocolMods}), + Sequencer: Sequencer.from({ + ${modules.join("\n ")} + }), + TransactionSender: InMemoryTransactionSender, + QueryTransportModule: StateServiceQueryModule, + NetworkStateTransportModule: BlockStorageNetworkStateModule, +}); + +export default async ${ + hasArgument ? "(args: Arguments)" : "()" + }: Promise => { + appChain.configure({ + Runtime: runtime.config, + Protocol: ${protocolCfg}, + Sequencer: { + ${config.join("\n ")} + }, + QueryTransportModule: {}, + NetworkStateTransportModule: {}, + TransactionSender: {}, + }); + return appChain; +}; +`; +} + +export function generateIndexerConfig(): string { + return `import { + Indexer, + IndexBlockTask, + IndexBatchTask, + IndexPendingTxTask, + IndexSettlementTask, + GeneratedResolverFactoryGraphqlModule, +} from "@proto-kit/indexer"; +import { GraphqlSequencerModule } from "@proto-kit/api"; +import { WorkerModule } from "@proto-kit/sequencer"; +import { PrismaRedisDatabase } from "@proto-kit/persistance"; +import { BullQueue } from "@proto-kit/deployment"; +import { Arguments } from "../../../start"; +import { Startable } from "@proto-kit/common"; + +const indexer = Indexer.from({ + Database: PrismaRedisDatabase, + TaskQueue: BullQueue, + TaskWorker: WorkerModule.from({ + IndexBlockTask, + IndexPendingTxTask, + IndexBatchTask, + IndexSettlementTask, + }), + Graphql: GraphqlSequencerModule.from({ + GeneratedResolverFactory: GeneratedResolverFactoryGraphqlModule, + }), +}); + +export default async (args: Arguments): Promise => { + indexer.configurePartial({ + Database: { + redis: { + host: process.env.REDIS_HOST ?? "localhost", + port: Number(process.env.REDIS_PORT ?? 6379), + password: process.env.REDIS_PASSWORD ?? "password", + }, + prisma: { + connection: process.env.INDEXER_DATABASE_URL!, + }, + pruneOnStartup: args.pruneOnStartup, + }, + TaskQueue: { + redis: { + host: process.env.REDIS_HOST ?? "localhost", + port: Number(process.env.REDIS_PORT ?? 6379), + password: process.env.REDIS_PASSWORD ?? "password", + db: 1, + }, + }, + TaskWorker: { + IndexBlockTask: {}, + IndexBatchTask: {}, + IndexPendingTxTask: {}, + IndexSettlementTask: {}, + }, + Graphql: { + GeneratedResolverFactory: {}, + containerConfig: { + port: Number(process.env.PROTOKIT_INDEXER_GRAPHQL_PORT ?? 8081), + host: process.env.PROTOKIT_INDEXER_GRAPHQL_HOST ?? "0.0.0.0", + graphiql: true, + }, + }, + }); + return indexer; +}; +`; +} + +export function generateProcessorConfig(): string { + return `import { + DatabasePruneModule, + Processor, + TimedProcessorTrigger, + BlockFetching, + HandlersExecutor, + ResolverFactoryGraphqlModule, +} from "@proto-kit/processor"; +import { GraphqlSequencerModule } from "@proto-kit/api"; +import { databaseModule } from "../../processor"; +import { Arguments } from "../../../start"; +import { Startable } from "@proto-kit/common"; +import { resolvers } from "../../processor/api/resolvers"; +import { handlers } from "../../processor/handlers"; + +const processor = Processor.from({ + Database: databaseModule, + DatabasePruneModule, + GraphqlSequencerModule: GraphqlSequencerModule.from({ + ResolverFactory: ResolverFactoryGraphqlModule.from(resolvers), + }), + HandlersExecutor: HandlersExecutor.from(handlers), + BlockFetching, + Trigger: TimedProcessorTrigger, +}); + +export default async (args: Arguments): Promise => { + processor.configurePartial({ + HandlersExecutor: {}, + BlockFetching: { + url: \`http://\${process.env.PROTOKIT_PROCESSOR_INDEXER_GRAPHQL_HOST}:\${process.env.PROTOKIT_INDEXER_GRAPHQL_PORT}\`, + }, + Trigger: { interval: 6000 }, + GraphqlSequencerModule: { + ResolverFactory: {}, + containerConfig: { + port: Number(process.env.PROTOKIT_PROCESSOR_GRAPHQL_PORT), + host: process.env.PROTOKIT_PROCESSOR_GRAPHQL_HOST!, + graphiql: true, + }, + }, + Database: {}, + DatabasePruneModule: { pruneOnStartup: args.pruneOnStartup }, + }); + return processor; +}; +`; +} + +export function generateWorkerConfig(answer: WizardAnswers): string { + if (answer.worker === "local") return ""; + const tasks = answer.settlement ? "allTasks()" : "withoutSettlement()"; + const protocolMods = answer.settlement + ? `{ + ...protocol.modules, + ...protocol.settlementModules, + }` + : "protocol.modules"; + const protocolCfg = answer.settlement + ? "{ ...protocol.config, ...protocol.settlementModulesConfig }" + : "protocol.config"; + + return `import { Runtime } from "@proto-kit/module"; +import { Protocol } from "@proto-kit/protocol"; +import { + Sequencer, + AppChain, + WorkerModule, + VanillaTaskWorkerModules, +} from "@proto-kit/sequencer"; +import { BullQueue } from "@proto-kit/deployment"; +import runtime from "../../../runtime"; +import * as protocol from "../../../protocol"; +import { Arguments } from "../../../start"; +import { log, Startable } from "@proto-kit/common"; + +const appChain = AppChain.from({ + Runtime: Runtime.from(runtime.modules), + Protocol: Protocol.from(${protocolMods}), + Sequencer: Sequencer.from({ + TaskQueue: BullQueue, + WorkerModule: WorkerModule.from( + VanillaTaskWorkerModules.${tasks} + ), + }), +}); + +export default async (args: Arguments): Promise => { + appChain.configure({ + Runtime: runtime.config, + Protocol: ${protocolCfg}, + Sequencer: { + WorkerModule: VanillaTaskWorkerModules.defaultConfig(), + TaskQueue: { + redis: { + host: process.env.REDIS_HOST!, + port: Number(process.env.REDIS_PORT!), + password: process.env.REDIS_PASSWORD, + db: 1, + }, + }, + }, + }); + log.setLevel("DEBUG"); + return appChain; +}; +`; +} + +export function copyAndUpdateEnvFile( + answer: WizardAnswers, + envDir: string +): boolean { + const presetEnvPath = path.join( + resolveChainPath(true), + "src", + "core", + "environments", + answer.preset, + ".env" + ); + if (!fs.existsSync(presetEnvPath)) { + console.warn(`Could not find .env at ${presetEnvPath}`); + return false; + } + try { + let content = fs.readFileSync(presetEnvPath, "utf-8"); + content = content.includes("PROTOKIT_ENV_FOLDER=") + ? content.replace( + /PROTOKIT_ENV_FOLDER=.*/g, + `PROTOKIT_ENV_FOLDER=${answer.environmentName}` + ) + : `PROTOKIT_ENV_FOLDER=${answer.environmentName}\n${content}`; + fs.writeFileSync(path.join(envDir, ".env"), content); + return true; + } catch (e) { + console.error(`Error copying .env: ${e}`); + return false; + } +} +/* eslint-enable @typescript-eslint/quotes */ From 47b41e1cea53922a0090065eb4b3832caf175768 Mon Sep 17 00:00:00 2001 From: stanlou Date: Wed, 1 Apr 2026 11:56:15 +0100 Subject: [PATCH 3/3] refactor: remove default modules --- packages/stack/src/index.ts | 3 - packages/stack/src/presets/config.ts | 184 ------- packages/stack/src/presets/modules/index.ts | 543 -------------------- packages/stack/src/presets/modules/types.ts | 63 --- packages/stack/src/presets/modules/utils.ts | 26 - 5 files changed, 819 deletions(-) delete mode 100644 packages/stack/src/presets/config.ts delete mode 100644 packages/stack/src/presets/modules/index.ts delete mode 100644 packages/stack/src/presets/modules/types.ts diff --git a/packages/stack/src/index.ts b/packages/stack/src/index.ts index fb31453cc..4939c8047 100644 --- a/packages/stack/src/index.ts +++ b/packages/stack/src/index.ts @@ -1,5 +1,2 @@ export * from "./scripts/graphql/server"; -export * from "./presets/config"; -export * from "./presets/modules/types"; export * from "./presets/modules/utils"; -export * from "./presets/modules"; diff --git a/packages/stack/src/presets/config.ts b/packages/stack/src/presets/config.ts deleted file mode 100644 index 040b14f42..000000000 --- a/packages/stack/src/presets/config.ts +++ /dev/null @@ -1,184 +0,0 @@ -export const inmemoryConfig = { - blockInterval: 5000, - graphqlHost: "localhost", - graphqlPort: 8080, - graphiqlEnabled: true, -}; -export const developmentConfig = { - proofsEnabled: false, - - shouldAttemptDbMigration: true, - shouldAttemptIndexerDbMigration: true, - shouldAttemptProcessorDbMigration: true, - - pruneOnStartup: false, - - blockInterval: 30000, - settlementInterval: 60000, - settlementEnabled: true, - - redisHost: "localhost", - redisPort: 6379, - redisPassword: "password", - - databaseUrl: - "postgresql://admin:password@localhost:5432/protokit?schema=public", - - indexerDatabaseUrl: - "postgresql://admin:password@localhost:5433/protokit-indexer?schema=public", - - processorDatabaseUrl: - "postgresql://admin:password@localhost:5434/protokit-processor?schema=public", - - graphqlHost: "0.0.0.0", - graphqlPort: 8080, - graphiqlEnabled: true, - - indexerGraphqlHost: "0.0.0.0", - indexerGraphqlPort: 8081, - indexerGraphqlEnabled: true, - - processorGraphqlHost: "0.0.0.0", - processorGraphqlPort: 8082, - processorGraphqlEnabled: true, - - processorIndexerGraphqlHost: "0.0.0.0", - - minaNetwork: "lightnet", - minaNodeGraphqlHost: "http://localhost", - minaNodeGraphqlPort: 8083, - - minaArchiveGraphqlHost: "http://localhost", - minaArchiveGraphqlPort: 8085, - - minaAccountManagerHost: "http://localhost", - minaAccountManagerPort: 8084, - minaExplorerPort: 3001, - - transactionFeeRecipientPrivateKey: - "EKEssvj33MMBCg2tcybTzL32nTKbbwFHm6yUxd3JassdhL3J5aT8", - transactionFeeRecipientPublicKey: - "B62qk4sNnzZqqjHp8YQXZUV3dBpnjiNieJVnsuh7mD2bMJ9PdbskH5H", - - sequencerPrivateKey: "EKEdKhgUHMuDvwWJEg2TdCMCeiTSd9hh2HrEr6uYJfPVuwur1s43", - sequencerPublicKey: "B62qizW6aroTxQorJz4ywVNZom4jA6W4QPPCK3wLeyhnJHtVStUNniL", - - settlementContractPrivateKey: - "EKErS9gYHZNawqKuwfMiwYYJtNptCrvca491QEvB3tz8sFsS5w66", - settlementContractPublicKey: - "B62qjKhzrvDgTPXCp34ozmpFSx4sC9owZe6eDzhdGPdoiUbGPmBkHTt", - - dispatcherContractPrivateKey: - "EKF9Ei5G9PeB5ULMh9R6P5LfWX2gs15XxPNsect1pbcbMY9vs6v7", - dispatcherContractPublicKey: - "B62qmAzUJ1jqcsEf2V3K1k2Ec4MLsEKnodEvvJ5uweTFSLYEUALe1zs", - - minaBridgeContractPrivateKey: - "EKFKTGqWU2egLKhMgoxX8mQ21zXSE1RZYkY82mmK9F3BxdSA7E5M", - minaBridgeContractPublicKey: - "B62qn8XRkWcaBvv6F7kvarKs4cViaKRMbTUHT8FrDXLnvxuV6n7CHsN", - - customTokenPrivateKey: "EKFZHQSo5YdrcU7neDaNZruYHvCiNncvdZyKXuS6MDCW1fyCFKDP", - - customTokenAdminPrivateKey: - "EKENQ2QRc4gAJkZjQXU86ZS9MDm1e7HFiNN6LgRJnniHJt1WXDn1", - - customTokenBridgePrivateKey: - "EKENQ2QRc4gAJkZjQXU86ZS9MDm1e7HFiNN6LgRJnniHJt1WXDn1", - - testAccount1PrivateKey: - "EKF5p3wQTFd4tRBiGicRf93yXK82bcRryokC1qoazRM6wq6gMzWJ", - testAccount1PublicKey: - "B62qkVfEwyfkm5yucHEqrRjxbyx98pgdWz82pHv7LYq9Qigs812iWZ8", - - openTelemetryTracingEnabled: true, - openTelemetryTracingUrl: "http://localhost:4318", - - openTelemetryMetricsEnabled: true, - openTelemetryMetricsHost: "0.0.0.0", - openTelemetryMetricsPort: 4320, - openTelemetryMetricsScrapingFrequency: 10, -}; -export const sovereignConfig = { - blockInterval: 10000, - settlementInterval: 30000, - settlementEnabled: true, - - shouldAttemptDbMigration: true, - shouldAttemptIndexerDbMigration: true, - shouldAttemptProcessorDbMigration: true, - - pruneOnStartup: false, - - redisHost: "redis", - redisPort: 6379, - redisPassword: "password", - - databaseUrl: - "postgresql://admin:password@postgres:5432/protokit?schema=public", - - indexerDatabaseUrl: - "postgresql://admin:password@indexer-postgres:5432/protokit-indexer?schema=public", - - processorDatabaseUrl: - "postgresql://admin:password@processor-postgres:5432/protokit-processor?schema=public", - - graphqlHost: "0.0.0.0", - graphqlPort: 8080, - graphiqlEnabled: true, - - indexerGraphqlHost: "0.0.0.0", - indexerGraphqlPort: 8081, - indexerGraphqlEnabled: true, - - processorGraphqlHost: "0.0.0.0", - processorGraphqlPort: 8082, - processorGraphqlEnabled: true, - processorIndexerGraphqlHost: "indexer", - - minaNetwork: "lightnet", - minaNodeGraphqlHost: "http://lightnet", - minaNodeGraphqlPort: 8080, - - minaArchiveGraphqlHost: "http://lightnet", - minaArchiveGraphqlPort: 8282, - - minaAccountManagerHost: "http://lightnet", - minaAccountManagerPort: 8084, - minaExplorerPort: 3001, - transactionFeeRecipientPrivateKey: - "EKEssvj33MMBCg2tcybTzL32nTKbbwFHm6yUxd3JassdhL3J5aT8", - transactionFeeRecipientPublicKey: - "B62qk4sNnzZqqjHp8YQXZUV3dBpnjiNieJVnsuh7mD2bMJ9PdbskH5H", - - sequencerPrivateKey: "EKEdKhgUHMuDvwWJEg2TdCMCeiTSd9hh2HrEr6uYJfPVuwur1s43", - sequencerPublicKey: "B62qizW6aroTxQorJz4ywVNZom4jA6W4QPPCK3wLeyhnJHtVStUNniL", - - settlementContractPrivateKey: - "EKErS9gYHZNawqKuwfMiwYYJtNptCrvca491QEvB3tz8sFsS5w66", - settlementContractPublicKey: - "B62qjKhzrvDgTPXCp34ozmpFSx4sC9owZe6eDzhdGPdoiUbGPmBkHTt", - - dispatcherContractPrivateKey: - "EKF9Ei5G9PeB5ULMh9R6P5LfWX2gs15XxPNsect1pbcbMY9vs6v7", - dispatcherContractPublicKey: - "B62qmAzUJ1jqcsEf2V3K1k2Ec4MLsEKnodEvvJ5uweTFSLYEUALe1zs", - - minaBridgeContractPrivateKey: - "EKFKTGqWU2egLKhMgoxX8mQ21zXSE1RZYkY82mmK9F3BxdSA7E5M", - minaBridgeContractPublicKey: - "B62qn8XRkWcaBvv6F7kvarKs4cViaKRMbTUHT8FrDXLnvxuV6n7CHsN", - - testAccount1PrivateKey: - "EKF5p3wQTFd4tRBiGicRf93yXK82bcRryokC1qoazRM6wq6gMzWJ", - testAccount1PublicKey: - "B62qkVfEwyfkm5yucHEqrRjxbyx98pgdWz82pHv7LYq9Qigs812iWZ8", - - openTelemetryTracingEnabled: true, - openTelemetryTracingUrl: "http://otel-collector:4317", - - openTelemetryMetricsEnabled: true, - openTelemetryMetricsHost: "0.0.0.0", - openTelemetryMetricsPort: 4320, - openTelemetryMetricsScrapingFrequency: 10, -}; diff --git a/packages/stack/src/presets/modules/index.ts b/packages/stack/src/presets/modules/index.ts deleted file mode 100644 index 0a8403bca..000000000 --- a/packages/stack/src/presets/modules/index.ts +++ /dev/null @@ -1,543 +0,0 @@ -import { - VanillaGraphqlModules, - GraphqlSequencerModule, - OpenTelemetryServer, -} from "@proto-kit/api"; -import { - PrivateMempool, - SequencerModulesRecord, - TimedBlockTrigger, - BlockProducerModule, - SequencerStartupModule, - WorkerModule, - VanillaTaskWorkerModules, - MinaBaseLayer, - ConstantFeeStrategy, - BatchProducerModule, - SettlementModule, - InMemoryDatabase, - LocalTaskQueue, - AppChainModulesRecord, - InMemoryMinaSigner, - BridgingModule, -} from "@proto-kit/sequencer"; -import { - IndexerNotifier, - GeneratedResolverFactoryGraphqlModule, - IndexBlockTask, - IndexBatchTask, - IndexPendingTxTask, - IndexSettlementTask, -} from "@proto-kit/indexer"; -import { PrismaRedisDatabase } from "@proto-kit/persistance"; -import { BullQueue } from "@proto-kit/deployment"; -import { - TimedProcessorTrigger, - BlockFetching, - HandlersExecutor, - ResolverFactoryGraphqlModule, - HandlersRecord, - BasePrismaClient, -} from "@proto-kit/processor"; -import { - BlockStorageNetworkStateModule, - InMemoryTransactionSender, - StateServiceQueryModule, -} from "@proto-kit/sdk"; -import { PrivateKey } from "o1js"; -import { NonEmptyArray } from "type-graphql"; -import { ModulesConfig } from "@proto-kit/common"; - -import { - buildCustomTokenConfig, - buildSettlementTokenConfig, - resolveEnv, -} from "./utils"; -import { - Environment, - CoreEnv, - MetricsEnv, - IndexerEnv, - ProcessorEnv, - SettlementEnv, - RedisEnv, - DatabaseEnv, - RedisTaskQueueEnv, - GraphqlServerEnv, -} from "./types"; - -export class DefaultModules { - static api() { - return { - Graphql: GraphqlSequencerModule.from(VanillaGraphqlModules.with({})), - } satisfies SequencerModulesRecord; - } - - static core(options?: { settlementEnabled?: boolean }) { - const settlementEnabled = options?.settlementEnabled ?? false; - return { - ...(settlementEnabled ? DefaultModules.settlement() : {}), - ...DefaultModules.api(), - Mempool: PrivateMempool, - BlockProducerModule, - BlockTrigger: TimedBlockTrigger, - SequencerStartupModule, - } satisfies SequencerModulesRecord; - } - - static metrics() { - return { - OpenTelemetryServer, - } satisfies SequencerModulesRecord; - } - - static settlement() { - return { - BaseLayer: MinaBaseLayer, - FeeStrategy: ConstantFeeStrategy, - BatchProducerModule, - SettlementModule, - SettlementSigner: InMemoryMinaSigner, - BridgingModule, - } satisfies SequencerModulesRecord; - } - - static sequencerIndexer() { - return { - IndexerNotifier, - } satisfies SequencerModulesRecord; - } - - static indexer() { - return { - Database: PrismaRedisDatabase, - TaskQueue: BullQueue, - TaskWorker: WorkerModule.from({ - IndexBlockTask, - IndexPendingTxTask, - IndexBatchTask, - IndexSettlementTask, - }), - Graphql: GraphqlSequencerModule.from({ - GeneratedResolverFactory: GeneratedResolverFactoryGraphqlModule, - }), - } satisfies SequencerModulesRecord; - } - - static processor( - resolvers: NonEmptyArray, - handlers: HandlersRecord - ) { - return { - GraphqlSequencerModule: GraphqlSequencerModule.from({ - ResolverFactory: ResolverFactoryGraphqlModule.from(resolvers), - }), - HandlersExecutor: HandlersExecutor.from(handlers), - BlockFetching, - Trigger: TimedProcessorTrigger, - } satisfies SequencerModulesRecord; - } - - static inMemoryDatabase() { - return { - Database: InMemoryDatabase, - } satisfies SequencerModulesRecord; - } - - static prismaRedisDatabase() { - return { - Database: PrismaRedisDatabase, - } satisfies SequencerModulesRecord; - } - - static localWorker(options?: { settlementEnabled?: boolean }) { - return { - WorkerModule: WorkerModule.from( - options?.settlementEnabled === true - ? VanillaTaskWorkerModules.allTasks() - : VanillaTaskWorkerModules.withoutSettlement() - ), - TaskQueue: LocalTaskQueue, - } satisfies SequencerModulesRecord; - } - - static redisTaskQueue() { - return { - TaskQueue: BullQueue, - } satisfies SequencerModulesRecord; - } - - static remoteWorker() { - return { - TaskQueue: BullQueue, - WorkerModule: WorkerModule.from(VanillaTaskWorkerModules.allTasks()), - } satisfies SequencerModulesRecord; - } - - static appChainBase() { - return { - TransactionSender: InMemoryTransactionSender, - QueryTransportModule: StateServiceQueryModule, - NetworkStateTransportModule: BlockStorageNetworkStateModule, - } satisfies AppChainModulesRecord; - } - - static settlementScript() { - return { - ...DefaultModules.settlement(), - Mempool: PrivateMempool, - TaskQueue: LocalTaskQueue, - WorkerModule: WorkerModule.from(VanillaTaskWorkerModules.allTasks()), - SequencerStartupModule, - BridgingModule: BridgingModule, - } satisfies SequencerModulesRecord; - } -} -export class DefaultConfigs { - static api(options?: { - preset?: Environment; - overrides?: Partial; - }) { - const serverConfig = DefaultConfigs.graphqlServer({ - preset: options?.preset, - overrides: options?.overrides, - }); - return { - Graphql: { - ...VanillaGraphqlModules.defaultConfig(), - ...serverConfig.GraphqlServer, - }, - }; - } - - static core(options?: { - preset?: Environment; - overrides?: Partial & - Partial & - Partial; - settlementEnabled?: boolean; - }) { - const settlementEnabled = options?.settlementEnabled ?? false; - const config = resolveEnv(options?.preset, options?.overrides); - const apiConfig = DefaultConfigs.api({ - preset: options?.preset, - overrides: options?.overrides, - }); - const settlementConfig = settlementEnabled - ? DefaultConfigs.settlement({ - preset: options?.preset, - overrides: options?.overrides, - }) - : {}; - const blockTriggerConfig = { - blockInterval: config.blockInterval, - produceEmptyBlocks: true, - ...(settlementEnabled - ? { - settlementInterval: config.settlementInterval, - settlementTokenConfig: buildSettlementTokenConfig( - config.minaBridgeContractPrivateKey!, - buildCustomTokenConfig( - config.customTokenPrivateKey, - config.customTokenBridgePrivateKey - ) - ), - } - : { settlementTokenConfig: {} }), - }; - - return { - ...apiConfig, - Mempool: {}, - BlockProducerModule: {}, - BlockTrigger: blockTriggerConfig, - SequencerStartupModule: {}, - WorkerModule: VanillaTaskWorkerModules.defaultConfig(), - ...settlementConfig, - }; - } - - static metrics(options?: { - preset?: Environment; - overrides?: Partial; - }) { - const config = resolveEnv(options?.preset, options?.overrides); - return { - OpenTelemetryServer: { - metrics: { - enabled: config.metricsEnabled, - prometheus: { - host: config.metricsHost, - port: config.metricsPort, - appendTimestamp: true, - }, - nodeScrapeInterval: config.metricsScrapingFrequency, - }, - tracing: { - enabled: config.tracingEnabled, - otlp: { - url: config.tracingUrl, - }, - }, - }, - }; - } - - static sequencerIndexer() { - return { IndexerNotifier: {} }; - } - - static indexer(options?: { - preset?: Environment; - overrides?: Partial; - }) { - const config = resolveEnv(options?.preset, options?.overrides); - const taskQueueConfig = DefaultConfigs.redisTaskQueue({ - preset: options?.preset, - overrides: options?.overrides, - }); - const databaseConfig = DefaultConfigs.prismaRedisDatabase({ - preset: options?.preset, - overrides: { - databaseUrl: config.indexerDatabaseUrl, - ...options?.overrides, - }, - }); - const graphqlServerConfig = DefaultConfigs.graphqlServer({ - preset: options?.preset, - overrides: { - graphqlHost: config.indexerGraphqlHost, - graphqlPort: config.indexerGraphqlPort, - graphiqlEnabled: config.indexerGraphqlEnabled, - ...options?.overrides, - }, - }); - - return { - ...databaseConfig, - ...taskQueueConfig, - TaskWorker: { - IndexBlockTask: {}, - IndexBatchTask: {}, - IndexPendingTxTask: {}, - IndexSettlementTask: {}, - }, - Graphql: { - GeneratedResolverFactory: {}, - ...graphqlServerConfig.GraphqlServer, - }, - }; - } - - static processor(options?: { - preset?: Environment; - overrides?: Partial; - }) { - const config = resolveEnv( - options?.preset, - options?.overrides - ); - const graphqlServerConfig = DefaultConfigs.graphqlServer({ - preset: options?.preset, - overrides: { - graphqlHost: config.processorGraphqlHost, - graphqlPort: config.processorGraphqlPort, - graphiqlEnabled: config.processorGraphqlEnabled, - ...options?.overrides, - }, - }); - return { - HandlersExecutor: {}, - BlockFetching: { - url: `http://${config.processorIndexerGraphqlHost}:${config.indexerGraphqlPort}`, - }, - Trigger: { - interval: Number(config.blockInterval) / 5, - }, - GraphqlSequencerModule: { - ResolverFactory: {}, - ...graphqlServerConfig.GraphqlServer, - }, - }; - } - - static settlement(options?: { - preset?: Environment; - overrides?: Partial; - }) { - const config = resolveEnv( - options?.preset, - options?.overrides - ); - - return { - BaseLayer: { - network: { - type: config.minaNetwork, - graphql: `${config.minaNodeGraphqlHost}:${config.minaNodeGraphqlPort}/graphql`, - archive: `${config.minaArchiveGraphqlHost}:${config.minaArchiveGraphqlPort}`, - accountManager: `${config.minaAccountManagerHost}:${config.minaAccountManagerPort}`, - }, - }, - SettlementModule: { - addresses: { - SettlementContract: PrivateKey.fromBase58( - config.settlementContractPrivateKey - ).toPublicKey(), - }, - }, - BridgingModule: { - addresses: { - DispatchContract: PrivateKey.fromBase58( - config.dispatcherContractPrivateKey - ).toPublicKey(), - }, - }, - SettlementSigner: { - feepayer: PrivateKey.fromBase58(config.sequencerPrivateKey), - contractKeys: [ - PrivateKey.fromBase58(config.settlementContractPrivateKey), - PrivateKey.fromBase58(config.dispatcherContractPrivateKey), - PrivateKey.fromBase58(config.minaBridgeContractPrivateKey), - ], - }, - FeeStrategy: {}, - BatchProducerModule: {}, - WorkerModule: VanillaTaskWorkerModules.defaultConfig(), - }; - } - - static inMemoryDatabase() { - return { Database: {} }; - } - - static prismaRedisDatabase(options?: { - preset?: Environment; - overrides?: Partial; - }) { - const preset = options?.preset ?? "development"; - const config = resolveEnv(preset, options?.overrides); - const redisConfig = DefaultConfigs.redis({ - preset, - overrides: options?.overrides, - }); - return { - Database: { - ...redisConfig, - prisma: { - connection: config.databaseUrl, - }, - databasePruneModule: { - pruneOnStartup: config.pruneOnStartup, - }, - }, - }; - } - - static localWorker() { - return { - TaskQueue: {}, - WorkerModule: { - ...VanillaTaskWorkerModules.defaultConfig(), - }, - } satisfies ModulesConfig>; - } - - static redisTaskQueue(options?: { - preset?: Environment; - overrides?: Partial; - }) { - const config = resolveEnv( - options?.preset, - options?.overrides - ); - - return { - TaskQueue: { - redis: { - host: config.redisHost, - port: config.redisPort, - password: config.redisPassword, - db: config.redisDb, - }, - retryAttempts: config.retryAttempts, - }, - }; - } - - static graphqlServer(options?: { - preset?: Environment; - overrides?: Partial; - }) { - const config = resolveEnv( - options?.preset, - options?.overrides - ); - - return { - GraphqlServer: { - port: config.graphqlPort, - host: config.graphqlHost, - graphiql: config.graphiqlEnabled, - }, - }; - } - - static redis(options?: { - preset?: Environment; - overrides?: Partial; - }) { - const config = resolveEnv(options?.preset, options?.overrides); - - return { - redis: { - host: config.redisHost, - port: config.redisPort, - password: config.redisPassword, - }, - }; - } - - static appChainBase() { - return { - QueryTransportModule: {}, - NetworkStateTransportModule: {}, - TransactionSender: {}, - }; - } - - static worker(options?: { - preset?: Environment; - overrides?: Partial; - }) { - const taskQueueConfig = DefaultConfigs.redisTaskQueue({ - preset: options?.preset, - overrides: options?.overrides, - }); - - return { - ...taskQueueConfig, - WorkerModule: VanillaTaskWorkerModules.defaultConfig(), - }; - } - - static settlementScript(options?: { - preset?: Environment; - overrides?: Partial; - }) { - const settlementConfig = DefaultConfigs.settlement({ - preset: options?.preset, - overrides: options?.overrides, - }); - return { - ...settlementConfig, - SequencerStartupModule: {}, - TaskQueue: { - simulatedDuration: 0, - }, - LocalTaskWorker: VanillaTaskWorkerModules.defaultConfig(), - Mempool: {}, - BridgingModule: {}, - }; - } -} diff --git a/packages/stack/src/presets/modules/types.ts b/packages/stack/src/presets/modules/types.ts deleted file mode 100644 index 0d5ca4d0c..000000000 --- a/packages/stack/src/presets/modules/types.ts +++ /dev/null @@ -1,63 +0,0 @@ -export type Environment = "inmemory" | "development" | "sovereign"; - -export type GraphqlServerEnv = { - graphqlPort: number; - graphqlHost: string; - graphiqlEnabled: boolean; -}; -export type CoreEnv = { - blockInterval: number; - settlementInterval?: number; - minaBridgeContractPrivateKey?: string; - customTokenPrivateKey?: string; - customTokenBridgePrivateKey?: string; -}; -export type MetricsEnv = { - metricsEnabled: boolean; - metricsHost: string; - metricsPort: number; - metricsScrapingFrequency: number; - tracingEnabled: boolean; - tracingUrl: string; -}; -export type SettlementEnv = { - minaNetwork: "lightnet" | "remote"; - minaNodeGraphqlHost: string; - minaNodeGraphqlPort: number; - minaArchiveGraphqlHost: string; - minaArchiveGraphqlPort: number; - minaAccountManagerHost: string; - minaAccountManagerPort: number; - sequencerPrivateKey: string; - settlementContractPrivateKey: string; - dispatcherContractPrivateKey: string; - minaBridgeContractPrivateKey: string; -}; -export type IndexerEnv = RedisTaskQueueEnv & { - indexerDatabaseUrl: string; - indexerGraphqlHost: string; - indexerGraphqlPort: number; - indexerGraphqlEnabled: boolean; - pruneOnStartup?: boolean; -}; -export type ProcessorEnv = { - processorIndexerGraphqlHost: string; - indexerGraphqlPort: number; - blockInterval: number; - processorGraphqlHost: string; - processorGraphqlPort: number; - processorGraphqlEnabled: boolean; -}; -export type DatabaseEnv = RedisEnv & { - databaseUrl: string; - pruneOnStartup?: boolean; -}; -export type RedisEnv = { - redisHost: string; - redisPort: number; - redisPassword: string; -}; -export type RedisTaskQueueEnv = RedisEnv & { - redisDb?: number; - retryAttempts?: number; -}; diff --git a/packages/stack/src/presets/modules/utils.ts b/packages/stack/src/presets/modules/utils.ts index 4edf31e9c..d24ca35f1 100644 --- a/packages/stack/src/presets/modules/utils.ts +++ b/packages/stack/src/presets/modules/utils.ts @@ -2,32 +2,6 @@ import { PrivateKey, TokenId } from "o1js"; import { FungibleToken } from "mina-fungible-token"; import { SettlementTokenConfig } from "@proto-kit/sequencer"; -import { developmentConfig, inmemoryConfig, sovereignConfig } from "../config"; - -import { Environment } from "./types"; - -export function getConfigs(preset: Environment) { - switch (preset) { - case "development": - return developmentConfig; - case "sovereign": - return sovereignConfig; - case "inmemory": - default: - return inmemoryConfig; - } -} -export function resolveEnv( - preset: Environment = "inmemory", - envs?: Partial | undefined -): T { - const config = getConfigs(preset); - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - if (!envs) return config as T; - const resolved = { ...config, ...envs } satisfies Partial; - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - return resolved as T; -} export function buildCustomTokenConfig( customTokenPrivateKey?: string, customTokenBridgePrivateKey?: string