@black-market/sdk 0.6.4 → 0.7.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 +28 -4
- package/dist/addresses.d.ts +1 -1
- package/dist/addresses.js +4 -4
- package/dist/launch.d.ts +61 -0
- package/dist/launch.d.ts.map +1 -1
- package/dist/launch.js +171 -6
- package/dist/launch.js.map +1 -1
- package/dist/lifecycle/abi.d.ts +1999 -253
- package/dist/lifecycle/abi.d.ts.map +1 -1
- package/dist/lifecycle/abi.js +43 -14
- package/dist/lifecycle/abi.js.map +1 -1
- package/dist/lifecycle/allocations.d.ts +11 -0
- package/dist/lifecycle/allocations.d.ts.map +1 -0
- package/dist/lifecycle/allocations.js +36 -0
- package/dist/lifecycle/allocations.js.map +1 -0
- package/dist/lifecycle/calibration.d.ts +19 -0
- package/dist/lifecycle/calibration.d.ts.map +1 -0
- package/dist/lifecycle/calibration.js +65 -0
- package/dist/lifecycle/calibration.js.map +1 -0
- package/dist/lifecycle/deployments/fixed-fee-pool-hook-v1-20261008.d.ts +11 -0
- package/dist/lifecycle/deployments/fixed-fee-pool-hook-v1-20261008.d.ts.map +1 -0
- package/dist/lifecycle/deployments/fixed-fee-pool-hook-v1-20261008.js +10 -0
- package/dist/lifecycle/deployments/fixed-fee-pool-hook-v1-20261008.js.map +1 -0
- package/dist/lifecycle/index.d.ts +4 -2
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/index.js +4 -2
- package/dist/lifecycle/index.js.map +1 -1
- package/dist/lifecycle/markets.d.ts +170 -6
- package/dist/lifecycle/markets.d.ts.map +1 -1
- package/dist/lifecycle/markets.js +164 -37
- package/dist/lifecycle/markets.js.map +1 -1
- package/dist/lifecycle/planner.d.ts +7 -14
- package/dist/lifecycle/planner.d.ts.map +1 -1
- package/dist/lifecycle/planner.js +354 -400
- package/dist/lifecycle/planner.js.map +1 -1
- package/dist/lifecycle/presets.d.ts +62 -0
- package/dist/lifecycle/presets.d.ts.map +1 -0
- package/dist/lifecycle/presets.js +195 -0
- package/dist/lifecycle/presets.js.map +1 -0
- package/dist/lifecycle/progress.d.ts +5 -1
- package/dist/lifecycle/progress.d.ts.map +1 -1
- package/dist/lifecycle/progress.js +212 -53
- package/dist/lifecycle/progress.js.map +1 -1
- package/dist/lifecycle/rpc.d.ts +13 -2
- package/dist/lifecycle/rpc.d.ts.map +1 -1
- package/dist/lifecycle/rpc.js +92 -8
- package/dist/lifecycle/rpc.js.map +1 -1
- package/dist/lifecycle/simulation.d.ts +14 -4
- package/dist/lifecycle/simulation.d.ts.map +1 -1
- package/dist/lifecycle/simulation.js +383 -116
- package/dist/lifecycle/simulation.js.map +1 -1
- package/dist/lifecycle/transactions.d.ts +6 -0
- package/dist/lifecycle/transactions.d.ts.map +1 -0
- package/dist/lifecycle/transactions.js +40 -0
- package/dist/lifecycle/transactions.js.map +1 -0
- package/dist/lifecycle/types.d.ts +39 -2
- package/dist/lifecycle/types.d.ts.map +1 -1
- package/dist/lifecycle/types.js.map +1 -1
- package/docs/launch-lifecycle.md +228 -54
- package/examples/README.md +108 -18
- package/examples/generate-lifecycle-commitment-fixture.mjs +3 -1
- package/examples/launch-example-support.mjs +1 -1
- package/examples/launch-example.mjs +73 -26
- package/examples/launch-lifecycle-smoke.mjs +13 -11
- package/examples/launch.mjs +12 -11
- package/examples/quickstart.mjs +1 -1
- package/package.json +1 -1
- package/src/addresses.ts +4 -4
- package/src/launch.ts +213 -6
- package/src/lifecycle/abi.ts +43 -14
- package/src/lifecycle/allocations.ts +35 -0
- package/src/lifecycle/calibration.ts +59 -0
- package/src/lifecycle/deployments/fixed-fee-pool-hook-v1-20261008.ts +11 -0
- package/src/lifecycle/index.ts +4 -2
- package/src/lifecycle/markets.ts +169 -35
- package/src/lifecycle/planner.ts +326 -330
- package/src/lifecycle/presets.ts +243 -0
- package/src/lifecycle/progress.ts +193 -50
- package/src/lifecycle/rpc.ts +97 -9
- package/src/lifecycle/simulation.ts +370 -109
- package/src/lifecycle/transactions.ts +34 -0
- package/src/lifecycle/types.ts +35 -3
|
@@ -1,15 +1,69 @@
|
|
|
1
1
|
import { concatHex, decodeFunctionData, encodeAbiParameters, encodeFunctionData, keccak256, stringToHex, toHex, zeroAddress, zeroHash } from "viem";
|
|
2
|
-
import { launchLifecycleAbi, lifecycleAdapterAbi, lifecycleDirectoryAbi, lifecycleErc20Abi, lifecycleOracleFactoryAbi, lifecycleRegistryAbi, poolFeeCollectorFactoryV1Abi, lifecycleV4HookAbi, lifecycleV4LockerAbi, poolHookDeployerV1Abi, sharedHookDeployerV1Abi, fixedFeePoolHookV1Abi, poolMarketAdapterV1Abi, sharedMarketAdapterV1Abi } from "./abi.js";
|
|
3
|
-
import { decodePoolBoundV4LifecycleMarketConfig, encodePoolBoundHookParameters, hasLifecycleV4HookPermissions,
|
|
4
|
-
import { hashLaunchIdentity, hashLaunchPlan, hashLaunchBounds, hashLaunchDependencies, hashLifecycleProfile, launchProgressV1Components, LifecycleMode, LifecyclePhase, LifecycleVenue, LIFECYCLE_REQUIRED_CAPABILITIES, LIFECYCLE_ERC404_CAPABILITY, LIFECYCLE_MULTI_POSITION_CAPABILITY } from "./schema.js";
|
|
5
|
-
import { assertLifecycleBlock, lifecyclePinnedRpc, lifecycleRpc, lifecycleStage, readLifecycleBlock, readLifecycleContract, readLifecycleProfileTopology, rpcHex, rpcObject, rpcQuantity, withLifecycleReadClient } from "./rpc.js";
|
|
2
|
+
import { launchLifecycleAbi, lifecycleAdapterAbi, lifecycleDirectoryAbi, lifecycleErc20Abi, lifecycleOracleFactoryAbi, lifecycleRegistryAbi, poolFeeCollectorFactoryV1Abi, poolFeeCollectorFactoryConfigV6Abi, lifecycleV4HookAbi, lifecycleV4LockerAbi, poolHookDeployerV1Abi, poolHookDeployerConfigV6Abi, sharedHookDeployerV1Abi, fixedFeePoolHookV1Abi, fixedFeePoolHookConfigV6Abi, poolMarketAdapterV1Abi, sharedMarketAdapterV1Abi } from "./abi.js";
|
|
3
|
+
import { buildPoolBoundHookParameters, decodePoolBoundV4LifecycleMarketConfig, encodePoolBoundHookParameters, hasLifecycleV4HookPermissions, isPoolBoundV4ConfigVersion, poolBoundHookInitCodeHash, predictPoolBoundHookAddress, validateV4LifecycleMarket, V4_LIFECYCLE_CONFIG_SCHEMA, V4_POOL_BOUND_LIFECYCLE_CONFIG_SCHEMA_V5, V4_POOL_BOUND_LIFECYCLE_CONFIG_SCHEMA_V6 } from "./markets.js";
|
|
4
|
+
import { hashLaunchIdentity, hashLaunchPlan, hashLaunchBounds, hashLaunchDependencies, hashLifecycleProfile, lifecycleTokenComponents, launchProgressV1Components, LifecycleMode, LifecyclePhase, LifecycleVenue, LIFECYCLE_REQUIRED_CAPABILITIES, LIFECYCLE_ERC404_CAPABILITY, LIFECYCLE_MULTI_POSITION_CAPABILITY } from "./schema.js";
|
|
5
|
+
import { assertLifecycleBlock, assertLifecycleReadClientOpen, lifecycleFailureReason, lifecyclePinnedRpc, lifecycleRpc, lifecycleStage, readLifecycleBlock, readLifecycleChainId, readLifecycleContract, readLifecycleProfileTopology, rpcHex, rpcObject, rpcQuantity, withLifecycleReadClient } from "./rpc.js";
|
|
6
6
|
import { LifecyclePlanningError } from "./types.js";
|
|
7
|
+
import { deriveKnownPoolBoundHookDeployment } from "./calibration.js";
|
|
8
|
+
import { getKnownLifecycleDeployment, getKnownLifecycleProfile } from "./presets.js";
|
|
7
9
|
export const ABYSS_LIFECYCLE_CONFIG_SCHEMA = keccak256(stringToHex("(uint8,uint24,bytes32,uint160,(int24,int24,uint128,uint256)[])"));
|
|
10
|
+
const lifecycleTokenPredictionAbi = [{
|
|
11
|
+
type: "function", name: "predictToken", stateMutability: "view",
|
|
12
|
+
inputs: [{ name: "launchId", type: "bytes32" }, { name: "config", type: "tuple", components: lifecycleTokenComponents }],
|
|
13
|
+
outputs: [{ type: "address" }],
|
|
14
|
+
}];
|
|
15
|
+
/** The known immutable factory accepts only launch identity and token config,
|
|
16
|
+
* avoiding full-plan prediction reads after salts or buy minima change. */
|
|
17
|
+
export async function readLifecycleTokenAtBlock(client, plan, block) {
|
|
18
|
+
const deployment = getKnownLifecycleDeployment({ chainId: plan.chainId, orchestrator: plan.orchestrator });
|
|
19
|
+
return deployment === undefined
|
|
20
|
+
? readLifecycleContract(client, plan.orchestrator, launchLifecycleAbi, "predictToken", [plan], block)
|
|
21
|
+
: readLifecycleContract(client, deployment.tokenFactory, lifecycleTokenPredictionAbi, "predictToken", [hashLaunchIdentity(plan), plan.token], block);
|
|
22
|
+
}
|
|
8
23
|
export async function predictLifecycleToken(options) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
24
|
+
return withLifecycleReadClient(options.client, async (client) => {
|
|
25
|
+
const block = await readLifecycleBlock(client);
|
|
26
|
+
const token = await readLifecycleTokenAtBlock(client, options.plan, block);
|
|
27
|
+
await assertLifecycleBlock(client, block, options.plan.chainId);
|
|
28
|
+
return token;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
const certifiedProfilesByInvocation = new WeakMap();
|
|
32
|
+
const boundDeploymentsByInvocation = new WeakMap();
|
|
33
|
+
function assertDerivedReadClientOpen(client) {
|
|
34
|
+
try {
|
|
35
|
+
return assertLifecycleReadClientOpen(client);
|
|
36
|
+
}
|
|
37
|
+
catch (failure) {
|
|
38
|
+
certifiedProfilesByInvocation.delete(client);
|
|
39
|
+
boundDeploymentsByInvocation.delete(client);
|
|
40
|
+
throw failure;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function lifecycleProfileCertificateKey(orchestrator, registry, id, registration, adapter, topology, envelope, block, chainId) {
|
|
44
|
+
const g = envelope?.graph;
|
|
45
|
+
const b = envelope?.bounds;
|
|
46
|
+
// Fixed ABI field order preserves every scalar, including flags delegated to
|
|
47
|
+
// registry eligibility. Keys never rehash runtime, chunk or creation bytes.
|
|
48
|
+
return JSON.stringify([
|
|
49
|
+
block.number.toString(), block.hash.toLowerCase(), chainId.toString(), orchestrator.toLowerCase(), registry.toLowerCase(), id.toLowerCase(),
|
|
50
|
+
[registration.adapterId.toLowerCase(), registration.configSchema.toLowerCase(), registration.dependencyDigest.toLowerCase(),
|
|
51
|
+
registration.venue.toLowerCase(), registration.factory.toLowerCase(), registration.hook.toLowerCase(), registration.capabilities.toString(), registration.enabled],
|
|
52
|
+
[adapter.implementation.toLowerCase(), adapter.codeHash.toLowerCase(), adapter.capabilities.toString(), adapter.configVersion, adapter.enabled],
|
|
53
|
+
[topology.hookTopology, topology.configVersion, topology.hookDeployer.toLowerCase(), topology.hookCreationCodeHash.toLowerCase()],
|
|
54
|
+
envelope === undefined ? null : [
|
|
55
|
+
envelope.artifactDigest.toLowerCase(), envelope.reviewManifestDigest.toLowerCase(), envelope.configBoundsDigest.toLowerCase(), envelope.termsDigest.toLowerCase(),
|
|
56
|
+
envelope.topology, envelope.configVersion, envelope.economicVersion, envelope.capabilities.toString(), envelope.flags.toString(),
|
|
57
|
+
envelope.callbackFlags, envelope.callbackMask, envelope.protocolTreasury.toLowerCase(), envelope.protocolFeeDenominator,
|
|
58
|
+
envelope.beneficiary.toLowerCase(), envelope.maximumDeveloperFeeBps,
|
|
59
|
+
[b.minimumTickSpacing, b.maximumTickSpacing, b.maximumPositions, b.maximumOracleCardinality, b.feeModeFlags],
|
|
60
|
+
[g.manager.toLowerCase(), g.hookRoot.toLowerCase(), g.oracleFactory.toLowerCase(), g.locker.toLowerCase(), g.collectorFactory.toLowerCase(),
|
|
61
|
+
g.collectorDeployer.toLowerCase(), g.hookDeployer.toLowerCase(), g.coreCodeHash.toLowerCase(), g.managerCodeHash.toLowerCase(),
|
|
62
|
+
g.hookRuntimeCodeHash.toLowerCase(), g.oracleFactoryCodeHash.toLowerCase(), g.lockerCodeHash.toLowerCase(), g.collectorFactoryCodeHash.toLowerCase(),
|
|
63
|
+
g.collectorDeployerCodeHash.toLowerCase(), g.hookDeployerCodeHash.toLowerCase(), g.hookCreationCodeHash.toLowerCase(),
|
|
64
|
+
g.codeChunk0.toLowerCase(), g.codeChunk0Hash.toLowerCase(), g.codeChunk1.toLowerCase(), g.codeChunk1Hash.toLowerCase(), g.sharedHookSalt.toLowerCase()],
|
|
65
|
+
],
|
|
66
|
+
]);
|
|
13
67
|
}
|
|
14
68
|
async function readDependencyCode(client, address, block) {
|
|
15
69
|
const code = rpcHex(await lifecyclePinnedRpc(client, "eth_getCode", [address, toHex(block.number)], block), "lifecycle dependency code");
|
|
@@ -17,13 +71,39 @@ async function readDependencyCode(client, address, block) {
|
|
|
17
71
|
throw new LifecyclePlanningError("PROFILE_DEPENDENCY", `Lifecycle dependency ${address} has no deployed code`);
|
|
18
72
|
return code;
|
|
19
73
|
}
|
|
20
|
-
async function certifyLifecycleProfile(client, orchestrator, registry, id, registration, adapter, topology, envelope, block, chainId) {
|
|
74
|
+
async function certifyLifecycleProfile(client, orchestrator, registry, id, registration, adapter, topology, envelope, block, chainId, eligibility) {
|
|
75
|
+
if (!assertDerivedReadClientOpen(client))
|
|
76
|
+
return certifyLifecycleProfileFresh(client, orchestrator, registry, id, registration, adapter, topology, envelope, block, chainId, eligibility);
|
|
77
|
+
const key = lifecycleProfileCertificateKey(orchestrator, registry, id, registration, adapter, topology, envelope, block, chainId);
|
|
78
|
+
let certificates = certifiedProfilesByInvocation.get(client);
|
|
79
|
+
if (certificates === undefined) {
|
|
80
|
+
certificates = new Map();
|
|
81
|
+
certifiedProfilesByInvocation.set(client, certificates);
|
|
82
|
+
}
|
|
83
|
+
let pending = certificates.get(key);
|
|
84
|
+
if (pending === undefined) {
|
|
85
|
+
pending = certifyLifecycleProfileFresh(client, orchestrator, registry, id, registration, adapter, topology, envelope, block, chainId, eligibility);
|
|
86
|
+
certificates.set(key, pending);
|
|
87
|
+
const owned = pending;
|
|
88
|
+
void pending.catch(() => { if (certificates.get(key) === owned)
|
|
89
|
+
certificates.delete(key); });
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
return await pending;
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
assertDerivedReadClientOpen(client);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async function certifyLifecycleProfileFresh(client, orchestrator, registry, id, registration, adapter, topology, envelope, block, chainId, eligibility) {
|
|
21
99
|
const shared = registration.configSchema.toLowerCase() === V4_LIFECYCLE_CONFIG_SCHEMA.toLowerCase();
|
|
22
|
-
const
|
|
100
|
+
const boundVersion = registration.configSchema.toLowerCase() === V4_POOL_BOUND_LIFECYCLE_CONFIG_SCHEMA_V5.toLowerCase() ? 5
|
|
101
|
+
: registration.configSchema.toLowerCase() === V4_POOL_BOUND_LIFECYCLE_CONFIG_SCHEMA_V6.toLowerCase() ? 6 : undefined;
|
|
102
|
+
const bound = boundVersion !== undefined;
|
|
23
103
|
const abyss = registration.configSchema.toLowerCase() === ABYSS_LIFECYCLE_CONFIG_SCHEMA.toLowerCase();
|
|
24
104
|
if (!shared && !bound && !abyss)
|
|
25
105
|
throw new LifecyclePlanningError("UNSUPPORTED_SCHEMA", `Unsupported lifecycle config schema ${registration.configSchema}`);
|
|
26
|
-
const version = shared ? 4 :
|
|
106
|
+
const version = shared ? 4 : boundVersion ?? 1;
|
|
27
107
|
if (adapter.configVersion !== version || topology.configVersion !== version || topology.hookTopology !== (shared ? 1 : bound ? 2 : 0))
|
|
28
108
|
throw new LifecyclePlanningError("UNCERTIFIED_TOPOLOGY", "Registry topology/version differs from the supported config schema");
|
|
29
109
|
if (abyss) {
|
|
@@ -36,10 +116,12 @@ async function certifyLifecycleProfile(client, orchestrator, registry, id, regis
|
|
|
36
116
|
const known = [0, 1, 2, 3].some((variant) => keccak256(encodeAbiParameters([{ type: "bytes32" }, { type: "uint256" }, { type: "address" }, { type: "uint8" }], [domain, chainId, factory, variant])).toLowerCase() === id.toLowerCase());
|
|
37
117
|
if (!known || registration.venue.toLowerCase() !== factory.toLowerCase() || registration.hook !== zeroAddress)
|
|
38
118
|
throw new LifecyclePlanningError("PROFILE_DEPENDENCY", "Abyss profile is not the exact canonical factory/variant/schema binding");
|
|
39
|
-
|
|
119
|
+
if (eligibility !== undefined && "failure" in eligibility)
|
|
120
|
+
throw eligibility.failure;
|
|
121
|
+
const eligible = eligibility?.eligible ?? await readLifecycleContract(client, registry, lifecycleRegistryAbi, "requireEligible", [registration.adapterId, id, version, LIFECYCLE_REQUIRED_CAPABILITIES], block);
|
|
40
122
|
if (eligible.toLowerCase() !== adapter.implementation.toLowerCase())
|
|
41
123
|
throw new LifecyclePlanningError("INELIGIBLE_PROFILE", "Registry refuses this profile for pending source admission");
|
|
42
|
-
return { topology };
|
|
124
|
+
return Object.freeze({ topology: Object.freeze({ ...topology }) });
|
|
43
125
|
}
|
|
44
126
|
if (envelope === undefined || envelope.topology !== topology.hookTopology || envelope.configVersion !== version || envelope.economicVersion !== 3 ||
|
|
45
127
|
envelope.flags !== 0n || envelope.callbackFlags !== 0x1afc || envelope.callbackMask !== 0x3fff ||
|
|
@@ -61,7 +143,7 @@ async function certifyLifecycleProfile(client, orchestrator, registry, id, regis
|
|
|
61
143
|
if (registration.factory !== zeroAddress || registration.venue.toLowerCase() !== g.manager.toLowerCase() || registration.hook.toLowerCase() !== g.hookRoot.toLowerCase() ||
|
|
62
144
|
topology.hookDeployer.toLowerCase() !== g.hookDeployer.toLowerCase() || topology.hookCreationCodeHash.toLowerCase() !== g.hookCreationCodeHash.toLowerCase())
|
|
63
145
|
throw new LifecyclePlanningError("PROFILE_DEPENDENCY", "Reviewed venue/topology differs from the frozen envelope graph");
|
|
64
|
-
const deployerAbi = shared ? sharedHookDeployerV1Abi : poolHookDeployerV1Abi;
|
|
146
|
+
const deployerAbi = shared ? sharedHookDeployerV1Abi : boundVersion === 5 ? poolHookDeployerV1Abi : poolHookDeployerConfigV6Abi;
|
|
65
147
|
const codeBindings = [[orchestrator, g.coreCodeHash], [g.manager, g.managerCodeHash], [g.oracleFactory, g.oracleFactoryCodeHash],
|
|
66
148
|
[g.locker, g.lockerCodeHash], [g.collectorFactory, g.collectorFactoryCodeHash], [g.collectorDeployer, g.collectorDeployerCodeHash],
|
|
67
149
|
[g.hookDeployer, g.hookDeployerCodeHash]];
|
|
@@ -118,7 +200,7 @@ async function certifyLifecycleProfile(client, orchestrator, registry, id, regis
|
|
|
118
200
|
keccak256(chunk0Code).toLowerCase() !== g.codeChunk0Hash.toLowerCase() || (chunk1 === zeroAddress ? zeroHash : keccak256(chunk1Code)).toLowerCase() !== g.codeChunk1Hash.toLowerCase())
|
|
119
201
|
throw new LifecyclePlanningError("PROFILE_DEPENDENCY", "Reviewed deployer chunks differ from exact immutable STOP-prefixed bytecode evidence");
|
|
120
202
|
const creationCode = concatHex([`0x${chunk0Code.slice(4)}`, chunk1 === zeroAddress ? "0x" : `0x${chunk1Code.slice(4)}`]);
|
|
121
|
-
if ((creationCode.length - 2) / 2 + (shared ? 96 : 576) > 49152 || creationCodeHash === zeroHash ||
|
|
203
|
+
if ((creationCode.length - 2) / 2 + (shared ? 96 : boundVersion === 5 ? 576 : 640) > 49152 || creationCodeHash === zeroHash ||
|
|
122
204
|
creationCodeHash.toLowerCase() !== g.hookCreationCodeHash.toLowerCase() || keccak256(creationCode).toLowerCase() !== creationCodeHash.toLowerCase() ||
|
|
123
205
|
hashLaunchDependencies({ chainId, core: orchestrator, registry, registrar: adapter.implementation, graph: g }).toLowerCase() !== registration.dependencyDigest.toLowerCase())
|
|
124
206
|
throw new LifecyclePlanningError("PROFILE_DEPENDENCY", "Reviewed creation artifact, constructor bound or graph digest differs from admission");
|
|
@@ -140,7 +222,7 @@ async function certifyLifecycleProfile(client, orchestrator, registry, id, regis
|
|
|
140
222
|
throw new LifecyclePlanningError("PROFILE_DEPENDENCY", "Bound topology cannot reuse a shared root");
|
|
141
223
|
if ([zeroAddress, orchestrator, registry, adapter.implementation, g.manager, g.hookRoot, g.locker, g.collectorFactory, g.collectorDeployer, g.hookDeployer].some((address) => address.toLowerCase() === envelope.beneficiary.toLowerCase()))
|
|
142
224
|
throw new LifecyclePlanningError("PROFILE_TERMS", "Stable author identity is an excluded reviewed graph destination");
|
|
143
|
-
return { topology, boundGraph: bound ? { manager: g.manager, oracleFactory: g.oracleFactory, locker: g.locker, collectorFactory: g.collectorFactory, deployer: g.hookDeployer, creationCode } : undefined };
|
|
225
|
+
return Object.freeze({ topology: Object.freeze({ ...topology }), boundGraph: bound ? Object.freeze({ manager: g.manager, oracleFactory: g.oracleFactory, locker: g.locker, collectorFactory: g.collectorFactory, deployer: g.hookDeployer, creationCode }) : undefined });
|
|
144
226
|
}
|
|
145
227
|
/** Validate the market-selected registered oracle against the certified factory at the pinned block. */
|
|
146
228
|
export async function validateV4LifecycleOracle(options) {
|
|
@@ -172,7 +254,7 @@ export async function readLifecycleProfiles(options, pinnedBlock) {
|
|
|
172
254
|
const readProfile = async (id) => {
|
|
173
255
|
const registration = await readLifecycleContract(client, registry, lifecycleRegistryAbi, "profile", [id], block);
|
|
174
256
|
const schema = registration.configSchema.toLowerCase();
|
|
175
|
-
const venueKind = schema === V4_LIFECYCLE_CONFIG_SCHEMA.toLowerCase() || schema ===
|
|
257
|
+
const venueKind = schema === V4_LIFECYCLE_CONFIG_SCHEMA.toLowerCase() || schema === V4_POOL_BOUND_LIFECYCLE_CONFIG_SCHEMA_V5.toLowerCase() || schema === V4_POOL_BOUND_LIFECYCLE_CONFIG_SCHEMA_V6.toLowerCase() ? "uniswap-v4" : schema === ABYSS_LIFECYCLE_CONFIG_SCHEMA.toLowerCase() ? "abyss" : "unknown";
|
|
176
258
|
const [adapter, certification] = await Promise.all([
|
|
177
259
|
readLifecycleContract(client, registry, lifecycleRegistryAbi, "adapter", [registration.adapterId], block),
|
|
178
260
|
venueKind === "unknown" ? undefined : Promise.all([
|
|
@@ -182,6 +264,10 @@ export async function readLifecycleProfiles(options, pinnedBlock) {
|
|
|
182
264
|
readLifecycleContract(client, registry, lifecycleRegistryAbi, "developerTerms", [id], block),
|
|
183
265
|
readProtocolMaximumDeveloperFeeBps(),
|
|
184
266
|
]) : undefined,
|
|
267
|
+
// The exact Abyss arguments follow from registration, independently
|
|
268
|
+
// of adapter/topology responses. Handle refusal immediately, but
|
|
269
|
+
// keep its original place after canonical/schema/topology checks.
|
|
270
|
+
venueKind === "abyss" ? chainIdPromise.then(() => readLifecycleContract(client, registry, lifecycleRegistryAbi, "requireEligible", [registration.adapterId, id, 1, LIFECYCLE_REQUIRED_CAPABILITIES], block)).then((eligible) => ({ eligible }), (failure) => ({ failure })) : undefined,
|
|
185
271
|
]).then((values) => ({ values }), (failure) => ({ failure })),
|
|
186
272
|
]);
|
|
187
273
|
const chainId = await chainIdPromise;
|
|
@@ -196,7 +282,7 @@ export async function readLifecycleProfiles(options, pinnedBlock) {
|
|
|
196
282
|
throw new LifecyclePlanningError("UNSUPPORTED_SCHEMA", `Unsupported lifecycle config schema ${registration.configSchema}`);
|
|
197
283
|
if ("failure" in certification)
|
|
198
284
|
throw certification.failure;
|
|
199
|
-
const [recordedTopology, reviewed] = certification.values;
|
|
285
|
+
const [recordedTopology, reviewed, eligibility] = certification.values;
|
|
200
286
|
topology = recordedTopology;
|
|
201
287
|
if (reviewed !== undefined) {
|
|
202
288
|
envelope = reviewed[0];
|
|
@@ -208,13 +294,13 @@ export async function readLifecycleProfiles(options, pinnedBlock) {
|
|
|
208
294
|
maximumDeveloperFeeBps > reviewed[2])
|
|
209
295
|
throw new LifecyclePlanningError("PROFILE_TERMS", "Registry developer terms differ from the frozen reviewed envelope");
|
|
210
296
|
}
|
|
211
|
-
await certifyLifecycleProfile(client, options.orchestrator, registry, id, registration, adapter, topology, envelope, block, chainId);
|
|
297
|
+
await certifyLifecycleProfile(client, options.orchestrator, registry, id, registration, adapter, topology, envelope, block, chainId, eligibility);
|
|
212
298
|
if (developerTerms !== undefined && !developerTerms.enabled)
|
|
213
299
|
throw new LifecyclePlanningError("INELIGIBLE_PROFILE", "Registry refuses this profile for pending source admission");
|
|
214
300
|
admitted = true;
|
|
215
301
|
}
|
|
216
302
|
catch (failure) {
|
|
217
|
-
reason =
|
|
303
|
+
reason = lifecycleFailureReason(failure);
|
|
218
304
|
}
|
|
219
305
|
return { id, registration, adapter, topology, envelope, developerTerms, protocolMaximumDeveloperFeeBps: venueKind === "uniswap-v4" ? protocolMaximumDeveloperFeeBps : undefined, admitted, reason, venueKind };
|
|
220
306
|
};
|
|
@@ -235,14 +321,67 @@ export async function readLifecycleProfiles(options, pinnedBlock) {
|
|
|
235
321
|
return profiles;
|
|
236
322
|
}));
|
|
237
323
|
}
|
|
238
|
-
async function readPoolBoundHookDeploymentDetails(options, block) {
|
|
324
|
+
async function readPoolBoundHookDeploymentDetails(options, block, commitment) {
|
|
239
325
|
const { client, plan, marketIndex } = options;
|
|
240
326
|
const market = plan.markets[marketIndex];
|
|
241
|
-
if (market === undefined || market.configVersion
|
|
242
|
-
throw new LifecyclePlanningError("INVALID_BOUND_MARKET", "Market index must select
|
|
243
|
-
|
|
327
|
+
if (market === undefined || !isPoolBoundV4ConfigVersion(market.configVersion))
|
|
328
|
+
throw new LifecyclePlanningError("INVALID_BOUND_MARKET", "Market index must select explicit pool-bound config version 5 or 6");
|
|
329
|
+
let chainId;
|
|
330
|
+
try {
|
|
331
|
+
chainId = rpcQuantity(await lifecycleRpc(client, "eth_chainId"), "chain ID");
|
|
332
|
+
}
|
|
333
|
+
finally {
|
|
334
|
+
assertDerivedReadClientOpen(client);
|
|
335
|
+
}
|
|
336
|
+
if (hashLaunchPlan(plan) !== commitment)
|
|
337
|
+
throw new LifecyclePlanningError("PLAN_MUTATED", "Economic plan changed while observing the hook deployment chain");
|
|
244
338
|
if (chainId !== plan.chainId)
|
|
245
339
|
throw new LifecyclePlanningError("CHAIN_MISMATCH", "RPC chain differs from the committed plan");
|
|
340
|
+
if (getKnownLifecycleProfile({ chainId, orchestrator: plan.orchestrator, profileId: market.profileId }) !== undefined) {
|
|
341
|
+
const token = await readLifecycleTokenAtBlock(client, plan, block);
|
|
342
|
+
const details = deriveKnownPoolBoundHookDeployment({ plan, marketIndex, token });
|
|
343
|
+
if (details === undefined)
|
|
344
|
+
throw new LifecyclePlanningError("INVALID_BOUND_MARKET", "Known profile has no bound-hook construction metadata");
|
|
345
|
+
if (hashLaunchPlan(plan) !== commitment)
|
|
346
|
+
throw new LifecyclePlanningError("PLAN_MUTATED", "Economic plan changed while deriving hook deployment metadata");
|
|
347
|
+
assertDerivedReadClientOpen(client);
|
|
348
|
+
return Object.freeze({ deployment: Object.freeze(details.deployment), parameters: Object.freeze(details.parameters), configVersion: details.configVersion });
|
|
349
|
+
}
|
|
350
|
+
if (!assertDerivedReadClientOpen(client)) {
|
|
351
|
+
const details = await readPoolBoundHookDeploymentDetailsFresh(options, block, chainId);
|
|
352
|
+
if (hashLaunchPlan(plan) !== commitment)
|
|
353
|
+
throw new LifecyclePlanningError("PLAN_MUTATED", "Economic plan changed while reading hook deployment metadata");
|
|
354
|
+
return details;
|
|
355
|
+
}
|
|
356
|
+
const key = JSON.stringify([block.number.toString(), block.hash.toLowerCase(), chainId.toString(), commitment.toLowerCase(), marketIndex]);
|
|
357
|
+
let deployments = boundDeploymentsByInvocation.get(client);
|
|
358
|
+
if (deployments === undefined) {
|
|
359
|
+
deployments = new Map();
|
|
360
|
+
boundDeploymentsByInvocation.set(client, deployments);
|
|
361
|
+
}
|
|
362
|
+
let pending = deployments.get(key);
|
|
363
|
+
if (pending === undefined) {
|
|
364
|
+
pending = readPoolBoundHookDeploymentDetailsFresh(options, block, chainId).then((details) => {
|
|
365
|
+
if (hashLaunchPlan(plan) !== commitment)
|
|
366
|
+
throw new LifecyclePlanningError("PLAN_MUTATED", "Economic plan changed while reading hook deployment metadata");
|
|
367
|
+
assertDerivedReadClientOpen(client);
|
|
368
|
+
return details;
|
|
369
|
+
});
|
|
370
|
+
deployments.set(key, pending);
|
|
371
|
+
const owned = pending;
|
|
372
|
+
void pending.catch(() => { if (deployments.get(key) === owned)
|
|
373
|
+
deployments.delete(key); });
|
|
374
|
+
}
|
|
375
|
+
try {
|
|
376
|
+
return await pending;
|
|
377
|
+
}
|
|
378
|
+
finally {
|
|
379
|
+
assertDerivedReadClientOpen(client);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
async function readPoolBoundHookDeploymentDetailsFresh(options, block, chainId) {
|
|
383
|
+
const { client, plan, marketIndex } = options;
|
|
384
|
+
const market = plan.markets[marketIndex];
|
|
246
385
|
const registry = await readLifecycleContract(client, plan.orchestrator, launchLifecycleAbi, "registry", [], block);
|
|
247
386
|
const [authority, registration] = await Promise.all([
|
|
248
387
|
readLifecycleContract(client, registry, lifecycleRegistryAbi, "core", [], block),
|
|
@@ -263,35 +402,35 @@ async function readPoolBoundHookDeploymentDetails(options, block) {
|
|
|
263
402
|
const { boundGraph } = await certifyLifecycleProfile(client, plan.orchestrator, registry, market.profileId, registration, adapter, recorded, envelope, block, chainId);
|
|
264
403
|
if (boundGraph === undefined)
|
|
265
404
|
throw new LifecyclePlanningError("UNCERTIFIED_TOPOLOGY", "Market does not have a certified pool-bound deployer graph");
|
|
266
|
-
|
|
405
|
+
if (!isPoolBoundV4ConfigVersion(market.configVersion))
|
|
406
|
+
throw new LifecyclePlanningError("INVALID_BOUND_MARKET", "Market must select an explicit pool-bound version");
|
|
407
|
+
const config = decodePoolBoundV4LifecycleMarketConfig(market.config, market.configVersion);
|
|
267
408
|
validateV4LifecycleMarket({ market, config, token, profile: {
|
|
268
409
|
id: market.profileId, registration, adapter, topology: recorded, envelope,
|
|
269
410
|
developerTerms: { adapter: implementation, beneficiary, maximumDeveloperFeeBps, termsDigest, enabled },
|
|
270
411
|
protocolMaximumDeveloperFeeBps, venueKind: "uniswap-v4", admitted: enabled,
|
|
271
412
|
} });
|
|
272
413
|
await validateV4LifecycleOracle({ client, envelope, oracleConfigId: config.oracleConfigId, block });
|
|
273
|
-
const expected = {
|
|
274
|
-
|
|
275
|
-
liquidityLocker: boundGraph.locker, token,
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
expectedPositionCount: config.positions.length,
|
|
280
|
-
};
|
|
414
|
+
const expected = buildPoolBoundHookParameters({
|
|
415
|
+
chainId, core: plan.orchestrator, poolManager: boundGraph.manager, registrar: adapter.implementation,
|
|
416
|
+
oracleFactory: boundGraph.oracleFactory, liquidityLocker: boundGraph.locker, token, market, config,
|
|
417
|
+
});
|
|
418
|
+
const deployerAbi = config.version === 5 ? poolHookDeployerV1Abi : poolHookDeployerConfigV6Abi;
|
|
419
|
+
const collectorAbi = config.version === 5 ? poolFeeCollectorFactoryV1Abi : poolFeeCollectorFactoryConfigV6Abi;
|
|
281
420
|
const [[parameters, parameterSalt], [deployer, initCodeHash, salt, predictedHook], remoteInitCodeHash, remotePrediction] = await Promise.all([
|
|
282
|
-
readLifecycleContract(client, boundGraph.collectorFactory,
|
|
421
|
+
readLifecycleContract(client, boundGraph.collectorFactory, collectorAbi, "poolBoundHookParameters", [adapter.implementation, token, market], block),
|
|
283
422
|
readLifecycleContract(client, adapter.implementation, poolMarketAdapterV1Abi, "hookDeploymentMetadata", [token, market], block),
|
|
284
|
-
readLifecycleContract(client, boundGraph.deployer,
|
|
285
|
-
readLifecycleContract(client, boundGraph.deployer,
|
|
423
|
+
readLifecycleContract(client, boundGraph.deployer, deployerAbi, "initCodeHash", [expected], block),
|
|
424
|
+
readLifecycleContract(client, boundGraph.deployer, deployerAbi, "predict", [expected, config.hookSalt], block),
|
|
286
425
|
]);
|
|
287
|
-
const localInitCodeHash = poolBoundHookInitCodeHash(boundGraph.creationCode, expected);
|
|
426
|
+
const localInitCodeHash = poolBoundHookInitCodeHash(boundGraph.creationCode, expected, config.version);
|
|
288
427
|
const localPrediction = predictPoolBoundHookAddress({ deployer: boundGraph.deployer, initCodeHash: localInitCodeHash, salt: config.hookSalt });
|
|
289
|
-
if (encodePoolBoundHookParameters(parameters).toLowerCase() !== encodePoolBoundHookParameters(expected).toLowerCase() || parameterSalt.toLowerCase() !== config.hookSalt.toLowerCase() || salt.toLowerCase() !== config.hookSalt.toLowerCase() || deployer.toLowerCase() !== boundGraph.deployer.toLowerCase() || initCodeHash.toLowerCase() !== localInitCodeHash.toLowerCase() || remoteInitCodeHash.toLowerCase() !== localInitCodeHash.toLowerCase() || predictedHook.toLowerCase() !== localPrediction.toLowerCase() || remotePrediction.toLowerCase() !== localPrediction.toLowerCase())
|
|
290
|
-
throw new LifecyclePlanningError("HOOK_DEPLOYMENT_CHANGED", "Bound hook metadata differs from the exact certified constructor economics, factory, initcode, salt or CREATE2 prediction");
|
|
428
|
+
if (encodePoolBoundHookParameters(parameters, config.version).toLowerCase() !== encodePoolBoundHookParameters(expected, config.version).toLowerCase() || parameterSalt.toLowerCase() !== config.hookSalt.toLowerCase() || salt.toLowerCase() !== config.hookSalt.toLowerCase() || deployer.toLowerCase() !== boundGraph.deployer.toLowerCase() || initCodeHash.toLowerCase() !== localInitCodeHash.toLowerCase() || remoteInitCodeHash.toLowerCase() !== localInitCodeHash.toLowerCase() || predictedHook.toLowerCase() !== localPrediction.toLowerCase() || remotePrediction.toLowerCase() !== localPrediction.toLowerCase())
|
|
429
|
+
throw new LifecyclePlanningError("HOOK_DEPLOYMENT_CHANGED", "Bound hook metadata differs from the exact certified version, constructor economics, factory, initcode, salt or CREATE2 prediction");
|
|
291
430
|
const hookCode = rpcHex(await lifecyclePinnedRpc(client, "eth_getCode", [predictedHook, toHex(block.number)], block), "bound hook code");
|
|
292
431
|
if (hookCode !== "0x") {
|
|
293
|
-
const [deployedCodeHash, dependencies, requiredFlags, allFlags, actualPoolId, deploymentConfigHash, marketCommitment, opening, positions] = await Promise.all([
|
|
294
|
-
readLifecycleContract(client, deployer,
|
|
432
|
+
const [deployedCodeHash, dependencies, requiredFlags, allFlags, actualPoolId, deploymentConfigHash, marketCommitment, opening, positions, feePolicy] = await Promise.all([
|
|
433
|
+
readLifecycleContract(client, deployer, deployerAbi, "deployedCodeHash", [predictedHook], block),
|
|
295
434
|
Promise.all(["poolManager", "registrar", "oracleFactory", "core", "liquidityLocker", "token"].map((getter) => readLifecycleContract(client, predictedHook, fixedFeePoolHookV1Abi, getter, [], block))),
|
|
296
435
|
readLifecycleContract(client, predictedHook, fixedFeePoolHookV1Abi, "REQUIRED_HOOK_FLAGS", [], block),
|
|
297
436
|
readLifecycleContract(client, predictedHook, fixedFeePoolHookV1Abi, "ALL_HOOK_MASK", [], block),
|
|
@@ -300,6 +439,10 @@ async function readPoolBoundHookDeploymentDetails(options, block) {
|
|
|
300
439
|
readLifecycleContract(client, predictedHook, fixedFeePoolHookV1Abi, "marketCommitment", [], block),
|
|
301
440
|
readLifecycleContract(client, predictedHook, fixedFeePoolHookV1Abi, "openingSqrtPriceX96", [], block),
|
|
302
441
|
readLifecycleContract(client, predictedHook, fixedFeePoolHookV1Abi, "expectedPositionCount", [], block),
|
|
442
|
+
config.version === 6 ? Promise.all([
|
|
443
|
+
readLifecycleContract(client, predictedHook, fixedFeePoolHookConfigV6Abi, "minimumHookFeePips", [], block),
|
|
444
|
+
readLifecycleContract(client, predictedHook, fixedFeePoolHookConfigV6Abi, "feeSensitivityPipsSecondsPerTick", [], block),
|
|
445
|
+
]) : undefined,
|
|
303
446
|
]);
|
|
304
447
|
if ((hookCode.length - 2) / 2 > 24576 || deployedCodeHash === zeroHash || deployedCodeHash.toLowerCase() !== keccak256(hookCode).toLowerCase())
|
|
305
448
|
throw new LifecyclePlanningError("HOOK_DEPLOYMENT_CHANGED", "Existing hook lacks this exact typed deployer's bounded runtime-code provenance");
|
|
@@ -311,22 +454,24 @@ async function readPoolBoundHookDeploymentDetails(options, block) {
|
|
|
311
454
|
throw new LifecyclePlanningError("HOOK_DEPLOYMENT_CHANGED", "Existing bound hook callback declarations differ from the reviewed envelope");
|
|
312
455
|
const [currency0, currency1] = BigInt(token) < BigInt(market.quoteAsset) ? [token, market.quoteAsset] : [market.quoteAsset, token];
|
|
313
456
|
const expectedPoolId = keccak256(encodeAbiParameters([{ type: "address" }, { type: "address" }, { type: "uint24" }, { type: "int24" }, { type: "address" }], [currency0, currency1, config.lpFeePips, config.tickSpacing, predictedHook]));
|
|
314
|
-
if (actualPoolId.toLowerCase() !== expectedPoolId.toLowerCase() || deploymentConfigHash.toLowerCase() !== keccak256(encodePoolBoundHookParameters(expected)).toLowerCase() || marketCommitment.toLowerCase() !== expected.marketCommitment.toLowerCase() || opening !== expected.sqrtPriceX96 || positions !== expected.expectedPositionCount
|
|
315
|
-
|
|
457
|
+
if (actualPoolId.toLowerCase() !== expectedPoolId.toLowerCase() || deploymentConfigHash.toLowerCase() !== keccak256(encodePoolBoundHookParameters(expected, config.version)).toLowerCase() || marketCommitment.toLowerCase() !== expected.marketCommitment.toLowerCase() || opening !== expected.sqrtPriceX96 || positions !== expected.expectedPositionCount ||
|
|
458
|
+
(config.version === 6 && (feePolicy?.[0] !== config.minimumHookFeePips || feePolicy?.[1] !== config.feeSensitivityPipsSecondsPerTick)))
|
|
459
|
+
throw new LifecyclePlanningError("HOOK_DEPLOYMENT_CHANGED", "Existing hook key, economic commitment, versioned fee policy or immutable opening geometry changed");
|
|
316
460
|
}
|
|
317
|
-
return { deployment: { deployer, initCodeHash, salt, predictedHook }, parameters: expected };
|
|
461
|
+
return Object.freeze({ deployment: Object.freeze({ deployer, initCodeHash, salt, predictedHook }), parameters: Object.freeze(expected), configVersion: config.version });
|
|
318
462
|
}
|
|
319
|
-
/**
|
|
463
|
+
/** Known identities use frozen local construction data; unlisted identities use
|
|
464
|
+
* explicit live certification. Exact launch execution, not either DTO, authorizes. */
|
|
320
465
|
export async function readPoolBoundHookDeployment(options, pinnedBlock) {
|
|
321
466
|
const commitment = hashLaunchPlan(options.plan);
|
|
322
467
|
const block = pinnedBlock ?? await readLifecycleBlock(options.client);
|
|
323
468
|
return withLifecycleReadClient(options.client, async (client) => {
|
|
324
|
-
const { deployment } = await readPoolBoundHookDeploymentDetails({ ...options, client }, block);
|
|
469
|
+
const { deployment } = await readPoolBoundHookDeploymentDetails({ ...options, client }, block, commitment);
|
|
325
470
|
if (hashLaunchPlan(options.plan) !== commitment)
|
|
326
471
|
throw new LifecyclePlanningError("PLAN_MUTATED", "Economic plan changed while reading hook deployment metadata");
|
|
327
472
|
if (pinnedBlock === undefined)
|
|
328
473
|
await assertLifecycleBlock(client, block, options.plan.chainId);
|
|
329
|
-
return deployment;
|
|
474
|
+
return { ...deployment };
|
|
330
475
|
});
|
|
331
476
|
}
|
|
332
477
|
/** Optional permissionless predeployment calldata; no token deployment, pool registration or wallet broadcast. */
|
|
@@ -334,13 +479,14 @@ export async function buildPoolBoundHookDeploymentTransaction(options) {
|
|
|
334
479
|
const commitment = hashLaunchPlan(options.plan);
|
|
335
480
|
const block = await readLifecycleBlock(options.client);
|
|
336
481
|
return withLifecycleReadClient(options.client, async (client) => {
|
|
337
|
-
const { deployment, parameters } = await readPoolBoundHookDeploymentDetails({ ...options, client }, block);
|
|
482
|
+
const { deployment, parameters, configVersion } = await readPoolBoundHookDeploymentDetails({ ...options, client }, block, commitment);
|
|
338
483
|
if (!hasLifecycleV4HookPermissions(deployment.predictedHook))
|
|
339
484
|
throw new LifecyclePlanningError("INVALID_HOOK_BITS", "Finalize a salt with the exact lifecycle hook permissions before predeployment");
|
|
340
485
|
if (hashLaunchPlan(options.plan) !== commitment)
|
|
341
486
|
throw new LifecyclePlanningError("PLAN_MUTATED", "Economic plan changed while constructing hook deployment calldata");
|
|
342
487
|
await assertLifecycleBlock(client, block, options.plan.chainId);
|
|
343
|
-
|
|
488
|
+
const abi = configVersion === 5 ? poolHookDeployerV1Abi : poolHookDeployerConfigV6Abi;
|
|
489
|
+
return { to: deployment.deployer, data: encodeFunctionData({ abi, functionName: "deploy", args: [parameters, deployment.salt] }), value: 0n, deployment: { ...deployment } };
|
|
344
490
|
});
|
|
345
491
|
}
|
|
346
492
|
export function validateLifecycleMarketIdentity(identity, plan, token, marketIndex) {
|
|
@@ -447,20 +593,28 @@ async function readReceipt(reference, options, block) {
|
|
|
447
593
|
}
|
|
448
594
|
/** Reads confirmation-bound canonical state and receipt provenance; local step counters are never used. */
|
|
449
595
|
export async function readLaunchProgress(options, pinnedBlock) {
|
|
596
|
+
if (options.signal?.aborted)
|
|
597
|
+
throw Object.assign(new Error("Lifecycle progress read cancelled"), { name: "AbortError" });
|
|
450
598
|
const block = pinnedBlock ?? await readLifecycleBlock(options.client);
|
|
451
599
|
return withLifecycleReadClient(options.client, (client) => readLaunchProgressAtBlock(options, client, block, pinnedBlock === undefined));
|
|
452
600
|
}
|
|
453
601
|
async function readLaunchProgressAtBlock(options, client, block, checkBlock) {
|
|
454
602
|
const { planned } = options;
|
|
603
|
+
const assertActive = () => {
|
|
604
|
+
if (options.signal?.aborted)
|
|
605
|
+
throw Object.assign(new Error("Lifecycle progress read cancelled"), { name: "AbortError" });
|
|
606
|
+
};
|
|
607
|
+
assertActive();
|
|
455
608
|
const confirmationDepth = options.confirmations ?? planned.confirmations;
|
|
456
609
|
if (!Number.isSafeInteger(confirmationDepth) || confirmationDepth < 1)
|
|
457
610
|
throw new LifecyclePlanningError("INVALID_CONFIRMATIONS", "Confirmation depth must be a positive integer");
|
|
458
611
|
const confirmedNumber = block.number >= BigInt(confirmationDepth - 1) ? block.number - BigInt(confirmationDepth - 1) : 0n;
|
|
459
612
|
const [confirmedBlock, chainIdValue] = await Promise.all([
|
|
460
613
|
confirmedNumber === block.number ? block : readLifecycleBlock(client, toHex(confirmedNumber)),
|
|
461
|
-
|
|
614
|
+
readLifecycleChainId(client),
|
|
462
615
|
]);
|
|
463
|
-
|
|
616
|
+
assertActive();
|
|
617
|
+
const chainId = chainIdValue;
|
|
464
618
|
if (chainId !== planned.plan.chainId)
|
|
465
619
|
throw new LifecyclePlanningError("CHAIN_MISMATCH", "RPC chain differs from the committed launch");
|
|
466
620
|
if (planned.account.toLowerCase() !== planned.plan.creator.toLowerCase())
|
|
@@ -470,11 +624,12 @@ async function readLaunchProgressAtBlock(options, client, block, checkBlock) {
|
|
|
470
624
|
const [head, confirmedProgress, token, confirmedNonce, headNonce, pendingNonce] = await Promise.all([
|
|
471
625
|
readLifecycleContract(client, planned.plan.orchestrator, launchLifecycleAbi, "readLaunchProgress", [planned.launchId], block, planned.account),
|
|
472
626
|
confirmedNumber === block.number ? undefined : readLifecycleContract(client, planned.plan.orchestrator, launchLifecycleAbi, "readLaunchProgress", [planned.launchId], confirmedBlock, planned.account),
|
|
473
|
-
|
|
627
|
+
readLifecycleTokenAtBlock(client, planned.plan, block),
|
|
474
628
|
lifecycleRpc(client, "eth_getTransactionCount", [planned.account, toHex(confirmedBlock.number)]),
|
|
475
629
|
confirmedNumber === block.number ? undefined : lifecycleRpc(client, "eth_getTransactionCount", [planned.account, toHex(block.number)]),
|
|
476
630
|
lifecycleRpc(client, "eth_getTransactionCount", [planned.account, "pending"]),
|
|
477
631
|
]);
|
|
632
|
+
assertActive();
|
|
478
633
|
const canonical = confirmedProgress ?? head;
|
|
479
634
|
if (token.toLowerCase() !== planned.predictedToken.toLowerCase())
|
|
480
635
|
throw new LifecyclePlanningError("TOKEN_IDENTITY", "Predicted token changed for the committed token configuration");
|
|
@@ -493,6 +648,7 @@ async function readLaunchProgressAtBlock(options, client, block, checkBlock) {
|
|
|
493
648
|
const progressParameters = [{ type: "tuple", components: launchProgressV1Components }];
|
|
494
649
|
const confirmationSafe = confirmedAccountNonce === headAccountNonce && pendingAccountNonce === headAccountNonce && keccak256(encodeAbiParameters(progressParameters, [canonical])) === keccak256(encodeAbiParameters(progressParameters, [head]));
|
|
495
650
|
const receipts = await Promise.all((options.receipts ?? []).map((reference) => readReceipt({ ...reference, confirmations: Math.max(reference.confirmations ?? 1, confirmationDepth) }, { ...options, client }, block)));
|
|
651
|
+
assertActive();
|
|
496
652
|
let markets = [];
|
|
497
653
|
if (canonical.preparedMarkets > 0) {
|
|
498
654
|
const directory = await readLifecycleContract(client, planned.plan.orchestrator, launchLifecycleAbi, "directory", [], confirmedBlock);
|
|
@@ -504,7 +660,9 @@ async function readLaunchProgressAtBlock(options, client, block, checkBlock) {
|
|
|
504
660
|
return { index, prepared, live };
|
|
505
661
|
}
|
|
506
662
|
catch (failure) {
|
|
507
|
-
|
|
663
|
+
if (failure instanceof Error && failure.name === "AbortError")
|
|
664
|
+
throw failure;
|
|
665
|
+
return { index, prepared, error: lifecycleFailureReason(failure) };
|
|
508
666
|
}
|
|
509
667
|
}));
|
|
510
668
|
}
|
|
@@ -514,6 +672,7 @@ async function readLaunchProgressAtBlock(options, client, block, checkBlock) {
|
|
|
514
672
|
await assertLifecycleBlock(client, confirmedBlock, chainId);
|
|
515
673
|
if (hashLaunchPlan(planned.plan).toLowerCase() !== planned.planHash.toLowerCase() || hashLaunchIdentity(planned.plan).toLowerCase() !== planned.launchId.toLowerCase())
|
|
516
674
|
throw new LifecyclePlanningError("PLAN_MUTATED", "Stored economic plan changed during asynchronous progress reads");
|
|
675
|
+
assertActive();
|
|
517
676
|
return { canonical, head, confirmationDepth, confirmedBlockNumber: confirmedBlock.number, confirmedBlockHash: confirmedBlock.hash, confirmedAccountNonce, headAccountNonce, pendingAccountNonce, confirmationSafe, token, planHash: planned.planHash, launchId: planned.launchId, chainId, blockNumber: block.number, blockHash: block.hash, markets, receipts };
|
|
518
677
|
}
|
|
519
678
|
//# sourceMappingURL=progress.js.map
|