@black-market/sdk 0.11.0 → 0.12.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 +17 -0
- package/dist/lifecycle/abi.d.ts +1 -0
- package/dist/lifecycle/abi.d.ts.map +1 -1
- package/dist/lifecycle/abi.js +1 -0
- package/dist/lifecycle/abi.js.map +1 -1
- package/dist/lifecycle/deployments/ufg-dynamic-pool-20261021.d.ts +71 -0
- package/dist/lifecycle/deployments/ufg-dynamic-pool-20261021.d.ts.map +1 -0
- package/dist/lifecycle/deployments/ufg-dynamic-pool-20261021.js +73 -0
- package/dist/lifecycle/deployments/ufg-dynamic-pool-20261021.js.map +1 -0
- package/dist/lifecycle/deployments/ufg-fixed-pool-20261021.d.ts +71 -0
- package/dist/lifecycle/deployments/ufg-fixed-pool-20261021.d.ts.map +1 -0
- package/dist/lifecycle/deployments/ufg-fixed-pool-20261021.js +73 -0
- package/dist/lifecycle/deployments/ufg-fixed-pool-20261021.js.map +1 -0
- package/dist/lifecycle/index.d.ts +1 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/index.js +1 -0
- package/dist/lifecycle/index.js.map +1 -1
- package/dist/lifecycle/markets.d.ts +3 -1
- package/dist/lifecycle/markets.d.ts.map +1 -1
- package/dist/lifecycle/markets.js +20 -3
- package/dist/lifecycle/markets.js.map +1 -1
- package/dist/lifecycle/planner.d.ts.map +1 -1
- package/dist/lifecycle/planner.js +42 -22
- package/dist/lifecycle/planner.js.map +1 -1
- package/dist/lifecycle/presets.d.ts +4 -2
- package/dist/lifecycle/presets.d.ts.map +1 -1
- package/dist/lifecycle/presets.js +11 -4
- package/dist/lifecycle/presets.js.map +1 -1
- package/dist/lifecycle/progress.d.ts.map +1 -1
- package/dist/lifecycle/progress.js +26 -5
- package/dist/lifecycle/progress.js.map +1 -1
- package/dist/lifecycle/simulation.d.ts.map +1 -1
- package/dist/lifecycle/simulation.js +4 -1
- package/dist/lifecycle/simulation.js.map +1 -1
- package/dist/lifecycle/types.d.ts +11 -1
- package/dist/lifecycle/types.d.ts.map +1 -1
- package/dist/lifecycle/types.js.map +1 -1
- package/dist/lifecycle/ufg-abi.d.ts +2386 -0
- package/dist/lifecycle/ufg-abi.d.ts.map +1 -0
- package/dist/lifecycle/ufg-abi.js +171 -0
- package/dist/lifecycle/ufg-abi.js.map +1 -0
- package/dist/lifecycle/ufg-config.d.ts +38 -0
- package/dist/lifecycle/ufg-config.d.ts.map +1 -0
- package/dist/lifecycle/ufg-config.js +75 -0
- package/dist/lifecycle/ufg-config.js.map +1 -0
- package/dist/lifecycle/ufg.d.ts +21 -0
- package/dist/lifecycle/ufg.d.ts.map +1 -0
- package/dist/lifecycle/ufg.js +98 -0
- package/dist/lifecycle/ufg.js.map +1 -0
- package/docs/launch-lifecycle.md +138 -0
- package/examples/README.md +16 -2
- package/examples/ufg-launch-config.ts +31 -0
- package/package.json +1 -1
- package/src/lifecycle/abi.ts +1 -0
- package/src/lifecycle/deployments/ufg-dynamic-pool-20261021.ts +74 -0
- package/src/lifecycle/deployments/ufg-fixed-pool-20261021.ts +74 -0
- package/src/lifecycle/index.ts +1 -0
- package/src/lifecycle/markets.ts +16 -3
- package/src/lifecycle/planner.ts +37 -22
- package/src/lifecycle/presets.ts +11 -4
- package/src/lifecycle/progress.ts +22 -5
- package/src/lifecycle/simulation.ts +3 -1
- package/src/lifecycle/types.ts +6 -1
- package/src/lifecycle/ufg-abi.ts +173 -0
- package/src/lifecycle/ufg-config.ts +77 -0
- package/src/lifecycle/ufg.ts +86 -0
package/src/lifecycle/planner.ts
CHANGED
|
@@ -12,9 +12,12 @@ import { deriveKnownPoolBoundHookDeployment, protectOrderedBuyMinimums, validate
|
|
|
12
12
|
import { getKnownLifecycleDeployment, getKnownLifecycleProfile } from "./presets.js";
|
|
13
13
|
import { buildLaunchTransactions, packPreparationBatches } from "./transactions.js";
|
|
14
14
|
import { assertLifecycleMarketAllocation, assertLifecycleSupplyAllocation } from "./allocations.js";
|
|
15
|
+
import { assertUfgLaunchAccount, hashUfgLaunchConfig, UFG_LAUNCH_PROXY_ADDRESS } from "./ufg-config.js";
|
|
16
|
+
import { buildUfgLaunchTransactions, buildUfgCancelTransaction, validateUfgLaunchPlan } from "./ufg.js";
|
|
15
17
|
|
|
16
18
|
type ValidatedPlanShape = {
|
|
17
19
|
commitment: Hex;
|
|
20
|
+
ufgConfigHash?: Hex;
|
|
18
21
|
marketConfigs: (AbyssLifecycleMarketConfig | V4LifecycleMarketConfig | V4PoolBoundLifecycleMarketConfig)[];
|
|
19
22
|
};
|
|
20
23
|
|
|
@@ -75,19 +78,19 @@ function validatePlanShape(plan: LaunchPlanV1): ValidatedPlanShape {
|
|
|
75
78
|
return { commitment, marketConfigs };
|
|
76
79
|
}
|
|
77
80
|
|
|
78
|
-
async function readFunding(client: LifecycleRpcClient, plan: LaunchPlanV1, block: LifecycleBlock): Promise<{ prerequisites: LifecycleFundingPrerequisite[]; approvals: LifecycleTransaction[] }> {
|
|
81
|
+
async function readFunding(client: LifecycleRpcClient, plan: LaunchPlanV1, block: LifecycleBlock, account = plan.creator, ufg = false): Promise<{ prerequisites: LifecycleFundingPrerequisite[]; approvals: LifecycleTransaction[] }> {
|
|
79
82
|
if (plan.funding.length === 0) return { prerequisites: [], approvals: [] };
|
|
80
83
|
const preset = getKnownLifecycleDeployment({ chainId: plan.chainId, orchestrator: plan.orchestrator });
|
|
81
|
-
const escrow = preset?.fundingEscrow ?? await readLifecycleContract<Address>(client, plan.orchestrator, launchLifecycleAbi, "fundingEscrow", [], block);
|
|
84
|
+
const escrow = ufg ? UFG_LAUNCH_PROXY_ADDRESS : preset?.fundingEscrow ?? await readLifecycleContract<Address>(client, plan.orchestrator, launchLifecycleAbi, "fundingEscrow", [], block);
|
|
82
85
|
// Only allowance observations construct approval commands. Core execution
|
|
83
86
|
// enforces input/output asset, funding target, balance and conversion predicates.
|
|
84
87
|
const prerequisites = await Promise.all(plan.funding.map(async (funding): Promise<LifecycleFundingPrerequisite> => {
|
|
85
88
|
const native = funding.kind === LifecycleFundingKind.NativeWrap || funding.kind === LifecycleFundingKind.Swap && funding.inputAsset === zeroAddress;
|
|
86
89
|
const [inputBalance, allowance] = native
|
|
87
|
-
? [rpcQuantity(await lifecyclePinnedRpc(client, "eth_getBalance", [
|
|
90
|
+
? [rpcQuantity(await lifecyclePinnedRpc(client, "eth_getBalance", [account, toHex(block.number)], block), "creator native balance"), undefined] as const
|
|
88
91
|
: await Promise.all([
|
|
89
|
-
readLifecycleContract<bigint>(client, funding.inputAsset, lifecycleErc20Abi, "balanceOf", [
|
|
90
|
-
readLifecycleContract<bigint>(client, funding.inputAsset, lifecycleErc20Abi, "allowance", [
|
|
92
|
+
readLifecycleContract<bigint>(client, funding.inputAsset, lifecycleErc20Abi, "balanceOf", [account], block),
|
|
93
|
+
readLifecycleContract<bigint>(client, funding.inputAsset, lifecycleErc20Abi, "allowance", [account, escrow], block),
|
|
91
94
|
]);
|
|
92
95
|
return { funding, inputBalance, requiredInput: funding.inputAmount, allowance, spender: escrow, nativeValue: native ? funding.inputAmount : 0n, conversion: funding.kind === LifecycleFundingKind.NativeWrap ? "native-wrap" : funding.kind === LifecycleFundingKind.Swap ? "allowlisted-swap" : "none" };
|
|
93
96
|
}));
|
|
@@ -103,8 +106,8 @@ async function readFunding(client: LifecycleRpcClient, plan: LaunchPlanV1, block
|
|
|
103
106
|
const approvals: LifecycleTransaction[] = [];
|
|
104
107
|
for (const total of totals.values()) {
|
|
105
108
|
if (total.allowance >= total.amount) continue;
|
|
106
|
-
if (total.allowance > 0n) approvals.push({ id: `approve-reset:${total.asset}`, kind: "approve-reset", chainId: Number(plan.chainId), from:
|
|
107
|
-
approvals.push({ id: `approve:${total.asset}`, kind: "approve", chainId: Number(plan.chainId), from:
|
|
109
|
+
if (total.allowance > 0n) approvals.push({ id: `approve-reset:${total.asset}`, kind: "approve-reset", chainId: Number(plan.chainId), from: account, to: total.asset, data: encodeFunctionData({ abi: lifecycleErc20Abi, functionName: "approve", args: [escrow, 0n] }), value: 0n, dependencies: [], postconditions: [{ kind: "allowance", asset: total.asset, spender: escrow, minimum: 0n, exact: 0n }] });
|
|
110
|
+
approvals.push({ id: `approve:${total.asset}`, kind: "approve", chainId: Number(plan.chainId), from: account, to: total.asset, data: encodeFunctionData({ abi: lifecycleErc20Abi, functionName: "approve", args: [escrow, total.amount] }), value: 0n, dependencies: [], postconditions: [{ kind: "allowance", asset: total.asset, spender: escrow, minimum: total.amount }] });
|
|
108
111
|
}
|
|
109
112
|
return { prerequisites, approvals };
|
|
110
113
|
}
|
|
@@ -132,10 +135,12 @@ async function readPlanMetadata(client: LifecycleRpcClient, plan: LaunchPlanV1,
|
|
|
132
135
|
return { profiles, hookDeployments, reasons: live.filter((profile) => !profile.admitted).map((profile) => profile.reason ?? "Unlisted lifecycle profile is unavailable") };
|
|
133
136
|
}
|
|
134
137
|
|
|
135
|
-
function orderedTransactions(plan: LaunchPlanV1, mode: "atomic" | "staged", progress: CanonicalLaunchProgress, approvals: readonly LifecycleTransaction[], batches: readonly number[]): LifecycleTransaction[] {
|
|
138
|
+
function orderedTransactions(plan: LaunchPlanV1, mode: "atomic" | "staged", progress: CanonicalLaunchProgress, approvals: readonly LifecycleTransaction[], batches: readonly number[], ufg?: PlanLaunchOptions["ufg"]): LifecycleTransaction[] {
|
|
136
139
|
if (progress.canonical.phase === LifecyclePhase.Active || progress.canonical.phase === LifecyclePhase.Cancelled) return [];
|
|
137
140
|
const prepared = progress.canonical.preparedMarkets;
|
|
138
|
-
const
|
|
141
|
+
const compiled = ufg === undefined ? buildLaunchTransactions({ plan, mode, preparationBatches: mode === "staged" ? batches : undefined })
|
|
142
|
+
: buildUfgLaunchTransactions({ plan, config: ufg.config, mode, preparationBatches: mode === "staged" ? batches : undefined });
|
|
143
|
+
const commands = compiled.filter((transaction) =>
|
|
139
144
|
(transaction.kind !== "begin" && transaction.kind !== "atomic" || progress.canonical.phase === LifecyclePhase.None) &&
|
|
140
145
|
(transaction.kind !== "prepare" || (transaction.marketStart ?? 0) >= prepared));
|
|
141
146
|
const transactions = progress.canonical.phase === LifecyclePhase.None ? [...approvals, ...commands] : commands;
|
|
@@ -175,7 +180,8 @@ function matchesReviewedNextTransaction(next: LifecycleTransaction | ReviewedNex
|
|
|
175
180
|
}
|
|
176
181
|
|
|
177
182
|
function assertReviewedTransactionUnchanged(options: BuildNextTransactionOptions, reviewed: ReviewedNextTransaction | undefined, next: LifecycleTransaction): void {
|
|
178
|
-
if (options.planned.
|
|
183
|
+
if (options.planned.ufg !== undefined) assertUfgLaunchAccount(options.planned.plan, options.planned.account);
|
|
184
|
+
if (options.planned.account.toLowerCase() !== next.from.toLowerCase() || options.planned.ufg === undefined && options.planned.plan.creator.toLowerCase() !== next.from.toLowerCase()) {
|
|
179
185
|
throw new LifecyclePlanningError("ACCOUNT_MISMATCH", "Execution account changed during fresh execution or submission proof");
|
|
180
186
|
}
|
|
181
187
|
if (options.planned.chainId !== BigInt(next.chainId) || options.planned.plan.chainId !== BigInt(next.chainId)) {
|
|
@@ -202,6 +208,7 @@ async function readTokenFactoryBinding(client: LifecycleRpcClient, orchestrator:
|
|
|
202
208
|
|
|
203
209
|
function assertPlannedDeploymentBindings(stored: PlannedLaunch, current: PlannedLaunch): void {
|
|
204
210
|
if (hashLaunchPlan(stored.plan).toLowerCase() !== stored.planHash.toLowerCase() || current.planHash.toLowerCase() !== stored.planHash.toLowerCase() || current.launchId.toLowerCase() !== stored.launchId.toLowerCase()) throw new LifecyclePlanningError("PLAN_MUTATED", "Stored economic plan no longer matches its finalized commitment");
|
|
211
|
+
if (stored.ufg === undefined ? current.ufg !== undefined : current.ufg === undefined || hashUfgLaunchConfig(stored.ufg.config) !== stored.ufg.configHash || current.ufg.configHash !== stored.ufg.configHash) throw new LifecyclePlanningError("PLAN_MUTATED", "Stored UFG route or treasury config no longer matches its finalized commitment");
|
|
205
212
|
if (current.predictedToken.toLowerCase() !== stored.predictedToken.toLowerCase() || current.tokenFactory.toLowerCase() !== stored.tokenFactory.toLowerCase() || current.tokenFactoryCodeHash.toLowerCase() !== stored.tokenFactoryCodeHash.toLowerCase()) throw new LifecyclePlanningError("TOKEN_FACTORY_BINDING", "Final token prediction or its immutable factory code binding changed before execution");
|
|
206
213
|
if (!current.simulation.admitted || current.progress.canonical.phase === LifecyclePhase.Active || current.progress.canonical.phase === LifecyclePhase.Cancelled) return;
|
|
207
214
|
if (stored.hookDeployments.length !== current.hookDeployments.length || stored.hookDeployments.some((deployment, index) => {
|
|
@@ -218,9 +225,11 @@ function validatePlanningIntent(options: PlanLaunchOptions): ValidatedPlanShape
|
|
|
218
225
|
const { plan, account, mode } = options;
|
|
219
226
|
assertPlanningActive(options.signal);
|
|
220
227
|
if (mode !== "atomic" && mode !== "staged") throw new LifecyclePlanningError("EXPLICIT_MODE_REQUIRED", "Choose atomic or staged explicitly; staged is never silently substituted");
|
|
221
|
-
if (
|
|
228
|
+
if (options.ufg !== undefined) assertUfgLaunchAccount(plan, account);
|
|
229
|
+
else if (account.toLowerCase() !== plan.creator.toLowerCase()) throw new LifecyclePlanningError("ACCOUNT_MISMATCH", "Execution account must be the committed creator/payer/refund account");
|
|
222
230
|
if (plan.chainId > BigInt(Number.MAX_SAFE_INTEGER) || plan.chainId <= 0n) throw new LifecyclePlanningError("CHAIN_MISMATCH", "Chain ID must be a positive exact JavaScript integer");
|
|
223
|
-
|
|
231
|
+
const shape = validatePlanShape(plan);
|
|
232
|
+
return options.ufg === undefined ? shape : { ...shape, ufgConfigHash: hashUfgLaunchConfig(options.ufg.config) };
|
|
224
233
|
}
|
|
225
234
|
|
|
226
235
|
function validatePlanTopology(options: PlanLaunchOptions, marketConfigs: ValidatedPlanShape["marketConfigs"], token: Address): void {
|
|
@@ -256,12 +265,13 @@ function validatePlanTopology(options: PlanLaunchOptions, marketConfigs: Validat
|
|
|
256
265
|
function assertFinalPlan(planned: PlannedLaunch, signal?: AbortSignal): void {
|
|
257
266
|
assertPlanningActive(signal);
|
|
258
267
|
if (hashLaunchPlan(planned.plan).toLowerCase() !== planned.planHash.toLowerCase() || hashLaunchIdentity(planned.plan).toLowerCase() !== planned.launchId.toLowerCase()) throw new LifecyclePlanningError("PLAN_MUTATED", "Economic plan changed during asynchronous planning; previous reads and simulation are invalid");
|
|
268
|
+
if (planned.ufg !== undefined && hashUfgLaunchConfig(planned.ufg.config) !== planned.ufg.configHash) throw new LifecyclePlanningError("PLAN_MUTATED", "UFG treasury config changed during asynchronous planning");
|
|
259
269
|
}
|
|
260
270
|
|
|
261
271
|
/** One owner constructs and mines, optionally measures actual ordered buy outputs,
|
|
262
272
|
* then proves only the final protected commitment. No cached execution certificates. */
|
|
263
273
|
export async function prepareAndPlanLifecycleLaunch(options: PrepareAndPlanLifecycleLaunchOptions): Promise<PlannedLaunch> {
|
|
264
|
-
const { commitment } = validatePlanningIntent(options);
|
|
274
|
+
const { commitment, ufgConfigHash } = validatePlanningIntent(options);
|
|
265
275
|
if (options.buySlippageBps !== undefined) validateBuySlippageBps(options.buySlippageBps);
|
|
266
276
|
const needsMining = options.plan.markets.some((market) => isPoolBoundV4ConfigVersion(market.configVersion));
|
|
267
277
|
const needsProtection = options.buySlippageBps !== undefined && options.plan.buys.length !== 0;
|
|
@@ -270,6 +280,7 @@ export async function prepareAndPlanLifecycleLaunch(options: PrepareAndPlanLifec
|
|
|
270
280
|
const assertDraft = () => {
|
|
271
281
|
assertPlanningActive(options.signal);
|
|
272
282
|
if (hashLaunchPlan(options.plan).toLowerCase() !== commitment.toLowerCase()) throw new LifecyclePlanningError("PLAN_MUTATED", "Economic plan changed during asynchronous preparation; previous construction is invalid");
|
|
283
|
+
if ((options.ufg === undefined ? undefined : hashUfgLaunchConfig(options.ufg.config)) !== ufgConfigHash) throw new LifecyclePlanningError("PLAN_MUTATED", "UFG config changed during asynchronous preparation");
|
|
273
284
|
};
|
|
274
285
|
return withLifecycleReadClient(options.client, async (client) => {
|
|
275
286
|
const [block, chainId] = await Promise.all([readLifecycleBlock(client), readLifecycleChainId(client)]);
|
|
@@ -334,8 +345,8 @@ export async function planLaunch(options: PlanLaunchOptions): Promise<PlannedLau
|
|
|
334
345
|
}
|
|
335
346
|
|
|
336
347
|
async function planLaunchWithReads(options: PlanLaunchOptions, client: LifecycleRpcClient, pinnedBlock?: LifecycleBlock, ownedProgress?: CanonicalLaunchProgress, diagnostic = false, reviewed?: ReviewedNextTransaction): Promise<PlannedLaunch> {
|
|
337
|
-
const { plan, account, mode } = options;
|
|
338
|
-
const { commitment: planHash, marketConfigs } = validatePlanningIntent(options);
|
|
348
|
+
const { plan, account, mode, ufg } = options;
|
|
349
|
+
const { commitment: planHash, marketConfigs, ufgConfigHash } = validatePlanningIntent(options);
|
|
339
350
|
const launchId = hashLaunchIdentity(plan);
|
|
340
351
|
const [block, chainId] = await lifecycleStage(client, "plan.context", () => Promise.all([
|
|
341
352
|
pinnedBlock ?? readLifecycleBlock(client), readLifecycleChainId(client),
|
|
@@ -356,7 +367,9 @@ async function planLaunchWithReads(options: PlanLaunchOptions, client: Lifecycle
|
|
|
356
367
|
resolveLifecycleLimits({ client, account, orchestrator: plan.orchestrator, block, chainId: plan.chainId }, options.limits),
|
|
357
368
|
lifecycleStage(client, "plan.progress", async () => {
|
|
358
369
|
const predictedToken = await readLifecycleTokenAtBlock(client, plan, block);
|
|
359
|
-
const identity = { plan, planHash, launchId, predictedToken, account, mode, confirmations: options.confirmations ?? 1
|
|
370
|
+
const identity = { plan, planHash, launchId, predictedToken, account, mode, confirmations: options.confirmations ?? 1,
|
|
371
|
+
...(ufg === undefined ? {} : { ufg: { config: ufg.config, configHash: ufgConfigHash! } }) };
|
|
372
|
+
if (ufg !== undefined) validateUfgLaunchPlan(plan, ufg.config, predictedToken);
|
|
360
373
|
const progress = ownedProgress ?? await readLaunchProgress({ client, planned: identity, receipts: options.receipts, signal: options.signal }, block);
|
|
361
374
|
if (ownedProgress !== undefined && progress.token.toLowerCase() !== predictedToken.toLowerCase()) throw new LifecyclePlanningError("TOKEN_IDENTITY", "Predicted token changed after its canonical progress was read");
|
|
362
375
|
return { identity, progress };
|
|
@@ -387,7 +400,7 @@ async function planLaunchWithReads(options: PlanLaunchOptions, client: Lifecycle
|
|
|
387
400
|
// immutable onchain commitments. Waiting and terminal launches returned above.
|
|
388
401
|
if (progress.canonical.phase === LifecyclePhase.None && progress.head.phase === LifecyclePhase.None) validatePlanTopology(options, marketConfigs, identity.predictedToken);
|
|
389
402
|
const [funding, metadata] = await lifecycleStage(client, "plan.inputs", () => Promise.all([
|
|
390
|
-
progress.canonical.phase === LifecyclePhase.None ? readFunding(client, plan, block) : { prerequisites: [], approvals: [] },
|
|
403
|
+
progress.canonical.phase === LifecyclePhase.None ? readFunding(client, plan, block, ufg === undefined ? plan.creator : account, ufg !== undefined) : { prerequisites: [], approvals: [] },
|
|
391
404
|
readPlanMetadata(client, plan, identity.predictedToken, block),
|
|
392
405
|
]));
|
|
393
406
|
assertFinalPlan(planned, options.signal);
|
|
@@ -405,7 +418,7 @@ async function planLaunchWithReads(options: PlanLaunchOptions, client: Lifecycle
|
|
|
405
418
|
if (start !== prepared || !Number.isSafeInteger(count) || count <= 0 || start + count > plan.markets.length) throw new LifecyclePlanningError("REVIEWED_TRANSACTION_MISMATCH", "Reviewed preparation differs from canonical unfinished progress");
|
|
406
419
|
batches = [...(prepared > 0 ? [prepared] : []), count, ...(start + count < plan.markets.length ? [plan.markets.length - start - count] : [])];
|
|
407
420
|
}
|
|
408
|
-
let transactions = orderedTransactions(plan, mode, progress, funding.approvals, batches);
|
|
421
|
+
let transactions = orderedTransactions(plan, mode, progress, funding.approvals, batches, ufg);
|
|
409
422
|
if (reviewed !== undefined) {
|
|
410
423
|
if (!matchesReviewedNextTransaction(transactions[0], reviewed)) throw new LifecyclePlanningError("REVIEWED_TRANSACTION_MISMATCH", "Reviewed transaction differs from the current canonical unfinished next step");
|
|
411
424
|
transactions = transactions.slice(0, 1);
|
|
@@ -432,7 +445,7 @@ async function planLaunchWithReads(options: PlanLaunchOptions, client: Lifecycle
|
|
|
432
445
|
const batchIndex = batches.findIndex((batch) => { const matches = first === failedTransaction?.marketStart; first += batch; return matches; });
|
|
433
446
|
const left = Math.ceil(count / 2);
|
|
434
447
|
batches.splice(batchIndex, 1, left, count - left);
|
|
435
|
-
transactions = orderedTransactions(plan, mode, progress, funding.approvals, batches);
|
|
448
|
+
transactions = orderedTransactions(plan, mode, progress, funding.approvals, batches, ufg);
|
|
436
449
|
simulation = await execute(transactions);
|
|
437
450
|
assertFinalPlan(planned, options.signal);
|
|
438
451
|
}
|
|
@@ -507,7 +520,9 @@ export async function buildNextTransaction(options: BuildNextTransactionOptions)
|
|
|
507
520
|
if (options.action === "cancel") {
|
|
508
521
|
if (progress.canonical.phase === LifecyclePhase.None) throw new LifecyclePlanningError("NOT_STARTED", "An unstarted plan has no launch escrow to cancel");
|
|
509
522
|
const { planned } = options;
|
|
510
|
-
|
|
523
|
+
assertFinalPlan(planned, options.signal);
|
|
524
|
+
const transaction: LifecycleTransaction = planned.ufg !== undefined ? buildUfgCancelTransaction({ plan: planned.plan, preparedMarkets: progress.canonical.preparedMarkets })
|
|
525
|
+
: { id: "cancel", kind: "cancel", chainId: Number(planned.chainId), from: planned.account, to: planned.plan.orchestrator, data: encodeFunctionData({ abi: launchLifecycleAbi, functionName: "cancelLaunch", args: [planned.plan] }), value: 0n, dependencies: [], postconditions: [{ kind: "launch", phase: "Cancelled", preparedMarkets: progress.canonical.preparedMarkets }] };
|
|
511
526
|
if (reviewed !== undefined && !matchesReviewedNextTransaction(transaction, reviewed)) throw new LifecyclePlanningError("REVIEWED_TRANSACTION_MISMATCH", "Reviewed transaction differs from the current canonical cancellation");
|
|
512
527
|
const simulation = await simulateLaunchTransactions({ client, planned, limits: options.limits === undefined ? planned.limits : options.limits, fork: options.fork, signal: options.signal }, [transaction], block, reviewed);
|
|
513
528
|
if (!simulation.admitted) throw new LifecyclePlanningError("CANCEL_NOT_ADMITTED", simulation.reason ?? "Cancellation could not be proved against current canonical state", simulation);
|
|
@@ -516,7 +531,7 @@ export async function buildNextTransaction(options: BuildNextTransactionOptions)
|
|
|
516
531
|
return preflightNextTransaction(options, next, simulation, reviewed);
|
|
517
532
|
}
|
|
518
533
|
const refreshed = await lifecycleStage(client, "plan", () => planLaunchWithReads({ client, plan: options.planned.plan, account: options.planned.account, mode: options.planned.mode, limits: options.limits === undefined ? options.planned.limits : options.limits, fork: options.fork, receipts: options.receipts, confirmations: options.confirmations ?? options.planned.confirmations, signal: options.signal,
|
|
519
|
-
allowTopologyGaps: options.planned.allowTopologyGaps, allowMissingPermanentTail: options.planned.allowMissingPermanentTail }, client, block, progress, false, reviewed));
|
|
534
|
+
ufg: options.planned.ufg, allowTopologyGaps: options.planned.allowTopologyGaps, allowMissingPermanentTail: options.planned.allowMissingPermanentTail }, client, block, progress, false, reviewed));
|
|
520
535
|
assertFinalPlan(refreshed, options.signal);
|
|
521
536
|
if (!refreshed.simulation.admitted) throw new LifecyclePlanningError("PLAN_NOT_ADMITTED", refreshed.simulation.reason ?? "Remaining exact launch sequence is not admitted", refreshed.simulation);
|
|
522
537
|
assertPlannedDeploymentBindings(options.planned, refreshed);
|
|
@@ -535,7 +550,7 @@ export async function simulateLaunchPlan(options: SimulateLaunchPlanOptions): Pr
|
|
|
535
550
|
client, plan: planned.plan, account: planned.account, mode: planned.mode,
|
|
536
551
|
limits: options.limits === undefined ? planned.limits : options.limits, fork: options.fork,
|
|
537
552
|
receipts: options.receipts, confirmations: options.confirmations ?? planned.confirmations, signal: options.signal,
|
|
538
|
-
allowTopologyGaps: planned.allowTopologyGaps, allowMissingPermanentTail: planned.allowMissingPermanentTail,
|
|
553
|
+
ufg: planned.ufg, allowTopologyGaps: planned.allowTopologyGaps, allowMissingPermanentTail: planned.allowMissingPermanentTail,
|
|
539
554
|
});
|
|
540
555
|
assertPlannedDeploymentBindings(planned, current);
|
|
541
556
|
return current.simulation;
|
package/src/lifecycle/presets.ts
CHANGED
|
@@ -8,6 +8,9 @@ import { dynamicFeePoolHookV1CreationCode } from "./deployments/dynamic-fee-pool
|
|
|
8
8
|
import { hmmFixedEnvelope, hmmFixedAdapter, hmmFixedRegistration, hmmFixedCreationCode } from "./deployments/hmm-fixed-pool-hook-v2-20261017.js";
|
|
9
9
|
import { hmmDynamicEnvelope, hmmDynamicAdapter, hmmDynamicRegistration, hmmDynamicCreationCode } from "./deployments/hmm-dynamic-pool-hook-v2-20261017.js";
|
|
10
10
|
import { HMM_FIXED_PROFILE_ID, HMM_DYNAMIC_PROFILE_ID, isHmmProfile } from "./hmm.js";
|
|
11
|
+
import { ufgFixedEnvelope, ufgFixedAdapter, ufgFixedRegistration } from "./deployments/ufg-fixed-pool-20261021.js";
|
|
12
|
+
import { ufgDynamicEnvelope, ufgDynamicAdapter, ufgDynamicRegistration } from "./deployments/ufg-dynamic-pool-20261021.js";
|
|
13
|
+
import { UFG_FIXED_PROFILE_ID, UFG_DYNAMIC_PROFILE_ID } from "./ufg-config.js";
|
|
11
14
|
|
|
12
15
|
export enum KnownLifecycleProfile {
|
|
13
16
|
Abyss0 = "abyss-0",
|
|
@@ -18,6 +21,8 @@ export enum KnownLifecycleProfile {
|
|
|
18
21
|
V4DynamicFeePool = "v4-dynamic-fee-pool",
|
|
19
22
|
V4HmmFixedPool = "v4-hmm-fixed-pool",
|
|
20
23
|
V4HmmDynamicPool = "v4-hmm-dynamic-pool",
|
|
24
|
+
V4UfgFixedPool = "v4-ufg-fixed-pool",
|
|
25
|
+
V4UfgDynamicPool = "v4-ufg-dynamic-pool",
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
export enum KnownLifecycleHook {
|
|
@@ -294,7 +299,7 @@ export function hmmV2ForwardingReserve(plan: LaunchPlanV1): bigint {
|
|
|
294
299
|
return count === 0 ? 0n : HMM_V2_FORWARDING_BASE_GAS + HMM_V2_FORWARDING_EXTRA_MARKET_GAS * BigInt(count - 1);
|
|
295
300
|
}
|
|
296
301
|
|
|
297
|
-
function
|
|
302
|
+
function poolBoundPreset(key: KnownLifecycleProfile, id: Hex, envelope: LaunchEnvelopeV2,
|
|
298
303
|
adapter: AdapterRegistrationV1, registration: ProfileRegistrationV1, creationCode: Hex): LifecycleProfilePreset {
|
|
299
304
|
return Object.freeze({
|
|
300
305
|
key, supported: true,
|
|
@@ -327,8 +332,10 @@ const knownDeployment: LifecycleDeploymentPreset = Object.freeze({
|
|
|
327
332
|
abyssPreset(KnownLifecycleProfile.Abyss3, "0xe1bd95e086469e6afd9791ccc957e4d22b735012cb3db4a747f703673c62c306"),
|
|
328
333
|
fixedFeePoolPreset,
|
|
329
334
|
dynamicFeePoolPreset,
|
|
330
|
-
|
|
331
|
-
|
|
335
|
+
poolBoundPreset(KnownLifecycleProfile.V4HmmFixedPool, HMM_FIXED_PROFILE_ID, hmmFixedEnvelope, hmmFixedAdapter, hmmFixedRegistration, hmmFixedCreationCode),
|
|
336
|
+
poolBoundPreset(KnownLifecycleProfile.V4HmmDynamicPool, HMM_DYNAMIC_PROFILE_ID, hmmDynamicEnvelope, hmmDynamicAdapter, hmmDynamicRegistration, hmmDynamicCreationCode),
|
|
337
|
+
poolBoundPreset(KnownLifecycleProfile.V4UfgFixedPool, UFG_FIXED_PROFILE_ID, ufgFixedEnvelope, ufgFixedAdapter, ufgFixedRegistration, fixedFeePoolHookV1CreationCode),
|
|
338
|
+
poolBoundPreset(KnownLifecycleProfile.V4UfgDynamicPool, UFG_DYNAMIC_PROFILE_ID, ufgDynamicEnvelope, ufgDynamicAdapter, ufgDynamicRegistration, dynamicFeePoolHookV1CreationCode),
|
|
332
339
|
]),
|
|
333
340
|
provenance: Object.freeze({ sourceCommit: "e174ce695c7010f50ca9211b07b9f178c990beac", deploymentBlock: 82880546n }),
|
|
334
341
|
});
|
|
@@ -358,7 +365,7 @@ export function getKnownLifecycleProfile(options: KnownLifecycleDeploymentIdenti
|
|
|
358
365
|
return undefined;
|
|
359
366
|
}
|
|
360
367
|
|
|
361
|
-
/** Stable, frozen order: Abyss variants
|
|
368
|
+
/** Stable, frozen order: Abyss variants, fixed/dynamic V4, HMM, then UFG fixed/dynamic. */
|
|
362
369
|
export function listKnownLifecycleProfiles(options: KnownLifecycleDeploymentIdentity): readonly LifecycleProfilePreset[] {
|
|
363
370
|
return getKnownLifecycleDeployment(options)?.profiles ?? noProfiles;
|
|
364
371
|
}
|
|
@@ -6,6 +6,8 @@ import { assertLifecycleBlock, assertLifecycleReadClientOpen, lifecycleFailureRe
|
|
|
6
6
|
import { LifecyclePlanningError, type CanonicalLaunchProgress, type LifecycleBlock, type LifecycleMarketProgress, type LifecycleProfile, type LifecycleReceiptReference, type LifecycleReceiptStatus, type LifecycleRpcClient, type PoolBoundHookDeployment, type ReadLaunchProgressOptions } from "./types.js";
|
|
7
7
|
import { deriveKnownPoolBoundHookDeployment } from "./calibration.js";
|
|
8
8
|
import { getKnownLifecycleDeployment, getKnownLifecycleProfile } from "./presets.js";
|
|
9
|
+
import { ufgLaunchProxyV1Abi } from "./ufg-abi.js";
|
|
10
|
+
import { assertUfgLaunchAccount, hashUfgLaunchConfig, UFG_LAUNCH_PROXY_ADDRESS, type UfgLaunchConfig } from "./ufg-config.js";
|
|
9
11
|
|
|
10
12
|
export const ABYSS_LIFECYCLE_CONFIG_SCHEMA = keccak256(stringToHex("(uint8,uint24,bytes32,uint160,(int24,int24,uint128,uint256)[])"));
|
|
11
13
|
|
|
@@ -460,12 +462,17 @@ async function transactionMatchesLaunch(transaction: Record<string, unknown>, op
|
|
|
460
462
|
if (transaction.chainId !== undefined && rpcQuantity(transaction.chainId, "receipt transaction chain") !== planned.plan.chainId) throw new LifecyclePlanningError("RECEIPT_CHAIN", "Receipt transaction belongs to a different chain");
|
|
461
463
|
const to = transaction.to === null ? zeroAddress : rpcHex(transaction.to, "receipt transaction target");
|
|
462
464
|
const input = rpcHex(transaction.input ?? transaction.data, "receipt transaction calldata");
|
|
463
|
-
|
|
465
|
+
const target = planned.ufg === undefined ? planned.plan.orchestrator : UFG_LAUNCH_PROXY_ADDRESS;
|
|
466
|
+
if (to.toLowerCase() === target.toLowerCase()) {
|
|
464
467
|
try {
|
|
465
|
-
const decoded = decodeFunctionData({ abi: launchLifecycleAbi, data: input });
|
|
468
|
+
const decoded = decodeFunctionData({ abi: planned.ufg === undefined ? launchLifecycleAbi : ufgLaunchProxyV1Abi, data: input });
|
|
466
469
|
if (!["launchAtomic", "beginLaunch", "prepareMarkets", "activateLaunch", "cancelLaunch"].includes(decoded.functionName) || !Array.isArray(decoded.args)) return false;
|
|
467
470
|
// Decoded plan layout is validated by the exact ABI, not trusted raw RPC JSON.
|
|
468
471
|
const plan = decoded.args[0] as LaunchPlanV1;
|
|
472
|
+
if (planned.ufg !== undefined && (decoded.functionName === "launchAtomic" || decoded.functionName === "beginLaunch")) {
|
|
473
|
+
const config = decoded.args[decoded.functionName === "launchAtomic" ? 1 : 2] as UfgLaunchConfig;
|
|
474
|
+
if (hashUfgLaunchConfig(config) !== planned.ufg.configHash) return false;
|
|
475
|
+
}
|
|
469
476
|
return hashLaunchPlan(plan).toLowerCase() === planned.planHash.toLowerCase();
|
|
470
477
|
} catch { return false; }
|
|
471
478
|
}
|
|
@@ -477,7 +484,7 @@ async function transactionMatchesLaunch(transaction: Record<string, unknown>, op
|
|
|
477
484
|
if (decoded.functionName !== "approve" || rpcQuantity(transaction.value ?? "0x0", "approval native value") !== 0n) return false;
|
|
478
485
|
spender = decoded.args[0];
|
|
479
486
|
} catch { return false; }
|
|
480
|
-
const escrow = await readLifecycleContract<Address>(options.client, planned.plan.orchestrator, launchLifecycleAbi, "fundingEscrow", [], block);
|
|
487
|
+
const escrow = planned.ufg !== undefined ? UFG_LAUNCH_PROXY_ADDRESS : await readLifecycleContract<Address>(options.client, planned.plan.orchestrator, launchLifecycleAbi, "fundingEscrow", [], block);
|
|
481
488
|
return spender.toLowerCase() === escrow.toLowerCase();
|
|
482
489
|
}
|
|
483
490
|
|
|
@@ -542,7 +549,10 @@ async function readLaunchProgressAtBlock(options: ReadLaunchProgressOptions, cli
|
|
|
542
549
|
assertActive();
|
|
543
550
|
const chainId = chainIdValue;
|
|
544
551
|
if (chainId !== planned.plan.chainId) throw new LifecyclePlanningError("CHAIN_MISMATCH", "RPC chain differs from the committed launch");
|
|
545
|
-
if (planned.
|
|
552
|
+
if (planned.ufg !== undefined) {
|
|
553
|
+
assertUfgLaunchAccount(planned.plan, planned.account);
|
|
554
|
+
if (hashUfgLaunchConfig(planned.ufg.config) !== planned.ufg.configHash) throw new LifecyclePlanningError("PLAN_MUTATED", "UFG config differs from its finalized commitment");
|
|
555
|
+
} else if (planned.account.toLowerCase() !== planned.plan.creator.toLowerCase()) throw new LifecyclePlanningError("ACCOUNT_MISMATCH", "Execution account must be the committed creator/payer/refund account");
|
|
546
556
|
if (hashLaunchPlan(planned.plan).toLowerCase() !== planned.planHash.toLowerCase() || hashLaunchIdentity(planned.plan).toLowerCase() !== planned.launchId.toLowerCase()) throw new LifecyclePlanningError("PLAN_MUTATED", "Stored economic plan no longer matches its commitment");
|
|
547
557
|
const [head, confirmedProgress, token, confirmedNonce, headNonce, pendingNonce] = await Promise.all([
|
|
548
558
|
readLifecycleContract<LaunchProgressV1>(client, planned.plan.orchestrator, launchLifecycleAbi, "readLaunchProgress", [planned.launchId], block, planned.account),
|
|
@@ -556,10 +566,17 @@ async function readLaunchProgressAtBlock(options: ReadLaunchProgressOptions, cli
|
|
|
556
566
|
const canonical = confirmedProgress ?? head;
|
|
557
567
|
if (token.toLowerCase() !== planned.predictedToken.toLowerCase()) throw new LifecyclePlanningError("TOKEN_IDENTITY", "Predicted token changed for the committed token configuration");
|
|
558
568
|
for (const progress of [canonical, head]) if (progress.phase !== LifecyclePhase.None) {
|
|
559
|
-
if (progress.planHash.toLowerCase() !== planned.planHash.toLowerCase() || progress.launchId.toLowerCase() !== planned.launchId.toLowerCase() || progress.creator.toLowerCase() !== planned.
|
|
569
|
+
if (progress.planHash.toLowerCase() !== planned.planHash.toLowerCase() || progress.launchId.toLowerCase() !== planned.launchId.toLowerCase() || progress.creator.toLowerCase() !== planned.plan.creator.toLowerCase() || progress.nonce !== planned.plan.nonce || progress.token.toLowerCase() !== token.toLowerCase() || progress.marketCount !== planned.plan.markets.length || progress.buyCount !== planned.plan.buys.length || progress.deadline !== planned.plan.deadline) throw new LifecyclePlanningError("PLAN_REPLAY", "This creator nonce is already bound to a different economic launch");
|
|
560
570
|
if (progress.mode !== (planned.mode === "atomic" ? LifecycleMode.Atomic : LifecycleMode.Staged)) throw new LifecyclePlanningError("MODE_MISMATCH", "Execution mode differs from the already recorded launch");
|
|
561
571
|
if (progress.preparedMarkets > progress.marketCount || progress.phase === LifecyclePhase.Activating || (progress.phase === LifecyclePhase.Ready && progress.preparedMarkets !== progress.marketCount)) throw new LifecyclePlanningError("INVALID_PROGRESS", "Canonical progress violates the committed ordered state machine");
|
|
562
572
|
}
|
|
573
|
+
if (planned.ufg !== undefined && head.phase !== LifecyclePhase.None) {
|
|
574
|
+
const [principal, configHash] = await Promise.all([
|
|
575
|
+
readLifecycleContract<Address>(client, UFG_LAUNCH_PROXY_ADDRESS, ufgLaunchProxyV1Abi, "principalOf", [planned.launchId], block),
|
|
576
|
+
readLifecycleContract<Hash>(client, UFG_LAUNCH_PROXY_ADDRESS, ufgLaunchProxyV1Abi, "launchConfigHash", [planned.launchId], block),
|
|
577
|
+
]);
|
|
578
|
+
if (principal.toLowerCase() !== planned.account.toLowerCase() || configHash.toLowerCase() !== planned.ufg.configHash.toLowerCase()) throw new LifecyclePlanningError("PLAN_REPLAY", "UFG proxy principal or launch config differs from the stored launch");
|
|
579
|
+
}
|
|
563
580
|
const confirmedAccountNonce = rpcQuantity(confirmedNonce, "confirmed account nonce");
|
|
564
581
|
const headAccountNonce = headNonce === undefined ? confirmedAccountNonce : rpcQuantity(headNonce, "head account nonce");
|
|
565
582
|
const pendingAccountNonce = rpcQuantity(pendingNonce, "pending account nonce");
|
|
@@ -6,6 +6,7 @@ import { LifecyclePlanningError, type ControlledLifecycleFork, type LifecycleBlo
|
|
|
6
6
|
import { decodeAbyssLifecycleMarketConfig, decodePoolBoundV4LifecycleMarketConfig, decodeV4LifecycleMarketConfig, isPoolBoundV4ConfigVersion } from "./markets.js";
|
|
7
7
|
import { hmmV2ForwardingReserve } from "./presets.js";
|
|
8
8
|
import { transactionGasEstimate } from "./transactions.js";
|
|
9
|
+
import { hashUfgLaunchConfig } from "./ufg-config.js";
|
|
9
10
|
|
|
10
11
|
function committedPositionCount(plan: LaunchPlanV1): number {
|
|
11
12
|
let count = 0;
|
|
@@ -163,7 +164,7 @@ async function simulateRpcPass(client: LifecycleRpcClient, planned: PlannedLaunc
|
|
|
163
164
|
const buy = planned.plan.buys[buyIndex];
|
|
164
165
|
if (buy === undefined || (receipt.quoteSpent[buyIndex] ?? buy.quoteAmountIn + 1n) > buy.quoteAmountIn || (receipt.tokenOut[buyIndex] ?? 0n) < buy.minTokenOut) throw new LifecyclePlanningError("POSTCONDITION_FAILED", "Simulation did not satisfy every ordered buy's budget and output minimum");
|
|
165
166
|
}
|
|
166
|
-
} else if (transaction.kind === "cancel" && !events.some((event) => event.eventName === "LaunchCancelled" && event.args.launchId.toLowerCase() === planned.launchId.toLowerCase() && event.args.creator.toLowerCase() === planned.
|
|
167
|
+
} else if (transaction.kind === "cancel" && !events.some((event) => event.eventName === "LaunchCancelled" && event.args.launchId.toLowerCase() === planned.launchId.toLowerCase() && event.args.creator.toLowerCase() === planned.plan.creator.toLowerCase())) throw new LifecyclePlanningError("POSTCONDITION_FAILED", "Simulation did not cancel the exact committed launch");
|
|
167
168
|
else if ((transaction.kind === "approve" || transaction.kind === "approve-reset") && returnData !== "0x" && decodeFunctionResult({ abi: lifecycleErc20Abi, functionName: "approve", data: returnData }) !== true) throw new LifecyclePlanningError("POSTCONDITION_FAILED", "Funding approval returned failure");
|
|
168
169
|
} catch (failure) { error = failure instanceof LifecyclePlanningError ? failure.message : "Simulation returned an invalid committed postcondition"; }
|
|
169
170
|
}
|
|
@@ -346,6 +347,7 @@ export function prepareLaunchSimulation(options: SimulateLaunchPlanOptions, bloc
|
|
|
346
347
|
if (hashLaunchPlan(plan).toLowerCase() !== planHash.toLowerCase() || hashLaunchIdentity(plan).toLowerCase() !== launchId.toLowerCase()) {
|
|
347
348
|
throw new LifecyclePlanningError("PLAN_MUTATED", "Economic plan changed after its simulation context was bound");
|
|
348
349
|
}
|
|
350
|
+
if (planned.ufg !== undefined && hashUfgLaunchConfig(planned.ufg.config) !== planned.ufg.configHash) throw new LifecyclePlanningError("PLAN_MUTATED", "UFG treasury config changed after its simulation context was bound");
|
|
349
351
|
};
|
|
350
352
|
const pending = (async () => {
|
|
351
353
|
if (fork !== undefined && lifecycleSourceClient(fork.client) === sourceClient) throw new LifecyclePlanningError("UNSAFE_FORK", "Source RPC must never receive controlled simulation transactions/snapshots/resets");
|
package/src/lifecycle/types.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Address, Hash, Hex } from "viem";
|
|
2
|
+
import type { UfgLaunchConfig } from "./ufg-config.js";
|
|
2
3
|
import type {
|
|
3
4
|
AdapterRegistrationV1, AssetFundingV1, LaunchExecutionMode, LaunchPlanV1, LaunchProgressV1,
|
|
4
5
|
MarketLiveStateV1, PreparedMarketV1, ProfileRegistrationV1, ProfileTopologyV1, LaunchEnvelopeV2, LifecycleDeveloperTerms,
|
|
@@ -161,6 +162,8 @@ export type PlannedLaunch = {
|
|
|
161
162
|
plan: LaunchPlanV1; planHash: Hash; launchId: Hash; predictedToken: Address;
|
|
162
163
|
tokenFactory: Address; tokenFactoryCodeHash: Hex;
|
|
163
164
|
account: Address; chainId: bigint; mode: LaunchExecutionMode; confirmations: number;
|
|
165
|
+
/** Opt-in proxy routing; the committed creator remains the proxy, account is its principal. */
|
|
166
|
+
ufg?: { config: UfgLaunchConfig; configHash: Hash };
|
|
164
167
|
transactions: readonly LifecycleTransaction[]; prerequisites: readonly LifecycleFundingPrerequisite[];
|
|
165
168
|
/** Sum of exact calldata bytes across the returned remaining commands, including approvals. */
|
|
166
169
|
totalCalldataBytes: number;
|
|
@@ -176,6 +179,8 @@ export type PlanLaunchOptions = {
|
|
|
176
179
|
client: LifecycleRpcClient; account: Address; plan: LaunchPlanV1; mode: LaunchExecutionMode;
|
|
177
180
|
limits?: LifecycleLimitSource; fork?: ControlledLifecycleFork; receipts?: readonly LifecycleReceiptReference[];
|
|
178
181
|
confirmations?: number; signal?: AbortSignal;
|
|
182
|
+
/** Opt in to the pinned UFG proxy; sender must match the nonce's principal namespace. */
|
|
183
|
+
ufg?: { config: UfgLaunchConfig };
|
|
179
184
|
/** Unstarted only: permit interior gaps; opening containment, aligned ticks and the tail remain required. */
|
|
180
185
|
allowTopologyGaps?: boolean;
|
|
181
186
|
/** Unstarted only: waive the funded boundary tail; all other topology checks remain required. */
|
|
@@ -201,7 +206,7 @@ export type SimulateLaunchPlanOptions = {
|
|
|
201
206
|
signal?: AbortSignal;
|
|
202
207
|
};
|
|
203
208
|
export type ReadLaunchProgressOptions = {
|
|
204
|
-
client: LifecycleRpcClient; planned: Pick<PlannedLaunch, "plan" | "planHash" | "launchId" | "predictedToken" | "account" | "mode" | "confirmations">;
|
|
209
|
+
client: LifecycleRpcClient; planned: Pick<PlannedLaunch, "plan" | "planHash" | "launchId" | "predictedToken" | "account" | "mode" | "confirmations" | "ufg">;
|
|
205
210
|
receipts?: readonly LifecycleReceiptReference[]; confirmations?: number;
|
|
206
211
|
signal?: AbortSignal;
|
|
207
212
|
};
|