# launch:create-config — Ammora SDK 0.3.8 Create a permissionless launch config by starting from one that already works. npm run launch:create-config -- A config has around fifty fields covering the fee schedule, rate limiter, dynamic fee, graduation mode, post-migration pool shape, vesting. Writing them all out from scratch is how a launch ends up with a graduation that can never reconcile, so this reads an existing config, changes the two things a new launch usually differs in, and creates a new one. Read the template first with `launch:get-config ` and make sure it is the shape you want. No governance approval or registrar role is required. The caller becomes the immutable Config owner, while `partnerRecipient` remains the fee claimer encoded in the Config. [Read in Docs](https://docs.ammora.org/examples?command=launch%3Acreate-config) · [Detailed guide](https://docs.ammora.org/guides/launchpad) · [Local setup](https://docs.ammora.org/guides/example-setup) ## Context and execution GIWA Sepolia 91342. Public SDK 0.3.8, Math 0.3.0. Source revision: `a9b3fd798fa51495e8defec99a25de59e4d70672`. Install the SDK in your app, or prepare the example project using the local setup guide for its npm commands. Review the entry source and guide for required arguments, roles and deployment selection. `AMMORA_SEND=0` does not turn send-only commands into previews. Never put CLI keys in a browser or resend an uncertain transaction. Command: `npm run launch:create-config -- ` (`tsx src/launch/create-config.ts`). Omit `--` and arguments when the source accepts none. ## Entry file: `src/launch/create-config.ts` ```typescript /** * Create a permissionless launch config by starting from one that already works. * * npm run launch:create-config -- * * A config has around fifty fields covering the fee schedule, rate limiter, dynamic fee, graduation * mode, post-migration pool shape, vesting. Writing them all out from scratch is how a * launch ends up with a graduation that can never reconcile, so this reads an existing * config, changes the two things a new launch usually differs in, and creates a new one. * * Read the template first with `launch:get-config ` and make sure it is the shape you * want. No governance approval or registrar role is required. The caller becomes the * immutable Config owner, while `partnerRecipient` remains the fee claimer encoded in the Config. */ import { buildCreateAmmoraV4Config, getAmmoraV4ConfigAtBlock, parseAmmoraV4ConfigCreatedReceipt, verifyAmmoraV4CoreAtBlock, } from "@ammora-protocol/sdk"; import { readBlock, reader, signer, v4Deployment } from "../shared/env.js"; import { main, send } from "../shared/run.js"; import { deriveConfigFromTemplate } from "./config-from-template.js"; main(async () => { const [templateArgument, supplyArgument, thresholdArgument] = process.argv.slice(2); if (!templateArgument || !supplyArgument || !thresholdArgument) { throw new Error("usage: "); } if (!/^\d+$/.test(templateArgument)) throw new Error("template config id must be a positive integer"); const saleSupply = BigInt(supplyArgument); const migrationQuoteThreshold = BigInt(thresholdArgument); const { wallet, account } = signer(); const client = await reader(); const contracts = v4Deployment().contracts.launch; const { number: snapshotBlock } = await readBlock(client); await verifyAmmoraV4CoreAtBlock(client, { configRegistry: contracts.configRegistry, launchFactory: contracts.factory, graduationAdapter: contracts.graduationAdapter, feePolicy: contracts.feePolicy, }, snapshotBlock); const template = await getAmmoraV4ConfigAtBlock( client, contracts.configRegistry, BigInt(templateArgument), snapshotBlock, ); if (!template.enabled) throw new Error(`template Config #${templateArgument} is disabled`); const { config, curve } = deriveConfigFromTemplate( template, saleSupply, migrationQuoteThreshold, ); console.log(`registry ${contracts.configRegistry}`); console.log(`template #${templateArgument}`); console.log(`sale ${saleSupply}`); console.log(`threshold ${migrationQuoteThreshold}`); console.log(`curve sqrt ${curve[0]!.lowerSqrtPrice} .. ${curve[0]!.upperSqrtPrice}`); console.log(`liquidity ${curve[0]!.liquidity}`); console.log(`owner ${account.address}`); console.log(`feeClaimer ${config.partnerRecipient}`); console.log(`totalSupply ${config.totalSupply}`); console.log(`migrationBase ${config.migrationBaseThreshold}`); console.log(""); // Validation runs before encoding, so a config whose graduation could never reconcile // fails here, not after buyers have deposited into it. const { data } = buildCreateAmmoraV4Config(config, curve, account.address); const hash = await send( "create config", { to: contracts.configRegistry, data }, wallet, account, client, ); const receipt = await client.getTransactionReceipt({ hash }); const created = parseAmmoraV4ConfigCreatedReceipt(receipt, contracts.configRegistry); const confirmed = await getAmmoraV4ConfigAtBlock( client, contracts.configRegistry, created.configId, receipt.blockNumber, ); if (confirmed.configHash !== created.configHash || confirmed.identity.owner.toLowerCase() !== created.owner.toLowerCase() || confirmed.identity.feeClaimer.toLowerCase() !== created.feeClaimer.toLowerCase()) { throw new Error("receipt-bound V4 Config identity does not match registry storage"); } console.log(""); console.log(`config #${created.configId}`); console.log(`hash ${created.configHash}`); console.log(`owner ${created.owner}`); console.log(`feeClaimer ${created.feeClaimer}`); console.log(`block ${created.createdAtBlock}`); console.log(`policy ${created.feePolicyVersion}`); }); ``` ## Supporting files Keep relative paths intact. Local imports are listed recursively; JSON templates are choices to review, not approved inputs. Runtime-selected files and wallet/environment setup still follow the detailed guide. - [src/launch/config-from-template.ts](https://docs.ammora.org/examples/source/src/launch/config-from-template.ts.txt) - [src/shared/env.ts](https://docs.ammora.org/examples/source/src/shared/env.ts.txt) - [src/shared/input.ts](https://docs.ammora.org/examples/source/src/shared/input.ts.txt) - [src/shared/retry.ts](https://docs.ammora.org/examples/source/src/shared/retry.ts.txt) - [src/shared/rpc.ts](https://docs.ammora.org/examples/source/src/shared/rpc.ts.txt) - [src/shared/run.ts](https://docs.ammora.org/examples/source/src/shared/run.ts.txt) - [src/launch/config-document.example.json](https://docs.ammora.org/examples/source/src/launch/config-document.example.json.txt) - [src/launch/team/fees.example.json](https://docs.ammora.org/examples/source/src/launch/team/fees.example.json.txt) - [src/launch/team/recipients.example.json](https://docs.ammora.org/examples/source/src/launch/team/recipients.example.json.txt) - [package.json](https://docs.ammora.org/examples/source/package.json.txt) - [package-lock.json](https://docs.ammora.org/examples/source/package-lock.json.txt) - [tsconfig.json](https://docs.ammora.org/examples/source/tsconfig.json.txt) - [.env.giwa.example](https://docs.ammora.org/examples/source/file.env.giwa.example.txt) - [.nvmrc](https://docs.ammora.org/examples/source/file.nvmrc.txt) ## AI implementation checklist Use the actual SDK exports and parameter types in the linked files. Start with reads and unsigned plans where supported. Use a connected wallet for browser signing. Preserve raw integer units, exact approvals, caller authority, deadlines and receipt reconciliation. Report local checks separately from live-chain verification.