@black-market/sdk 0.1.0 → 0.4.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 (88) hide show
  1. package/README.md +92 -20
  2. package/dist/abyss.d.ts +4 -2748
  3. package/dist/abyss.d.ts.map +1 -1
  4. package/dist/abyss.js +3 -1029
  5. package/dist/abyss.js.map +1 -1
  6. package/dist/addresses.d.ts +3 -7
  7. package/dist/addresses.d.ts.map +1 -1
  8. package/dist/addresses.js +6 -21
  9. package/dist/addresses.js.map +1 -1
  10. package/dist/auction.d.ts +2 -2
  11. package/dist/auction.d.ts.map +1 -1
  12. package/dist/auction.js +2 -2
  13. package/dist/auction.js.map +1 -1
  14. package/dist/index.d.ts +2 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +2 -0
  17. package/dist/index.js.map +1 -1
  18. package/dist/launch-api.d.ts +151 -0
  19. package/dist/launch-api.d.ts.map +1 -0
  20. package/dist/launch-api.js +117 -0
  21. package/dist/launch-api.js.map +1 -0
  22. package/dist/launch.d.ts +23 -185
  23. package/dist/launch.d.ts.map +1 -1
  24. package/dist/launch.js +35 -272
  25. package/dist/launch.js.map +1 -1
  26. package/dist/lifecycle/abi.d.ts +6391 -0
  27. package/dist/lifecycle/abi.d.ts.map +1 -0
  28. package/dist/lifecycle/abi.js +335 -0
  29. package/dist/lifecycle/abi.js.map +1 -0
  30. package/dist/lifecycle/authors.d.ts +133 -0
  31. package/dist/lifecycle/authors.d.ts.map +1 -0
  32. package/dist/lifecycle/authors.js +217 -0
  33. package/dist/lifecycle/authors.js.map +1 -0
  34. package/dist/lifecycle/fork.d.ts +15 -0
  35. package/dist/lifecycle/fork.d.ts.map +1 -0
  36. package/dist/lifecycle/fork.js +36 -0
  37. package/dist/lifecycle/fork.js.map +1 -0
  38. package/dist/lifecycle/index.d.ts +10 -0
  39. package/dist/lifecycle/index.d.ts.map +1 -0
  40. package/dist/lifecycle/index.js +10 -0
  41. package/dist/lifecycle/index.js.map +1 -0
  42. package/dist/lifecycle/markets.d.ts +349 -0
  43. package/dist/lifecycle/markets.d.ts.map +1 -0
  44. package/dist/lifecycle/markets.js +186 -0
  45. package/dist/lifecycle/markets.js.map +1 -0
  46. package/dist/lifecycle/planner.d.ts +20 -0
  47. package/dist/lifecycle/planner.d.ts.map +1 -0
  48. package/dist/lifecycle/planner.js +481 -0
  49. package/dist/lifecycle/planner.js.map +1 -0
  50. package/dist/lifecycle/progress.d.ts +43 -0
  51. package/dist/lifecycle/progress.d.ts.map +1 -0
  52. package/dist/lifecycle/progress.js +449 -0
  53. package/dist/lifecycle/progress.js.map +1 -0
  54. package/dist/lifecycle/rpc.d.ts +17 -0
  55. package/dist/lifecycle/rpc.d.ts.map +1 -0
  56. package/dist/lifecycle/rpc.js +100 -0
  57. package/dist/lifecycle/rpc.js.map +1 -0
  58. package/dist/lifecycle/schema.d.ts +1068 -0
  59. package/dist/lifecycle/schema.d.ts.map +1 -0
  60. package/dist/lifecycle/schema.js +335 -0
  61. package/dist/lifecycle/schema.js.map +1 -0
  62. package/dist/lifecycle/simulation.d.ts +3 -0
  63. package/dist/lifecycle/simulation.d.ts.map +1 -0
  64. package/dist/lifecycle/simulation.js +393 -0
  65. package/dist/lifecycle/simulation.js.map +1 -0
  66. package/dist/lifecycle/types.d.ts +273 -0
  67. package/dist/lifecycle/types.d.ts.map +1 -0
  68. package/dist/lifecycle/types.js +11 -0
  69. package/dist/lifecycle/types.js.map +1 -0
  70. package/docs/launch-lifecycle.md +243 -0
  71. package/package.json +12 -3
  72. package/src/abyss.ts +3 -1059
  73. package/src/addresses.ts +9 -26
  74. package/src/auction.ts +2 -2
  75. package/src/index.ts +2 -0
  76. package/src/launch-api.ts +110 -0
  77. package/src/launch.ts +43 -411
  78. package/src/lifecycle/abi.ts +343 -0
  79. package/src/lifecycle/authors.ts +203 -0
  80. package/src/lifecycle/fork.ts +36 -0
  81. package/src/lifecycle/index.ts +9 -0
  82. package/src/lifecycle/markets.ts +205 -0
  83. package/src/lifecycle/planner.ts +396 -0
  84. package/src/lifecycle/progress.ts +378 -0
  85. package/src/lifecycle/rpc.ts +88 -0
  86. package/src/lifecycle/schema.ts +387 -0
  87. package/src/lifecycle/simulation.ts +329 -0
  88. package/src/lifecycle/types.ts +132 -0
package/src/launch.ts CHANGED
@@ -1,236 +1,37 @@
1
- import {
2
- type Address,
3
- encodeFunctionData,
4
- type Hex,
5
- isAddress,
6
- keccak256,
7
- stringToHex,
8
- zeroAddress,
9
- } from "viem";
10
- import { ABYSS_FEE_TIERS, AbyssPoolProfile, atomicLaunchFactoryAbi, TokenKind } from "./abyss.js";
1
+ import { ABYSS_FEE_TIERS } from "./abyss.js";
11
2
  import { AUCTION_SUPPLY } from "./auction.js";
12
3
 
13
- export const INITIAL_TEMPLATE_VERSION = 1 as const;
14
- export const STANDARD_TEMPLATE_ID = keccak256(stringToHex("black-market.standard"));
15
- export const QUOTE_STAKING_TEMPLATE_ID = keccak256(stringToHex("black-market.quote-staking"));
16
- export const QUOTE_DIVIDENDS_TEMPLATE_ID = keccak256(stringToHex("black-market.quote-dividends"));
17
- export const DUAL_STAKING_TEMPLATE_ID = keccak256(stringToHex("black-market.dual-staking"));
18
- export const DUAL_DIVIDENDS_TEMPLATE_ID = keccak256(stringToHex("black-market.dual-dividends"));
19
- export const FEE_BURN_TEMPLATE_ID = keccak256(stringToHex("black-market.fee-burn"));
20
- export const STANDARD_TEMPLATE_VERSION = INITIAL_TEMPLATE_VERSION;
21
4
  export const LAUNCH_REWARD_DURATION = 7 * 24 * 60 * 60;
22
5
 
23
- /**
24
- * Canonical registered volatile/P3 oracle configuration for Atomic launches.
25
- * Deployment evidence records maxAbsTickMove=17 and cardinality=4096 for this ID.
26
- */
27
- export const ATOMIC_LAUNCH_ORACLE_CONFIG_ID =
6
+ /** Canonical P3 oracle default; reviewed V4 markets may select any valid registered oracle within their cardinality bounds. */
7
+ export const LAUNCH_ORACLE_CONFIG_ID =
28
8
  "0xc0e9bed88d70a13fd3ab31451fefdd073b7266e838aee0ad1c236c8c9eff855d" as const;
9
+ export const LAUNCH_DEADLINE_SECONDS = 20 * 60;
10
+ export const LAUNCH_BUY_SLIPPAGE_BPS = 50;
11
+ export const DEFAULT_LAUNCH_TARGET_MARKET_CAP_USD = 5_000;
29
12
 
30
- /** Existing Abyss execution policy: Atomic launch requests expire after 20 minutes. */
31
- export const ATOMIC_LAUNCH_DEADLINE_SECONDS = 20 * 60;
32
-
33
- /** Existing Abyss execution policy: initial Atomic buys tolerate 50 basis points of price movement. */
34
- export const ATOMIC_LAUNCH_BUY_SLIPPAGE_BPS = 50;
35
-
36
- /**
37
- * Atomic lifecycle recipes and tests use a $5,000 initial fully diluted market cap.
38
- * The form converts this editable whole-USD default to an X18 value before recipe derivation.
39
- */
40
- export const DEFAULT_ATOMIC_LAUNCH_TARGET_MARKET_CAP_USD = 5_000;
41
-
42
-
43
- export enum LaunchRewardMode {
44
- None,
45
- Staking,
46
- Dividends,
47
- }
48
13
 
49
- export enum LaunchFeeAssetMode {
50
- Profile,
51
- PairedOnly,
52
- Both,
53
- LaunchedOnly,
54
- }
55
-
56
- export enum LaunchFeeDestination {
57
- Owner = 1 << 0,
58
- Rewards = 1 << 1,
59
- Burn = 1 << 2,
60
- }
61
-
62
- export type LaunchTemplateId =
63
- | "standard"
64
- | "quote-staking"
65
- | "quote-dividends"
66
- | "dual-staking"
67
- | "dual-dividends"
68
- | "fee-burn";
69
14
  export type AbyssFeePips = (typeof ABYSS_FEE_TIERS)[number]["feePips"];
70
15
 
71
- export type FeeDisposition = {
72
- ownerBps: number;
73
- rewardsBps: number;
74
- burnBps: number;
75
- };
76
-
77
- export type LaunchTemplate = {
78
- id: LaunchTemplateId;
79
- templateId: Hex;
80
- version: typeof INITIAL_TEMPLATE_VERSION;
81
- label: string;
82
- tokenKinds: readonly TokenKind[];
83
- poolProfiles: readonly AbyssPoolProfile[];
84
- launchedTokenIsQuote: boolean;
85
- rewardMode: LaunchRewardMode;
86
- rewardDuration: number;
87
- feeAssetMode: LaunchFeeAssetMode;
88
- launchedTokenDestinations: number;
89
- pairedTokenDestinations: number;
90
- };
91
-
92
- const OWNER = LaunchFeeDestination.Owner;
93
- const REWARDS = LaunchFeeDestination.Rewards;
94
- const BURN = LaunchFeeDestination.Burn;
95
- const BURNABLE_ONLY = [TokenKind.Burnable] as const;
96
- const DIVIDEND_ONLY = [TokenKind.HolderDividend] as const;
97
-
98
- export const LAUNCH_TEMPLATES = [
99
- {
100
- id: "standard",
101
- templateId: STANDARD_TEMPLATE_ID,
102
- version: INITIAL_TEMPLATE_VERSION,
103
- label: "Standard",
104
- tokenKinds: BURNABLE_ONLY,
105
- poolProfiles: [AbyssPoolProfile.StandardOracle, AbyssPoolProfile.QuoteOracle],
106
- launchedTokenIsQuote: false,
107
- rewardMode: LaunchRewardMode.None,
108
- rewardDuration: 0,
109
- feeAssetMode: LaunchFeeAssetMode.Profile,
110
- launchedTokenDestinations: OWNER,
111
- pairedTokenDestinations: OWNER,
112
- },
113
- {
114
- id: "quote-staking",
115
- templateId: QUOTE_STAKING_TEMPLATE_ID,
116
- version: INITIAL_TEMPLATE_VERSION,
117
- label: "Quote Staking",
118
- tokenKinds: BURNABLE_ONLY,
119
- poolProfiles: [AbyssPoolProfile.QuoteOracle],
120
- launchedTokenIsQuote: false,
121
- rewardMode: LaunchRewardMode.Staking,
122
- rewardDuration: LAUNCH_REWARD_DURATION,
123
- feeAssetMode: LaunchFeeAssetMode.PairedOnly,
124
- launchedTokenDestinations: 0,
125
- pairedTokenDestinations: OWNER | REWARDS,
126
- },
127
- {
128
- id: "quote-dividends",
129
- templateId: QUOTE_DIVIDENDS_TEMPLATE_ID,
130
- version: INITIAL_TEMPLATE_VERSION,
131
- label: "Quote Dividends",
132
- tokenKinds: DIVIDEND_ONLY,
133
- poolProfiles: [AbyssPoolProfile.QuoteOracle],
134
- launchedTokenIsQuote: false,
135
- rewardMode: LaunchRewardMode.Dividends,
136
- rewardDuration: LAUNCH_REWARD_DURATION,
137
- feeAssetMode: LaunchFeeAssetMode.PairedOnly,
138
- launchedTokenDestinations: 0,
139
- pairedTokenDestinations: OWNER | REWARDS,
140
- },
141
- {
142
- id: "dual-staking",
143
- templateId: DUAL_STAKING_TEMPLATE_ID,
144
- version: INITIAL_TEMPLATE_VERSION,
145
- label: "Dual Staking",
146
- tokenKinds: BURNABLE_ONLY,
147
- poolProfiles: [AbyssPoolProfile.StandardOracle],
148
- launchedTokenIsQuote: false,
149
- rewardMode: LaunchRewardMode.Staking,
150
- rewardDuration: LAUNCH_REWARD_DURATION,
151
- feeAssetMode: LaunchFeeAssetMode.Both,
152
- launchedTokenDestinations: OWNER | REWARDS | BURN,
153
- pairedTokenDestinations: OWNER | REWARDS,
154
- },
155
- {
156
- id: "dual-dividends",
157
- templateId: DUAL_DIVIDENDS_TEMPLATE_ID,
158
- version: INITIAL_TEMPLATE_VERSION,
159
- label: "Dual Dividends",
160
- tokenKinds: DIVIDEND_ONLY,
161
- poolProfiles: [AbyssPoolProfile.StandardOracle],
162
- launchedTokenIsQuote: false,
163
- rewardMode: LaunchRewardMode.Dividends,
164
- rewardDuration: LAUNCH_REWARD_DURATION,
165
- feeAssetMode: LaunchFeeAssetMode.Both,
166
- launchedTokenDestinations: OWNER | REWARDS | BURN,
167
- pairedTokenDestinations: OWNER | REWARDS,
168
- },
169
- {
170
- id: "fee-burn",
171
- templateId: FEE_BURN_TEMPLATE_ID,
172
- version: INITIAL_TEMPLATE_VERSION,
173
- label: "Fee Burn",
174
- tokenKinds: BURNABLE_ONLY,
175
- poolProfiles: [AbyssPoolProfile.QuoteOracle],
176
- launchedTokenIsQuote: true,
177
- rewardMode: LaunchRewardMode.None,
178
- rewardDuration: 0,
179
- feeAssetMode: LaunchFeeAssetMode.LaunchedOnly,
180
- launchedTokenDestinations: BURN,
181
- pairedTokenDestinations: 0,
182
- },
183
- ] as const satisfies readonly LaunchTemplate[];
184
-
185
- export type AtomicLaunchRequest = {
186
- creator: Address;
187
- templateId: Hex;
188
- templateVersion: number;
189
- token: {
190
- kind: TokenKind;
191
- name: string;
192
- symbol: string;
193
- decimals: number;
194
- supply: bigint;
195
- };
196
- pool: {
197
- pairedToken: Address;
198
- launchedTokenIsQuote: boolean;
199
- profile: AbyssPoolProfile;
200
- fee: AbyssFeePips;
201
- oracleConfigId: Hex;
202
- launchTick: number;
203
- liquidity: bigint;
204
- launchedTokenAmountMaximum: bigint;
205
- pairedTokenAmountMaximum: bigint;
206
- };
207
- initialBuy: {
208
- pairedTokenAmountIn: bigint;
209
- launchedTokenAmountOutMinimum: bigint;
210
- sqrtPriceLimitX96: bigint;
211
- };
212
- launchedTokenFees: FeeDisposition;
213
- pairedTokenFees: FeeDisposition;
214
- deadline: bigint;
215
- };
216
-
217
- export type AtomicLaunchPoolRecipeInput = {
16
+ export type LaunchPoolRecipeInput = {
218
17
  pairedTokenDecimals: number;
219
18
  pairedTokenUsdPriceX18: bigint;
220
19
  targetMarketCapUsdX18: bigint;
221
20
  launchedTokenIsQuote: boolean;
222
21
  fee: AbyssFeePips;
22
+ supply?: bigint; tokenBudget?: bigint; tickSpacing?: number;
223
23
  };
224
24
 
225
- export type AtomicLaunchPoolRecipe = {
25
+ export type LaunchPoolRecipe = {
226
26
  launchTick: number;
227
27
  launchSqrtPriceX96: bigint;
228
28
  liquidity: bigint;
229
29
  launchedTokenAmountMaximum: bigint;
230
30
  pairedTokenAmountMaximum: 0n;
31
+ tickLower: number; tickUpper: number;
231
32
  };
232
33
 
233
- export type AtomicLaunchBuySqrtPriceLimitInput = {
34
+ export type LaunchBuySqrtPriceLimitInput = {
234
35
  launchSqrtPriceX96: bigint;
235
36
  launchedTokenIsQuote: boolean;
236
37
  slippageBps?: number;
@@ -249,8 +50,6 @@ const MIN_TICK = -887_272;
249
50
  const MAX_TICK = 887_272;
250
51
  const MIN_SQRT_RATIO = 4_295_128_739n;
251
52
  const MAX_SQRT_RATIO = 1_461_446_703_485_210_103_287_273_052_203_988_822_378_723_970_342n;
252
- const ZERO_BYTES32 = `0x${"00".repeat(32)}`;
253
- const BYTES32_HEX = /^0x[0-9a-fA-F]{64}$/;
254
53
 
255
54
  // Canonical Uniswap v3 TickMath multipliers, matching contracts/src/oracles/TickMathLib.sol.
256
55
  const TICK_MULTIPLIERS = [
@@ -304,15 +103,6 @@ function assertTokenDecimals(value: unknown, name: string): asserts value is num
304
103
  }
305
104
  }
306
105
 
307
- function assertOracleConfigId(value: unknown): asserts value is Hex {
308
- if (
309
- typeof value !== "string" ||
310
- !BYTES32_HEX.test(value) ||
311
- value.toLowerCase() === ZERO_BYTES32
312
- ) {
313
- throw new Error("pool.oracleConfigId must be a nonzero bytes32");
314
- }
315
- }
316
106
 
317
107
  function assertCanonicalSqrtPrice(value: unknown, name: string): asserts value is bigint {
318
108
  assertUint(value, MAX_UINT160, name);
@@ -373,7 +163,7 @@ function getTickAtSqrtRatio(sqrtPriceX96: bigint): number {
373
163
  return low;
374
164
  }
375
165
 
376
- // Deliberately retain Solidity's signed-remainder alignment semantics from AtomicLaunchFactory.
166
+ // Signed-remainder alignment matches the canonical Solidity range geometry.
377
167
  function alignDown(tick: number, tickSpacing: number): number {
378
168
  const remainder = tick % tickSpacing;
379
169
  return remainder === 0 ? tick : tick - remainder;
@@ -400,7 +190,7 @@ function launchBand(
400
190
  tickUpper > MAX_TICK ||
401
191
  tickLower >= tickUpper
402
192
  ) {
403
- throw new Error("launchTick cannot form a valid Atomic launch band");
193
+ throw new Error("launchTick cannot form a valid one-sided launch band");
404
194
  }
405
195
  return { tickLower, tickUpper };
406
196
  }
@@ -414,12 +204,12 @@ function liquidityForAmount1(amount1: bigint, sqrtLowerX96: bigint, sqrtUpperX96
414
204
  }
415
205
 
416
206
  /**
417
- * Derives the one-sided Atomic launch position from a target fully diluted market cap.
207
+ * Derives one-sided launch geometry from explicit supply, budget and fully diluted market cap.
418
208
  * The raw price remains a rational bigint until the canonical Q64.96 square root conversion.
419
209
  */
420
- export function deriveAtomicLaunchPoolRecipe(
421
- input: AtomicLaunchPoolRecipeInput,
422
- ): AtomicLaunchPoolRecipe {
210
+ export function deriveLaunchPoolRecipe(
211
+ input: LaunchPoolRecipeInput,
212
+ ): LaunchPoolRecipe {
423
213
  assertTokenDecimals(input.pairedTokenDecimals, "pairedTokenDecimals");
424
214
  assertPositiveUint(input.pairedTokenUsdPriceX18, MAX_UINT256, "pairedTokenUsdPriceX18");
425
215
  assertPositiveUint(input.targetMarketCapUsdX18, MAX_UINT256, "targetMarketCapUsdX18");
@@ -427,27 +217,31 @@ export function deriveAtomicLaunchPoolRecipe(
427
217
  throw new Error("launchedTokenIsQuote must be a boolean");
428
218
  }
429
219
 
430
- const feeTier = getFeeTier(input.fee);
220
+ const supply = input.supply ?? AUCTION_SUPPLY; const tokenBudget = input.tokenBudget ?? supply;
221
+ assertPositiveUint(supply, MAX_UINT256, "supply"); assertPositiveUint(tokenBudget, MAX_UINT256, "tokenBudget");
222
+ if (tokenBudget > supply) throw new Error("tokenBudget cannot exceed supply");
223
+ const tickSpacing = input.tickSpacing ?? getFeeTier(input.fee).tickSpacing;
224
+ if (!Number.isInteger(tickSpacing) || tickSpacing < 1 || tickSpacing > 32767) throw new Error("tickSpacing must be 1..32767");
431
225
  const pairedTokenUnit = 10n ** BigInt(input.pairedTokenDecimals);
432
- const [priceNumerator, priceDenominator]: [bigint, bigint] = input.launchedTokenIsQuote ? [input.targetMarketCapUsdX18 * pairedTokenUnit, input.pairedTokenUsdPriceX18 * AUCTION_SUPPLY] : [input.pairedTokenUsdPriceX18 * AUCTION_SUPPLY, input.targetMarketCapUsdX18 * pairedTokenUnit];
226
+ const [priceNumerator, priceDenominator]: [bigint, bigint] = input.launchedTokenIsQuote ? [input.targetMarketCapUsdX18 * pairedTokenUnit, input.pairedTokenUsdPriceX18 * supply] : [input.pairedTokenUsdPriceX18 * supply, input.targetMarketCapUsdX18 * pairedTokenUnit];
433
227
  const targetSqrtPriceX96 = integerSqrt((priceNumerator << 192n) / priceDenominator);
434
228
  const rawTick = getTickAtSqrtRatio(targetSqrtPriceX96);
435
229
  const launchTick = input.launchedTokenIsQuote
436
- ? Math.ceil(rawTick / feeTier.tickSpacing) * feeTier.tickSpacing
437
- : Math.floor(rawTick / feeTier.tickSpacing) * feeTier.tickSpacing;
230
+ ? Math.ceil(rawTick / tickSpacing) * tickSpacing
231
+ : Math.floor(rawTick / tickSpacing) * tickSpacing;
438
232
  assertInt24(launchTick, "launchTick");
439
233
 
440
234
  const { tickLower, tickUpper } = launchBand(
441
235
  launchTick,
442
- feeTier.tickSpacing,
236
+ tickSpacing,
443
237
  input.launchedTokenIsQuote,
444
238
  );
445
239
  const sqrtLowerX96 = getSqrtRatioAtTick(tickLower);
446
240
  const sqrtUpperX96 = getSqrtRatioAtTick(tickUpper);
447
241
  const launchSqrtPriceX96 = input.launchedTokenIsQuote ? sqrtLowerX96 : sqrtUpperX96;
448
242
  const liquidity = input.launchedTokenIsQuote
449
- ? liquidityForAmount0(AUCTION_SUPPLY, sqrtLowerX96, sqrtUpperX96)
450
- : liquidityForAmount1(AUCTION_SUPPLY, sqrtLowerX96, sqrtUpperX96);
243
+ ? liquidityForAmount0(tokenBudget, sqrtLowerX96, sqrtUpperX96)
244
+ : liquidityForAmount1(tokenBudget, sqrtLowerX96, sqrtUpperX96);
451
245
  if (liquidity <= 0n || liquidity > MAX_UINT128) {
452
246
  throw new Error("Derived liquidity is outside the uint128 range");
453
247
  }
@@ -456,39 +250,41 @@ export function deriveAtomicLaunchPoolRecipe(
456
250
  launchTick,
457
251
  launchSqrtPriceX96,
458
252
  liquidity,
459
- launchedTokenAmountMaximum: AUCTION_SUPPLY,
253
+ launchedTokenAmountMaximum: tokenBudget,
460
254
  pairedTokenAmountMaximum: 0n,
255
+ tickLower, tickUpper,
461
256
  };
462
257
  }
463
258
 
464
- export type AtomicLaunchInitialBuyEstimateInput = {
259
+ export type LaunchInitialBuyEstimateInput = {
465
260
  launchSqrtPriceX96: bigint;
466
261
  liquidity: bigint;
467
262
  pairedTokenAmountIn: bigint;
468
263
  launchedTokenIsQuote: boolean;
469
- fee: AbyssFeePips;
264
+ fee: number;
470
265
  sqrtPriceLimitX96?: bigint;
471
266
  };
472
267
 
473
- export type AtomicLaunchInitialBuyEstimate = {
268
+ export type LaunchInitialBuyEstimate = {
474
269
  launchedTokenAmountOut: bigint;
475
270
  pairedTokenAmountConsumed: bigint;
476
271
  sqrtPriceAfterX96: bigint;
477
272
  };
478
273
 
479
274
  /**
480
- * Pure estimate for the first exact-input buy against a fresh one-sided Atomic
275
+ * Pure estimate for the first exact-input buy against a fresh one-sided
481
276
  * launch position. No wallet balance, allowance, pool deployment, or RPC state.
482
277
  * Uses conservative input-fee rounding; execution simulation remains the final
483
278
  * authority before submission.
484
279
  */
485
- export function estimateAtomicLaunchInitialBuy(
486
- input: AtomicLaunchInitialBuyEstimateInput,
487
- ): AtomicLaunchInitialBuyEstimate {
280
+ export function estimateLaunchInitialBuy(
281
+ input: LaunchInitialBuyEstimateInput,
282
+ ): LaunchInitialBuyEstimate {
488
283
  assertCanonicalSqrtPrice(input.launchSqrtPriceX96, "launchSqrtPriceX96");
489
284
  assertPositiveUint(input.liquidity, MAX_UINT128, "liquidity");
490
285
  assertUint(input.pairedTokenAmountIn, MAX_UINT256, "pairedTokenAmountIn");
491
- const fee = BigInt(getFeeTier(input.fee).feePips);
286
+ if (!Number.isInteger(input.fee) || input.fee < 0 || input.fee >= 1000000) throw new Error("fee must be pips below 1000000");
287
+ const fee = BigInt(input.fee);
492
288
  const amountAfterFee =
493
289
  (input.pairedTokenAmountIn * (1_000_000n - fee)) / 1_000_000n;
494
290
  if (amountAfterFee === 0n) {
@@ -533,17 +329,17 @@ function ceilSqrtRatio(numerator: bigint, denominator: bigint): bigint {
533
329
 
534
330
  /**
535
331
  * Produces the router's exclusive sqrt-price guard for the paired-token initial buy.
536
- * A paired input moves down for deployAbove and up for fee-burn/deployBelow.
332
+ * A paired input moves down when the launch token is currency1 and up when it is currency0.
537
333
  */
538
- export function deriveAtomicLaunchBuySqrtPriceLimitX96(
539
- input: AtomicLaunchBuySqrtPriceLimitInput,
334
+ export function deriveLaunchBuySqrtPriceLimitX96(
335
+ input: LaunchBuySqrtPriceLimitInput,
540
336
  ): bigint {
541
337
  assertCanonicalSqrtPrice(input.launchSqrtPriceX96, "launchSqrtPriceX96");
542
338
  if (typeof input.launchedTokenIsQuote !== "boolean") {
543
339
  throw new Error("launchedTokenIsQuote must be a boolean");
544
340
  }
545
341
 
546
- const slippageBps = input.slippageBps ?? ATOMIC_LAUNCH_BUY_SLIPPAGE_BPS;
342
+ const slippageBps = input.slippageBps ?? LAUNCH_BUY_SLIPPAGE_BPS;
547
343
  if (!Number.isInteger(slippageBps) || slippageBps < 1 || slippageBps > 9_999) {
548
344
  throw new Error("slippageBps must be an integer from 1 through 9,999");
549
345
  }
@@ -558,167 +354,3 @@ export function deriveAtomicLaunchBuySqrtPriceLimitX96(
558
354
  const limit = ceilSqrtRatio(squaredLaunchSqrt * (10_000n - slippage), 10_000n);
559
355
  return limit <= MIN_SQRT_RATIO ? MIN_SQRT_RATIO + 1n : limit;
560
356
  }
561
- export function getLaunchTemplate(id: LaunchTemplateId): LaunchTemplate {
562
- const template = LAUNCH_TEMPLATES.find((candidate) => candidate.id === id);
563
- if (!template) throw new Error(`Unknown launch template: ${id}`);
564
- return template;
565
- }
566
-
567
- export function getLaunchTemplateByHash(templateId: Hex): LaunchTemplate {
568
- const template = LAUNCH_TEMPLATES.find(
569
- (candidate) => candidate.templateId.toLowerCase() === templateId.toLowerCase(),
570
- );
571
- if (!template) throw new Error(`Unknown launch template id: ${templateId}`);
572
- return template;
573
- }
574
-
575
- function dispositionTotal(disposition: FeeDisposition): number {
576
- return disposition.ownerBps + disposition.rewardsBps + disposition.burnBps;
577
- }
578
-
579
- function dispositionDestinations(disposition: FeeDisposition): number {
580
- return (
581
- (disposition.ownerBps === 0 ? 0 : OWNER) |
582
- (disposition.rewardsBps === 0 ? 0 : REWARDS) |
583
- (disposition.burnBps === 0 ? 0 : BURN)
584
- );
585
- }
586
-
587
- function validateDisposition(
588
- name: "launchedTokenFees" | "pairedTokenFees",
589
- disposition: FeeDisposition,
590
- active: boolean,
591
- allowedDestinations: number,
592
- ): void {
593
- if (
594
- !Number.isInteger(disposition.ownerBps) ||
595
- !Number.isInteger(disposition.rewardsBps) ||
596
- !Number.isInteger(disposition.burnBps) ||
597
- disposition.ownerBps < 0 ||
598
- disposition.rewardsBps < 0 ||
599
- disposition.burnBps < 0
600
- ) {
601
- throw new Error(`${name} must contain nonnegative integer basis points`);
602
- }
603
-
604
- const total = dispositionTotal(disposition);
605
- if (active ? total !== 10_000 : total !== 0) {
606
- throw new Error(`${name} must total ${active ? "10,000" : "zero"} basis points`);
607
- }
608
- if (dispositionDestinations(disposition) & ~allowedDestinations) {
609
- throw new Error(`${name} uses a destination not allowed by the template`);
610
- }
611
- }
612
-
613
- export function buildAtomicLaunchCalldata(
614
- request: AtomicLaunchRequest,
615
- nativeBuyAmount = 0n,
616
- ): Hex {
617
- if (!isAddress(request.creator) || request.creator.toLowerCase() === zeroAddress) {
618
- throw new Error("creator must be a nonzero address");
619
- }
620
- const template = getLaunchTemplateByHash(request.templateId);
621
- if (request.templateVersion !== template.version) throw new Error("Unsupported template version");
622
- if (!template.tokenKinds.includes(request.token.kind)) {
623
- throw new Error("Token kind is not supported by the template");
624
- }
625
- if (!template.poolProfiles.includes(request.pool.profile)) {
626
- throw new Error("Pool profile is not supported by the template");
627
- }
628
- if (!isAddress(request.pool.pairedToken) || request.pool.pairedToken.toLowerCase() === zeroAddress) {
629
- throw new Error("pairedToken must be a nonzero address");
630
- }
631
- if (request.pool.launchedTokenIsQuote !== template.launchedTokenIsQuote) {
632
- throw new Error("Pool orientation does not match the template");
633
- }
634
-
635
- const feeTier = getFeeTier(request.pool.fee);
636
- assertOracleConfigId(request.pool.oracleConfigId);
637
- assertInt24(request.pool.launchTick, "pool.launchTick");
638
- assertTokenDecimals(request.token.decimals, "token.decimals");
639
- assertPositiveUint(request.token.supply, MAX_UINT256, "token.supply");
640
- assertPositiveUint(request.pool.liquidity, MAX_UINT128, "pool.liquidity");
641
- assertPositiveUint(
642
- request.pool.launchedTokenAmountMaximum,
643
- MAX_UINT256,
644
- "pool.launchedTokenAmountMaximum",
645
- );
646
- assertUint(request.pool.pairedTokenAmountMaximum, MAX_UINT256, "pool.pairedTokenAmountMaximum");
647
- if (request.pool.pairedTokenAmountMaximum !== 0n) {
648
- throw new Error("pool.pairedTokenAmountMaximum must be zero for a single-sided Atomic launch");
649
- }
650
- if (request.pool.launchedTokenAmountMaximum > request.token.supply) {
651
- throw new Error("pool.launchedTokenAmountMaximum cannot exceed token.supply");
652
- }
653
- assertPositiveUint(request.deadline, MAX_UINT256, "deadline");
654
-
655
- assertUint(request.initialBuy.pairedTokenAmountIn, MAX_UINT256, "initialBuy.pairedTokenAmountIn");
656
- assertUint(nativeBuyAmount, MAX_UINT256, "nativeBuyAmount");
657
- assertUint(
658
- request.initialBuy.launchedTokenAmountOutMinimum,
659
- MAX_UINT256,
660
- "initialBuy.launchedTokenAmountOutMinimum",
661
- );
662
- assertUint(request.initialBuy.sqrtPriceLimitX96, MAX_UINT160, "initialBuy.sqrtPriceLimitX96");
663
- const effectiveBuyAmount = request.initialBuy.pairedTokenAmountIn + nativeBuyAmount;
664
- if (effectiveBuyAmount === 0n) {
665
- if (
666
- request.initialBuy.launchedTokenAmountOutMinimum !== 0n ||
667
- request.initialBuy.sqrtPriceLimitX96 !== 0n
668
- ) {
669
- throw new Error("zero initial buy cannot set output or price limits");
670
- }
671
- } else {
672
- if (request.initialBuy.sqrtPriceLimitX96 === 0n) {
673
- throw new Error("nonzero initial buy requires a directional price limit");
674
- }
675
- assertCanonicalSqrtPrice(
676
- request.initialBuy.sqrtPriceLimitX96,
677
- "initialBuy.sqrtPriceLimitX96",
678
- );
679
- const { tickLower, tickUpper } = launchBand(
680
- request.pool.launchTick,
681
- feeTier.tickSpacing,
682
- request.pool.launchedTokenIsQuote,
683
- );
684
- const launchSqrtPriceX96 = getSqrtRatioAtTick(
685
- request.pool.launchedTokenIsQuote ? tickLower : tickUpper,
686
- );
687
- if (
688
- (!request.pool.launchedTokenIsQuote &&
689
- request.initialBuy.sqrtPriceLimitX96 >= launchSqrtPriceX96) ||
690
- (request.pool.launchedTokenIsQuote &&
691
- request.initialBuy.sqrtPriceLimitX96 <= launchSqrtPriceX96)
692
- ) {
693
- throw new Error("initial buy price limit does not protect the paired-token swap direction");
694
- }
695
- }
696
-
697
- const launchedFeesActive =
698
- template.feeAssetMode === LaunchFeeAssetMode.Both ||
699
- template.feeAssetMode === LaunchFeeAssetMode.LaunchedOnly ||
700
- (template.feeAssetMode === LaunchFeeAssetMode.Profile &&
701
- request.pool.profile === AbyssPoolProfile.StandardOracle);
702
- const pairedFeesActive =
703
- template.feeAssetMode === LaunchFeeAssetMode.Profile ||
704
- template.feeAssetMode === LaunchFeeAssetMode.PairedOnly ||
705
- template.feeAssetMode === LaunchFeeAssetMode.Both;
706
- validateDisposition(
707
- "launchedTokenFees",
708
- request.launchedTokenFees,
709
- launchedFeesActive,
710
- template.launchedTokenDestinations,
711
- );
712
- validateDisposition(
713
- "pairedTokenFees",
714
- request.pairedTokenFees,
715
- pairedFeesActive,
716
- template.pairedTokenDestinations,
717
- );
718
-
719
- return encodeFunctionData({
720
- abi: atomicLaunchFactoryAbi,
721
- functionName: "deployAndLaunch",
722
- args: [request],
723
- });
724
- }