@black-market/sdk 0.6.3 → 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 +355 -392
- 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 +286 -118
- 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 -1
- package/dist/lifecycle/simulation.d.ts.map +1 -1
- package/dist/lifecycle/simulation.js +400 -100
- 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 +236 -39
- 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 -321
- package/src/lifecycle/presets.ts +243 -0
- package/src/lifecycle/progress.ts +254 -102
- package/src/lifecycle/rpc.ts +97 -9
- package/src/lifecycle/simulation.ts +391 -94
- 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) {
|
|
@@ -154,89 +236,152 @@ export async function validateV4LifecycleOracle(options) {
|
|
|
154
236
|
}
|
|
155
237
|
export async function readLifecycleProfiles(options, pinnedBlock) {
|
|
156
238
|
return withLifecycleReadClient(options.client, (client) => lifecycleStage(client, "profile.certification", async () => {
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
]
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
239
|
+
const chainIdPromise = lifecycleRpc(client, "eth_chainId").then((value) => rpcQuantity(value, "chain ID"));
|
|
240
|
+
// Block-bound discovery does not depend on chain identity. Join both branches
|
|
241
|
+
// immediately so a failed chain read cannot become detached or a profile refusal.
|
|
242
|
+
const [chainId, { block, profiles }] = await Promise.all([
|
|
243
|
+
chainIdPromise,
|
|
244
|
+
(async () => {
|
|
245
|
+
const block = pinnedBlock ?? await readLifecycleBlock(client);
|
|
246
|
+
const registry = await readLifecycleContract(client, options.orchestrator, launchLifecycleAbi, "registry", [], block);
|
|
247
|
+
const offset = options.offset ?? 0n;
|
|
248
|
+
const limit = options.limit ?? 100n;
|
|
249
|
+
if (offset < 0n || limit < 0n || limit > 100n)
|
|
250
|
+
throw new LifecyclePlanningError("INVALID_PROFILE_PAGE", "Registry profile pages require unsigned offset and limit at most 100");
|
|
251
|
+
let protocolMaximumDeveloperFeeBpsPromise;
|
|
252
|
+
const readProtocolMaximumDeveloperFeeBps = () => protocolMaximumDeveloperFeeBpsPromise ??=
|
|
253
|
+
readLifecycleContract(client, registry, lifecycleRegistryAbi, "protocolMaximumDeveloperFeeBps", [], block);
|
|
254
|
+
const readProfile = async (id) => {
|
|
255
|
+
const registration = await readLifecycleContract(client, registry, lifecycleRegistryAbi, "profile", [id], block);
|
|
256
|
+
const schema = registration.configSchema.toLowerCase();
|
|
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";
|
|
258
|
+
const [adapter, certification] = await Promise.all([
|
|
259
|
+
readLifecycleContract(client, registry, lifecycleRegistryAbi, "adapter", [registration.adapterId], block),
|
|
260
|
+
venueKind === "unknown" ? undefined : Promise.all([
|
|
261
|
+
readLifecycleProfileTopology(client, registry, id, block),
|
|
262
|
+
venueKind === "uniswap-v4" ? Promise.all([
|
|
263
|
+
readLifecycleContract(client, registry, lifecycleRegistryAbi, "profileEnvelope", [id], block),
|
|
264
|
+
readLifecycleContract(client, registry, lifecycleRegistryAbi, "developerTerms", [id], block),
|
|
265
|
+
readProtocolMaximumDeveloperFeeBps(),
|
|
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,
|
|
271
|
+
]).then((values) => ({ values }), (failure) => ({ failure })),
|
|
272
|
+
]);
|
|
273
|
+
const chainId = await chainIdPromise;
|
|
274
|
+
let topology = { hookTopology: 0, configVersion: adapter.configVersion, hookDeployer: zeroAddress, hookCreationCodeHash: zeroHash };
|
|
275
|
+
let envelope;
|
|
276
|
+
let developerTerms;
|
|
277
|
+
let protocolMaximumDeveloperFeeBps;
|
|
278
|
+
let admitted = false;
|
|
279
|
+
let reason;
|
|
280
|
+
try {
|
|
281
|
+
if (certification === undefined)
|
|
282
|
+
throw new LifecyclePlanningError("UNSUPPORTED_SCHEMA", `Unsupported lifecycle config schema ${registration.configSchema}`);
|
|
283
|
+
if ("failure" in certification)
|
|
284
|
+
throw certification.failure;
|
|
285
|
+
const [recordedTopology, reviewed, eligibility] = certification.values;
|
|
286
|
+
topology = recordedTopology;
|
|
287
|
+
if (reviewed !== undefined) {
|
|
288
|
+
envelope = reviewed[0];
|
|
289
|
+
protocolMaximumDeveloperFeeBps = reviewed[2];
|
|
290
|
+
const [implementation, beneficiary, maximumDeveloperFeeBps, termsDigest, enabled] = reviewed[1];
|
|
291
|
+
developerTerms = { adapter: implementation, beneficiary, maximumDeveloperFeeBps, termsDigest, enabled };
|
|
292
|
+
if (implementation.toLowerCase() !== adapter.implementation.toLowerCase() || beneficiary.toLowerCase() !== envelope.beneficiary.toLowerCase() ||
|
|
293
|
+
termsDigest.toLowerCase() !== envelope.termsDigest.toLowerCase() || maximumDeveloperFeeBps !== envelope.maximumDeveloperFeeBps ||
|
|
294
|
+
maximumDeveloperFeeBps > reviewed[2])
|
|
295
|
+
throw new LifecyclePlanningError("PROFILE_TERMS", "Registry developer terms differ from the frozen reviewed envelope");
|
|
296
|
+
}
|
|
297
|
+
await certifyLifecycleProfile(client, options.orchestrator, registry, id, registration, adapter, topology, envelope, block, chainId, eligibility);
|
|
298
|
+
if (developerTerms !== undefined && !developerTerms.enabled)
|
|
299
|
+
throw new LifecyclePlanningError("INELIGIBLE_PROFILE", "Registry refuses this profile for pending source admission");
|
|
300
|
+
admitted = true;
|
|
301
|
+
}
|
|
302
|
+
catch (failure) {
|
|
303
|
+
reason = lifecycleFailureReason(failure);
|
|
304
|
+
}
|
|
305
|
+
return { id, registration, adapter, topology, envelope, developerTerms, protocolMaximumDeveloperFeeBps: venueKind === "uniswap-v4" ? protocolMaximumDeveloperFeeBps : undefined, admitted, reason, venueKind };
|
|
306
|
+
};
|
|
307
|
+
const [authority, profiles] = await Promise.all([
|
|
308
|
+
readLifecycleContract(client, registry, lifecycleRegistryAbi, "core", [], block),
|
|
309
|
+
Promise.resolve(options.profileIds ?? readLifecycleContract(client, registry, lifecycleRegistryAbi, "profileIds", [offset, limit], block)).then((ids) => Promise.all(ids.map(readProfile))),
|
|
310
|
+
]);
|
|
311
|
+
if (authority.toLowerCase() !== options.orchestrator.toLowerCase())
|
|
312
|
+
throw new LifecyclePlanningError("REGISTRY_BINDING", "Registry is not bound to the selected lifecycle orchestrator");
|
|
313
|
+
// A required V4 cap read still rejects the invocation on failure; Abyss has no developer-fee terms.
|
|
314
|
+
if (protocolMaximumDeveloperFeeBpsPromise !== undefined)
|
|
315
|
+
await protocolMaximumDeveloperFeeBpsPromise;
|
|
316
|
+
return { block, profiles };
|
|
317
|
+
})(),
|
|
221
318
|
]);
|
|
222
|
-
if (authority.toLowerCase() !== options.orchestrator.toLowerCase())
|
|
223
|
-
throw new LifecyclePlanningError("REGISTRY_BINDING", "Registry is not bound to the selected lifecycle orchestrator");
|
|
224
|
-
// A required V4 cap read still rejects the invocation on failure; Abyss has no developer-fee terms.
|
|
225
|
-
if (protocolMaximumDeveloperFeeBpsPromise !== undefined)
|
|
226
|
-
await protocolMaximumDeveloperFeeBpsPromise;
|
|
227
319
|
if (pinnedBlock === undefined)
|
|
228
320
|
await assertLifecycleBlock(client, block, chainId);
|
|
229
321
|
return profiles;
|
|
230
322
|
}));
|
|
231
323
|
}
|
|
232
|
-
async function readPoolBoundHookDeploymentDetails(options, block) {
|
|
324
|
+
async function readPoolBoundHookDeploymentDetails(options, block, commitment) {
|
|
233
325
|
const { client, plan, marketIndex } = options;
|
|
234
326
|
const market = plan.markets[marketIndex];
|
|
235
|
-
if (market === undefined || market.configVersion
|
|
236
|
-
throw new LifecyclePlanningError("INVALID_BOUND_MARKET", "Market index must select
|
|
237
|
-
|
|
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");
|
|
238
338
|
if (chainId !== plan.chainId)
|
|
239
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];
|
|
240
385
|
const registry = await readLifecycleContract(client, plan.orchestrator, launchLifecycleAbi, "registry", [], block);
|
|
241
386
|
const [authority, registration] = await Promise.all([
|
|
242
387
|
readLifecycleContract(client, registry, lifecycleRegistryAbi, "core", [], block),
|
|
@@ -257,35 +402,35 @@ async function readPoolBoundHookDeploymentDetails(options, block) {
|
|
|
257
402
|
const { boundGraph } = await certifyLifecycleProfile(client, plan.orchestrator, registry, market.profileId, registration, adapter, recorded, envelope, block, chainId);
|
|
258
403
|
if (boundGraph === undefined)
|
|
259
404
|
throw new LifecyclePlanningError("UNCERTIFIED_TOPOLOGY", "Market does not have a certified pool-bound deployer graph");
|
|
260
|
-
|
|
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);
|
|
261
408
|
validateV4LifecycleMarket({ market, config, token, profile: {
|
|
262
409
|
id: market.profileId, registration, adapter, topology: recorded, envelope,
|
|
263
410
|
developerTerms: { adapter: implementation, beneficiary, maximumDeveloperFeeBps, termsDigest, enabled },
|
|
264
411
|
protocolMaximumDeveloperFeeBps, venueKind: "uniswap-v4", admitted: enabled,
|
|
265
412
|
} });
|
|
266
413
|
await validateV4LifecycleOracle({ client, envelope, oracleConfigId: config.oracleConfigId, block });
|
|
267
|
-
const expected = {
|
|
268
|
-
|
|
269
|
-
liquidityLocker: boundGraph.locker, token,
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
expectedPositionCount: config.positions.length,
|
|
274
|
-
};
|
|
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;
|
|
275
420
|
const [[parameters, parameterSalt], [deployer, initCodeHash, salt, predictedHook], remoteInitCodeHash, remotePrediction] = await Promise.all([
|
|
276
|
-
readLifecycleContract(client, boundGraph.collectorFactory,
|
|
421
|
+
readLifecycleContract(client, boundGraph.collectorFactory, collectorAbi, "poolBoundHookParameters", [adapter.implementation, token, market], block),
|
|
277
422
|
readLifecycleContract(client, adapter.implementation, poolMarketAdapterV1Abi, "hookDeploymentMetadata", [token, market], block),
|
|
278
|
-
readLifecycleContract(client, boundGraph.deployer,
|
|
279
|
-
readLifecycleContract(client, boundGraph.deployer,
|
|
423
|
+
readLifecycleContract(client, boundGraph.deployer, deployerAbi, "initCodeHash", [expected], block),
|
|
424
|
+
readLifecycleContract(client, boundGraph.deployer, deployerAbi, "predict", [expected, config.hookSalt], block),
|
|
280
425
|
]);
|
|
281
|
-
const localInitCodeHash = poolBoundHookInitCodeHash(boundGraph.creationCode, expected);
|
|
426
|
+
const localInitCodeHash = poolBoundHookInitCodeHash(boundGraph.creationCode, expected, config.version);
|
|
282
427
|
const localPrediction = predictPoolBoundHookAddress({ deployer: boundGraph.deployer, initCodeHash: localInitCodeHash, salt: config.hookSalt });
|
|
283
|
-
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())
|
|
284
|
-
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");
|
|
285
430
|
const hookCode = rpcHex(await lifecyclePinnedRpc(client, "eth_getCode", [predictedHook, toHex(block.number)], block), "bound hook code");
|
|
286
431
|
if (hookCode !== "0x") {
|
|
287
|
-
const [deployedCodeHash, dependencies, requiredFlags, allFlags, actualPoolId, deploymentConfigHash, marketCommitment, opening, positions] = await Promise.all([
|
|
288
|
-
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),
|
|
289
434
|
Promise.all(["poolManager", "registrar", "oracleFactory", "core", "liquidityLocker", "token"].map((getter) => readLifecycleContract(client, predictedHook, fixedFeePoolHookV1Abi, getter, [], block))),
|
|
290
435
|
readLifecycleContract(client, predictedHook, fixedFeePoolHookV1Abi, "REQUIRED_HOOK_FLAGS", [], block),
|
|
291
436
|
readLifecycleContract(client, predictedHook, fixedFeePoolHookV1Abi, "ALL_HOOK_MASK", [], block),
|
|
@@ -294,6 +439,10 @@ async function readPoolBoundHookDeploymentDetails(options, block) {
|
|
|
294
439
|
readLifecycleContract(client, predictedHook, fixedFeePoolHookV1Abi, "marketCommitment", [], block),
|
|
295
440
|
readLifecycleContract(client, predictedHook, fixedFeePoolHookV1Abi, "openingSqrtPriceX96", [], block),
|
|
296
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,
|
|
297
446
|
]);
|
|
298
447
|
if ((hookCode.length - 2) / 2 > 24576 || deployedCodeHash === zeroHash || deployedCodeHash.toLowerCase() !== keccak256(hookCode).toLowerCase())
|
|
299
448
|
throw new LifecyclePlanningError("HOOK_DEPLOYMENT_CHANGED", "Existing hook lacks this exact typed deployer's bounded runtime-code provenance");
|
|
@@ -305,22 +454,24 @@ async function readPoolBoundHookDeploymentDetails(options, block) {
|
|
|
305
454
|
throw new LifecyclePlanningError("HOOK_DEPLOYMENT_CHANGED", "Existing bound hook callback declarations differ from the reviewed envelope");
|
|
306
455
|
const [currency0, currency1] = BigInt(token) < BigInt(market.quoteAsset) ? [token, market.quoteAsset] : [market.quoteAsset, token];
|
|
307
456
|
const expectedPoolId = keccak256(encodeAbiParameters([{ type: "address" }, { type: "address" }, { type: "uint24" }, { type: "int24" }, { type: "address" }], [currency0, currency1, config.lpFeePips, config.tickSpacing, predictedHook]));
|
|
308
|
-
if (actualPoolId.toLowerCase() !== expectedPoolId.toLowerCase() || deploymentConfigHash.toLowerCase() !== keccak256(encodePoolBoundHookParameters(expected)).toLowerCase() || marketCommitment.toLowerCase() !== expected.marketCommitment.toLowerCase() || opening !== expected.sqrtPriceX96 || positions !== expected.expectedPositionCount
|
|
309
|
-
|
|
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");
|
|
310
460
|
}
|
|
311
|
-
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 });
|
|
312
462
|
}
|
|
313
|
-
/**
|
|
463
|
+
/** Known identities use frozen local construction data; unlisted identities use
|
|
464
|
+
* explicit live certification. Exact launch execution, not either DTO, authorizes. */
|
|
314
465
|
export async function readPoolBoundHookDeployment(options, pinnedBlock) {
|
|
315
466
|
const commitment = hashLaunchPlan(options.plan);
|
|
316
467
|
const block = pinnedBlock ?? await readLifecycleBlock(options.client);
|
|
317
468
|
return withLifecycleReadClient(options.client, async (client) => {
|
|
318
|
-
const { deployment } = await readPoolBoundHookDeploymentDetails({ ...options, client }, block);
|
|
469
|
+
const { deployment } = await readPoolBoundHookDeploymentDetails({ ...options, client }, block, commitment);
|
|
319
470
|
if (hashLaunchPlan(options.plan) !== commitment)
|
|
320
471
|
throw new LifecyclePlanningError("PLAN_MUTATED", "Economic plan changed while reading hook deployment metadata");
|
|
321
472
|
if (pinnedBlock === undefined)
|
|
322
473
|
await assertLifecycleBlock(client, block, options.plan.chainId);
|
|
323
|
-
return deployment;
|
|
474
|
+
return { ...deployment };
|
|
324
475
|
});
|
|
325
476
|
}
|
|
326
477
|
/** Optional permissionless predeployment calldata; no token deployment, pool registration or wallet broadcast. */
|
|
@@ -328,13 +479,14 @@ export async function buildPoolBoundHookDeploymentTransaction(options) {
|
|
|
328
479
|
const commitment = hashLaunchPlan(options.plan);
|
|
329
480
|
const block = await readLifecycleBlock(options.client);
|
|
330
481
|
return withLifecycleReadClient(options.client, async (client) => {
|
|
331
|
-
const { deployment, parameters } = await readPoolBoundHookDeploymentDetails({ ...options, client }, block);
|
|
482
|
+
const { deployment, parameters, configVersion } = await readPoolBoundHookDeploymentDetails({ ...options, client }, block, commitment);
|
|
332
483
|
if (!hasLifecycleV4HookPermissions(deployment.predictedHook))
|
|
333
484
|
throw new LifecyclePlanningError("INVALID_HOOK_BITS", "Finalize a salt with the exact lifecycle hook permissions before predeployment");
|
|
334
485
|
if (hashLaunchPlan(options.plan) !== commitment)
|
|
335
486
|
throw new LifecyclePlanningError("PLAN_MUTATED", "Economic plan changed while constructing hook deployment calldata");
|
|
336
487
|
await assertLifecycleBlock(client, block, options.plan.chainId);
|
|
337
|
-
|
|
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 } };
|
|
338
490
|
});
|
|
339
491
|
}
|
|
340
492
|
export function validateLifecycleMarketIdentity(identity, plan, token, marketIndex) {
|
|
@@ -441,17 +593,28 @@ async function readReceipt(reference, options, block) {
|
|
|
441
593
|
}
|
|
442
594
|
/** Reads confirmation-bound canonical state and receipt provenance; local step counters are never used. */
|
|
443
595
|
export async function readLaunchProgress(options, pinnedBlock) {
|
|
596
|
+
if (options.signal?.aborted)
|
|
597
|
+
throw Object.assign(new Error("Lifecycle progress read cancelled"), { name: "AbortError" });
|
|
444
598
|
const block = pinnedBlock ?? await readLifecycleBlock(options.client);
|
|
445
599
|
return withLifecycleReadClient(options.client, (client) => readLaunchProgressAtBlock(options, client, block, pinnedBlock === undefined));
|
|
446
600
|
}
|
|
447
601
|
async function readLaunchProgressAtBlock(options, client, block, checkBlock) {
|
|
448
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();
|
|
449
608
|
const confirmationDepth = options.confirmations ?? planned.confirmations;
|
|
450
609
|
if (!Number.isSafeInteger(confirmationDepth) || confirmationDepth < 1)
|
|
451
610
|
throw new LifecyclePlanningError("INVALID_CONFIRMATIONS", "Confirmation depth must be a positive integer");
|
|
452
611
|
const confirmedNumber = block.number >= BigInt(confirmationDepth - 1) ? block.number - BigInt(confirmationDepth - 1) : 0n;
|
|
453
|
-
const confirmedBlock =
|
|
454
|
-
|
|
612
|
+
const [confirmedBlock, chainIdValue] = await Promise.all([
|
|
613
|
+
confirmedNumber === block.number ? block : readLifecycleBlock(client, toHex(confirmedNumber)),
|
|
614
|
+
readLifecycleChainId(client),
|
|
615
|
+
]);
|
|
616
|
+
assertActive();
|
|
617
|
+
const chainId = chainIdValue;
|
|
455
618
|
if (chainId !== planned.plan.chainId)
|
|
456
619
|
throw new LifecyclePlanningError("CHAIN_MISMATCH", "RPC chain differs from the committed launch");
|
|
457
620
|
if (planned.account.toLowerCase() !== planned.plan.creator.toLowerCase())
|
|
@@ -461,11 +624,12 @@ async function readLaunchProgressAtBlock(options, client, block, checkBlock) {
|
|
|
461
624
|
const [head, confirmedProgress, token, confirmedNonce, headNonce, pendingNonce] = await Promise.all([
|
|
462
625
|
readLifecycleContract(client, planned.plan.orchestrator, launchLifecycleAbi, "readLaunchProgress", [planned.launchId], block, planned.account),
|
|
463
626
|
confirmedNumber === block.number ? undefined : readLifecycleContract(client, planned.plan.orchestrator, launchLifecycleAbi, "readLaunchProgress", [planned.launchId], confirmedBlock, planned.account),
|
|
464
|
-
|
|
627
|
+
readLifecycleTokenAtBlock(client, planned.plan, block),
|
|
465
628
|
lifecycleRpc(client, "eth_getTransactionCount", [planned.account, toHex(confirmedBlock.number)]),
|
|
466
629
|
confirmedNumber === block.number ? undefined : lifecycleRpc(client, "eth_getTransactionCount", [planned.account, toHex(block.number)]),
|
|
467
630
|
lifecycleRpc(client, "eth_getTransactionCount", [planned.account, "pending"]),
|
|
468
631
|
]);
|
|
632
|
+
assertActive();
|
|
469
633
|
const canonical = confirmedProgress ?? head;
|
|
470
634
|
if (token.toLowerCase() !== planned.predictedToken.toLowerCase())
|
|
471
635
|
throw new LifecyclePlanningError("TOKEN_IDENTITY", "Predicted token changed for the committed token configuration");
|
|
@@ -484,6 +648,7 @@ async function readLaunchProgressAtBlock(options, client, block, checkBlock) {
|
|
|
484
648
|
const progressParameters = [{ type: "tuple", components: launchProgressV1Components }];
|
|
485
649
|
const confirmationSafe = confirmedAccountNonce === headAccountNonce && pendingAccountNonce === headAccountNonce && keccak256(encodeAbiParameters(progressParameters, [canonical])) === keccak256(encodeAbiParameters(progressParameters, [head]));
|
|
486
650
|
const receipts = await Promise.all((options.receipts ?? []).map((reference) => readReceipt({ ...reference, confirmations: Math.max(reference.confirmations ?? 1, confirmationDepth) }, { ...options, client }, block)));
|
|
651
|
+
assertActive();
|
|
487
652
|
let markets = [];
|
|
488
653
|
if (canonical.preparedMarkets > 0) {
|
|
489
654
|
const directory = await readLifecycleContract(client, planned.plan.orchestrator, launchLifecycleAbi, "directory", [], confirmedBlock);
|
|
@@ -495,7 +660,9 @@ async function readLaunchProgressAtBlock(options, client, block, checkBlock) {
|
|
|
495
660
|
return { index, prepared, live };
|
|
496
661
|
}
|
|
497
662
|
catch (failure) {
|
|
498
|
-
|
|
663
|
+
if (failure instanceof Error && failure.name === "AbortError")
|
|
664
|
+
throw failure;
|
|
665
|
+
return { index, prepared, error: lifecycleFailureReason(failure) };
|
|
499
666
|
}
|
|
500
667
|
}));
|
|
501
668
|
}
|
|
@@ -505,6 +672,7 @@ async function readLaunchProgressAtBlock(options, client, block, checkBlock) {
|
|
|
505
672
|
await assertLifecycleBlock(client, confirmedBlock, chainId);
|
|
506
673
|
if (hashLaunchPlan(planned.plan).toLowerCase() !== planned.planHash.toLowerCase() || hashLaunchIdentity(planned.plan).toLowerCase() !== planned.launchId.toLowerCase())
|
|
507
674
|
throw new LifecyclePlanningError("PLAN_MUTATED", "Stored economic plan changed during asynchronous progress reads");
|
|
675
|
+
assertActive();
|
|
508
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 };
|
|
509
677
|
}
|
|
510
678
|
//# sourceMappingURL=progress.js.map
|