@peddles/sdk 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/CLAUDE_PROMPT.md +159 -0
  2. package/LICENSE +21 -0
  3. package/README.md +186 -0
  4. package/dist/abis.d.ts +19 -0
  5. package/dist/abis.d.ts.map +1 -0
  6. package/dist/abis.generated.d.ts +2029 -0
  7. package/dist/abis.generated.d.ts.map +1 -0
  8. package/dist/abis.generated.js +2723 -0
  9. package/dist/abis.generated.js.map +1 -0
  10. package/dist/abis.js +19 -0
  11. package/dist/abis.js.map +1 -0
  12. package/dist/artDex.d.ts +937 -0
  13. package/dist/artDex.d.ts.map +1 -0
  14. package/dist/artDex.js +155 -0
  15. package/dist/artDex.js.map +1 -0
  16. package/dist/client.d.ts +10 -0
  17. package/dist/client.d.ts.map +1 -0
  18. package/dist/client.js +2 -0
  19. package/dist/client.js.map +1 -0
  20. package/dist/clog.d.ts +75 -0
  21. package/dist/clog.d.ts.map +1 -0
  22. package/dist/clog.js +91 -0
  23. package/dist/clog.js.map +1 -0
  24. package/dist/clogInflow.d.ts +136 -0
  25. package/dist/clogInflow.d.ts.map +1 -0
  26. package/dist/clogInflow.js +177 -0
  27. package/dist/clogInflow.js.map +1 -0
  28. package/dist/deployments.d.ts +30 -0
  29. package/dist/deployments.d.ts.map +1 -0
  30. package/dist/deployments.generated.d.ts +92 -0
  31. package/dist/deployments.generated.d.ts.map +1 -0
  32. package/dist/deployments.generated.js +93 -0
  33. package/dist/deployments.generated.js.map +1 -0
  34. package/dist/deployments.js +52 -0
  35. package/dist/deployments.js.map +1 -0
  36. package/dist/feeSplit.d.ts +54 -0
  37. package/dist/feeSplit.d.ts.map +1 -0
  38. package/dist/feeSplit.js +69 -0
  39. package/dist/feeSplit.js.map +1 -0
  40. package/dist/feeTerms.d.ts +46 -0
  41. package/dist/feeTerms.d.ts.map +1 -0
  42. package/dist/feeTerms.js +80 -0
  43. package/dist/feeTerms.js.map +1 -0
  44. package/dist/index.d.ts +102 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +108 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/launch/abi.generated.d.ts +2667 -0
  49. package/dist/launch/abi.generated.d.ts.map +1 -0
  50. package/dist/launch/abi.generated.js +2472 -0
  51. package/dist/launch/abi.generated.js.map +1 -0
  52. package/dist/launch/addresses.d.ts +24 -0
  53. package/dist/launch/addresses.d.ts.map +1 -0
  54. package/dist/launch/addresses.js +35 -0
  55. package/dist/launch/addresses.js.map +1 -0
  56. package/dist/launch/feeTerms.d.ts +52 -0
  57. package/dist/launch/feeTerms.d.ts.map +1 -0
  58. package/dist/launch/feeTerms.js +72 -0
  59. package/dist/launch/feeTerms.js.map +1 -0
  60. package/dist/launch/index.d.ts +33 -0
  61. package/dist/launch/index.d.ts.map +1 -0
  62. package/dist/launch/index.js +24 -0
  63. package/dist/launch/index.js.map +1 -0
  64. package/dist/launch/revert.d.ts +23 -0
  65. package/dist/launch/revert.d.ts.map +1 -0
  66. package/dist/launch/revert.js +169 -0
  67. package/dist/launch/revert.js.map +1 -0
  68. package/dist/launch/salt.d.ts +90 -0
  69. package/dist/launch/salt.d.ts.map +1 -0
  70. package/dist/launch/salt.js +153 -0
  71. package/dist/launch/salt.js.map +1 -0
  72. package/dist/launch/stockCall.d.ts +122 -0
  73. package/dist/launch/stockCall.d.ts.map +1 -0
  74. package/dist/launch/stockCall.js +144 -0
  75. package/dist/launch/stockCall.js.map +1 -0
  76. package/dist/launch/tickMath.d.ts +39 -0
  77. package/dist/launch/tickMath.d.ts.map +1 -0
  78. package/dist/launch/tickMath.js +109 -0
  79. package/dist/launch/tickMath.js.map +1 -0
  80. package/dist/launch/variants.d.ts +64 -0
  81. package/dist/launch/variants.d.ts.map +1 -0
  82. package/dist/launch/variants.js +131 -0
  83. package/dist/launch/variants.js.map +1 -0
  84. package/dist/launch/wethCall.d.ts +133 -0
  85. package/dist/launch/wethCall.d.ts.map +1 -0
  86. package/dist/launch/wethCall.js +173 -0
  87. package/dist/launch/wethCall.js.map +1 -0
  88. package/dist/launch/wethPlan.d.ts +71 -0
  89. package/dist/launch/wethPlan.d.ts.map +1 -0
  90. package/dist/launch/wethPlan.js +146 -0
  91. package/dist/launch/wethPlan.js.map +1 -0
  92. package/dist/perps/abi.generated.d.ts +794 -0
  93. package/dist/perps/abi.generated.d.ts.map +1 -0
  94. package/dist/perps/abi.generated.js +738 -0
  95. package/dist/perps/abi.generated.js.map +1 -0
  96. package/dist/perps/index.d.ts +160 -0
  97. package/dist/perps/index.d.ts.map +1 -0
  98. package/dist/perps/index.js +147 -0
  99. package/dist/perps/index.js.map +1 -0
  100. package/package.json +105 -0
  101. package/src/abis.generated.ts +2738 -0
  102. package/src/abis.ts +35 -0
  103. package/src/artDex.ts +254 -0
  104. package/src/client.ts +10 -0
  105. package/src/clog.ts +171 -0
  106. package/src/clogInflow.ts +217 -0
  107. package/src/deployments.generated.ts +98 -0
  108. package/src/deployments.ts +60 -0
  109. package/src/feeSplit.ts +76 -0
  110. package/src/feeTerms.ts +137 -0
  111. package/src/index.ts +233 -0
  112. package/src/launch/abi.generated.ts +2481 -0
  113. package/src/launch/addresses.ts +58 -0
  114. package/src/launch/feeTerms.ts +93 -0
  115. package/src/launch/index.ts +93 -0
  116. package/src/launch/revert.ts +188 -0
  117. package/src/launch/salt.ts +216 -0
  118. package/src/launch/stockCall.ts +223 -0
  119. package/src/launch/tickMath.ts +139 -0
  120. package/src/launch/variants.ts +183 -0
  121. package/src/launch/wethCall.ts +240 -0
  122. package/src/launch/wethPlan.ts +257 -0
  123. package/src/perps/abi.generated.ts +741 -0
  124. package/src/perps/index.ts +263 -0
@@ -0,0 +1,240 @@
1
+ import type { ReadClient } from '../client.js';
2
+ import { encodeAbiParameters, encodeFunctionData } from 'viem';
3
+ import type { Address, ContractFunctionArgs, Hex } from 'viem';
4
+ import { launchOrchestratorAbi, tokenMetadataAbi } from './abi.generated.js';
5
+ import { validateFeeTerms, type FeeTerms } from './feeTerms.js';
6
+ import { LAUNCH_COIN_DECIMALS, type WethLaunchPlan } from './wethPlan.js';
7
+
8
+ /**
9
+ * The WETH-type launch call — `PeddlesLaunchOrchestratorV20.launch(LaunchInput)`,
10
+ * or `launchAndBuy(LaunchInput, devBuyValue, minTokensOut)` with a first buy.
11
+ * A pure port of the web app's `buildWethLaunchInput` / `buildWethLaunchCall`
12
+ * (`features/launch/useWethLaunch.ts`), held identical by test.
13
+ *
14
+ * ONE builder, every consumer: the web form, the REST API's `/prepare`, the CLI
15
+ * and the widget all sign exactly these bytes. Assembling the struct twice is how
16
+ * a form ends up simulating one transaction and sending another.
17
+ *
18
+ * `msg.value` is the orchestrator's `launchFee()` plus the first buy. The fee is read LIVE by the
19
+ * caller (it is owner-tunable beneath an immutable cap) and passed in as `launchFeeWei`; the
20
+ * orchestrator takes it off the top and reverts `LAUNCH_FEE_REQUIRED` when the value is short.
21
+ */
22
+
23
+ /** The components of the `AssetParamsV1` struct `LaunchInput.params` carries — encoded as ONE tuple. */
24
+ export const assetParamsV1Components = [
25
+ { name: 'version', type: 'uint8' },
26
+ { name: 'name', type: 'string' },
27
+ { name: 'symbol', type: 'string' },
28
+ { name: 'initialAdmin', type: 'address' },
29
+ { name: 'decimals', type: 'uint8' },
30
+ ] as const;
31
+
32
+ /** On-chain metadata carried by `initCalls`. Only these writes are permitted. */
33
+ export interface WethMetadata {
34
+ readonly contractUri: string;
35
+ readonly image: string;
36
+ readonly website: string;
37
+ readonly x: string;
38
+ readonly telegram: string;
39
+ }
40
+
41
+ export const EMPTY_WETH_METADATA: WethMetadata = { contractUri: '', image: '', website: '', x: '', telegram: '' };
42
+
43
+ export interface WethLaunchArgs {
44
+ readonly name: string;
45
+ readonly symbol: string;
46
+ readonly salt: `0x${string}`;
47
+ /** The wallet that will sign. The orchestrator requires `vaultInput.creator == msg.sender`. */
48
+ readonly creator: `0x${string}`;
49
+ /** The creator's permanent fee terms. `null` is refused, never defaulted. */
50
+ readonly feeTerms: FeeTerms | null;
51
+ readonly metadata: WethMetadata;
52
+ readonly plan: WethLaunchPlan;
53
+ /** Native wei spent on the first buy. 0n launches without one. */
54
+ readonly devBuyWei: bigint;
55
+ /** Slippage floor for the first buy, in the launched token's base units. 0n with no first buy. */
56
+ readonly minTokensOut: bigint;
57
+ /** The orchestrator's `launchFee()`, read from the chain. Never assumed. */
58
+ readonly launchFeeWei: bigint;
59
+ }
60
+
61
+ /**
62
+ * The metadata writes, in `initCalls` order. Empty fields are OMITTED rather than
63
+ * written as empty strings — an `extraMetadata("x", "")` entry is a permanent
64
+ * on-chain claim, and clearing it later costs another transaction.
65
+ */
66
+ export function buildInitCalls(metadata: WethMetadata): readonly Hex[] {
67
+ const calls: Hex[] = [];
68
+ if (metadata.contractUri.trim().length > 0) {
69
+ calls.push(encodeFunctionData({ abi: tokenMetadataAbi, functionName: 'updateContractURI', args: [metadata.contractUri.trim()] }));
70
+ }
71
+ const entries: readonly [string, string][] = [
72
+ ['image', metadata.image],
73
+ ['website', metadata.website],
74
+ ['x', metadata.x],
75
+ ['telegram', metadata.telegram],
76
+ ];
77
+ for (const [key, value] of entries) {
78
+ if (value.trim().length === 0) continue;
79
+ calls.push(encodeFunctionData({ abi: tokenMetadataAbi, functionName: 'updateExtraMetadata', args: [key, value.trim()] }));
80
+ }
81
+ return calls;
82
+ }
83
+
84
+ /** The `LaunchInput` struct, as the generated ABI types it. */
85
+ export type WethLaunchInput = ContractFunctionArgs<typeof launchOrchestratorAbi, 'payable' | 'nonpayable', 'launch'>[0];
86
+
87
+ export type WethLaunchCall =
88
+ | {
89
+ readonly functionName: 'launch';
90
+ readonly input: WethLaunchInput;
91
+ readonly args: readonly [WethLaunchInput];
92
+ readonly value: bigint;
93
+ }
94
+ | {
95
+ readonly functionName: 'launchAndBuy';
96
+ readonly input: WethLaunchInput;
97
+ readonly args: readonly [WethLaunchInput, bigint, bigint];
98
+ readonly value: bigint;
99
+ };
100
+
101
+ /** Why these terms cannot ride in a launch, or null when they can. */
102
+ export function wethLaunchTermsIssue(feeTerms: FeeTerms | null): string | null {
103
+ if (feeTerms === null) return 'Choose the trading fee for this token before launching.';
104
+ if (validateFeeTerms(feeTerms) !== null) return 'The trading fee or its split is outside the range a launch can set.';
105
+ return null;
106
+ }
107
+
108
+ /** The `LaunchInput` itself. Throws when the terms are missing or out of range. */
109
+ export function buildWethLaunchInput(args: WethLaunchArgs, orchestrator: `0x${string}`): WethLaunchInput {
110
+ const { plan, feeTerms } = args;
111
+ const issue = wethLaunchTermsIssue(feeTerms);
112
+ if (issue !== null) throw new Error(issue);
113
+ if (feeTerms === null) throw new Error('Choose the trading fee for this token before launching.');
114
+
115
+ return {
116
+ variant: plan.variant,
117
+ salt: args.salt,
118
+ // AssetParamsV1 is a DYNAMIC STRUCT, so it must be encoded as a single tuple (with the leading
119
+ // offset word), never as a flat parameter list — the flat form reverts with EMPTY data.
120
+ params: encodeAbiParameters(
121
+ [{ type: 'tuple', components: assetParamsV1Components }],
122
+ [{ version: 1, name: args.name, symbol: args.symbol, initialAdmin: orchestrator, decimals: LAUNCH_COIN_DECIMALS }],
123
+ ),
124
+ initCalls: [...buildInitCalls(args.metadata)],
125
+ vaultInput: {
126
+ // The orchestrator sets this itself and REJECTS a pre-set one, so it goes in as zero.
127
+ token: '0x0000000000000000000000000000000000000000',
128
+ creator: args.creator,
129
+ // MIRRORS THE LAUNCH TYPE the plan read from the chain; the orchestrator refuses any mismatch.
130
+ liquidityBps: plan.allocationBps.liquidity,
131
+ airdropBps: 0,
132
+ vestingBps: 0,
133
+ burnBps: 0,
134
+ vaultBps: 0,
135
+ clogBps: plan.allocationBps.clog,
136
+ airdropEnabled: false,
137
+ liquidityAmount: plan.pooledSupply,
138
+ airdropAmount: 0n,
139
+ vestingAmount: 0n,
140
+ burnAmount: 0n,
141
+ vestingStart: 0n,
142
+ vestingCliff: 5_184_000n,
143
+ vestingDuration: 46_656_000n,
144
+ airdropStartsAt: 0n,
145
+ airdropEpochLength: 864_000,
146
+ airdropEpochCount: 10,
147
+ burnStartsAt: 0n,
148
+ burnEpochLength: 864_000,
149
+ firstBurnBps: 1_000,
150
+ minVoteBps: 0,
151
+ maxVoteBps: 2_000,
152
+ defaultVoteBps: 1_000,
153
+ quoteToken: plan.quoteToken,
154
+ poolManager: plan.poolManager,
155
+ positionManager: plan.positionManager,
156
+ hook: plan.hook,
157
+ liquidityManager: plan.liquidityManager,
158
+ airdropPublisher: plan.airdropPublisher,
159
+ fee: plan.poolFeePpm,
160
+ tickSpacing: plan.tickSpacing,
161
+ },
162
+ // uint16 on-chain, bounded by `validateFeeTerms` above, so `Number` is exact.
163
+ creatorTaxBps: Number(feeTerms.taxBps),
164
+ excessToCreatorBps: Number(feeTerms.excessToCreatorBps),
165
+ factoryValue: 0n,
166
+ amountPeddles: plan.pooledSupply,
167
+ sqrtPriceX96: plan.sqrtPriceX96,
168
+ tickLower: plan.tickLower,
169
+ tickUpper: plan.tickUpper,
170
+ liquidity: plan.liquidity,
171
+ };
172
+ }
173
+
174
+ /** Assemble the exact call for a launch. `launch` without a first buy, `launchAndBuy` with one. */
175
+ export function buildWethLaunchCall(args: WethLaunchArgs, orchestrator: `0x${string}`): WethLaunchCall {
176
+ const input = buildWethLaunchInput(args, orchestrator);
177
+ if (args.devBuyWei < 0n || args.minTokensOut < 0n) throw new Error('A first-buy amount cannot be negative.');
178
+ if (args.launchFeeWei < 0n) throw new Error('The launch fee cannot be negative.');
179
+ if (args.devBuyWei === 0n) {
180
+ // A floor with nothing to spend is `NO_DEV_BUY` on-chain; refused here, never dropped.
181
+ if (args.minTokensOut !== 0n) throw new Error('Set a first-buy amount, or clear the minimum.');
182
+ return { functionName: 'launch', input, args: [input], value: args.launchFeeWei };
183
+ }
184
+ return {
185
+ functionName: 'launchAndBuy',
186
+ input,
187
+ args: [input, args.devBuyWei, args.minTokensOut],
188
+ value: args.launchFeeWei + args.devBuyWei,
189
+ };
190
+ }
191
+
192
+ /** An unsigned transaction: the bytes a wallet, the CLI or an agent signs. */
193
+ export interface UnsignedLaunchTx {
194
+ readonly to: `0x${string}`;
195
+ readonly data: Hex;
196
+ /** Native wei to attach. */
197
+ readonly value: bigint;
198
+ }
199
+
200
+ /** The calldata of a WETH-type launch call. */
201
+ export function encodeWethLaunchCall(call: WethLaunchCall, orchestrator: `0x${string}`): UnsignedLaunchTx {
202
+ const data =
203
+ call.functionName === 'launchAndBuy'
204
+ ? encodeFunctionData({ abi: launchOrchestratorAbi, functionName: 'launchAndBuy', args: call.args })
205
+ : encodeFunctionData({ abi: launchOrchestratorAbi, functionName: 'launch', args: call.args });
206
+ return { to: orchestrator, data, value: call.value };
207
+ }
208
+
209
+ /**
210
+ * The two launch-fee reads every V20 orchestrator (`PeddlesLaunchOrchestratorV20`,
211
+ * `PeddlesNFTBondingGraduationOrchestratorV20`) exposes since 2026-09-19. Hand-written because it
212
+ * MIRRORS `PeddlesAtomicBaseV20` in `contracts/src/PeddlesV20.sol` ahead of the next ABI
213
+ * regeneration; the signatures are the same ones the stock launchpad has always had.
214
+ */
215
+ export const orchestratorLaunchFeeAbi = [
216
+ { type: 'function', name: 'launchFee', stateMutability: 'view', inputs: [], outputs: [{ name: '', type: 'uint256' }] },
217
+ { type: 'function', name: 'maxLaunchFee', stateMutability: 'view', inputs: [], outputs: [{ name: '', type: 'uint256' }] },
218
+ ] as const;
219
+
220
+ export interface OrchestratorLaunchFee {
221
+ /** Exact wei the orchestrator takes off the top of `msg.value`. */
222
+ readonly launchFee: bigint;
223
+ /** Immutable per-deployment ceiling — disclosure, not something to send. */
224
+ readonly maxLaunchFee: bigint;
225
+ }
226
+
227
+ /**
228
+ * A V20 orchestrator's launch fee, READ FROM THE CHAIN. Owner-tunable storage under an immutable
229
+ * cap, so never a constant; a failed read throws rather than falling back to zero.
230
+ */
231
+ export async function readOrchestratorLaunchFee(client: ReadClient, orchestrator: Address): Promise<OrchestratorLaunchFee> {
232
+ const [launchFee, maxLaunchFee] = (await client.multicall({
233
+ allowFailure: false,
234
+ contracts: [
235
+ { address: orchestrator, abi: orchestratorLaunchFeeAbi, functionName: 'launchFee' },
236
+ { address: orchestrator, abi: orchestratorLaunchFeeAbi, functionName: 'maxLaunchFee' },
237
+ ],
238
+ })) as readonly [bigint, bigint];
239
+ return { launchFee, maxLaunchFee };
240
+ }
@@ -0,0 +1,257 @@
1
+ import type { ReadClient } from '../client.js';
2
+ import { factoryAbi } from '../abis.js';
3
+ import { launchOrchestratorAbi, liquidityExecutorLaunchAbi } from './abi.generated.js';
4
+ import type { LaunchAddresses } from './addresses.js';
5
+ import { liquidityForAmount, getSqrtPriceAtTick } from './tickMath.js';
6
+ import { VARIANT_ASSET } from './variants.js';
7
+
8
+ /**
9
+ * The WETH-type launch plan — everything `PeddlesLaunchOrchestratorV20.launch`
10
+ * needs, derived from LIVE chain reads. A pure port of the web app's
11
+ * `buildWethLaunchPlan` (`features/launch/useWethLaunch.ts`).
12
+ *
13
+ * The orchestrator takes `sqrtPriceX96`, `tickLower`, `tickUpper` and
14
+ * `liquidity` as INPUTS and mints exactly the position they describe, so every
15
+ * one of them is derived from reads:
16
+ * - fee tier, tick spacing, pool manager, position manager, hook and quote
17
+ * token come from `PeddlesV4LiquidityExecutor`
18
+ * - the tick range comes from the executor's `defaultSingleSidedTicks` for THIS
19
+ * token's predicted address
20
+ * - the start tick is recovered from that range and the spacing
21
+ * - the liquidity is the largest that fits the POOLED share of the 1e9 supply
22
+ * - the allocation is the launch type's own (orchestrator defaults for type 0,
23
+ * the registered split for any other)
24
+ *
25
+ * Any failed read throws. There is no fallback: a launch assembled from stale
26
+ * pool parameters is a launch that burns a creator's gas.
27
+ */
28
+
29
+ /** Every Peddles launch is exactly one billion tokens; the orchestrator enforces it. */
30
+ export const SUPPLY_UNITS = 1_000_000_000n;
31
+ /** Every Peddles token launches at 18 decimals. Not a creator choice. */
32
+ export const LAUNCH_COIN_DECIMALS = 18;
33
+
34
+ export interface WethLaunchPlan {
35
+ readonly predictedToken: `0x${string}`;
36
+ readonly quoteToken: `0x${string}`;
37
+ readonly tokenIsCurrency0: boolean;
38
+ readonly tickLower: number;
39
+ readonly tickUpper: number;
40
+ readonly startTick: number;
41
+ readonly sqrtPriceX96: bigint;
42
+ readonly liquidity: bigint;
43
+ /** Total supply in base units — 1e9 × 10^18. */
44
+ readonly totalSupply: bigint;
45
+ /** The liquidity share of it, in base units — what the position is sized to hold. */
46
+ readonly pooledSupply: bigint;
47
+ readonly poolFeePpm: number;
48
+ readonly tickSpacing: number;
49
+ readonly hook: `0x${string}`;
50
+ readonly poolManager: `0x${string}`;
51
+ readonly positionManager: `0x${string}`;
52
+ readonly liquidityManager: `0x${string}`;
53
+ readonly airdropPublisher: `0x${string}`;
54
+ readonly allocationBps: {
55
+ readonly liquidity: number;
56
+ readonly airdrop: number;
57
+ readonly vesting: number;
58
+ readonly burn: number;
59
+ readonly clog: number;
60
+ };
61
+ /** The clog terms, when this launch type has one. `null` on a plain launch. */
62
+ readonly clog: {
63
+ readonly bps: number;
64
+ readonly sliceBps: number;
65
+ readonly minIntervalSeconds: number;
66
+ } | null;
67
+ /** The launch TYPE this plan was built for; the input builder cannot assemble another. */
68
+ readonly variant: number;
69
+ }
70
+
71
+ export interface WethPlanArgs {
72
+ readonly salt: `0x${string}`;
73
+ /** The locally mined address this salt must produce; the factory must agree or the plan fails closed. */
74
+ readonly expectedToken?: `0x${string}` | null;
75
+ /** The factory's variant id, as the creator chose it. REQUIRED — there is no default type. */
76
+ readonly variant: number;
77
+ }
78
+
79
+ export async function buildWethLaunchPlan(
80
+ client: ReadClient,
81
+ addresses: LaunchAddresses,
82
+ args: WethPlanArgs,
83
+ ): Promise<WethLaunchPlan> {
84
+ const { orchestrator, factory, liquidityExecutor } = addresses;
85
+ const variant = args.variant;
86
+
87
+ const config = await client.multicall({
88
+ allowFailure: false,
89
+ contracts: [
90
+ { address: liquidityExecutor, abi: liquidityExecutorLaunchAbi, functionName: 'quoteToken' },
91
+ { address: liquidityExecutor, abi: liquidityExecutorLaunchAbi, functionName: 'poolManager' },
92
+ { address: liquidityExecutor, abi: liquidityExecutorLaunchAbi, functionName: 'positionManager' },
93
+ { address: liquidityExecutor, abi: liquidityExecutorLaunchAbi, functionName: 'hook' },
94
+ { address: liquidityExecutor, abi: liquidityExecutorLaunchAbi, functionName: 'fee' },
95
+ { address: liquidityExecutor, abi: liquidityExecutorLaunchAbi, functionName: 'tickSpacing' },
96
+ { address: orchestrator, abi: launchOrchestratorAbi, functionName: 'airdropPublisher' },
97
+ { address: orchestrator, abi: launchOrchestratorAbi, functionName: 'defaultLiquidityBps' },
98
+ { address: orchestrator, abi: launchOrchestratorAbi, functionName: 'defaultAirdropBps' },
99
+ { address: orchestrator, abi: launchOrchestratorAbi, functionName: 'defaultVestingBps' },
100
+ { address: orchestrator, abi: launchOrchestratorAbi, functionName: 'defaultBurnBps' },
101
+ { address: factory, abi: factoryAbi, functionName: 'getPeddlesAddress', args: [variant, orchestrator, args.salt] },
102
+ { address: factory, abi: factoryAbi, functionName: 'isVariantSupported', args: [variant] },
103
+ { address: orchestrator, abi: launchOrchestratorAbi, functionName: 'feeHook' },
104
+ ],
105
+ });
106
+
107
+ const [
108
+ quoteToken,
109
+ poolManager,
110
+ positionManager,
111
+ hook,
112
+ fee,
113
+ tickSpacing,
114
+ airdropPublisher,
115
+ liquidityBps,
116
+ airdropBps,
117
+ vestingBps,
118
+ burnBps,
119
+ predictedToken,
120
+ variantSupported,
121
+ orchestratorHook,
122
+ ] = config as unknown as [
123
+ `0x${string}`,
124
+ `0x${string}`,
125
+ `0x${string}`,
126
+ `0x${string}`,
127
+ number,
128
+ number,
129
+ `0x${string}`,
130
+ number,
131
+ number,
132
+ number,
133
+ number,
134
+ `0x${string}`,
135
+ boolean,
136
+ `0x${string}`,
137
+ ];
138
+
139
+ if (!variantSupported) {
140
+ throw new Error('This launch type is not open for new launches, so launching is disabled.');
141
+ }
142
+ // Every launch pool is hooked by the fee hook, and the orchestrator refuses an executor pointed
143
+ // anywhere else. Checked here so a repointed contract refuses with a reason instead of reverting.
144
+ const expected = addresses.feeHook.toLowerCase();
145
+ if (hook.toLowerCase() !== expected || orchestratorHook.toLowerCase() !== expected) {
146
+ throw new Error(
147
+ 'The launch contracts on this network are not wired to the trading-fee contract this build expects, so launching is disabled.',
148
+ );
149
+ }
150
+ if (args.expectedToken && predictedToken.toLowerCase() !== args.expectedToken.toLowerCase()) {
151
+ throw new Error(
152
+ 'The factory predicts a different address for this launch than the one reserved for it. Launching is disabled rather than sending it somewhere else.',
153
+ );
154
+ }
155
+
156
+ let alloc = {
157
+ liquidity: Number(liquidityBps),
158
+ airdrop: Number(airdropBps),
159
+ vesting: Number(vestingBps),
160
+ burn: Number(burnBps),
161
+ clog: 0,
162
+ };
163
+ let clogTerms: WethLaunchPlan['clog'] = null;
164
+
165
+ if (variant !== VARIANT_ASSET) {
166
+ const declared = (await client.readContract({
167
+ address: factory,
168
+ abi: factoryAbi,
169
+ functionName: 'variantAllocation',
170
+ args: [variant],
171
+ })) as {
172
+ liquidityBps: number;
173
+ airdropBps: number;
174
+ vestingBps: number;
175
+ burnBps: number;
176
+ clogBps: number;
177
+ clogSliceBps: number;
178
+ clogMinInterval: bigint;
179
+ };
180
+ alloc = {
181
+ liquidity: Number(declared.liquidityBps),
182
+ airdrop: Number(declared.airdropBps),
183
+ vesting: Number(declared.vestingBps),
184
+ burn: Number(declared.burnBps),
185
+ clog: Number(declared.clogBps),
186
+ };
187
+ if (alloc.clog > 0) {
188
+ clogTerms = {
189
+ bps: Number(declared.clogBps),
190
+ sliceBps: Number(declared.clogSliceBps),
191
+ minIntervalSeconds: Number(declared.clogMinInterval),
192
+ };
193
+ if (!addresses.clogVaultFactory) {
194
+ throw new Error(
195
+ 'This launch type withholds supply into a clog vault, and no clog vault factory is deployed on this chain. Launching is disabled rather than launching without it.',
196
+ );
197
+ }
198
+ }
199
+ }
200
+
201
+ if (alloc.airdrop !== 0 || alloc.burn !== 0 || alloc.vesting !== 0) {
202
+ throw new Error(
203
+ `This launch type allocates ${alloc.liquidity}/${alloc.airdrop}/${alloc.vesting}/${alloc.burn}/${alloc.clog} bps (liquidity/airdrop/vesting/burn/clog). Launching is disabled until it matches what this build would state.`,
204
+ );
205
+ }
206
+ if (alloc.liquidity + alloc.clog !== 10_000) {
207
+ throw new Error(`This launch type's allocation sums to ${alloc.liquidity + alloc.clog} bps, not 10000. Launching is disabled.`);
208
+ }
209
+
210
+ const [tickLower, tickUpper] = (await client.readContract({
211
+ address: liquidityExecutor,
212
+ abi: liquidityExecutorLaunchAbi,
213
+ functionName: 'defaultSingleSidedTicks',
214
+ args: [predictedToken],
215
+ })) as readonly [number, number];
216
+
217
+ const tokenIsCurrency0 = BigInt(predictedToken) < BigInt(quoteToken);
218
+ const spacing = Number(tickSpacing);
219
+ // Exactly the harness's derivation: the start price sits one spacing outside the single-sided
220
+ // range, on the side the token does not occupy.
221
+ const startTick = tokenIsCurrency0 ? Number(tickLower) - spacing : Number(tickUpper) + spacing;
222
+ const sqrtPriceX96 = getSqrtPriceAtTick(startTick);
223
+
224
+ const totalSupply = SUPPLY_UNITS * 10n ** BigInt(LAUNCH_COIN_DECIMALS);
225
+ // The position holds the LIQUIDITY share, not the whole supply: on a clog type the rest is minted
226
+ // to the clog vault, and a position sized to the whole supply reverts every clog launch.
227
+ const pooledSupply = (totalSupply * BigInt(alloc.liquidity)) / 10_000n;
228
+ const quote = liquidityForAmount(tokenIsCurrency0, Number(tickLower), Number(tickUpper), pooledSupply);
229
+ if (!quote) {
230
+ throw new Error(
231
+ 'No single-sided position fits the pooled supply at the executor’s current tick range, so the pool parameters for this launch cannot be derived.',
232
+ );
233
+ }
234
+
235
+ return {
236
+ predictedToken,
237
+ quoteToken,
238
+ tokenIsCurrency0,
239
+ tickLower: Number(tickLower),
240
+ tickUpper: Number(tickUpper),
241
+ startTick,
242
+ sqrtPriceX96,
243
+ liquidity: quote.liquidity,
244
+ totalSupply,
245
+ pooledSupply,
246
+ poolFeePpm: Number(fee),
247
+ tickSpacing: spacing,
248
+ hook,
249
+ poolManager,
250
+ positionManager,
251
+ liquidityManager: liquidityExecutor,
252
+ airdropPublisher,
253
+ allocationBps: { ...alloc },
254
+ clog: clogTerms,
255
+ variant,
256
+ };
257
+ }