@black-market/sdk 0.15.0 → 0.16.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 +10 -8
- package/dist/lifecycle/auto-compound.d.ts +21 -0
- package/dist/lifecycle/auto-compound.d.ts.map +1 -0
- package/dist/lifecycle/auto-compound.js +34 -0
- package/dist/lifecycle/auto-compound.js.map +1 -0
- package/dist/lifecycle/calibration.d.ts +2 -4
- package/dist/lifecycle/calibration.d.ts.map +1 -1
- package/dist/lifecycle/calibration.js +1 -26
- package/dist/lifecycle/calibration.js.map +1 -1
- package/dist/lifecycle/deployments/auto-compound-dynamic-pool-v1-20261028.d.ts +73 -0
- package/dist/lifecycle/deployments/auto-compound-dynamic-pool-v1-20261028.d.ts.map +1 -0
- package/dist/lifecycle/deployments/auto-compound-dynamic-pool-v1-20261028.js +75 -0
- package/dist/lifecycle/deployments/auto-compound-dynamic-pool-v1-20261028.js.map +1 -0
- package/dist/lifecycle/deployments/auto-compound-static-pool-v1-20261028.d.ts +73 -0
- package/dist/lifecycle/deployments/auto-compound-static-pool-v1-20261028.d.ts.map +1 -0
- package/dist/lifecycle/deployments/auto-compound-static-pool-v1-20261028.js +75 -0
- package/dist/lifecycle/deployments/auto-compound-static-pool-v1-20261028.js.map +1 -0
- package/dist/lifecycle/deployments/raeto-dynamic-pool-v1-20261029.d.ts +71 -0
- package/dist/lifecycle/deployments/raeto-dynamic-pool-v1-20261029.d.ts.map +1 -0
- package/dist/lifecycle/deployments/raeto-dynamic-pool-v1-20261029.js +73 -0
- package/dist/lifecycle/deployments/raeto-dynamic-pool-v1-20261029.js.map +1 -0
- package/dist/lifecycle/deployments/raeto-fixed-pool-v1-20261029.d.ts +71 -0
- package/dist/lifecycle/deployments/raeto-fixed-pool-v1-20261029.d.ts.map +1 -0
- package/dist/lifecycle/deployments/raeto-fixed-pool-v1-20261029.js +73 -0
- package/dist/lifecycle/deployments/raeto-fixed-pool-v1-20261029.js.map +1 -0
- package/dist/lifecycle/gas-profile.d.ts +17 -13
- package/dist/lifecycle/gas-profile.d.ts.map +1 -1
- package/dist/lifecycle/gas-profile.js +133 -113
- package/dist/lifecycle/gas-profile.js.map +1 -1
- package/dist/lifecycle/index.d.ts +6 -4
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/index.js +6 -4
- package/dist/lifecycle/index.js.map +1 -1
- package/dist/lifecycle/markets.d.ts +4 -0
- package/dist/lifecycle/markets.d.ts.map +1 -1
- package/dist/lifecycle/markets.js +58 -3
- package/dist/lifecycle/markets.js.map +1 -1
- package/dist/lifecycle/opening-buys.js +3 -3
- package/dist/lifecycle/opening-buys.js.map +1 -1
- package/dist/lifecycle/planner.d.ts +6 -9
- package/dist/lifecycle/planner.d.ts.map +1 -1
- package/dist/lifecycle/planner.js +122 -208
- package/dist/lifecycle/planner.js.map +1 -1
- package/dist/lifecycle/presets.d.ts +9 -3
- package/dist/lifecycle/presets.d.ts.map +1 -1
- package/dist/lifecycle/presets.js +17 -1
- package/dist/lifecycle/presets.js.map +1 -1
- package/dist/lifecycle/raeto-abi.d.ts +175 -0
- package/dist/lifecycle/raeto-abi.d.ts.map +1 -0
- package/dist/lifecycle/raeto-abi.js +224 -0
- package/dist/lifecycle/raeto-abi.js.map +1 -0
- package/dist/lifecycle/raeto.d.ts +21 -0
- package/dist/lifecycle/raeto.d.ts.map +1 -0
- package/dist/lifecycle/raeto.js +59 -0
- package/dist/lifecycle/raeto.js.map +1 -0
- package/dist/lifecycle/rpc.d.ts +3 -13
- package/dist/lifecycle/rpc.d.ts.map +1 -1
- package/dist/lifecycle/rpc.js +11 -37
- package/dist/lifecycle/rpc.js.map +1 -1
- package/dist/lifecycle/snapshot.d.ts.map +1 -1
- package/dist/lifecycle/snapshot.js +1 -2
- package/dist/lifecycle/snapshot.js.map +1 -1
- package/dist/lifecycle/transactions.d.ts +10 -3
- package/dist/lifecycle/transactions.d.ts.map +1 -1
- package/dist/lifecycle/transactions.js +47 -10
- package/dist/lifecycle/transactions.js.map +1 -1
- package/dist/lifecycle/types.d.ts +32 -107
- package/dist/lifecycle/types.d.ts.map +1 -1
- package/dist/lifecycle/types.js +3 -3
- package/dist/lifecycle/types.js.map +1 -1
- package/dist/lifecycle/ufg.d.ts +1 -1
- package/dist/lifecycle/ufg.d.ts.map +1 -1
- package/dist/lifecycle/ufg.js +1 -1
- package/dist/lifecycle/ufg.js.map +1 -1
- package/docs/launch-lifecycle.md +113 -1
- package/docs/lifecycle-gas-profiles.md +141 -24
- package/docs/reference.md +112 -3
- package/examples/.env.example +0 -3
- package/examples/README.md +11 -11
- package/examples/launch-example-support.ts +4 -9
- package/examples/launch-example.ts +12 -28
- package/examples/launch-lifecycle-smoke.ts +10 -22
- package/examples/launch.ts +8 -11
- package/package.json +1 -1
- package/src/lifecycle/auto-compound.ts +38 -0
- package/src/lifecycle/calibration.ts +3 -23
- package/src/lifecycle/deployments/auto-compound-dynamic-pool-v1-20261028.ts +77 -0
- package/src/lifecycle/deployments/auto-compound-static-pool-v1-20261028.ts +77 -0
- package/src/lifecycle/deployments/raeto-dynamic-pool-v1-20261029.ts +74 -0
- package/src/lifecycle/deployments/raeto-fixed-pool-v1-20261029.ts +74 -0
- package/src/lifecycle/gas-profile.ts +125 -104
- package/src/lifecycle/index.ts +6 -4
- package/src/lifecycle/markets.ts +47 -3
- package/src/lifecycle/opening-buys.ts +3 -3
- package/src/lifecycle/planner.ts +116 -191
- package/src/lifecycle/presets.ts +17 -1
- package/src/lifecycle/raeto-abi.ts +223 -0
- package/src/lifecycle/raeto.ts +61 -0
- package/src/lifecycle/rpc.ts +10 -32
- package/src/lifecycle/snapshot.ts +1 -2
- package/src/lifecycle/transactions.ts +46 -9
- package/src/lifecycle/types.ts +38 -87
- package/src/lifecycle/ufg.ts +1 -1
- package/dist/lifecycle/fork.d.ts +0 -15
- package/dist/lifecycle/fork.d.ts.map +0 -1
- package/dist/lifecycle/fork.js +0 -36
- package/dist/lifecycle/fork.js.map +0 -1
- package/dist/lifecycle/simulation.d.ts +0 -17
- package/dist/lifecycle/simulation.d.ts.map +0 -1
- package/dist/lifecycle/simulation.js +0 -605
- package/dist/lifecycle/simulation.js.map +0 -1
- package/src/lifecycle/fork.ts +0 -36
- package/src/lifecycle/simulation.ts +0 -543
|
@@ -1,42 +1,27 @@
|
|
|
1
|
-
import { toHex,
|
|
2
|
-
import { AUCTION_QUOTE_OPTIONS } from "../auction.js";
|
|
3
|
-
import { getTickAtSqrtRatio } from "../launch.js";
|
|
1
|
+
import { toHex, type Address } from "viem";
|
|
4
2
|
import { decodeAbyssLifecycleMarketConfig, decodePoolBoundV4LifecycleMarketConfig } from "./markets.js";
|
|
5
3
|
import { computeLifecycleOpeningBuys, hasProfiledOpeningMath } from "./opening-buys.js";
|
|
6
|
-
import {
|
|
7
|
-
import { lifecyclePinnedRpc,
|
|
4
|
+
import { hmmV2ForwardingReserve } from "./presets.js";
|
|
5
|
+
import { lifecyclePinnedRpc, lifecycleSourceClient, nitroPosterGasBound, readLifecycleGasPrice, readNitroPrices, registerLifecycleFinalCheck, resolveLifecycleLimits, rpcQuantity } from "./rpc.js";
|
|
8
6
|
import { LifecycleFundingKind, LifecycleRewardMode, LifecycleTokenKind, type LaunchPlanV1 } from "./schema.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import type
|
|
12
|
-
import { LifecyclePlanningError, type LifecycleBlock, type LifecycleLaunchGasBounds, type LifecycleOpeningBuyQuote, type
|
|
7
|
+
import { isAutoCompoundProfile } from "./auto-compound.js";
|
|
8
|
+
import { buildLifecycleCommand, LIFECYCLE_CAPACITY_PADDING_BPS, preparationCalldataGas, preparationGasEstimate } from "./transactions.js";
|
|
9
|
+
import { UFG_LAUNCH_PROXY_ADDRESS, isUfgProfile, ufgPlanPrincipal, type UfgLaunchConfig } from "./ufg-config.js";
|
|
10
|
+
import { LifecyclePlanningError, type LifecycleBlock, type LifecycleLaunchGasBounds, type LifecycleLimitSource, type LifecycleOpeningBuyQuote, type LifecyclePlanningAssessment, type LifecycleRpcClient, type LifecycleTransaction, type PlannedLaunch } from "./types.js";
|
|
11
|
+
import { isHmmProfile } from "./hmm.js";
|
|
12
|
+
import { isRaetoProfile } from "./raeto.js";
|
|
13
13
|
|
|
14
|
-
export const LIFECYCLE_GAS_PROFILE_VERSION = "robinhood-4663-
|
|
15
|
-
|
|
16
|
-
// Coverage, not token admission: unlisted currencies still use exact execution.
|
|
17
|
-
const profiledQuotes = new Set<string>();
|
|
18
|
-
for (const quote of AUCTION_QUOTE_OPTIONS) if (quote.address !== zeroAddress) profiledQuotes.add(quote.address.toLowerCase());
|
|
14
|
+
export const LIFECYCLE_GAS_PROFILE_VERSION = "robinhood-4663-capacity-v4";
|
|
15
|
+
export type NextTransactionEnvelope = Readonly<{ gas: bigint; gasPrice: bigint }>;
|
|
19
16
|
const ceil = (value: bigint, divisor: bigint) => (value + divisor - 1n) / divisor;
|
|
20
17
|
const approval = (transaction: LifecycleTransaction) => transaction.kind === "approve" || transaction.kind === "approve-reset";
|
|
21
|
-
type MarketGas = { abyss: boolean; hmm: boolean; ufgHook: boolean;
|
|
22
|
-
|
|
23
|
-
function covered(plan: LaunchPlanV1): boolean {
|
|
24
|
-
return plan.chainId === 4663n && hasProfiledOpeningMath(plan) &&
|
|
25
|
-
plan.markets.every((market) => profiledQuotes.has(market.quoteAsset.toLowerCase())) &&
|
|
26
|
-
plan.funding.every((funding) => funding.kind !== LifecycleFundingKind.ERC20 || profiledQuotes.has(funding.inputAsset.toLowerCase()));
|
|
27
|
-
}
|
|
18
|
+
type MarketGas = { abyss: boolean; hmm: boolean; ufgHook: boolean; raeto: boolean; autoCompound: boolean; positions: number };
|
|
28
19
|
|
|
29
20
|
function marketGas(plan: LaunchPlanV1): MarketGas[] {
|
|
30
21
|
return plan.markets.map((market) => {
|
|
31
|
-
const preset = getKnownLifecycleProfile({ chainId: plan.chainId, orchestrator: plan.orchestrator, profileId: market.profileId })!;
|
|
32
22
|
const abyss = market.configVersion === 1;
|
|
33
23
|
const config = abyss ? decodeAbyssLifecycleMarketConfig(market.config) : decodePoolBoundV4LifecycleMarketConfig(market.config, 6);
|
|
34
|
-
|
|
35
|
-
const spacing = "tickSpacing" in config ? config.tickSpacing : config.fee === 100 ? 1 : config.fee === 500 ? 10 : config.fee === 3000 ? 60 : 200;
|
|
36
|
-
const ticks = new Set<number>();
|
|
37
|
-
for (const position of config.positions) { ticks.add(position.tickLower); ticks.add(position.tickUpper); }
|
|
38
|
-
return { abyss, hmm: preset.key.includes("hmm"), ufgHook: preset.key.includes("ufg"), positions: config.positions.length,
|
|
39
|
-
spacing, openingTick: getTickAtSqrtRatio(price), ticks: [...ticks] };
|
|
24
|
+
return { abyss, hmm: isHmmProfile(market.profileId), ufgHook: isUfgProfile(market.profileId), raeto: isRaetoProfile(market.profileId), autoCompound: isAutoCompoundProfile(market.profileId), positions: config.positions.length };
|
|
40
25
|
});
|
|
41
26
|
}
|
|
42
27
|
|
|
@@ -55,121 +40,157 @@ function utf8Bytes(value: string): number {
|
|
|
55
40
|
function tokenCreationGas(token: LaunchPlanV1["token"]): bigint {
|
|
56
41
|
return (token.kind === LifecycleTokenKind.ERC404 ? 2_500_000n : 0n) +
|
|
57
42
|
(token.rewardMode === LifecycleRewardMode.Staking ? 2_500_000n : token.rewardMode === LifecycleRewardMode.Dividends ? 500_000n : 0n) +
|
|
58
|
-
|
|
43
|
+
25_000n * BigInt(Math.ceil(utf8Bytes(token.name) / 32) + Math.ceil(utf8Bytes(token.symbol) / 32) + Math.ceil(utf8Bytes(token.metadataURI) / 32));
|
|
59
44
|
}
|
|
60
45
|
|
|
61
|
-
|
|
46
|
+
// Max observed compute: native Abyss atomic21,584,262; V4 fixed20,833,003;
|
|
47
|
+
// HMM26,395,772; UFG27,941,420 (historical-receipts.json,195 transactions).
|
|
48
|
+
// fork-matrix.json: Abyss activation8 positions11.08M/32 positions39.72M;
|
|
49
|
+
// fork-v4-matrix.json: dynamic V4 eight positions5,906,285.
|
|
50
|
+
// Native mixed HMM8/Abyss8/HMM8 activation23,883,893 (0xf05e…fe98).
|
|
51
|
+
const activationCosts = {
|
|
52
|
+
call: 1_500_000n,
|
|
53
|
+
abyss: { market: 500_000n, position: 1_250_000n },
|
|
54
|
+
v4: { market: 250_000n, position: 500_000n },
|
|
55
|
+
raeto: { market: 500_000n, position: 500_000n },
|
|
56
|
+
autoCompound: { market: 750_000n, position: 500_000n },
|
|
57
|
+
hmm: 1_000_000n,
|
|
58
|
+
ufg: 250_000n,
|
|
59
|
+
// Fork Abyss32: 64 buys78.44M minimum versus one buy47.49M;
|
|
60
|
+
// max observed marginal is below0.5M/buy. ERC404 pairs add below0.1M/NFT.
|
|
61
|
+
buy: 500_000n,
|
|
62
|
+
nft: 100_000n,
|
|
63
|
+
} as const;
|
|
64
|
+
|
|
65
|
+
/** Measured-max template budgets, never gas-limit sizing or execution evidence. */
|
|
62
66
|
function capacityBasis(plan: LaunchPlanV1, transaction: LifecycleTransaction, markets: readonly MarketGas[], quotes: readonly LifecycleOpeningBuyQuote[], ufgRoute: boolean): bigint {
|
|
63
67
|
const atomic = transaction.kind === "atomic";
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
// template-gas-matrix.json: Raeto/auto-compound atomic1 <=19.60M,
|
|
69
|
+
// atomic8 <=23.99M; atomic32 exceeds32M. The18M calibration base plus
|
|
70
|
+
// position/buy work and25% padding bounds compute and forwarding envelopes.
|
|
71
|
+
let gas = atomic ? (markets[0]!.raeto || markets[0]!.autoCompound ? 18_000_000n : 22_000_000n) + tokenCreationGas(plan.token) + (ufgRoute ? 6_500_000n : 0n)
|
|
72
|
+
: activationCosts.call + (ufgRoute ? activationCosts.ufg : 0n);
|
|
73
|
+
for (const market of markets) {
|
|
74
|
+
if (atomic) gas += (market.hmm ? 7_000_000n : market.ufgHook ? 1_000_000n : market.abyss ? 500_000n : 0n) +
|
|
69
75
|
(market.abyss ? 1_500_000n : 600_000n) * BigInt(market.positions - 1);
|
|
70
|
-
|
|
76
|
+
else {
|
|
77
|
+
const cost = market.abyss ? activationCosts.abyss : market.raeto ? activationCosts.raeto : market.autoCompound ? activationCosts.autoCompound : activationCosts.v4;
|
|
78
|
+
gas += cost.market + cost.position * BigInt(market.positions) + (market.hmm ? activationCosts.hmm : market.ufgHook ? activationCosts.ufg : 0n);
|
|
79
|
+
}
|
|
71
80
|
}
|
|
72
|
-
|
|
81
|
+
gas += activationCosts.buy * BigInt(quotes.length);
|
|
82
|
+
// These collectors revalidate the prepared position set for each buy. At32
|
|
83
|
+
// positions the measured marginal exceeds1M/buy; flat0.5M is insufficient.
|
|
73
84
|
for (const quote of quotes) {
|
|
74
|
-
const market = markets[quote.marketIndex]
|
|
75
|
-
|
|
76
|
-
const words = Math.abs(Math.floor(after / (market.spacing * 256)) - Math.floor(before / (market.spacing * 256))) + 2;
|
|
77
|
-
let crossings = 0;
|
|
78
|
-
for (const tick of market.ticks) if (tick >= low && tick <= high) crossings += 1;
|
|
79
|
-
gas += 300_000n + 8_000n * BigInt(words) + 80_000n * BigInt(crossings);
|
|
80
|
-
if (plan.token.kind === LifecycleTokenKind.ERC404) gas += 70_000n * ceil(quote.tokenOut, plan.token.nftUnit);
|
|
81
|
-
ticks[quote.marketIndex] = after;
|
|
85
|
+
const market = markets[quote.marketIndex]!;
|
|
86
|
+
if (market.raeto || market.autoCompound) gas += 25_000n * BigInt(market.positions);
|
|
82
87
|
}
|
|
88
|
+
if (plan.token.kind === LifecycleTokenKind.ERC404) for (const quote of quotes) gas += activationCosts.nft * ceil(quote.tokenOut, plan.token.nftUnit);
|
|
83
89
|
return gas + 24n * BigInt((transaction.data.length - 2) / 2) + 100_000n * BigInt(plan.feeAssets.length);
|
|
84
90
|
}
|
|
85
91
|
|
|
86
92
|
type CapacityOptions = { plan: LaunchPlanV1; predictedToken: Address; ufg?: { config: UfgLaunchConfig }; headroomBps?: number };
|
|
87
93
|
export type ProfiledLaunchCapacity = {
|
|
88
94
|
bounds: LifecycleLaunchGasBounds; initialBuys: readonly LifecycleOpeningBuyQuote[]; headroomBps: number;
|
|
89
|
-
atomic
|
|
95
|
+
atomic?: LifecycleTransaction; activation: LifecycleTransaction;
|
|
90
96
|
};
|
|
91
97
|
|
|
92
|
-
/**
|
|
98
|
+
/** No quote-token allowlist: costs and exact curve math belong to the release template. */
|
|
93
99
|
export function deriveLifecycleLaunchCapacity(options: CapacityOptions, openingBuys?: readonly LifecycleOpeningBuyQuote[]): ProfiledLaunchCapacity | undefined {
|
|
94
100
|
const { plan, ufg } = options;
|
|
95
|
-
if (!
|
|
96
|
-
(plan.creator.toLowerCase() === UFG_LAUNCH_PROXY_ADDRESS.toLowerCase()) !== (ufg !== undefined)) return undefined;
|
|
101
|
+
if (!hasProfiledOpeningMath(plan) || (plan.creator.toLowerCase() === UFG_LAUNCH_PROXY_ADDRESS.toLowerCase()) !== (ufg !== undefined)) return undefined;
|
|
97
102
|
const headroomBps = options.headroomBps ?? 1500;
|
|
98
|
-
if (!Number.isSafeInteger(headroomBps) || headroomBps < 0 || headroomBps > 10000) throw new LifecyclePlanningError("INVALID_LIMITS", "Headroom must be between zero
|
|
103
|
+
if (!Number.isSafeInteger(headroomBps) || headroomBps < 0 || headroomBps > 10000) throw new LifecyclePlanningError("INVALID_LIMITS", "Headroom must be between zero and 10000 basis points");
|
|
99
104
|
const initialBuys = openingBuys ?? computeLifecycleOpeningBuys(options);
|
|
100
105
|
const route = ufg === undefined ? undefined : { account: ufgPlanPrincipal(plan), config: ufg.config };
|
|
101
|
-
const atomic =
|
|
106
|
+
const atomic = plan.markets.length === 1 && !plan.funding.some((funding) => funding.kind === LifecycleFundingKind.Swap)
|
|
107
|
+
? buildLifecycleCommand(plan, { kind: "atomic" }, route) : undefined;
|
|
108
|
+
const activation = buildLifecycleCommand(plan, { kind: "activate" }, route);
|
|
102
109
|
const markets = marketGas(plan), reserve = hmmV2ForwardingReserve(plan);
|
|
103
110
|
const bound = (transaction: LifecycleTransaction) => {
|
|
104
111
|
const basis = capacityBasis(plan, transaction, markets, initialBuys, ufg !== undefined);
|
|
105
|
-
const buffered = ceil(basis * BigInt(10000 + headroomBps), 10000n);
|
|
112
|
+
const buffered = ceil(basis * BigInt(10000 + Math.max(LIFECYCLE_CAPACITY_PADDING_BPS, headroomBps)), 10000n);
|
|
106
113
|
return buffered > basis + reserve ? buffered : basis + reserve;
|
|
107
114
|
};
|
|
108
|
-
return { bounds: { atomicComputeGasUpperBound: bound(atomic), activationComputeGasUpperBound: bound(activation) }, initialBuys, headroomBps, atomic, activation };
|
|
115
|
+
return { bounds: { ...(atomic === undefined ? {} : { atomicComputeGasUpperBound: bound(atomic) }), activationComputeGasUpperBound: bound(activation) }, initialBuys, headroomBps, atomic, activation };
|
|
109
116
|
}
|
|
110
117
|
|
|
111
|
-
/** Pure
|
|
112
|
-
*
|
|
118
|
+
/** Pure release-template budgets with headroom, excluding poster allocation.
|
|
119
|
+
* Missing coverage refuses planning; there is no execution fallback. */
|
|
113
120
|
export function computeLifecycleLaunchGasBounds(options: CapacityOptions): LifecycleLaunchGasBounds | undefined {
|
|
114
121
|
return deriveLifecycleLaunchCapacity(options)?.bounds;
|
|
115
122
|
}
|
|
116
123
|
|
|
117
|
-
export type
|
|
124
|
+
export type LifecycleBudgetOptions = { client: LifecycleRpcClient; planned: PlannedLaunch; limits?: LifecycleLimitSource; signal?: AbortSignal };
|
|
118
125
|
|
|
119
|
-
/**
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
reviewed?: NextTransactionEnvelope, openingBuys?: readonly LifecycleOpeningBuyQuote[], capacity?: ProfiledLaunchCapacity): Promise<ProfiledLifecycleTransactions | undefined> {
|
|
126
|
+
/** Checks pinned inputs and deterministic capacity only. Never executes launch calldata. */
|
|
127
|
+
export async function assessLifecycleTransactions(options: LifecycleBudgetOptions, transactions: readonly LifecycleTransaction[], block: LifecycleBlock,
|
|
128
|
+
reviewed?: NextTransactionEnvelope, openingBuys?: readonly LifecycleOpeningBuyQuote[], capacity?: ProfiledLaunchCapacity): Promise<LifecyclePlanningAssessment> {
|
|
123
129
|
const { client, planned } = options, plan = planned.plan;
|
|
124
|
-
const onlyApprovals = transactions.every(approval);
|
|
125
|
-
if (planned.chainId !== 4663n || !onlyApprovals && !covered(plan)) return undefined;
|
|
126
130
|
const context = { client, account: planned.account, orchestrator: plan.orchestrator, block, chainId: planned.chainId };
|
|
127
131
|
const observed = await resolveLifecycleLimits(context, options.limits === undefined ? planned.limits : options.limits);
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
readNitroPrices(client, block),
|
|
132
|
+
const [recommendation, balanceValue, prices] = await Promise.all([
|
|
133
|
+
readLifecycleGasPrice(client, block, planned.chainId),
|
|
131
134
|
lifecyclePinnedRpc(client, "eth_getBalance", [planned.account, toHex(block.number)], block),
|
|
132
|
-
|
|
135
|
+
observed.limits.protocol === "nitro" ? readNitroPrices(client, block) : undefined,
|
|
133
136
|
]);
|
|
134
|
-
if (rpcHex(accountCode, "account code") !== "0x") return undefined;
|
|
135
137
|
options.signal?.throwIfAborted(); observed.assertCurrent(); registerLifecycleFinalCheck(client, observed.assertCurrent);
|
|
136
|
-
const { limits } = observed, baseFee = block.baseFeePerGas ?? 0n;
|
|
137
|
-
const requiredGasPrice =
|
|
138
|
-
const gasPrice = reviewed?.gasPrice ?? (
|
|
139
|
-
if (reviewed !== undefined &&
|
|
140
|
-
const
|
|
141
|
-
const currentCapacity =
|
|
142
|
-
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
138
|
+
const { limits } = observed, baseFee = block.baseFeePerGas ?? 0n, gasLimit = limits.executionGasCeiling;
|
|
139
|
+
const requiredGasPrice = limits.protocol === "nitro" && recommendation > baseFee ? recommendation : baseFee;
|
|
140
|
+
const gasPrice = reviewed?.gasPrice ?? (recommendation > baseFee * 2n ? recommendation : baseFee * 2n);
|
|
141
|
+
if (reviewed !== undefined && reviewed.gas !== gasLimit) throw new LifecyclePlanningError("STALE_REVIEWED_GAS", "Reviewed gas differs from the native transaction ceiling. Refresh the review before continuing.");
|
|
142
|
+
const cancelOnly = transactions.every((transaction) => transaction.kind === "cancel");
|
|
143
|
+
const currentCapacity = cancelOnly ? undefined : capacity?.headroomBps === limits.headroomBps ? capacity
|
|
144
|
+
: deriveLifecycleLaunchCapacity({ plan, predictedToken: planned.predictedToken, ufg: planned.ufg, headroomBps: limits.headroomBps }, openingBuys);
|
|
145
|
+
const immediate = transactions[0], launch = transactions.find((transaction) => !approval(transaction));
|
|
146
|
+
const poster = (transaction: LifecycleTransaction) => prices === undefined ? 0n : nitroPosterGasBound(transaction, prices).posterGas;
|
|
147
|
+
const assessment: LifecyclePlanningAssessment = { ready: true, blockNumber: block.number, blockHash: block.hash,
|
|
148
|
+
account: planned.account, chainId: planned.chainId, limits,
|
|
149
|
+
...(currentCapacity === undefined ? {} : { gasProfile: { version: LIFECYCLE_GAS_PROFILE_VERSION, bounds: currentCapacity.bounds, initialBuys: currentCapacity.initialBuys } }) };
|
|
150
|
+
const refuse = (code: string, reason: string, failureCategory: LifecyclePlanningAssessment["failureCategory"], capacityConstraint?: LifecyclePlanningAssessment["capacityConstraint"], failedTransactionId?: string): LifecyclePlanningAssessment =>
|
|
151
|
+
({ ...assessment, ready: false, code, reason, failureCategory, capacityConstraint, failedTransactionId });
|
|
152
|
+
if (!cancelOnly && currentCapacity === undefined) return refuse("UNKNOWN_GAS_PROFILE", "Gas budgets are unavailable for this launch profile. Use a supported profile or update the SDK.", "capacity");
|
|
153
|
+
if (!cancelOnly && currentCapacity!.bounds.activationComputeGasUpperBound + poster(currentCapacity!.activation) > gasLimit) {
|
|
154
|
+
return refuse("ACTIVATION_GAS_LIMIT", "Too many positions or pools for one activation. Reduce them to fit the native transaction ceiling.", "capacity", "activation", "activate");
|
|
155
|
+
}
|
|
146
156
|
for (const transaction of transactions) {
|
|
147
|
-
if (transaction.kind
|
|
148
|
-
|
|
149
|
-
if (
|
|
157
|
+
if (transaction.kind === "atomic" && (currentCapacity?.bounds.atomicComputeGasUpperBound === undefined || currentCapacity.bounds.atomicComputeGasUpperBound + poster(transaction) > gasLimit))
|
|
158
|
+
return refuse("STAGED_EXECUTION_REQUIRED", "This launch requires staged execution. Refresh the review before continuing.", "capacity", "gas-envelope", transaction.id);
|
|
159
|
+
if (transaction.kind === "prepare" && ceil((preparationGasEstimate(plan, transaction.marketStart!, transaction.marketCount!) + preparationCalldataGas(transaction.data)) * BigInt(10000 + Math.max(LIFECYCLE_CAPACITY_PADDING_BPS, limits.headroomBps)), 10000n) + poster(transaction) > gasLimit)
|
|
160
|
+
return refuse("PREPARATION_GAS_LIMIT", "This preparation batch exceeds the gas budget. Refresh the review to split it.", "capacity", "preparation", transaction.id);
|
|
161
|
+
if (limits.maxCalldataBytes !== undefined && (transaction.data.length - 2) / 2 > limits.maxCalldataBytes)
|
|
162
|
+
return refuse("CALLDATA_LIMIT", "The launch exceeds the transaction data limit. Reduce its positions or pools.", "capacity", "calldata", transaction.id);
|
|
150
163
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
164
|
+
if (launch !== undefined && limits.transactionGasCeiling !== undefined && gasLimit > limits.transactionGasCeiling)
|
|
165
|
+
return refuse("GAS_POLICY_LIMIT", "The configured gas limit is below the chain's native transaction ceiling.", "capacity", "gas-envelope", launch.id);
|
|
166
|
+
if (reviewed !== undefined && gasPrice < requiredGasPrice) return refuse("REVIEWED_FEE_TOO_LOW", "The reviewed gas price is below the current required fee. Refresh the review before continuing.", "semantic", undefined, immediate?.id);
|
|
167
|
+
const quotes = currentCapacity?.initialBuys ?? [];
|
|
168
|
+
if (quotes.some((quote) => quote.tokenOut < plan.buys[quote.buyIndex]!.minTokenOut))
|
|
169
|
+
return refuse("BUY_MINIMUM_NOT_MET", "An opening buy is below its minimum output. Refresh the review before continuing.", "semantic");
|
|
170
|
+
if (block.timestamp > plan.deadline && !cancelOnly) return refuse("PLAN_EXPIRED", "The launch deadline has passed. Cancel an unfinished launch to recover its funds.", "semantic");
|
|
171
|
+
const inputs = new Map<string, { required: bigint; balance: bigint }>();
|
|
172
|
+
for (const prerequisite of planned.prerequisites) {
|
|
173
|
+
if (prerequisite.allowance === undefined) continue;
|
|
174
|
+
const key = prerequisite.funding.inputAsset.toLowerCase(), total = inputs.get(key) ?? { required: 0n, balance: prerequisite.inputBalance };
|
|
175
|
+
total.required += prerequisite.requiredInput; inputs.set(key, total);
|
|
176
|
+
}
|
|
177
|
+
if ([...inputs.values()].some((input) => input.required > input.balance)) return refuse("INSUFFICIENT_FUNDING_BALANCE", "There are not enough tokens to fund this launch. Add funds or reduce the purchase.", "affordability");
|
|
178
|
+
const balance = rpcQuantity(balanceValue, "creator native balance");
|
|
179
|
+
if (gasPrice >= 1n << 256n || launch !== undefined && launch.value + gasLimit * gasPrice >= 1n << 256n) throw new LifecyclePlanningError("INVALID_GAS_ENVELOPE", "Transaction value and native-ceiling fee must fit uint256");
|
|
180
|
+
if (launch !== undefined && launch.value + gasLimit * gasPrice > balance) return refuse("INSUFFICIENT_NATIVE_BALANCE", "There is not enough native currency for the launch amount and gas budget. Add funds or reduce the purchase.", "affordability", undefined, launch.id);
|
|
181
|
+
if (immediate !== undefined && !approval(immediate)) {
|
|
182
|
+
const source = lifecycleSourceClient(client);
|
|
183
|
+
const dataFee = limits.protocol === "nitro" ? 0n : observed.configuration.estimateDataFee === undefined ? undefined
|
|
184
|
+
: await observed.configuration.estimateDataFee({ ...immediate, gas: gasLimit, gasPrice }, source === client ? context : { ...context, client: source });
|
|
185
|
+
if (dataFee !== undefined && (typeof dataFee !== "bigint" || dataFee < 0n || dataFee >= 1n << 256n)) throw new LifecyclePlanningError("INVALID_DATA_FEE", "Data-fee estimate must be a nonnegative uint256 amount");
|
|
186
|
+
const executionFee = gasLimit * gasPrice, required = immediate.value + executionFee + (dataFee ?? 0n);
|
|
187
|
+
if (required >= 1n << 256n) throw new LifecyclePlanningError("INVALID_GAS_ENVELOPE", "Transaction value and gas/data fee budget must fit uint256");
|
|
188
|
+
if (required > balance) return refuse("INSUFFICIENT_NATIVE_BALANCE", "There is not enough native currency for the launch amount and gas budget. Add funds or reduce the purchase.", "affordability", undefined, immediate.id);
|
|
189
|
+
const posterGas = prices === undefined ? undefined : poster(immediate);
|
|
190
|
+
assessment.estimate = { gasLimit, gasPrice, executionFee, dataFee, totalFee: dataFee === undefined ? undefined : executionFee + dataFee,
|
|
191
|
+
posterGas, posterFee: posterGas === undefined ? undefined : posterGas * gasPrice, dataFeeIncludedInGas: limits.protocol === "nitro",
|
|
192
|
+
feeConfidence: dataFee === undefined ? "native-ceiling-only" : "native-ceiling-and-data" };
|
|
193
|
+
}
|
|
194
|
+
options.signal?.throwIfAborted(); observed.assertCurrent();
|
|
195
|
+
return assessment;
|
|
175
196
|
}
|
package/src/lifecycle/index.ts
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
/** Versioned construction metadata and
|
|
1
|
+
/** Versioned construction metadata, exact local curve math and launch budgets. */
|
|
2
2
|
export * from "./schema.js";
|
|
3
3
|
export * from "./types.js";
|
|
4
4
|
export * from "./abi.js";
|
|
5
5
|
export * from "./markets.js";
|
|
6
6
|
export * from "./hmm.js";
|
|
7
7
|
export * from "./ufg.js";
|
|
8
|
+
export * from "./auto-compound.js";
|
|
9
|
+
export * from "./raeto.js";
|
|
10
|
+
export * from "./raeto-abi.js";
|
|
8
11
|
export { ABYSS_LIFECYCLE_CONFIG_SCHEMA, readLifecycleTokenAtBlock, readLifecycleHeadProgress, predictLifecycleToken,
|
|
9
12
|
validateV4LifecycleOracle, readLifecycleProfiles, readPoolBoundHookDeployment,
|
|
10
13
|
buildPoolBoundHookDeploymentTransaction, validateLifecycleMarketIdentity, readLaunchProgress } from "./progress.js";
|
|
11
|
-
export * from "./fork.js";
|
|
12
14
|
export * from "./authors.js";
|
|
13
15
|
export * from "./presets.js";
|
|
14
|
-
export { preparationGasEstimate, packPreparationBatches, buildLaunchTransactions } from "./transactions.js";
|
|
16
|
+
export { preparationGasEstimate, packPreparationBatches, buildLaunchTransactions, LIFECYCLE_CAPACITY_PADDING_BPS } from "./transactions.js";
|
|
15
17
|
export { createLifecycleSessionCache } from "./rpc.js";
|
|
16
18
|
export { launchLensAbi } from "./lens.js";
|
|
17
19
|
export { hookSaltMinerAbi } from "./mining.js";
|
|
18
|
-
export { planLaunch,
|
|
20
|
+
export { planLaunch, buildNextTransaction, prepareAndPlanLifecycleLaunch } from "./planner.js";
|
|
19
21
|
export { computeLifecycleOpeningBuys } from "./opening-buys.js";
|
|
20
22
|
export { computeLifecycleLaunchGasBounds } from "./gas-profile.js";
|
|
21
23
|
export { assertLaunchTopologyCoverage, getTickAtSqrtRatio, type LaunchTopologyCoverageInput, type LaunchTopologyPosition } from "../launch.js";
|
package/src/lifecycle/markets.ts
CHANGED
|
@@ -3,6 +3,8 @@ import type { MarketConfigV1 } from "./schema.js";
|
|
|
3
3
|
import { LifecyclePlanningError, type LifecycleProfile, type PoolBoundHookSaltProgress } from "./types.js";
|
|
4
4
|
import { decodeHmmLaunchTail, encodeHmmLaunchTail, isHmmProfile, type HmmLaunchConfig } from "./hmm.js";
|
|
5
5
|
import { decodeUfgLaunchConfig, encodeUfgLaunchConfig, isUfgProfile, type UfgLaunchConfig } from "./ufg-config.js";
|
|
6
|
+
import { decodeAutoCompoundTail, encodeAutoCompoundTail, isAutoCompoundProfile, validateAutoCompoundConfig, type AutoCompoundConfigV1 } from "./auto-compound.js";
|
|
7
|
+
import { decodeRaetoLaunchTail, encodeRaetoLaunchTail, isRaetoProfile, RAETO_QUOTE_ASSETS, validateRaetoLaunchConfig, type RaetoLaunchConfigV1 } from "./raeto.js";
|
|
6
8
|
|
|
7
9
|
export type V4LifecyclePositionConfig = {
|
|
8
10
|
tickLower: number; tickUpper: number; liquidity: bigint; salt: Hex; maxTokenAmount: bigint;
|
|
@@ -46,6 +48,8 @@ export type V4PoolBoundLifecycleMarketConfigV6 = Omit<V4LifecycleMarketConfig, "
|
|
|
46
48
|
version: 6; minimumHookFeePips: number; feeSensitivityPipsSecondsPerTick: number; hookSalt: Hex;
|
|
47
49
|
hmmLaunchConfig?: HmmLaunchConfig;
|
|
48
50
|
ufgLaunchConfig?: UfgLaunchConfig;
|
|
51
|
+
autoCompoundConfig?: AutoCompoundConfigV1;
|
|
52
|
+
raetoLaunchConfig?: RaetoLaunchConfigV1;
|
|
49
53
|
};
|
|
50
54
|
export type V4PoolBoundLifecycleMarketConfig = V4PoolBoundLifecycleMarketConfigV5 | V4PoolBoundLifecycleMarketConfigV6;
|
|
51
55
|
export const V4_LIFECYCLE_CONFIG_SCHEMA = keccak256(stringToHex("(uint16,uint24,int24,uint160,uint24,uint8,uint8,address,bool,bytes32,bytes32,bytes32,address,uint16,(int24,int24,uint128,bytes32,uint256)[])"));
|
|
@@ -92,9 +96,34 @@ function assertV1HookFeeShape(config: object): void {
|
|
|
92
96
|
}
|
|
93
97
|
}
|
|
94
98
|
|
|
99
|
+
function validateAutoCompoundMarket(config: V4LifecycleMarketConfig | V4PoolBoundLifecycleMarketConfig): asserts config is V4PoolBoundLifecycleMarketConfigV6 & { autoCompoundConfig: AutoCompoundConfigV1 } {
|
|
100
|
+
if (config.version !== 6 || config.autoCompoundConfig === undefined) throw new LifecyclePlanningError("INVALID_AUTO_COMPOUND_CONFIG", "Auto-compounding profiles require config6 and an explicit autoCompoundConfig");
|
|
101
|
+
if (config.hmmLaunchConfig !== undefined || config.ufgLaunchConfig !== undefined || config.raetoLaunchConfig !== undefined) throw new LifecyclePlanningError("INVALID_AUTO_COMPOUND_CONFIG", "Auto-compounding profiles cannot carry HMM, UFG or Raeto tails");
|
|
102
|
+
if (config.lpFeePips !== 0) throw new LifecyclePlanningError("INVALID_HOOK_FEES", "Auto-compounding profiles require zero LP fee");
|
|
103
|
+
if (config.developerFeeBps !== 0) throw new LifecyclePlanningError("DEVELOPER_FEE_CEILING", "Auto-compounding profiles require zero developer fee");
|
|
104
|
+
validateAutoCompoundConfig(config.autoCompoundConfig);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function validateRaetoMarket(config: V4LifecycleMarketConfig | V4PoolBoundLifecycleMarketConfig): asserts config is V4PoolBoundLifecycleMarketConfigV6 & { raetoLaunchConfig: RaetoLaunchConfigV1 } {
|
|
108
|
+
if (config.version !== 6 || config.raetoLaunchConfig === undefined) throw new LifecyclePlanningError("INVALID_RAETO_CONFIG", "Raeto profiles require config6 and an explicit raetoLaunchConfig");
|
|
109
|
+
if (config.hmmLaunchConfig !== undefined || config.ufgLaunchConfig !== undefined || config.autoCompoundConfig !== undefined) throw new LifecyclePlanningError("INVALID_RAETO_CONFIG", "Raeto profiles cannot carry HMM, UFG or auto-compounding tails");
|
|
110
|
+
if (config.feeMode !== 1 || config.lpFeePips !== 0 || config.developerFeeBps !== 0) throw new LifecyclePlanningError("INVALID_RAETO_CONFIG", "Raeto requires QuoteOnly feeMode 1, zero LP fee and zero developer fee");
|
|
111
|
+
validateRaetoLaunchConfig(config.raetoLaunchConfig);
|
|
112
|
+
}
|
|
113
|
+
|
|
95
114
|
/** Auxiliary launch profiles require their typed tail and append it automatically. */
|
|
96
115
|
export function encodePoolBoundV4LifecycleMarketConfig(config: V4PoolBoundLifecycleMarketConfig): Hex {
|
|
97
116
|
const prefix = encodePoolBoundV4Prefix(config);
|
|
117
|
+
if (isRaetoProfile(config.profileId)) {
|
|
118
|
+
validateRaetoMarket(config);
|
|
119
|
+
return concatHex([prefix, encodeRaetoLaunchTail(config.raetoLaunchConfig)]);
|
|
120
|
+
}
|
|
121
|
+
if (config.version === 6 && config.raetoLaunchConfig !== undefined) throw new LifecyclePlanningError("INVALID_RAETO_CONFIG", "A Raeto tail requires a Raeto profile");
|
|
122
|
+
if (isAutoCompoundProfile(config.profileId)) {
|
|
123
|
+
validateAutoCompoundMarket(config);
|
|
124
|
+
return concatHex([prefix, encodeAutoCompoundTail(config.autoCompoundConfig)]);
|
|
125
|
+
}
|
|
126
|
+
if (config.version === 6 && config.autoCompoundConfig !== undefined) throw new LifecyclePlanningError("INVALID_AUTO_COMPOUND_CONFIG", "An auto-compounding tail requires an auto-compounding profile");
|
|
98
127
|
if (isUfgProfile(config.profileId)) {
|
|
99
128
|
if (config.version !== 6 || config.ufgLaunchConfig === undefined) throw new LifecyclePlanningError("INVALID_UFG_CONFIG", "UFG profiles require config6 and an explicit ufgLaunchConfig");
|
|
100
129
|
if (config.hmmLaunchConfig !== undefined) throw new LifecyclePlanningError("INVALID_UFG_CONFIG", "UFG profiles cannot carry an HMM tail");
|
|
@@ -130,6 +159,14 @@ export function decodePoolBoundV4LifecycleMarketConfig(encoded: Hex, expectedVer
|
|
|
130
159
|
const config = version === 5
|
|
131
160
|
? decodeAbiParameters([{ type: "tuple", components: poolBoundV4LifecycleMarketComponentsV5 }], encoded)[0] as V4PoolBoundLifecycleMarketConfigV5
|
|
132
161
|
: decodeAbiParameters([{ type: "tuple", components: poolBoundV4LifecycleMarketComponentsV6 }], encoded)[0] as V4PoolBoundLifecycleMarketConfigV6;
|
|
162
|
+
if (isRaetoProfile(config.profileId)) {
|
|
163
|
+
if (config.version !== 6) throw new LifecyclePlanningError("INVALID_RAETO_CONFIG", "Raeto profiles require config6");
|
|
164
|
+
config.raetoLaunchConfig = decodeRaetoLaunchTail(`0x${encoded.slice(encodePoolBoundV4Prefix(config).length)}`);
|
|
165
|
+
}
|
|
166
|
+
if (isAutoCompoundProfile(config.profileId)) {
|
|
167
|
+
if (config.version !== 6) throw new LifecyclePlanningError("INVALID_AUTO_COMPOUND_CONFIG", "Auto-compounding profiles require config6");
|
|
168
|
+
config.autoCompoundConfig = decodeAutoCompoundTail(`0x${encoded.slice(encodePoolBoundV4Prefix(config).length)}`);
|
|
169
|
+
}
|
|
133
170
|
if (isHmmProfile(config.profileId)) {
|
|
134
171
|
if (config.version !== 6) throw new LifecyclePlanningError("INVALID_HMM_CONFIG", "HMM profiles require config6");
|
|
135
172
|
const prefixLength = encodePoolBoundV4Prefix(config).length;
|
|
@@ -160,6 +197,11 @@ export function validateV4LifecycleMarket(options: {
|
|
|
160
197
|
if (config.version !== 4 && !isPoolBoundV4ConfigVersion(config.version)) throw new LifecyclePlanningError("UNSUPPORTED_CONFIG_VERSION", "Reviewed V4 config version must be 4, 5 or 6");
|
|
161
198
|
if (config.version === 6) validatePoolBoundHookFees(config);
|
|
162
199
|
else if (config.version === 5) assertV1HookFeeShape(config);
|
|
200
|
+
if (isAutoCompoundProfile(config.profileId)) validateAutoCompoundMarket(config);
|
|
201
|
+
if (isRaetoProfile(config.profileId)) {
|
|
202
|
+
validateRaetoMarket(config);
|
|
203
|
+
if (!RAETO_QUOTE_ASSETS.some((quote) => quote.toLowerCase() === market.quoteAsset.toLowerCase())) throw new LifecyclePlanningError("INVALID_RAETO_CONFIG", "Raeto quote asset must be WETH or USDG");
|
|
204
|
+
} else if (config.version === 6 && config.raetoLaunchConfig !== undefined) throw new LifecyclePlanningError("INVALID_RAETO_CONFIG", "A Raeto tail requires a Raeto profile");
|
|
163
205
|
const { envelope, developerTerms: terms, protocolMaximumDeveloperFeeBps: protocolCeiling } = profile;
|
|
164
206
|
if (envelope === undefined || terms === undefined || protocolCeiling === undefined || !terms.enabled ||
|
|
165
207
|
profile.topology.hookTopology !== (config.version === 4 ? 1 : 2) || market.configVersion !== config.version ||
|
|
@@ -258,9 +300,11 @@ export function hashPoolBoundV4MarketCommitment(options: { chainId: bigint; core
|
|
|
258
300
|
if (!isPoolBoundV4ConfigVersion(market.configVersion)) throw new LifecyclePlanningError("INVALID_BOUND_MARKET", "Pool-bound V4 market requires explicit config version 5 or 6");
|
|
259
301
|
const config = decodePoolBoundV4LifecycleMarketConfig(market.config, market.configVersion);
|
|
260
302
|
if (config.profileId.toLowerCase() !== market.profileId.toLowerCase()) throw new LifecyclePlanningError("INVALID_BOUND_MARKET", "Config profile differs from the committed market profile");
|
|
261
|
-
//
|
|
262
|
-
//
|
|
263
|
-
const
|
|
303
|
+
// Auto-compounding binds its tail into constructor economics; older factories
|
|
304
|
+
// bind only the prefix. Every full plan commitment also binds every tail byte.
|
|
305
|
+
const normalized = { ...config, hookSalt: zeroHash };
|
|
306
|
+
const configHash = keccak256(isAutoCompoundProfile(config.profileId)
|
|
307
|
+
? encodePoolBoundV4LifecycleMarketConfig(normalized) : encodePoolBoundV4Prefix(normalized));
|
|
264
308
|
return keccak256(encodeAbiParameters([
|
|
265
309
|
{ type: "bytes32" }, { type: "uint256" }, { type: "address" }, { type: "address" }, { type: "address" },
|
|
266
310
|
{ type: "bytes32" }, { type: "bytes32" }, { type: "address" }, { type: "uint256" }, { type: "uint32" }, { type: "bytes32" },
|
|
@@ -47,12 +47,12 @@ function openingState(plan: LaunchPlanV1, marketIndex: number): OpeningState {
|
|
|
47
47
|
price = config.openingSqrtPriceX96; spacing = tier.tickSpacing; lpFee = BigInt(config.fee); positions = config.positions;
|
|
48
48
|
} else {
|
|
49
49
|
// All current release-owned V4 offerings use config6. Dynamic first buys
|
|
50
|
-
// share the opening block's minimum fee
|
|
51
|
-
//
|
|
50
|
+
// share the opening block's minimum fee. HMM work starts onOpened and
|
|
51
|
+
// auto-compounding defers until openingCompletedAt, after all opening buys.
|
|
52
52
|
const config = decodePoolBoundV4LifecycleMarketConfig(market.config, 6);
|
|
53
53
|
if (config.profileId.toLowerCase() !== market.profileId.toLowerCase()) throw new LifecyclePlanningError("INVALID_MARKET", "V4 opening config differs from the selected profile");
|
|
54
54
|
price = config.sqrtPriceX96; spacing = config.tickSpacing; lpFee = BigInt(config.lpFeePips); positions = config.positions;
|
|
55
|
-
const dynamic = preset.key === KnownLifecycleProfile.V4DynamicFeePool || preset.key === KnownLifecycleProfile.V4HmmDynamicPool || preset.key === KnownLifecycleProfile.V4UfgDynamicPool;
|
|
55
|
+
const dynamic = preset.key === KnownLifecycleProfile.V4DynamicFeePool || preset.key === KnownLifecycleProfile.V4HmmDynamicPool || preset.key === KnownLifecycleProfile.V4UfgDynamicPool || preset.key === KnownLifecycleProfile.V4AutoCompoundDynamicPool || preset.key === KnownLifecycleProfile.V4RaetoDynamicPool;
|
|
56
56
|
hookFee = BigInt(dynamic ? config.minimumHookFeePips! : config.hookFeePips);
|
|
57
57
|
}
|
|
58
58
|
const tick = getTickAtSqrtRatio(price), liquidityNet = new Map<number, bigint>();
|