@black-market/sdk 0.3.1 → 0.4.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.
@@ -85,6 +85,10 @@ export const lifecycleRegistryAbi = [
85
85
  { name: "envelope", type: "tuple", components: launchEnvelopeV2Components },
86
86
  { name: "nonce", type: "uint256" }, { name: "deadline", type: "uint256" }, { name: "authorization", type: "bytes" },
87
87
  ], outputs: [] },
88
+ { type: "function", name: "registerAbyssProfile", stateMutability: "nonpayable", inputs: [
89
+ { name: "variant", type: "uint8" },
90
+ { name: "registration", type: "tuple", components: profileRegistrationV1Components },
91
+ ], outputs: [] },
88
92
  { type: "function", name: "authorPayout", stateMutability: "view", inputs: [{ type: "address" }], outputs: [{ type: "address" }] },
89
93
  { type: "function", name: "setAuthorPayout", stateMutability: "nonpayable", inputs: [{ name: "authorId", type: "address" }, { name: "payout", type: "address" }], outputs: [] },
90
94
  { type: "function", name: "authorHubCount", stateMutability: "view", inputs: [{ type: "address" }], outputs: [{ type: "uint256" }] },
@@ -108,7 +112,7 @@ export const lifecycleAdapterAbi = [
108
112
  { type: "function", name: "readPosition", stateMutability: "view", inputs: [{ type: "tuple", components: positionIdentityV1Components }], outputs: [{ type: "uint128" }, { type: "address" }] },
109
113
  ] as const;
110
114
 
111
- const reviewedV4AdapterMetadataAbi = [
115
+ const v4AdapterMetadataAbi = [
112
116
  ...lifecycleAdapterAbi,
113
117
  { type: "function", name: "PROFILE_ID", stateMutability: "view", inputs: [], outputs: [{ type: "bytes32" }] },
114
118
  { type: "function", name: "CONFIG_SCHEMA", stateMutability: "view", inputs: [], outputs: [{ type: "bytes32" }] },
@@ -121,8 +125,8 @@ const reviewedV4AdapterMetadataAbi = [
121
125
  { type: "function", name: "collectorFactory", stateMutability: "view", inputs: [], outputs: [{ type: "address" }] },
122
126
  { type: "function", name: "hookDeployer", stateMutability: "view", inputs: [], outputs: [{ type: "address" }] },
123
127
  ] as const;
124
- export const reviewedSharedV4MarketAdapterV1Abi = [
125
- ...reviewedV4AdapterMetadataAbi,
128
+ export const sharedMarketAdapterV1Abi = [
129
+ ...v4AdapterMetadataAbi,
126
130
  { type: "constructor", stateMutability: "nonpayable", inputs: [
127
131
  { name: "core_", type: "address" }, { name: "manager_", type: "address" }, { name: "root_", type: "address" },
128
132
  { name: "locker_", type: "address" }, { name: "helper_", type: "address" }, { name: "registry_", type: "address" },
@@ -136,8 +140,8 @@ export const abyssLifecycleAdapterAbi = [
136
140
  { type: "function", name: "profileId", stateMutability: "view", inputs: [{ type: "uint8" }], outputs: [{ type: "bytes32" }] },
137
141
  { type: "function", name: "factory", stateMutability: "view", inputs: [], outputs: [{ type: "address" }] },
138
142
  ] as const;
139
- export const reviewedPoolBoundV4MarketAdapterV1Abi = [
140
- ...reviewedV4AdapterMetadataAbi,
143
+ export const poolMarketAdapterV1Abi = [
144
+ ...v4AdapterMetadataAbi,
141
145
  { type: "constructor", stateMutability: "nonpayable", inputs: [
142
146
  { name: "core_", type: "address" }, { name: "manager_", type: "address" }, { name: "oracleFactory_", type: "address" },
143
147
  { name: "locker_", type: "address" }, { name: "deployer_", type: "address" }, { name: "helper_", type: "address" },
@@ -151,7 +155,7 @@ export const reviewedPoolBoundV4MarketAdapterV1Abi = [
151
155
  ] },
152
156
  ] as const;
153
157
  const boundParametersInput = { name: "parameters", type: "tuple", components: poolBoundHookParametersV1Components } as const;
154
- export const reviewedPoolBoundHookDeployerV1Abi = [
158
+ export const poolHookDeployerV1Abi = [
155
159
  { type: "constructor", stateMutability: "nonpayable", inputs: [{ name: "creationCode", type: "bytes" }] },
156
160
  { type: "function", name: "creationCodeHash", stateMutability: "view", inputs: [], outputs: [{ type: "bytes32" }] },
157
161
  { type: "function", name: "codeChunk0", stateMutability: "view", inputs: [], outputs: [{ type: "address" }] },
@@ -162,7 +166,7 @@ export const reviewedPoolBoundHookDeployerV1Abi = [
162
166
  { type: "function", name: "validHookAddress", stateMutability: "pure", inputs: [{ type: "address" }], outputs: [{ type: "bool" }] },
163
167
  { type: "function", name: "deploy", stateMutability: "nonpayable", inputs: [boundParametersInput, { name: "salt", type: "bytes32" }], outputs: [{ name: "hook", type: "address" }] },
164
168
  ] as const;
165
- export const reviewedSharedHookDeployerV1Abi = [
169
+ export const sharedHookDeployerV1Abi = [
166
170
  { type: "constructor", stateMutability: "nonpayable", inputs: [{ name: "creationCode", type: "bytes" }] },
167
171
  { type: "function", name: "creationCodeHash", stateMutability: "view", inputs: [], outputs: [{ type: "bytes32" }] },
168
172
  { type: "function", name: "codeChunk0", stateMutability: "view", inputs: [], outputs: [{ type: "address" }] },
@@ -219,14 +223,14 @@ export const lifecycleV4LockerAbi = [
219
223
  { type: "function", name: "positionCount", stateMutability: "view", inputs: [{ type: "bytes32" }], outputs: [{ type: "uint256" }] },
220
224
  { type: "function", name: "isSealed", stateMutability: "view", inputs: [{ type: "bytes32" }], outputs: [{ type: "bool" }] },
221
225
  ] as const;
222
- export const reviewedV4FeeCollectorFactoryV1Abi = [
226
+ export const poolFeeCollectorFactoryV1Abi = [
223
227
  { type: "function", name: "collectorDeployer", stateMutability: "view", inputs: [], outputs: [{ type: "address" }] },
224
228
  { type: "function", name: "poolBoundHookParameters", stateMutability: "view", inputs: [
225
229
  { name: "registrar", type: "address" }, { name: "token", type: "address" }, { name: "market", type: "tuple", components: lifecycleMarketComponents },
226
230
  ], outputs: [boundParametersInput, { name: "salt", type: "bytes32" }] },
227
231
  { type: "function", name: "decodeAndValidate", stateMutability: "view", inputs: [{ type: "address" }, { type: "address" }, { type: "tuple", components: lifecycleMarketComponents }], outputs: [{ type: "tuple", components: v4LifecycleMarketComponents }] },
228
232
  { type: "function", name: "poolBoundDeploymentMetadata", stateMutability: "view", inputs: [{ type: "address" }, { type: "address" }, { type: "tuple", components: lifecycleMarketComponents }], outputs: [{ type: "address" }, { type: "bytes32" }, { type: "bytes32" }, { type: "address" }] },
229
- { type: "function", name: "reviewedDependencyDigest", stateMutability: "view", inputs: [{ type: "address" }], outputs: [{ type: "bytes32" }] },
233
+ { type: "function", name: "dependencyDigest", stateMutability: "view", inputs: [{ type: "address" }], outputs: [{ type: "bytes32" }] },
230
234
  { type: "function", name: "create", stateMutability: "nonpayable", inputs: [{ type: "address" }, { type: "address" }, { type: "tuple", components: lifecycleV4PoolKeyComponents }, { type: "uint256" }], outputs: [{ type: "address" }] },
231
235
  ] as const;
232
236
  export const lifecycleDirectoryAbi = [
@@ -39,7 +39,7 @@ export function decodeV4LifecycleMarketConfig(encoded: Hex): V4LifecycleMarketCo
39
39
  export type V4PoolBoundLifecycleMarketConfig = Omit<V4LifecycleMarketConfig, "version"> & { version: 5; hookSalt: Hex };
40
40
  export const V4_LIFECYCLE_CONFIG_SCHEMA = keccak256(stringToHex("(uint16,uint24,int24,uint160,uint24,uint8,uint8,address,bool,bytes32,bytes32,bytes32,address,uint16,(int24,int24,uint128,bytes32,uint256)[])"));
41
41
  export const V4_POOL_BOUND_LIFECYCLE_CONFIG_SCHEMA = keccak256(stringToHex("(uint16,uint24,int24,uint160,uint24,uint8,uint8,address,bool,bytes32,bytes32,bytes32,bytes32,address,uint16,(int24,int24,uint128,bytes32,uint256)[])"));
42
- export const V4_POOL_BOUND_MARKET_ECONOMICS_DOMAIN = keccak256(stringToHex("black-market.reviewed-pool-bound-market-economics.v1"));
42
+ export const V4_POOL_BOUND_MARKET_ECONOMICS_DOMAIN = keccak256(stringToHex("black-market.pool-bound-market-economics.v1"));
43
43
  export const V4_LIFECYCLE_HOOK_PERMISSION_MASK = 0x3fffn;
44
44
  export const V4_LIFECYCLE_HOOK_PERMISSIONS = 0x1afcn;
45
45
  export const poolBoundV4LifecycleMarketComponents = [
@@ -65,7 +65,7 @@ export function decodePoolBoundV4LifecycleMarketConfig(encoded: Hex): V4PoolBoun
65
65
  }
66
66
 
67
67
  /** Exact pending-source admission terms; author identity is never the live payout address. */
68
- export function validateReviewedV4LifecycleMarket(options: {
68
+ export function validateV4LifecycleMarket(options: {
69
69
  market: MarketConfigV1; config: V4LifecycleMarketConfig | V4PoolBoundLifecycleMarketConfig; profile: LifecycleProfile; token: Address;
70
70
  }): void {
71
71
  const { market, config, profile, token } = options;
@@ -83,19 +83,20 @@ export function validateReviewedV4LifecycleMarket(options: {
83
83
  config.developerBeneficiary.toLowerCase() !== terms.beneficiary.toLowerCase() ||
84
84
  BigInt(config.developerBeneficiary) === 0n || config.developerBeneficiary.toLowerCase() === token.toLowerCase() ||
85
85
  config.developerBeneficiary.toLowerCase() === market.quoteAsset.toLowerCase()) {
86
- throw new LifecyclePlanningError("REVIEWED_TERMS_MISMATCH", "Market must explicitly bind the admitted profile, frozen terms and stable author identity");
86
+ throw new LifecyclePlanningError("PROFILE_TERMS_MISMATCH", "Market must explicitly bind the admitted profile, frozen terms and stable author identity");
87
87
  }
88
88
  if (!Number.isInteger(config.developerFeeBps) || config.developerFeeBps < 0 ||
89
89
  config.developerFeeBps > envelope.maximumDeveloperFeeBps || config.developerFeeBps > terms.maximumDeveloperFeeBps ||
90
90
  config.developerFeeBps > protocolCeiling) throw new LifecyclePlanningError("DEVELOPER_FEE_CEILING", "Explicit developer fee exceeds the reviewed or protocol ceiling");
91
91
  const b = envelope.bounds;
92
+ if (config.oracleConfigId.toLowerCase() === zeroHash) throw new LifecyclePlanningError("INVALID_ORACLE_CONFIG", "Market oracle configuration must be nonzero");
92
93
  if (config.treasury.toLowerCase() !== envelope.protocolTreasury.toLowerCase() ||
93
- config.protocolFeeDenominator !== envelope.protocolFeeDenominator || config.hookFeePips > b.maximumHookFeePips ||
94
- config.lpFeePips > b.maximumLpFeePips || !Number.isInteger(config.feeMode) || config.feeMode < 0 || config.feeMode > 1 || (b.feeModeFlags & (1 << config.feeMode)) === 0 ||
94
+ config.protocolFeeDenominator !== envelope.protocolFeeDenominator || !Number.isInteger(config.hookFeePips) || config.hookFeePips < 0 || config.hookFeePips >= 1000000 ||
95
+ !Number.isInteger(config.lpFeePips) || config.lpFeePips < 0 || config.lpFeePips >= 1000000 ||
96
+ !Number.isInteger(config.feeMode) || config.feeMode < 0 || config.feeMode > 1 || (b.feeModeFlags & (1 << config.feeMode)) === 0 ||
95
97
  config.tickSpacing < b.minimumTickSpacing || config.tickSpacing > b.maximumTickSpacing ||
96
- config.positions.length === 0 || config.positions.length > b.maximumPositions || config.externalLiquidityDisabled !== b.externalLiquidityDisabled ||
97
- config.oracleConfigId.toLowerCase() !== b.oracleConfigId.toLowerCase()) {
98
- throw new LifecyclePlanningError("REVIEWED_BOUNDS_MISMATCH", "Market economics exceed or differ from the exact admitted envelope bounds");
98
+ config.positions.length === 0 || config.positions.length > b.maximumPositions) {
99
+ throw new LifecyclePlanningError("PROFILE_BOUNDS_MISMATCH", "Market economics exceed or differ from the exact admitted envelope bounds");
99
100
  }
100
101
  }
101
102
 
@@ -1,8 +1,8 @@
1
1
  import { encodeAbiParameters, encodeFunctionData, keccak256, toHex, zeroAddress, type Address, type Hex } from "viem";
2
2
  import { launchLifecycleAbi, lifecycleAdapterAbi, lifecycleErc20Abi, lifecycleFundingEscrowAbi, lifecycleRegistryAbi } from "./abi.js";
3
3
  import { hashLaunchIdentity, hashLaunchPlan, LifecycleFundingKind, LifecycleMode, LifecyclePhase, LifecycleRewardMode, LifecycleTokenKind, LifecycleVenue, LIFECYCLE_ERC404_CAPABILITY, LIFECYCLE_MAX_REWARD_ERC20_SUPPLY, LIFECYCLE_MAX_ERC404_SUPPLY, LIFECYCLE_MULTI_POSITION_CAPABILITY, LIFECYCLE_REQUIRED_CAPABILITIES, marketIdentityV1Components, parseLaunchPlan, serializeLaunchPlan, type LaunchPlanV1, type MarketIdentityV1 } from "./schema.js";
4
- import { ABYSS_LIFECYCLE_CONFIG_SCHEMA, readLaunchProgress, readLifecycleProfiles, readPoolBoundHookDeployment, validateLifecycleMarketIdentity } from "./progress.js";
5
- import { decodeAbyssLifecycleMarketConfig, decodePoolBoundV4LifecycleMarketConfig, decodeV4LifecycleMarketConfig, encodePoolBoundV4LifecycleMarketConfig, hasLifecycleV4HookPermissions, minePoolBoundHookSalt, predictPoolBoundHookAddress, validateReviewedV4LifecycleMarket, V4_LIFECYCLE_CONFIG_SCHEMA, V4_POOL_BOUND_LIFECYCLE_CONFIG_SCHEMA, type V4LifecycleMarketConfig, type V4PoolBoundLifecycleMarketConfig } from "./markets.js";
4
+ import { ABYSS_LIFECYCLE_CONFIG_SCHEMA, readLaunchProgress, readLifecycleProfiles, readPoolBoundHookDeployment, validateLifecycleMarketIdentity, validateV4LifecycleOracle } from "./progress.js";
5
+ import { decodeAbyssLifecycleMarketConfig, decodePoolBoundV4LifecycleMarketConfig, decodeV4LifecycleMarketConfig, encodePoolBoundV4LifecycleMarketConfig, hasLifecycleV4HookPermissions, minePoolBoundHookSalt, predictPoolBoundHookAddress, validateV4LifecycleMarket, V4_LIFECYCLE_CONFIG_SCHEMA, V4_POOL_BOUND_LIFECYCLE_CONFIG_SCHEMA, type V4LifecycleMarketConfig, type V4PoolBoundLifecycleMarketConfig } from "./markets.js";
6
6
  import { assertLifecycleBlock, lifecycleRpc, readLifecycleBlock, readLifecycleContract, resolveLifecycleLimits, rpcHex, rpcQuantity } from "./rpc.js";
7
7
  import { simulateLaunchTransactions } from "./simulation.js";
8
8
  import { LifecyclePlanningError, type BuildNextTransactionOptions, type CanonicalLaunchProgress, type LifecycleBlock, type LifecycleFundingPrerequisite, type LifecyclePoolBoundHookDeployment, type LifecycleProfile, type LifecycleRpcClient, type LifecycleSimulation, type LifecycleTransaction, type PlannedLaunch, type PlanLaunchOptions, type PoolBoundLifecyclePreparationProgress, type SimulateLaunchPlanOptions } from "./types.js";
@@ -40,7 +40,7 @@ function validatePlanShape(plan: LaunchPlanV1): void {
40
40
  if (market.configVersion === 4) config = decodeV4LifecycleMarketConfig(market.config);
41
41
  else if (market.configVersion === 5) config = decodePoolBoundV4LifecycleMarketConfig(market.config);
42
42
  if (config !== undefined) {
43
- if (config.profileId.toLowerCase() !== market.profileId.toLowerCase()) throw new LifecyclePlanningError("REVIEWED_TERMS_MISMATCH", "Inner config profile differs from the committed market profile");
43
+ if (config.profileId.toLowerCase() !== market.profileId.toLowerCase()) throw new LifecyclePlanningError("PROFILE_TERMS_MISMATCH", "Inner config profile differs from the committed market profile");
44
44
  const quote = market.quoteAsset.toLowerCase();
45
45
  if (v4Quotes.has(quote)) throw new LifecyclePlanningError("DUPLICATE_V4_QUOTE", "Only one V4 market per quote asset is allowed across shared and pool-bound offerings; put multiple positions in that market");
46
46
  v4Quotes.add(quote);
@@ -137,7 +137,10 @@ async function validatePendingInputs(client: LifecycleRpcClient, planned: Planne
137
137
  const schema = profile.registration.configSchema.toLowerCase();
138
138
  const config = schema === V4_LIFECYCLE_CONFIG_SCHEMA.toLowerCase() ? decodeV4LifecycleMarketConfig(market.config) : schema === V4_POOL_BOUND_LIFECYCLE_CONFIG_SCHEMA.toLowerCase() ? decodePoolBoundV4LifecycleMarketConfig(market.config) : schema === ABYSS_LIFECYCLE_CONFIG_SCHEMA.toLowerCase() ? decodeAbyssLifecycleMarketConfig(market.config) : undefined;
139
139
  if (config === undefined) throw new LifecyclePlanningError("UNSUPPORTED_SCHEMA", `Unsupported lifecycle config schema ${profile.registration.configSchema}`);
140
- if ("developerFeeBps" in config) validateReviewedV4LifecycleMarket({ market, config, profile, token: planned.predictedToken });
140
+ if ("developerFeeBps" in config) {
141
+ validateV4LifecycleMarket({ market, config, profile, token: planned.predictedToken });
142
+ if (config.version === 4) await validateV4LifecycleOracle({ client, envelope: profile.envelope!, oracleConfigId: config.oracleConfigId, block });
143
+ }
141
144
  if (config.positions.length === 0 || config.positions.length > 32 || positionCount + config.positions.length > 32) throw new LifecyclePlanningError("INVALID_POSITION_COUNT", "A launch commits at most 32 positive positions across every market/venue");
142
145
  positionCount += config.positions.length;
143
146
  const rewards = progress.canonical.rewards === zeroAddress && plan.token.rewardMode === LifecycleRewardMode.Dividends ? planned.predictedToken : progress.canonical.rewards;
@@ -1,6 +1,6 @@
1
1
  import { concatHex, decodeFunctionData, encodeAbiParameters, encodeFunctionData, keccak256, stringToHex, toHex, zeroAddress, zeroHash, type Address, type Hash, type Hex } from "viem";
2
- import { abyssLifecycleAdapterAbi, launchLifecycleAbi, lifecycleAdapterAbi, lifecycleDirectoryAbi, lifecycleErc20Abi, lifecycleOracleFactoryAbi, lifecycleRegistryAbi, reviewedV4FeeCollectorFactoryV1Abi, lifecycleV4HookAbi, lifecycleV4LockerAbi, reviewedPoolBoundHookDeployerV1Abi, reviewedSharedHookDeployerV1Abi, poolBoundLaunchFeeHookV1Abi, reviewedPoolBoundV4MarketAdapterV1Abi, reviewedSharedV4MarketAdapterV1Abi } from "./abi.js";
3
- import { decodePoolBoundV4LifecycleMarketConfig, encodePoolBoundHookParameters, hasLifecycleV4HookPermissions, hashPoolBoundV4MarketCommitment, poolBoundHookInitCodeHash, predictPoolBoundHookAddress, validateReviewedV4LifecycleMarket, V4_LIFECYCLE_CONFIG_SCHEMA, V4_POOL_BOUND_LIFECYCLE_CONFIG_SCHEMA, type PoolBoundHookParametersV1 } from "./markets.js";
2
+ import { abyssLifecycleAdapterAbi, launchLifecycleAbi, lifecycleAdapterAbi, lifecycleDirectoryAbi, lifecycleErc20Abi, lifecycleOracleFactoryAbi, lifecycleRegistryAbi, poolFeeCollectorFactoryV1Abi, lifecycleV4HookAbi, lifecycleV4LockerAbi, poolHookDeployerV1Abi, sharedHookDeployerV1Abi, poolBoundLaunchFeeHookV1Abi, poolMarketAdapterV1Abi, sharedMarketAdapterV1Abi } from "./abi.js";
3
+ import { decodePoolBoundV4LifecycleMarketConfig, encodePoolBoundHookParameters, hasLifecycleV4HookPermissions, hashPoolBoundV4MarketCommitment, poolBoundHookInitCodeHash, predictPoolBoundHookAddress, validateV4LifecycleMarket, V4_LIFECYCLE_CONFIG_SCHEMA, V4_POOL_BOUND_LIFECYCLE_CONFIG_SCHEMA, type PoolBoundHookParametersV1 } from "./markets.js";
4
4
  import { hashLaunchIdentity, hashLaunchPlan, hashLaunchBounds, hashLaunchDependencies, hashLifecycleProfile, launchProgressV1Components, LifecycleMode, LifecyclePhase, LifecycleVenue, LIFECYCLE_REQUIRED_CAPABILITIES, LIFECYCLE_ERC404_CAPABILITY, LIFECYCLE_MULTI_POSITION_CAPABILITY, type AdapterRegistrationV1, type LaunchPlanV1, type LaunchProgressV1, type MarketIdentityV1, type MarketLiveStateV1, type PreparedMarketV1, type ProfileRegistrationV1, type ProfileTopologyV1, type LaunchEnvelopeV2, type LifecycleDeveloperTerms } from "./schema.js";
5
5
  import { assertLifecycleBlock, lifecycleRpc, readLifecycleBlock, readLifecycleContract, readLifecycleProfileTopology, rpcHex, rpcObject, rpcQuantity } from "./rpc.js";
6
6
  import { LifecyclePlanningError, type CanonicalLaunchProgress, type LifecycleBlock, type LifecycleMarketProgress, type LifecycleProfile, type LifecycleReceiptReference, type LifecycleReceiptStatus, type LifecycleRpcClient, type PoolBoundHookDeployment, type ReadLaunchProgressOptions } from "./types.js";
@@ -52,14 +52,14 @@ async function certifyLifecycleProfile(client: LifecycleRpcClient, orchestrator:
52
52
  hashLaunchBounds(envelope.bounds).toLowerCase() !== envelope.configBoundsDigest.toLowerCase() ||
53
53
  envelope.capabilities !== registration.capabilities || envelope.capabilities !== adapter.capabilities ||
54
54
  (envelope.capabilities & ~(LIFECYCLE_REQUIRED_CAPABILITIES | LIFECYCLE_ERC404_CAPABILITY | LIFECYCLE_MULTI_POSITION_CAPABILITY)) !== 0n) {
55
- throw new LifecyclePlanningError("REVIEWED_ENVELOPE", "Frozen reviewed profile identity, bounds, capabilities or economic version differs from registry admission");
55
+ throw new LifecyclePlanningError("PROFILE_ENVELOPE", "Frozen reviewed profile identity, bounds, capabilities or economic version differs from registry admission");
56
56
  }
57
57
  const b = envelope.bounds;
58
- if (b.maximumHookFeePips >= 1000000 || b.maximumLpFeePips >= 1000000 || b.minimumTickSpacing < 1 ||
58
+ if (b.minimumTickSpacing < 1 ||
59
59
  b.maximumTickSpacing > 32767 || b.minimumTickSpacing > b.maximumTickSpacing || b.maximumPositions < 1 || b.maximumPositions > 32 ||
60
60
  b.maximumOracleCardinality < 2 || b.maximumOracleCardinality > 4096 || b.feeModeFlags === 0 || (b.feeModeFlags & ~3) !== 0 ||
61
- envelope.protocolTreasury === zeroAddress || (envelope.protocolFeeDenominator !== 0 && (envelope.protocolFeeDenominator < 4 || envelope.protocolFeeDenominator > 10))) throw new LifecyclePlanningError("REVIEWED_BOUNDS", "Registry envelope has unsupported executable bounds");
62
- const abi = bound ? reviewedPoolBoundV4MarketAdapterV1Abi : reviewedSharedV4MarketAdapterV1Abi;
61
+ envelope.protocolTreasury === zeroAddress || (envelope.protocolFeeDenominator !== 0 && (envelope.protocolFeeDenominator < 4 || envelope.protocolFeeDenominator > 10))) throw new LifecyclePlanningError("PROFILE_BOUNDS", "Registry envelope has unsupported executable bounds");
62
+ const abi = bound ? poolMarketAdapterV1Abi : sharedMarketAdapterV1Abi;
63
63
  for (const [getter, expected] of [["PROFILE_ID", id], ["CONFIG_SCHEMA", registration.configSchema], ["implementationRegistry", registry]] as const) {
64
64
  const actual = await readLifecycleContract<Hex>(client, adapter.implementation, abi, getter, [], block);
65
65
  if (actual.toLowerCase() !== expected.toLowerCase()) throw new LifecyclePlanningError("PROFILE_DEPENDENCY", "Reviewed adapter metadata differs from registry certification");
@@ -77,12 +77,12 @@ async function certifyLifecycleProfile(client: LifecycleRpcClient, orchestrator:
77
77
  [g.hookDeployer, g.hookDeployerCodeHash]] as const) {
78
78
  if (keccak256(await readDependencyCode(client, address, block)).toLowerCase() !== expectedHash.toLowerCase()) throw new LifecyclePlanningError("PROFILE_CODE_HASH", "Live runtime differs from the frozen reviewed graph hash");
79
79
  }
80
- const collectorDeployer = await readLifecycleContract<Address>(client, g.collectorFactory, reviewedV4FeeCollectorFactoryV1Abi, "collectorDeployer", [], block);
80
+ const collectorDeployer = await readLifecycleContract<Address>(client, g.collectorFactory, poolFeeCollectorFactoryV1Abi, "collectorDeployer", [], block);
81
81
  const launcher = await readLifecycleContract<Address>(client, g.locker, lifecycleV4LockerAbi, "launcher", [], block);
82
82
  const lockerManager = await readLifecycleContract<Address>(client, g.locker, lifecycleV4LockerAbi, "poolManager", [], block);
83
83
  if (collectorDeployer.toLowerCase() !== g.collectorDeployer.toLowerCase() || launcher.toLowerCase() !== adapter.implementation.toLowerCase() ||
84
84
  lockerManager.toLowerCase() !== g.manager.toLowerCase() || g.locker.toLowerCase() === g.manager.toLowerCase() || g.locker.toLowerCase() === g.hookRoot.toLowerCase()) throw new LifecyclePlanningError("PROFILE_DEPENDENCY", "Reviewed collector or locker authority differs from the frozen graph");
85
- const deployerAbi = shared ? reviewedSharedHookDeployerV1Abi : reviewedPoolBoundHookDeployerV1Abi;
85
+ const deployerAbi = shared ? sharedHookDeployerV1Abi : poolHookDeployerV1Abi;
86
86
  const creationCodeHash = await readLifecycleContract<Hex>(client, g.hookDeployer, deployerAbi, "creationCodeHash", [], block);
87
87
  const chunk0 = await readLifecycleContract<Address>(client, g.hookDeployer, deployerAbi, "codeChunk0", [], block);
88
88
  const chunk1 = await readLifecycleContract<Address>(client, g.hookDeployer, deployerAbi, "codeChunk1", [], block);
@@ -101,7 +101,7 @@ async function certifyLifecycleProfile(client: LifecycleRpcClient, orchestrator:
101
101
  const recordedHash = await readLifecycleContract<Hex>(client, g.hookDeployer, deployerAbi, "deployedCodeHash", [g.hookRoot], block);
102
102
  const initCodeHash = keccak256(concatHex([creationCode, encodeAbiParameters([{ type: "address" }, { type: "address" }, { type: "address" }], [g.manager, adapter.implementation, g.oracleFactory])]));
103
103
  const prediction = predictPoolBoundHookAddress({ deployer: g.hookDeployer, initCodeHash, salt: g.sharedHookSalt });
104
- const remotePrediction = await readLifecycleContract<Address>(client, g.hookDeployer, reviewedSharedHookDeployerV1Abi, "predict", [g.manager, adapter.implementation, g.oracleFactory, g.sharedHookSalt], block);
104
+ const remotePrediction = await readLifecycleContract<Address>(client, g.hookDeployer, sharedHookDeployerV1Abi, "predict", [g.manager, adapter.implementation, g.oracleFactory, g.sharedHookSalt], block);
105
105
  if (recordedHash === zeroHash || recordedHash.toLowerCase() !== g.hookRuntimeCodeHash.toLowerCase() || keccak256(rootCode).toLowerCase() !== recordedHash.toLowerCase() ||
106
106
  !hasLifecycleV4HookPermissions(g.hookRoot) || prediction.toLowerCase() !== g.hookRoot.toLowerCase() || remotePrediction.toLowerCase() !== prediction.toLowerCase()) throw new LifecyclePlanningError("PROFILE_DEPENDENCY", "Shared root lacks exact typed-deployer runtime and constructor/salt provenance");
107
107
  for (const [getter, expected] of [["registrar", adapter.implementation], ["poolManager", g.manager], ["oracleFactory", g.oracleFactory]] as const) {
@@ -110,12 +110,22 @@ async function certifyLifecycleProfile(client: LifecycleRpcClient, orchestrator:
110
110
  if (await readLifecycleContract<bigint>(client, g.hookRoot, lifecycleV4HookAbi, "REQUIRED_HOOK_FLAGS", [], block) !== 0x1afcn ||
111
111
  await readLifecycleContract<bigint>(client, g.hookRoot, lifecycleV4HookAbi, "ALL_HOOK_MASK", [], block) !== 0x3fffn) throw new LifecyclePlanningError("PROFILE_DEPENDENCY", "Shared hook callbacks differ from admission");
112
112
  } else if (g.hookRoot !== zeroAddress || g.hookRuntimeCodeHash !== zeroHash || g.sharedHookSalt !== zeroHash) throw new LifecyclePlanningError("PROFILE_DEPENDENCY", "Bound topology cannot reuse a shared root");
113
- 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())) throw new LifecyclePlanningError("REVIEWED_TERMS", "Stable author identity is an excluded reviewed graph destination");
114
- const [move, cardinality] = await readLifecycleContract<readonly [number, number]>(client, g.oracleFactory, lifecycleOracleFactoryAbi, "oracleConfigs", [b.oracleConfigId], block);
115
- if (move === 0 || move > 887272 || cardinality < 2 || cardinality > b.maximumOracleCardinality) throw new LifecyclePlanningError("REVIEWED_BOUNDS", "Reviewed oracle configuration exceeds the admitted bounds");
113
+ 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())) throw new LifecyclePlanningError("PROFILE_TERMS", "Stable author identity is an excluded reviewed graph destination");
116
114
  return { topology, boundGraph: bound ? { manager: g.manager, oracleFactory: g.oracleFactory, locker: g.locker, collectorFactory: g.collectorFactory, deployer: g.hookDeployer, creationCode } : undefined };
117
115
  }
118
116
 
117
+ /** Validate the market-selected registered oracle against the certified factory at the pinned block. */
118
+ export async function validateV4LifecycleOracle(options: {
119
+ client: LifecycleRpcClient; envelope: LaunchEnvelopeV2; oracleConfigId: Hex; block: LifecycleBlock;
120
+ }): Promise<void> {
121
+ const { client, envelope, oracleConfigId, block } = options;
122
+ if (oracleConfigId.toLowerCase() === zeroHash) throw new LifecyclePlanningError("INVALID_ORACLE_CONFIG", "Market oracle configuration must be nonzero");
123
+ const [move, cardinality] = await readLifecycleContract<readonly [number, number]>(client, envelope.graph.oracleFactory, lifecycleOracleFactoryAbi, "oracleConfigs", [oracleConfigId], block);
124
+ if (move <= 0 || move > 887272 || cardinality < 2 || cardinality > envelope.bounds.maximumOracleCardinality) {
125
+ throw new LifecyclePlanningError("INVALID_ORACLE_CONFIG", "Selected market oracle is unregistered or exceeds the admitted cardinality bounds");
126
+ }
127
+ }
128
+
119
129
  export async function readLifecycleProfiles(options: { client: LifecycleRpcClient; orchestrator: Address; profileIds?: readonly Hex[]; offset?: bigint; limit?: bigint }, pinnedBlock?: LifecycleBlock): Promise<LifecycleProfile[]> {
120
130
  const block = pinnedBlock ?? await readLifecycleBlock(options.client);
121
131
  const chainId = rpcQuantity(await lifecycleRpc(options.client, "eth_chainId"), "chain ID");
@@ -143,7 +153,7 @@ export async function readLifecycleProfiles(options: { client: LifecycleRpcClien
143
153
  developerTerms = { adapter: implementation, beneficiary, maximumDeveloperFeeBps, termsDigest, enabled };
144
154
  if (implementation.toLowerCase() !== adapter.implementation.toLowerCase() || beneficiary.toLowerCase() !== envelope.beneficiary.toLowerCase() ||
145
155
  termsDigest.toLowerCase() !== envelope.termsDigest.toLowerCase() || maximumDeveloperFeeBps !== envelope.maximumDeveloperFeeBps ||
146
- maximumDeveloperFeeBps > protocolMaximumDeveloperFeeBps) throw new LifecyclePlanningError("REVIEWED_TERMS", "Registry developer terms differ from the frozen reviewed envelope");
156
+ maximumDeveloperFeeBps > protocolMaximumDeveloperFeeBps) throw new LifecyclePlanningError("PROFILE_TERMS", "Registry developer terms differ from the frozen reviewed envelope");
147
157
  }
148
158
  await certifyLifecycleProfile(options.client, options.orchestrator, registry, id, registration, adapter, topology, envelope, block, chainId);
149
159
  const eligible = await readLifecycleContract<Address>(options.client, registry, lifecycleRegistryAbi, "requireEligible", [registration.adapterId, id, topology.configVersion, LIFECYCLE_REQUIRED_CAPABILITIES], block);
@@ -175,11 +185,12 @@ async function readPoolBoundHookDeploymentDetails(options: { client: LifecycleRp
175
185
  if (boundGraph === undefined) throw new LifecyclePlanningError("UNCERTIFIED_TOPOLOGY", "Market does not have a certified pool-bound deployer graph");
176
186
  const token = await readLifecycleContract<Address>(client, plan.orchestrator, launchLifecycleAbi, "predictToken", [plan], block);
177
187
  const config = decodePoolBoundV4LifecycleMarketConfig(market.config);
178
- validateReviewedV4LifecycleMarket({ market, config, token, profile: {
188
+ validateV4LifecycleMarket({ market, config, token, profile: {
179
189
  id: market.profileId, registration, adapter, topology: recorded, envelope,
180
190
  developerTerms: { adapter: implementation, beneficiary, maximumDeveloperFeeBps, termsDigest, enabled },
181
191
  protocolMaximumDeveloperFeeBps, venueKind: "uniswap-v4", admitted: enabled,
182
192
  } });
193
+ await validateV4LifecycleOracle({ client, envelope, oracleConfigId: config.oracleConfigId, block });
183
194
  const expected: PoolBoundHookParametersV1 = {
184
195
  poolManager: boundGraph.manager, registrar: adapter.implementation, oracleFactory: boundGraph.oracleFactory, core: plan.orchestrator,
185
196
  liquidityLocker: boundGraph.locker, token, quoteCurrency: market.quoteAsset, lpFeePips: config.lpFeePips, tickSpacing: config.tickSpacing,
@@ -188,16 +199,16 @@ async function readPoolBoundHookDeploymentDetails(options: { client: LifecycleRp
188
199
  marketCommitment: hashPoolBoundV4MarketCommitment({ chainId, core: plan.orchestrator, registrar: adapter.implementation, token, market }),
189
200
  expectedPositionCount: config.positions.length,
190
201
  };
191
- const [parameters, parameterSalt] = await readLifecycleContract<readonly [PoolBoundHookParametersV1, Hex]>(client, boundGraph.collectorFactory, reviewedV4FeeCollectorFactoryV1Abi, "poolBoundHookParameters", [adapter.implementation, token, market], block);
192
- const [deployer, initCodeHash, salt, predictedHook] = await readLifecycleContract<readonly [Address, Hex, Hex, Address]>(client, adapter.implementation, reviewedPoolBoundV4MarketAdapterV1Abi, "hookDeploymentMetadata", [token, market], block);
193
- const remoteInitCodeHash = await readLifecycleContract<Hex>(client, boundGraph.deployer, reviewedPoolBoundHookDeployerV1Abi, "initCodeHash", [expected], block);
194
- const remotePrediction = await readLifecycleContract<Address>(client, boundGraph.deployer, reviewedPoolBoundHookDeployerV1Abi, "predict", [expected, config.hookSalt], block);
202
+ const [parameters, parameterSalt] = await readLifecycleContract<readonly [PoolBoundHookParametersV1, Hex]>(client, boundGraph.collectorFactory, poolFeeCollectorFactoryV1Abi, "poolBoundHookParameters", [adapter.implementation, token, market], block);
203
+ const [deployer, initCodeHash, salt, predictedHook] = await readLifecycleContract<readonly [Address, Hex, Hex, Address]>(client, adapter.implementation, poolMarketAdapterV1Abi, "hookDeploymentMetadata", [token, market], block);
204
+ const remoteInitCodeHash = await readLifecycleContract<Hex>(client, boundGraph.deployer, poolHookDeployerV1Abi, "initCodeHash", [expected], block);
205
+ const remotePrediction = await readLifecycleContract<Address>(client, boundGraph.deployer, poolHookDeployerV1Abi, "predict", [expected, config.hookSalt], block);
195
206
  const localInitCodeHash = poolBoundHookInitCodeHash(boundGraph.creationCode, expected);
196
207
  const localPrediction = predictPoolBoundHookAddress({ deployer: boundGraph.deployer, initCodeHash: localInitCodeHash, salt: config.hookSalt });
197
208
  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()) throw new LifecyclePlanningError("HOOK_DEPLOYMENT_CHANGED", "Bound hook metadata differs from the exact certified constructor economics, factory, initcode, salt or CREATE2 prediction");
198
209
  const hookCode = rpcHex(await lifecycleRpc(client, "eth_getCode", [predictedHook, toHex(block.number)]), "bound hook code");
199
210
  if (hookCode !== "0x") {
200
- const deployedCodeHash = await readLifecycleContract<Hex>(client, deployer, reviewedPoolBoundHookDeployerV1Abi, "deployedCodeHash", [predictedHook], block);
211
+ const deployedCodeHash = await readLifecycleContract<Hex>(client, deployer, poolHookDeployerV1Abi, "deployedCodeHash", [predictedHook], block);
201
212
  if ((hookCode.length - 2) / 2 > 24576 || deployedCodeHash === zeroHash || deployedCodeHash.toLowerCase() !== keccak256(hookCode).toLowerCase()) throw new LifecyclePlanningError("HOOK_DEPLOYMENT_CHANGED", "Existing hook lacks this exact typed deployer's bounded runtime-code provenance");
202
213
  for (const [getter, expectedAddress] of [
203
214
  ["poolManager", expected.poolManager], ["registrar", expected.registrar], ["oracleFactory", expected.oracleFactory],
@@ -238,7 +249,7 @@ export async function buildPoolBoundHookDeploymentTransaction(options: { client:
238
249
  if (!hasLifecycleV4HookPermissions(deployment.predictedHook)) throw new LifecyclePlanningError("INVALID_HOOK_BITS", "Finalize a salt with the exact lifecycle hook permissions before predeployment");
239
250
  if (hashLaunchPlan(options.plan) !== commitment) throw new LifecyclePlanningError("PLAN_MUTATED", "Economic plan changed while constructing hook deployment calldata");
240
251
  await assertLifecycleBlock(options.client, block);
241
- return { to: deployment.deployer, data: encodeFunctionData({ abi: reviewedPoolBoundHookDeployerV1Abi, functionName: "deploy", args: [parameters, deployment.salt] }), value: 0n, deployment };
252
+ return { to: deployment.deployer, data: encodeFunctionData({ abi: poolHookDeployerV1Abi, functionName: "deploy", args: [parameters, deployment.salt] }), value: 0n, deployment };
242
253
  }
243
254
 
244
255
  export function validateLifecycleMarketIdentity(identity: MarketIdentityV1, plan: LaunchPlanV1, token: Address, marketIndex: number): void {
@@ -71,9 +71,8 @@ export type MarketLiveStateV1 = {
71
71
  };
72
72
 
73
73
  export type LaunchBoundsV2 = {
74
- maximumHookFeePips: number; maximumLpFeePips: number; minimumTickSpacing: number; maximumTickSpacing: number;
74
+ minimumTickSpacing: number; maximumTickSpacing: number;
75
75
  maximumPositions: number; maximumOracleCardinality: number; feeModeFlags: number;
76
- externalLiquidityDisabled: boolean; oracleConfigId: Hex;
77
76
  };
78
77
  export type LaunchGraphV2 = {
79
78
  manager: Address; hookRoot: Address; oracleFactory: Address; locker: Address; collectorFactory: Address;
@@ -96,11 +95,9 @@ export type SourceTermsV3 = {
96
95
  maximumDeveloperFeeBps: number; developerFeeBps: number;
97
96
  };
98
97
  export const launchBoundsV2Components = [
99
- { name: "maximumHookFeePips", type: "uint24" }, { name: "maximumLpFeePips", type: "uint24" },
100
98
  { name: "minimumTickSpacing", type: "int24" }, { name: "maximumTickSpacing", type: "int24" },
101
99
  { name: "maximumPositions", type: "uint16" }, { name: "maximumOracleCardinality", type: "uint16" },
102
- { name: "feeModeFlags", type: "uint8" }, { name: "externalLiquidityDisabled", type: "bool" },
103
- { name: "oracleConfigId", type: "bytes32" },
100
+ { name: "feeModeFlags", type: "uint8" },
104
101
  ] as const;
105
102
  export const launchGraphV2Components = [
106
103
  { name: "manager", type: "address" }, { name: "hookRoot", type: "address" },
@@ -154,7 +151,7 @@ export function hashLifecycleProfile(envelope: LaunchEnvelopeV2): Hex {
154
151
  return keccak256(encodeAbiParameters([
155
152
  { type: "bytes32" }, { type: "bytes32" }, { type: "bytes32" }, { type: "bytes32" }, { type: "bytes32" },
156
153
  { type: "uint8" }, { type: "uint32" }, { type: "uint32" }, { type: "address" }, { type: "uint16" }, { type: "uint64" },
157
- ], [keccak256(stringToHex("black-market.reviewed-launch-profile.v2")), envelope.artifactDigest,
154
+ ], [keccak256(stringToHex("black-market.launch-profile.v2")), envelope.artifactDigest,
158
155
  envelope.reviewManifestDigest, envelope.configBoundsDigest, envelope.termsDigest, envelope.topology,
159
156
  envelope.configVersion, envelope.economicVersion, envelope.beneficiary, envelope.maximumDeveloperFeeBps, envelope.capabilities]));
160
157
  }
@@ -170,7 +167,7 @@ export function hashLaunchDependencies(options: {
170
167
  { type: "address" }, { type: "bytes32" }, { type: "address" }, { type: "bytes32" },
171
168
  { type: "address" }, { type: "bytes32" }, { type: "address" }, { type: "bytes32" }, { type: "bytes32" },
172
169
  { type: "address" }, { type: "bytes32" }, { type: "address" }, { type: "bytes32" },
173
- ], [keccak256(stringToHex("black-market.reviewed-v4-dependencies.v2")), chainId, core, g.coreCodeHash,
170
+ ], [keccak256(stringToHex("black-market.v4-dependencies.v2")), chainId, core, g.coreCodeHash,
174
171
  registry, registrar, g.manager, g.managerCodeHash, g.hookRoot, g.hookRuntimeCodeHash,
175
172
  g.oracleFactory, g.oracleFactoryCodeHash, g.locker, g.lockerCodeHash, g.collectorFactory, g.collectorFactoryCodeHash,
176
173
  g.collectorDeployer, g.collectorDeployerCodeHash, g.hookDeployer, g.hookDeployerCodeHash,