@black-market/sdk 0.13.0 → 0.14.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 +4 -4
- package/dist/lifecycle/index.d.ts +3 -1
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/index.js +3 -1
- package/dist/lifecycle/index.js.map +1 -1
- package/dist/lifecycle/lens.d.ts +409 -0
- package/dist/lifecycle/lens.d.ts.map +1 -0
- package/dist/lifecycle/lens.js +30 -0
- package/dist/lifecycle/lens.js.map +1 -0
- package/dist/lifecycle/mining.d.ts +47 -0
- package/dist/lifecycle/mining.d.ts.map +1 -0
- package/dist/lifecycle/mining.js +89 -0
- package/dist/lifecycle/mining.js.map +1 -0
- package/dist/lifecycle/planner.d.ts.map +1 -1
- package/dist/lifecycle/planner.js +70 -62
- package/dist/lifecycle/planner.js.map +1 -1
- package/dist/lifecycle/presets.d.ts +3 -0
- package/dist/lifecycle/presets.d.ts.map +1 -1
- package/dist/lifecycle/presets.js +1 -0
- package/dist/lifecycle/presets.js.map +1 -1
- package/dist/lifecycle/progress.d.ts +48 -0
- package/dist/lifecycle/progress.d.ts.map +1 -1
- package/dist/lifecycle/progress.js +31 -20
- package/dist/lifecycle/progress.js.map +1 -1
- package/dist/lifecycle/rpc.d.ts +6 -2
- package/dist/lifecycle/rpc.d.ts.map +1 -1
- package/dist/lifecycle/rpc.js +47 -44
- package/dist/lifecycle/rpc.js.map +1 -1
- package/dist/lifecycle/simulation.d.ts +5 -3
- package/dist/lifecycle/simulation.d.ts.map +1 -1
- package/dist/lifecycle/simulation.js +130 -109
- package/dist/lifecycle/simulation.js.map +1 -1
- package/dist/lifecycle/snapshot.d.ts +8 -0
- package/dist/lifecycle/snapshot.d.ts.map +1 -0
- package/dist/lifecycle/snapshot.js +79 -0
- package/dist/lifecycle/snapshot.js.map +1 -0
- package/dist/lifecycle/types.d.ts +15 -5
- package/dist/lifecycle/types.d.ts.map +1 -1
- package/dist/lifecycle/types.js.map +1 -1
- package/docs/launch-lifecycle.md +42 -20
- package/docs/reference.md +39 -6
- package/examples/README.md +2 -2
- package/package.json +1 -1
- package/src/lifecycle/index.ts +5 -1
- package/src/lifecycle/lens.ts +42 -0
- package/src/lifecycle/mining.ts +80 -0
- package/src/lifecycle/planner.ts +72 -58
- package/src/lifecycle/presets.ts +4 -0
- package/src/lifecycle/progress.ts +26 -16
- package/src/lifecycle/rpc.ts +45 -43
- package/src/lifecycle/simulation.ts +126 -100
- package/src/lifecycle/snapshot.ts +72 -0
- package/src/lifecycle/types.ts +15 -5
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { parseAbi } from "viem";
|
|
2
|
+
import { hasLifecycleV4HookPermissions, minePoolBoundHookSalt, predictPoolBoundHookAddress } from "./markets.js";
|
|
3
|
+
import { readLifecycleContract } from "./rpc.js";
|
|
4
|
+
import { LifecyclePlanningError } from "./types.js";
|
|
5
|
+
import { lifecycleLensSnapshot } from "./lens.js";
|
|
6
|
+
export const hookSaltMinerAbi = parseAbi(["function findMany((address deployer, bytes32 initCodeHash, uint256 startSalt, uint160 flags)[] searches, uint256 maxAttempts) pure returns (bytes32[] salts, address[] hooks, bool[] found)"]);
|
|
7
|
+
/** Read-only bounded EVM mining; every returned winner is checked with local CREATE2. */
|
|
8
|
+
export async function mineLifecycleHooks(options) {
|
|
9
|
+
const { deployments, signal, onProgress, miner, client, block } = options;
|
|
10
|
+
const checkActive = () => { if (signal?.aborted)
|
|
11
|
+
throw Object.assign(new Error("Lifecycle hook mining cancelled"), { name: "AbortError" }); };
|
|
12
|
+
checkActive();
|
|
13
|
+
if (miner === undefined)
|
|
14
|
+
return Promise.all(deployments.map(async (deployment) => ({ ...deployment,
|
|
15
|
+
...await minePoolBoundHookSalt({ ...deployment, startSalt: BigInt(deployment.salt), signal,
|
|
16
|
+
onProgress: onProgress === undefined ? undefined : (progress) => onProgress({ marketIndex: deployment.marketIndex, ...progress }) }),
|
|
17
|
+
})));
|
|
18
|
+
const results = deployments.map((deployment) => ({ ...deployment }));
|
|
19
|
+
const lens = lifecycleLensSnapshot(client, block);
|
|
20
|
+
let pending = [];
|
|
21
|
+
for (const [index, deployment] of deployments.entries()) {
|
|
22
|
+
const observed = lens?.hookDeployments.find((hook) => hook.marketIndex === deployment.marketIndex);
|
|
23
|
+
if (observed === undefined) {
|
|
24
|
+
pending.push(index);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const predictedHook = predictPoolBoundHookAddress({ ...deployment, salt: observed.salt });
|
|
28
|
+
if (observed.deployer.toLowerCase() !== deployment.deployer.toLowerCase() || observed.initCodeHash.toLowerCase() !== deployment.initCodeHash.toLowerCase() ||
|
|
29
|
+
BigInt(observed.salt) < BigInt(deployment.salt) || observed.found && (observed.hook.toLowerCase() !== predictedHook.toLowerCase() || !hasLifecycleV4HookPermissions(predictedHook))) {
|
|
30
|
+
throw new LifecyclePlanningError("INVALID_MINER_RESULT", "LaunchLens hook result differs from the locally derived constructor or CREATE2 address");
|
|
31
|
+
}
|
|
32
|
+
results[index] = { ...deployment, salt: observed.salt, predictedHook };
|
|
33
|
+
if (!observed.found)
|
|
34
|
+
pending.push(index);
|
|
35
|
+
else
|
|
36
|
+
onProgress?.({ marketIndex: deployment.marketIndex, salt: observed.salt, predictedHook, attempts: BigInt(observed.salt) - BigInt(deployment.salt) + 1n });
|
|
37
|
+
}
|
|
38
|
+
while (pending.length > 0) {
|
|
39
|
+
checkActive();
|
|
40
|
+
let maxAttempts = BigInt(Math.floor(150_000 / pending.length));
|
|
41
|
+
for (const index of pending) {
|
|
42
|
+
const available = (1n << 256n) - 1n - BigInt(results[index].salt);
|
|
43
|
+
if (available < maxAttempts)
|
|
44
|
+
maxAttempts = available;
|
|
45
|
+
}
|
|
46
|
+
if (maxAttempts === 0n) {
|
|
47
|
+
pending = pending.filter((index) => {
|
|
48
|
+
const deployment = results[index];
|
|
49
|
+
if (BigInt(deployment.salt) !== (1n << 256n) - 1n)
|
|
50
|
+
return true;
|
|
51
|
+
const predictedHook = predictPoolBoundHookAddress(deployment);
|
|
52
|
+
if (!hasLifecycleV4HookPermissions(predictedHook))
|
|
53
|
+
throw new LifecyclePlanningError("HOOK_SALT_EXHAUSTED", "No valid hook address remains in the uint256 salt range");
|
|
54
|
+
results[index] = { ...deployment, predictedHook };
|
|
55
|
+
onProgress?.({ marketIndex: deployment.marketIndex, salt: deployment.salt, predictedHook, attempts: BigInt(deployment.salt) - BigInt(deployments[index].salt) + 1n });
|
|
56
|
+
return false;
|
|
57
|
+
});
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const searches = pending.map((index) => ({ deployer: results[index].deployer, initCodeHash: results[index].initCodeHash, startSalt: BigInt(results[index].salt), flags: 0x1afcn }));
|
|
61
|
+
const [salts, hooks, found] = await readLifecycleContract(client, miner, hookSaltMinerAbi, "findMany", [searches, maxAttempts], block);
|
|
62
|
+
checkActive();
|
|
63
|
+
if (salts.length !== pending.length || hooks.length !== pending.length || found.length !== pending.length)
|
|
64
|
+
throw new LifecyclePlanningError("INVALID_MINER_RESULT", "Hook miner omitted a search result");
|
|
65
|
+
const unfinished = [];
|
|
66
|
+
for (const [ordinal, index] of pending.entries()) {
|
|
67
|
+
const deployment = results[index];
|
|
68
|
+
const salt = salts[ordinal];
|
|
69
|
+
const end = searches[ordinal].startSalt + maxAttempts;
|
|
70
|
+
const predictedHook = predictPoolBoundHookAddress({ ...deployment, salt });
|
|
71
|
+
if (found[ordinal]) {
|
|
72
|
+
if (BigInt(salt) < searches[ordinal].startSalt || BigInt(salt) >= end || hooks[ordinal].toLowerCase() !== predictedHook.toLowerCase() || !hasLifecycleV4HookPermissions(predictedHook))
|
|
73
|
+
throw new LifecyclePlanningError("INVALID_MINER_RESULT", "Hook miner returned an invalid CREATE2 salt or permission mask");
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
if (BigInt(salt) !== end)
|
|
77
|
+
throw new LifecyclePlanningError("INVALID_MINER_RESULT", "Hook miner did not advance to the next unsearched salt");
|
|
78
|
+
unfinished.push(index);
|
|
79
|
+
}
|
|
80
|
+
results[index] = { ...deployment, salt, predictedHook };
|
|
81
|
+
onProgress?.({ marketIndex: deployment.marketIndex, salt, predictedHook, attempts: BigInt(salt) - BigInt(deployments[index].salt) + (found[ordinal] ? 1n : 0n) });
|
|
82
|
+
checkActive();
|
|
83
|
+
}
|
|
84
|
+
pending = unfinished;
|
|
85
|
+
}
|
|
86
|
+
checkActive();
|
|
87
|
+
return results;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=mining.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mining.js","sourceRoot":"","sources":["../../src/lifecycle/mining.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner.d.ts","sourceRoot":"","sources":["../../src/lifecycle/planner.ts"],"names":[],"mappings":"AASA,OAAO,EAA0B,KAAK,2BAA2B,EAAkN,KAAK,mBAAmB,EAAE,KAAK,oBAAoB,EAAE,KAAK,aAAa,EAAE,KAAK,iBAAiB,EAAE,KAAK,oCAAoC,EAAE,KAAK,yBAAyB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"planner.d.ts","sourceRoot":"","sources":["../../src/lifecycle/planner.ts"],"names":[],"mappings":"AASA,OAAO,EAA0B,KAAK,2BAA2B,EAAkN,KAAK,mBAAmB,EAAE,KAAK,oBAAoB,EAAE,KAAK,aAAa,EAAE,KAAK,iBAAiB,EAAE,KAAK,oCAAoC,EAAE,KAAK,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAyQld;wFACwF;AACxF,wBAAsB,6BAA6B,CAAC,OAAO,EAAE,oCAAoC,GAAG,OAAO,CAAC,aAAa,CAAC,CA+EzH;AAED,qGAAqG;AACrG,wBAAsB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,aAAa,CAAC,CAQnF;AA2ID;uFACuF;AACvF,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAmF1H;AAED;qFACqF;AACrF,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAWzG"}
|
|
@@ -3,9 +3,9 @@ import { ABYSS_FEE_TIERS } from "../abyss.js";
|
|
|
3
3
|
import { assertLaunchTopologyCoverage, getTickAtSqrtRatio } from "../launch.js";
|
|
4
4
|
import { launchLifecycleAbi, lifecycleAdapterAbi, lifecycleErc20Abi } from "./abi.js";
|
|
5
5
|
import { hashLaunchIdentity, hashLaunchPlan, LifecycleFundingKind, LifecyclePhase, LifecycleRewardMode, LifecycleTokenKind, LIFECYCLE_MAX_REWARD_ERC20_SUPPLY, LIFECYCLE_MAX_ERC404_SUPPLY, parseLaunchPlan, serializeLaunchPlan } from "./schema.js";
|
|
6
|
-
import { readLaunchProgress, readLifecycleHeadProgress, readLifecycleProfiles, readLifecycleTokenAtBlock, readPoolBoundHookDeployment } from "./progress.js";
|
|
7
|
-
import { decodeAbyssLifecycleMarketConfig, decodePoolBoundV4LifecycleMarketConfig, decodeV4LifecycleMarketConfig, encodePoolBoundV4LifecycleMarketConfig, isPoolBoundV4ConfigVersion
|
|
8
|
-
import {
|
|
6
|
+
import { readLaunchProgress, readLifecycleExecutionProgress, readLifecycleHeadProgress, readLifecycleProfiles, readLifecycleTokenAtBlock, readPoolBoundHookDeployment } from "./progress.js";
|
|
7
|
+
import { decodeAbyssLifecycleMarketConfig, decodePoolBoundV4LifecycleMarketConfig, decodeV4LifecycleMarketConfig, encodePoolBoundV4LifecycleMarketConfig, isPoolBoundV4ConfigVersion } from "./markets.js";
|
|
8
|
+
import { assertLifecycleReadClientOpen, lifecycleFailureReason, lifecyclePinnedRpc, lifecycleStaticRpc, lifecycleRpc, lifecycleStage, readLifecycleChainId, readLifecycleContract, readLifecycleGasPrice, resolveLifecycleLimits, rpcHex, rpcQuantity, withLifecycleReadClient } from "./rpc.js";
|
|
9
9
|
import { prepareLaunchSimulation, simulateLaunchTransactions } from "./simulation.js";
|
|
10
10
|
import { LifecyclePlanningError } from "./types.js";
|
|
11
11
|
import { deriveKnownPoolBoundHookDeployment, protectOrderedBuyMinimums, validateBuySlippageBps } from "./calibration.js";
|
|
@@ -14,6 +14,8 @@ import { buildLaunchTransactions, packPreparationBatches } from "./transactions.
|
|
|
14
14
|
import { assertLifecycleMarketAllocation, assertLifecycleSupplyAllocation } from "./allocations.js";
|
|
15
15
|
import { assertUfgLaunchAccount, hashUfgLaunchConfig, UFG_LAUNCH_PROXY_ADDRESS } from "./ufg-config.js";
|
|
16
16
|
import { buildUfgLaunchTransactions, buildUfgCancelTransaction, validateUfgLaunchPlan } from "./ufg.js";
|
|
17
|
+
import { readLifecyclePlanningBlock } from "./snapshot.js";
|
|
18
|
+
import { mineLifecycleHooks } from "./mining.js";
|
|
17
19
|
function validatePlanShape(plan) {
|
|
18
20
|
// Encoding checks all exact integer widths, addresses, byte strings and tuple fields.
|
|
19
21
|
const commitment = hashLaunchPlan(plan);
|
|
@@ -321,32 +323,28 @@ export async function prepareAndPlanLifecycleLaunch(options) {
|
|
|
321
323
|
throw new LifecyclePlanningError("INVALID_GAS_ENVELOPE", "Fixed final envelope changed during preparation");
|
|
322
324
|
};
|
|
323
325
|
const result = await withLifecycleReadClient(options.client, async (client) => {
|
|
324
|
-
const
|
|
326
|
+
const block = await readLifecyclePlanningBlock(client, draft, options.account, options.ufg !== undefined, true);
|
|
327
|
+
const chainId = await readLifecycleChainId(client);
|
|
325
328
|
if (chainId !== draft.chainId)
|
|
326
329
|
throw new LifecyclePlanningError("CHAIN_MISMATCH", "RPC chain differs from the economic plan being finalized");
|
|
327
330
|
const metadata = needsMining ? await Promise.all(draft.markets.map((market, marketIndex) => isPoolBoundV4ConfigVersion(market.configVersion)
|
|
328
331
|
? readPoolBoundHookDeployment({ client, plan: draft, marketIndex }, block) : undefined)) : [];
|
|
329
332
|
assertDraft();
|
|
330
333
|
const markets = [...draft.markets];
|
|
331
|
-
const minedDeployments =
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
334
|
+
const minedDeployments = await mineLifecycleHooks({ client, block,
|
|
335
|
+
miner: getKnownLifecycleDeployment({ chainId, orchestrator: draft.orchestrator })?.hookSaltMiner,
|
|
336
|
+
deployments: metadata.flatMap((deployment, marketIndex) => deployment === undefined ? [] : [{ marketIndex, ...deployment }]),
|
|
337
|
+
signal: options.signal, onProgress: options.onProgress });
|
|
338
|
+
for (const mined of minedDeployments) {
|
|
335
339
|
assertDraft();
|
|
336
|
-
const
|
|
337
|
-
deployer: deployment.deployer, initCodeHash: deployment.initCodeHash, startSalt: BigInt(deployment.salt), signal: options.signal,
|
|
338
|
-
onProgress: options.onProgress === undefined ? undefined : (progress) => options.onProgress?.({ marketIndex, ...progress }),
|
|
339
|
-
});
|
|
340
|
+
const marketIndex = mined.marketIndex;
|
|
340
341
|
const market = markets[marketIndex];
|
|
341
342
|
if (!isPoolBoundV4ConfigVersion(market.configVersion))
|
|
342
343
|
throw new LifecyclePlanningError("INVALID_BOUND_MARKET", "Mined market lost its explicit pool-bound version");
|
|
343
344
|
markets[marketIndex] = { ...market, config: encodePoolBoundV4LifecycleMarketConfig({ ...decodePoolBoundV4LifecycleMarketConfig(market.config, market.configVersion), hookSalt: mined.salt }) };
|
|
344
|
-
minedDeployments.push({ marketIndex, deployer: deployment.deployer, initCodeHash: deployment.initCodeHash, ...mined });
|
|
345
345
|
}
|
|
346
346
|
assertDraft();
|
|
347
|
-
|
|
348
|
-
// start at a fresh post-mining snapshot and share it through final protection.
|
|
349
|
-
const executionBlock = needsMining ? await readLifecycleBlock(client) : block;
|
|
347
|
+
const executionBlock = block;
|
|
350
348
|
assertDraft();
|
|
351
349
|
const price = heldEnvelope === undefined ? undefined : heldEnvelope.gasPrice ?? rpcQuantity(await readLifecycleGasPrice(client), "gas price");
|
|
352
350
|
const baseFeeEnvelope = (executionBlock.baseFeePerGas ?? 0n) * 2n;
|
|
@@ -356,11 +354,12 @@ export async function prepareAndPlanLifecycleLaunch(options) {
|
|
|
356
354
|
});
|
|
357
355
|
let plan = { ...draft, markets };
|
|
358
356
|
let ownedProgress;
|
|
357
|
+
let sizing;
|
|
359
358
|
if (needsProtection) {
|
|
360
359
|
// A diagnostic minimum is never returned as an admitted or executable plan.
|
|
361
360
|
// Price guards, funding minima, recipients and all ordered budgets stay exact.
|
|
362
361
|
const diagnosticPlan = { ...plan, buys: plan.buys.map((buy) => ({ ...buy, minTokenOut: 1n })) };
|
|
363
|
-
const diagnostic = await lifecycleStage(client, "plan", () => planLaunchWithReads({ ...options, plan: diagnosticPlan }, client, executionBlock, undefined, true,
|
|
362
|
+
const diagnostic = await lifecycleStage(client, "plan", () => planLaunchWithReads({ ...options, plan: diagnosticPlan }, client, executionBlock, undefined, true, finalEnvelope));
|
|
364
363
|
assertDraft();
|
|
365
364
|
if (diagnostic.progress.canonical.phase !== LifecyclePhase.None || diagnostic.progress.head.phase !== LifecyclePhase.None)
|
|
366
365
|
throw new LifecyclePlanningError("LAUNCH_ALREADY_STARTED", "Buy protection cannot replace an already committed launch plan");
|
|
@@ -368,8 +367,9 @@ export async function prepareAndPlanLifecycleLaunch(options) {
|
|
|
368
367
|
// This is the real SDK-owned None observation, not an assumed new-launch
|
|
369
368
|
// state. No onchain commitment existed at this exact snapshot to replace.
|
|
370
369
|
ownedProgress = { ...diagnostic.progress, planHash: hashLaunchPlan(plan) };
|
|
370
|
+
sizing = new Map(diagnostic.simulation.steps.map((step) => [step.transactionId, step.gasRequired ?? step.gasUsed]));
|
|
371
371
|
}
|
|
372
|
-
const planned = await lifecycleStage(client, "plan", () => planLaunchWithReads({ ...options, plan }, client, executionBlock, ownedProgress, false,
|
|
372
|
+
const planned = await lifecycleStage(client, "plan", () => planLaunchWithReads({ ...options, plan }, client, executionBlock, ownedProgress, false, finalEnvelope, sizing));
|
|
373
373
|
assertDraft();
|
|
374
374
|
assertFinalPlan(planned, options.signal);
|
|
375
375
|
if (planned.progress.canonical.phase !== LifecyclePhase.Active && planned.progress.canonical.phase !== LifecyclePhase.Cancelled) {
|
|
@@ -397,12 +397,12 @@ export async function planLaunch(options) {
|
|
|
397
397
|
assertFinalPlan(result, options.signal);
|
|
398
398
|
return result;
|
|
399
399
|
}
|
|
400
|
-
async function planLaunchWithReads(options, client, pinnedBlock, ownedProgress, diagnostic = false,
|
|
400
|
+
async function planLaunchWithReads(options, client, pinnedBlock, ownedProgress, diagnostic = false, fixedFinalEnvelope, sizing) {
|
|
401
401
|
const { plan, account, mode, ufg } = options;
|
|
402
402
|
const { commitment: planHash, marketConfigs, ufgConfigHash } = validatePlanningIntent(options);
|
|
403
403
|
const launchId = hashLaunchIdentity(plan);
|
|
404
404
|
const [block, chainId] = await lifecycleStage(client, "plan.context", () => Promise.all([
|
|
405
|
-
pinnedBlock ??
|
|
405
|
+
pinnedBlock ?? readLifecyclePlanningBlock(client, plan, account, ufg !== undefined), readLifecycleChainId(client),
|
|
406
406
|
]));
|
|
407
407
|
if (chainId !== plan.chainId)
|
|
408
408
|
throw new LifecyclePlanningError("CHAIN_MISMATCH", "RPC chain differs from the committed plan");
|
|
@@ -451,11 +451,9 @@ async function planLaunchWithReads(options, client, pinnedBlock, ownedProgress,
|
|
|
451
451
|
hookDeployments.push({ marketIndex, ...(known?.deployment ?? await readPoolBoundHookDeployment({ client, plan, marketIndex }, block)) });
|
|
452
452
|
}
|
|
453
453
|
const waiting = { ...placeholder, confidence: "stateful", reason: !progress.confirmationSafe ? "Canonical confirmed progress/account nonce has not caught up with head or pending transactions; wait for the selected confirmation depth" : "Wait for canonical receipt confirmations or resolve replacement before requesting another transaction" };
|
|
454
|
-
await assertLifecycleBlock(client, block, plan.chainId);
|
|
455
454
|
return { ...planned, hookDeployments, simulation: waiting };
|
|
456
455
|
}
|
|
457
456
|
if (progress.canonical.phase === LifecyclePhase.Active || progress.canonical.phase === LifecyclePhase.Cancelled) {
|
|
458
|
-
await assertLifecycleBlock(client, block, plan.chainId);
|
|
459
457
|
return { ...planned, simulation: { ...placeholder, confidence: "stateful", executionProof: "proved", protocolFit: "proved", admitted: true, reason: "Canonical launch is terminal; no transaction remains" } };
|
|
460
458
|
}
|
|
461
459
|
// Topology is a creation policy, not a retroactive obstacle to recovery of
|
|
@@ -472,45 +470,29 @@ async function planLaunchWithReads(options, client, pinnedBlock, ownedProgress,
|
|
|
472
470
|
const computeCap = limits.transactionGasCeiling !== undefined && limits.transactionGasCeiling < limits.executionGasCeiling ? limits.transactionGasCeiling : limits.executionGasCeiling;
|
|
473
471
|
let batches = [];
|
|
474
472
|
if (mode === "staged")
|
|
475
|
-
batches =
|
|
476
|
-
: [...(prepared > 0 ? [prepared] : []), ...(prepared < plan.markets.length ? [plan.markets.length - prepared] : [])];
|
|
477
|
-
if (reviewed !== undefined && reviewed.kind === "prepare") {
|
|
478
|
-
// Canonical progress fixes the start; the exact held calldata fixes its span.
|
|
479
|
-
// Never rediscover a batch or enlarge a reviewed transaction.
|
|
480
|
-
const match = /^prepare:(\d+):(\d+)$/.exec(reviewed.id);
|
|
481
|
-
const start = Number(match?.[1]), count = Number(match?.[2]);
|
|
482
|
-
if (start !== prepared || !Number.isSafeInteger(count) || count <= 0 || start + count > plan.markets.length)
|
|
483
|
-
throw new LifecyclePlanningError("REVIEWED_TRANSACTION_MISMATCH", "Reviewed preparation differs from canonical unfinished progress");
|
|
484
|
-
batches = [...(prepared > 0 ? [prepared] : []), count, ...(start + count < plan.markets.length ? [plan.markets.length - start - count] : [])];
|
|
485
|
-
}
|
|
473
|
+
batches = packPreparationBatches(plan, prepared, computeCap);
|
|
486
474
|
let transactions = orderedTransactions(plan, mode, progress, funding.approvals, batches, ufg);
|
|
487
475
|
if (fixedFinalEnvelope !== undefined && (transactions.length !== 1 || transactions[0].kind !== "atomic")) {
|
|
488
476
|
throw new LifecyclePlanningError("INVALID_GAS_ENVELOPE", "Fixed final envelope requires one immediate atomic launch with no preceding approvals");
|
|
489
477
|
}
|
|
490
|
-
if (reviewed !== undefined) {
|
|
491
|
-
if (!matchesReviewedNextTransaction(transactions[0], reviewed))
|
|
492
|
-
throw new LifecyclePlanningError("REVIEWED_TRANSACTION_MISMATCH", "Reviewed transaction differs from the current canonical unfinished next step");
|
|
493
|
-
transactions = transactions.slice(0, 1);
|
|
494
|
-
}
|
|
495
478
|
if (metadata.reasons.length > 0) {
|
|
496
|
-
await assertLifecycleBlock(client, block, plan.chainId);
|
|
497
479
|
return { ...planned, simulation: { ...placeholder, confidence: "stateful", reason: metadata.reasons.join("; "), failureCategory: "semantic" }, transactions, totalCalldataBytes: transactions.reduce((total, transaction) => total + (transaction.data.length - 2) / 2, 0) };
|
|
498
480
|
}
|
|
499
|
-
const envelope = diagnostic ? undefined :
|
|
500
|
-
const preparedSimulation = prepareLaunchSimulation({ client, planned, limits: options.limits, fork: options.fork, signal: options.signal }, block, true, envelope, fixedFinalEnvelope?.gasPrice);
|
|
481
|
+
const envelope = diagnostic ? undefined : fixedFinalEnvelope;
|
|
482
|
+
const preparedSimulation = prepareLaunchSimulation({ client, planned, limits: options.limits, fork: options.fork, signal: options.signal }, block, true, envelope, fixedFinalEnvelope?.gasPrice, sizing);
|
|
501
483
|
try {
|
|
502
484
|
const execute = diagnostic ? preparedSimulation.measure : (candidate) => preparedSimulation(candidate, envelope);
|
|
503
485
|
let simulation = await execute(transactions);
|
|
504
486
|
assertFinalPlan(planned, options.signal);
|
|
505
487
|
if (mode === "staged") {
|
|
506
|
-
// Only
|
|
507
|
-
//
|
|
508
|
-
while (
|
|
488
|
+
// Only pressure at the native ceiling can invalidate packing. A smaller
|
|
489
|
+
// affordability/RPC/account envelope is not evidence for another wallet write.
|
|
490
|
+
while (!simulation.admitted && simulation.failedTransactionId?.startsWith("prepare:")) {
|
|
509
491
|
const failedIndex = transactions.findIndex((transaction) => transaction.id === simulation.failedTransactionId);
|
|
510
492
|
const failedTransaction = transactions[failedIndex];
|
|
511
493
|
const step = simulation.steps.find((candidate) => candidate.transactionId === simulation.failedTransactionId);
|
|
512
494
|
const count = failedTransaction?.marketCount ?? 0;
|
|
513
|
-
if (count <= 1 || step?.
|
|
495
|
+
if (count <= 1 || step?.gasUsed === undefined || step.gasLimit === undefined || step.gasLimit < limits.executionGasCeiling || step.gasUsed * 100n < limits.executionGasCeiling * 95n)
|
|
514
496
|
break;
|
|
515
497
|
let first = 0;
|
|
516
498
|
const batchIndex = batches.findIndex((batch) => { const matches = first === failedTransaction?.marketStart; first += batch; return matches; });
|
|
@@ -526,9 +508,6 @@ async function planLaunchWithReads(options, client, pinnedBlock, ownedProgress,
|
|
|
526
508
|
else if (!diagnostic && !simulation.admitted && !(simulation.capacityConstraint === "compute" && simulation.confidence === "provisional")) {
|
|
527
509
|
simulation = { ...simulation, reason: `${simulation.reason ?? "Exact atomic plan is not admitted"}; choose staged explicitly only if empty preparation can be partitioned and complete activation fits` };
|
|
528
510
|
}
|
|
529
|
-
if (reviewed !== undefined && !matchesReviewedNextTransaction(transactions[0], reviewed)) {
|
|
530
|
-
throw new LifecyclePlanningError("REVIEWED_TRANSACTION_MISMATCH", "Reviewed transaction differs from the current canonical unfinished next step");
|
|
531
|
-
}
|
|
532
511
|
return { ...planned, simulation, transactions: attachEstimates(transactions, simulation), totalCalldataBytes: transactions.reduce((total, transaction) => total + (transaction.data.length - 2) / 2, 0) };
|
|
533
512
|
}
|
|
534
513
|
finally {
|
|
@@ -556,8 +535,6 @@ async function preflightNextTransaction(options, next, simulation, reviewed) {
|
|
|
556
535
|
}, "latest"]), "submission gas estimate");
|
|
557
536
|
if (estimated <= 0n || estimated > next.gas)
|
|
558
537
|
throw new Error("Submission RPC gas estimate exceeds the exact reviewed envelope or is zero");
|
|
559
|
-
if (rpcQuantity(await lifecycleRpc(submissionClient, "eth_chainId"), "submission chain ID") !== simulation.chainId)
|
|
560
|
-
throw new Error("Active submission RPC chain changed during preflight");
|
|
561
538
|
assertPlanningActive(options.signal);
|
|
562
539
|
}
|
|
563
540
|
catch (failure) {
|
|
@@ -581,7 +558,7 @@ export async function buildNextTransaction(options) {
|
|
|
581
558
|
const reviewed = supplied === undefined ? undefined : Object.freeze({ id: supplied.id, kind: supplied.kind, chainId: supplied.chainId,
|
|
582
559
|
from: supplied.from, to: supplied.to, data: supplied.data, value: supplied.value, gas: supplied.gas, gasPrice: supplied.gasPrice, nonce: supplied.nonce });
|
|
583
560
|
const result = await withLifecycleReadClient(options.client, async (client) => {
|
|
584
|
-
const block = await
|
|
561
|
+
const block = await readLifecyclePlanningBlock(client, options.planned.plan, options.planned.account, options.planned.ufg !== undefined);
|
|
585
562
|
const limitSource = options.limits === undefined ? options.planned.limits : options.limits;
|
|
586
563
|
if (getKnownLifecycleDeployment({ chainId: options.planned.chainId, orchestrator: options.planned.plan.orchestrator }) !== undefined && typeof limitSource !== "function") {
|
|
587
564
|
await Promise.all([
|
|
@@ -590,12 +567,11 @@ export async function buildNextTransaction(options) {
|
|
|
590
567
|
resolveLifecycleLimits({ client, account: options.planned.account, orchestrator: options.planned.plan.orchestrator, block, chainId: options.planned.chainId }, limitSource),
|
|
591
568
|
]);
|
|
592
569
|
}
|
|
593
|
-
const progress = await
|
|
570
|
+
const progress = await readLifecycleExecutionProgress({ ...options, client }, block);
|
|
594
571
|
if (progress.canonical.phase === LifecyclePhase.Active || progress.canonical.phase === LifecyclePhase.Cancelled) {
|
|
595
572
|
// Only the confirmed canonical terminal phase is terminal; a head-only
|
|
596
573
|
// Active observation is not, and the confirmation/nonce/receipt gates below
|
|
597
574
|
// remain in force for every nonterminal case.
|
|
598
|
-
await assertLifecycleBlock(client, block, options.planned.chainId);
|
|
599
575
|
return undefined;
|
|
600
576
|
}
|
|
601
577
|
if (!progress.confirmationSafe)
|
|
@@ -621,16 +597,48 @@ export async function buildNextTransaction(options) {
|
|
|
621
597
|
throw new LifecyclePlanningError("MISSING_GAS_PROOF", "Cancellation omitted its exact simulation admission", simulation);
|
|
622
598
|
return preflightNextTransaction(options, next, simulation, reviewed);
|
|
623
599
|
}
|
|
624
|
-
const
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
if (
|
|
600
|
+
const { planned } = options;
|
|
601
|
+
assertFinalPlan(planned, options.signal);
|
|
602
|
+
const funding = progress.canonical.phase === LifecyclePhase.None
|
|
603
|
+
? await readFunding(client, planned.plan, block, planned.account, planned.ufg !== undefined)
|
|
604
|
+
: { prerequisites: [], approvals: [] };
|
|
605
|
+
const prepared = progress.canonical.preparedMarkets;
|
|
606
|
+
let batches = [];
|
|
607
|
+
if (planned.mode === "staged") {
|
|
608
|
+
let suffixStart = prepared;
|
|
609
|
+
const prefix = prepared > 0 ? [prepared] : [];
|
|
610
|
+
if (reviewed?.kind === "prepare") {
|
|
611
|
+
const span = /^prepare:(\d+):(\d+)$/.exec(reviewed.id);
|
|
612
|
+
const first = Number(span?.[1]), count = Number(span?.[2]);
|
|
613
|
+
if (first !== prepared || !Number.isSafeInteger(count) || count <= 0 || first + count > planned.plan.markets.length)
|
|
614
|
+
throw new LifecyclePlanningError("REVIEWED_TRANSACTION_MISMATCH", "Reviewed preparation does not start at the canonical unfinished market");
|
|
615
|
+
prefix.push(count);
|
|
616
|
+
suffixStart += count;
|
|
617
|
+
}
|
|
618
|
+
const suffix = planned.transactions
|
|
619
|
+
.filter((transaction) => transaction.kind === "prepare" && transaction.marketStart + transaction.marketCount > suffixStart)
|
|
620
|
+
.map((transaction) => transaction.marketStart + transaction.marketCount - Math.max(transaction.marketStart, suffixStart));
|
|
621
|
+
if (suffix.length === 0 && suffixStart < planned.plan.markets.length) {
|
|
622
|
+
const { limits } = await resolveLifecycleLimits({ client, account: planned.account, orchestrator: planned.plan.orchestrator, block, chainId: planned.chainId }, limitSource);
|
|
623
|
+
const cap = limits.transactionGasCeiling !== undefined && limits.transactionGasCeiling < limits.executionGasCeiling ? limits.transactionGasCeiling : limits.executionGasCeiling;
|
|
624
|
+
suffix.push(...packPreparationBatches(planned.plan, suffixStart, cap).slice(suffixStart > 0 ? 1 : 0));
|
|
625
|
+
}
|
|
626
|
+
batches = [...prefix, ...suffix];
|
|
627
|
+
}
|
|
628
|
+
const candidate = orderedTransactions(planned.plan, planned.mode, progress, funding.approvals, batches, planned.ufg)[0];
|
|
629
|
+
if (candidate === undefined)
|
|
632
630
|
return undefined;
|
|
633
|
-
|
|
631
|
+
if (reviewed !== undefined && !matchesReviewedNextTransaction(candidate, reviewed))
|
|
632
|
+
throw new LifecyclePlanningError("REVIEWED_TRANSACTION_MISMATCH", "Reviewed transaction differs from the current canonical unfinished next step");
|
|
633
|
+
const observed = planned.simulation.executionProof === "proved" ? planned.simulation.steps.find((step) => step.transactionId === candidate.id && step.success) : undefined;
|
|
634
|
+
const compute = observed?.computeGasRequired;
|
|
635
|
+
const sizing = reviewed !== undefined || compute === undefined ? undefined : new Map([[candidate.id, compute]]);
|
|
636
|
+
const simulation = await simulateLaunchTransactions({ client, planned, limits: limitSource, fork: options.fork, signal: options.signal }, [candidate], block, reviewed, sizing);
|
|
637
|
+
assertFinalPlan(planned, options.signal);
|
|
638
|
+
if (!simulation.admitted)
|
|
639
|
+
throw new LifecyclePlanningError("PLAN_NOT_ADMITTED", simulation.reason ?? "Next exact launch transaction is not admitted", simulation);
|
|
640
|
+
const next = attachEstimates([candidate], simulation)[0];
|
|
641
|
+
return preflightNextTransaction(options, next, simulation, reviewed);
|
|
634
642
|
});
|
|
635
643
|
assertFinalPlan(options.planned, options.signal);
|
|
636
644
|
if (result !== undefined)
|