@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.
- package/README.md +92 -20
- package/dist/abyss.d.ts +4 -2748
- package/dist/abyss.d.ts.map +1 -1
- package/dist/abyss.js +3 -1029
- package/dist/abyss.js.map +1 -1
- package/dist/addresses.d.ts +3 -7
- package/dist/addresses.d.ts.map +1 -1
- package/dist/addresses.js +6 -21
- package/dist/addresses.js.map +1 -1
- package/dist/auction.d.ts +2 -2
- package/dist/auction.d.ts.map +1 -1
- package/dist/auction.js +2 -2
- package/dist/auction.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/launch-api.d.ts +151 -0
- package/dist/launch-api.d.ts.map +1 -0
- package/dist/launch-api.js +117 -0
- package/dist/launch-api.js.map +1 -0
- package/dist/launch.d.ts +23 -185
- package/dist/launch.d.ts.map +1 -1
- package/dist/launch.js +35 -272
- package/dist/launch.js.map +1 -1
- package/dist/lifecycle/abi.d.ts +6391 -0
- package/dist/lifecycle/abi.d.ts.map +1 -0
- package/dist/lifecycle/abi.js +335 -0
- package/dist/lifecycle/abi.js.map +1 -0
- package/dist/lifecycle/authors.d.ts +133 -0
- package/dist/lifecycle/authors.d.ts.map +1 -0
- package/dist/lifecycle/authors.js +217 -0
- package/dist/lifecycle/authors.js.map +1 -0
- package/dist/lifecycle/fork.d.ts +15 -0
- package/dist/lifecycle/fork.d.ts.map +1 -0
- package/dist/lifecycle/fork.js +36 -0
- package/dist/lifecycle/fork.js.map +1 -0
- package/dist/lifecycle/index.d.ts +10 -0
- package/dist/lifecycle/index.d.ts.map +1 -0
- package/dist/lifecycle/index.js +10 -0
- package/dist/lifecycle/index.js.map +1 -0
- package/dist/lifecycle/markets.d.ts +349 -0
- package/dist/lifecycle/markets.d.ts.map +1 -0
- package/dist/lifecycle/markets.js +186 -0
- package/dist/lifecycle/markets.js.map +1 -0
- package/dist/lifecycle/planner.d.ts +20 -0
- package/dist/lifecycle/planner.d.ts.map +1 -0
- package/dist/lifecycle/planner.js +481 -0
- package/dist/lifecycle/planner.js.map +1 -0
- package/dist/lifecycle/progress.d.ts +43 -0
- package/dist/lifecycle/progress.d.ts.map +1 -0
- package/dist/lifecycle/progress.js +449 -0
- package/dist/lifecycle/progress.js.map +1 -0
- package/dist/lifecycle/rpc.d.ts +17 -0
- package/dist/lifecycle/rpc.d.ts.map +1 -0
- package/dist/lifecycle/rpc.js +100 -0
- package/dist/lifecycle/rpc.js.map +1 -0
- package/dist/lifecycle/schema.d.ts +1068 -0
- package/dist/lifecycle/schema.d.ts.map +1 -0
- package/dist/lifecycle/schema.js +335 -0
- package/dist/lifecycle/schema.js.map +1 -0
- package/dist/lifecycle/simulation.d.ts +3 -0
- package/dist/lifecycle/simulation.d.ts.map +1 -0
- package/dist/lifecycle/simulation.js +393 -0
- package/dist/lifecycle/simulation.js.map +1 -0
- package/dist/lifecycle/types.d.ts +273 -0
- package/dist/lifecycle/types.d.ts.map +1 -0
- package/dist/lifecycle/types.js +11 -0
- package/dist/lifecycle/types.js.map +1 -0
- package/docs/launch-lifecycle.md +243 -0
- package/package.json +12 -3
- package/src/abyss.ts +3 -1059
- package/src/addresses.ts +9 -26
- package/src/auction.ts +2 -2
- package/src/index.ts +2 -0
- package/src/launch-api.ts +110 -0
- package/src/launch.ts +43 -411
- package/src/lifecycle/abi.ts +343 -0
- package/src/lifecycle/authors.ts +203 -0
- package/src/lifecycle/fork.ts +36 -0
- package/src/lifecycle/index.ts +9 -0
- package/src/lifecycle/markets.ts +205 -0
- package/src/lifecycle/planner.ts +396 -0
- package/src/lifecycle/progress.ts +378 -0
- package/src/lifecycle/rpc.ts +88 -0
- package/src/lifecycle/schema.ts +387 -0
- package/src/lifecycle/simulation.ts +329 -0
- 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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
//
|
|
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
|
|
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
|
|
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
|
|
421
|
-
input:
|
|
422
|
-
):
|
|
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
|
|
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 *
|
|
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 /
|
|
437
|
-
: Math.floor(rawTick /
|
|
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
|
-
|
|
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(
|
|
450
|
-
: liquidityForAmount1(
|
|
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:
|
|
253
|
+
launchedTokenAmountMaximum: tokenBudget,
|
|
460
254
|
pairedTokenAmountMaximum: 0n,
|
|
255
|
+
tickLower, tickUpper,
|
|
461
256
|
};
|
|
462
257
|
}
|
|
463
258
|
|
|
464
|
-
export type
|
|
259
|
+
export type LaunchInitialBuyEstimateInput = {
|
|
465
260
|
launchSqrtPriceX96: bigint;
|
|
466
261
|
liquidity: bigint;
|
|
467
262
|
pairedTokenAmountIn: bigint;
|
|
468
263
|
launchedTokenIsQuote: boolean;
|
|
469
|
-
fee:
|
|
264
|
+
fee: number;
|
|
470
265
|
sqrtPriceLimitX96?: bigint;
|
|
471
266
|
};
|
|
472
267
|
|
|
473
|
-
export type
|
|
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
|
|
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
|
|
486
|
-
input:
|
|
487
|
-
):
|
|
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
|
-
|
|
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
|
|
332
|
+
* A paired input moves down when the launch token is currency1 and up when it is currency0.
|
|
537
333
|
*/
|
|
538
|
-
export function
|
|
539
|
-
input:
|
|
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 ??
|
|
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
|
-
}
|