@black-market/sdk 0.12.0 → 0.13.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.
@@ -1,1576 +1,686 @@
1
- # Launch lifecycle API
1
+ # Launch a token with the TypeScript SDK
2
2
 
3
- `@black-market/sdk/lifecycle` is the browser-safe launch API; its exports are also available from `@black-market/sdk`. Supply the exact chain, core, creator and registry domain. Planning, salt preparation, reads and unsigned builders never sign, publish metadata or write to the source chain. A successful controlled-local simulation is not production deployment approval.
3
+ Use SDK **0.13.0** on Robinhood Chain mainnet (**4663**). For a first launch, run
4
+ [`launch-erc20-abyss.ts`](../examples/launch-erc20-abyss.ts): one market, one position,
5
+ one buy. [Runner setup](../examples/README.md) covers its environment and commands.
6
+ This guide explains the economics and the public APIs for your own integration.
4
7
 
5
- The canonical authorities are the registry/certification/adapter/config contracts under
6
- `contracts/src/launch/lifecycle/v2`, typed hook deployers, and `contracts/src/launch/fees/v3`
7
- in the protocol repository. Plan identity is `LaunchPlanV1`. Pool-bound V4 uses **config 6**,
8
- canonical Abyss uses **config 1**, and generic current shared-V4 support uses **config 4**.
8
+ ## Choose an offering
9
9
 
10
- ## 0.12.0 release: opt-in UFG treasuries
10
+ Choose the token behavior, fee destination, and pool implementation separately.
11
+ `LifecycleTokenKind` selects ERC20 or ERC404; `LifecycleRewardMode` selects none,
12
+ staking, or dividends. A pool profile selects the admitted venue/hook implementation.
11
13
 
12
- UFG is an additive **proxy launch route**, not a new token kind or a replacement
13
- for the core path. Existing launch defaults, calldata, salts and HMM behavior are
14
- unchanged when `ufg` is omitted. The pinned Robinhood deployment (chain **4663**) is:
14
+ | Offering | `KnownLifecycleProfile` keys | Fee behavior / restrictions |
15
+ | --- | --- | --- |
16
+ | Canonical Abyss | `Abyss0`, `Abyss1`, `Abyss2`, `Abyss3` | STANDARD, STANDARD_ORACLE, QUOTE, QUOTE_ORACLE respectively; config1. |
17
+ | Fixed V4 | `V4FixedFeePool` | One bound hook per pool; fixed `hookFeePips`, optional LP fee. |
18
+ | Dynamic V4 | `V4DynamicFeePool` | Oracle-velocity fee between minimum and maximum; LP and developer fees 0. |
19
+ | HMM V2 | `V4HmmFixedPool`, `V4HmmDynamicPool` | Auxiliary fee-funded bids/asks; LP and developer fees 0; explicit risk guards. |
20
+ | UFG | Abyss QUOTE/QUOTE_ORACLE or `V4UfgFixedPool`, `V4UfgDynamicPool` | Treasury route; ERC20 without rewards; quote-only fees, executor bounty 0. |
15
21
 
16
- | Export | Address |
17
- | --- | --- |
18
- | `UFG_LAUNCH_PROXY_ADDRESS` | `0xf6673d859DE03eB74A0b01696Edb97f941dFD0CA` |
19
- | `UFG_TREASURY_FACTORY_ADDRESS` | `0x26Aa5D52134c8E6c4C01E9fb53A7568c0C64901F` |
20
- | `UFG_UPSTREAM_FACTORY_ADDRESS` | `0xA170b5A58C111334A7237F2Ea2c428ec5022f68E` |
21
- | `UFG_ORCHESTRATOR_ADDRESS` | `0x91560876033d568d25CDe98C78c33ff8FC43962c` |
22
-
23
- `KnownLifecycleProfile.V4UfgFixedPool` (`v4-ufg-fixed-pool`) selects profile
24
- `0x5c6045af480814e469f3dba7c120d264047c9320b869c63a064536041a2dbae6`;
25
- `V4UfgDynamicPool` (`v4-ufg-dynamic-pool`) selects
26
- `0x03de8d5e38f2957d80ecb9f2e59d555601ff091dfb17bce84178956a2e8d9b41`.
27
- `isUfgProfile` recognizes only these IDs. Frozen envelopes, adapter/registration/
28
- topology and bound-hook metadata come from committed registry readback at block
29
- **94,551,140** (`test/fixtures/ufg-registry-readback.json`). Regenerate with
30
- `node --experimental-transform-types scripts/generate-ufg-presets.mjs /path/to/black-market`;
31
- add `--readback` only to recapture that exact block with read-only RPC.
32
- The fixed and zero-royalty dynamic creation bytes exactly match the existing
33
- `FixedFeePoolV1` / `DynamicFeePoolV1` artifacts, so no duplicate hook enum is needed.
34
- UFG deployers, lockers, collectors and adapters are nevertheless different graphs.
35
- Frozen presets are construction evidence, not live admission.
36
-
37
- ### Configuration and supported markets
38
-
39
- `UfgLaunchConfig` is `{ treasuryShareBps, exitFeeBps, reserveAssets }`. The proxy
40
- requires treasury share **10000**, integer exit fee **0..2000 bps**, and **1..7**
41
- nonzero, unique, strictly ascending reserve addresses, excluding the predicted
42
- launch token. Reserves and market quotes must form exactly the same set.
43
-
44
- - ERC20 only, no staking/dividends, zero executor fee and at least one market.
45
- Neither inventory nor initial-buy recipients may be the proxy.
46
- - Native Abyss uses the canonical adapter with config1 profile **2 (QUOTE)** or
47
- **3 (QUOTE_ORACLE)** and the matching canonical profile ID. Its canonical
48
- config has **no tail**.
49
- - V4 uses config6 and one of the two UFG profiles, `feeMode: 1` (QuoteOnly) and
50
- `lpFeePips: 0`. Preset zero-royalty terms still apply. Supply/position budgets,
51
- price/ranges, oracle registration and ordinary lifecycle bounds are unchanged.
52
- - Fee policies contain exactly the token plus reserves in ascending order:
53
- token `{ ownerBps: 0, rewardsBps: 0, burnBps: 10000 }`, reserves
54
- `{ ownerBps: 10000, rewardsBps: 0, burnBps: 0 }`.
55
- `buildUfgFeeAssetPolicies(predictedToken, reserveAssets)` builds that exact set;
56
- reserve ordering is validated, never silently repaired.
57
-
58
- `encodeUfgLaunchConfig` encodes **one dynamic tuple**:
59
- `abi.encode((uint16,uint16,address[]))`, including the leading `0x20` offset.
60
- `decodeUfgLaunchConfig` checks canonical re-encoding; trailing bytes, alternate
61
- offsets and dirty padding are rejected. `hashUfgLaunchConfig` is its keccak256.
62
- Config6's typed `ufgLaunchConfig` automatically appends that encoding after the
63
- canonical V6 prefix and decodes/splits it on read. Every UFG V4 tail must equal
64
- the proxy config. The constructor economics hash remains **prefix-only with zero
65
- hook salt**; the full plan hash binds the tail. No UFG tail is added to ordinary
66
- V4, HMM or Abyss configs. Independent `cast abi-encode` fixtures are regenerated
67
- with `node scripts/generate-ufg-fixtures.mjs`.
68
-
69
- ### Principal funding, planning and recovery
70
-
71
- The economic plan's **creator is the proxy**. Its nonce is
72
- `ufgLaunchNonce(principal, nonce96) = (uint160(principal) << 96) | nonce96`.
73
- The `account` and every sender are the **same principal**, never the proxy.
74
- The core still derives launch ID/token from the committed core/proxy/nonce.
22
+ Use `getKnownLifecycleProfile` for construction metadata. Check current admission
23
+ with `readLifecycleProfiles`; registered totals include retired profiles.
24
+ This read-only snippet lists the live registry, including each refusal reason:
75
25
 
76
26
  ```ts
77
- const config = {
78
- treasuryShareBps: 10000, exitFeeBps: 100,
79
- reserveAssets: [getAddresses(4663).weth],
80
- };
81
- // economicPlan already contains your full token/funding/market/buy economics;
82
- // every UFG V4 market was encoded with this same ufgLaunchConfig.
83
- const plan = { ...economicPlan, creator: UFG_LAUNCH_PROXY_ADDRESS,
84
- nonce: ufgLaunchNonce(principal, 1n) };
85
- const token = await client.readContract({ address: plan.orchestrator,
86
- abi: launchLifecycleAbi, functionName: "predictToken", args: [plan] });
87
- plan.feeAssets = buildUfgFeeAssetPolicies(token, config.reserveAssets);
88
- validateUfgLaunchPlan(plan, config, token); // pure/offline proxy rules
89
- const planned = await prepareAndPlanLifecycleLaunch({
90
- client, account: principal, plan, mode: "staged", ufg: { config },
27
+ import {
28
+ createProtocolPublicClient, getAddresses, readLifecycleProfiles,
29
+ } from "@black-market/sdk";
30
+
31
+ const client = createProtocolPublicClient({ chainId: 4663 });
32
+ if (await client.getChainId() !== 4663) throw new Error("Wrong RPC chain");
33
+ const profiles = await readLifecycleProfiles({
34
+ client, orchestrator: getAddresses(4663).launchOrchestrator,
91
35
  });
92
- // No signing here. Obtain fresh admitted proxy envelopes before wallet execution:
93
- const next = await buildNextTransaction({ client, planned });
36
+ console.table(profiles.map((p) => ({
37
+ id: p.id, venue: p.venueKind, version: p.topology.configVersion,
38
+ admitted: p.admitted, reason: p.reason ?? "",
39
+ })));
94
40
  ```
95
41
 
96
- The opt-in works on `planLaunch` too. `PlannedLaunch.ufg` retains config and its
97
- hash; `simulateLaunchPlan`, `readLaunchProgress`, and `buildNextTransaction`
98
- (including `action: "cancel"`) retain the route and reject config mutation,
99
- wrong principal/nonce, wrong receipt target/config, or proxy binding mismatch.
100
- Salt mining and optional buy-minimum calibration use the existing machinery.
101
- Native Nitro metering/execution proofs remain mandatory on production chain4663.
102
-
103
- The proxy pulls summed non-native inputs from the principal: ERC20 asset/amount
104
- or swap inputAsset/inputAmount. **Approve the proxy, not the funding escrow**.
105
- `buildUfgApprovals(plan)` builds exact summed approvals; planning observes current
106
- allowances and includes zero-reset commands when needed. Native-wrap and native
107
- swap inputs are `msg.value` on atomic/begin only. Refunds return to the principal.
108
-
109
- Pure `buildUfgLaunchTransactions({ plan, config, mode, preparationBatches })`
110
- produces proxy `launchAtomic(plan, config)`, or `beginLaunch(plan, Staged, config)`,
111
- ordered `prepareMarkets` batches, then `activateLaunch`. The explicit batches
112
- must cover all markets. `buildUfgCancelTransaction({ plan, preparedMarkets })`
113
- targets the same proxy. These builders alone do not establish execution admission.
114
- `validateUfgLaunchPlan` checks offline proxy rules; live registry continuity,
115
- reserve/token code and supply bounds, transfers and treasury creation require RPC
116
- simulation. The existing non-UFG core transaction builder is intentionally unchanged.
117
-
118
- `readUfgTreasury(client, token)` reads the treasury factory's `treasuryOf(token)`
119
- and returns the upstream address, or `undefined` for zero. Complete
120
- `ufgLaunchProxyV1Abi`, `ufgTreasuryFactoryV1Abi`, `ufgBlackMarketFactoryAbi` and
121
- `ufgBlackMarketTreasuryAbi` expose proxy config hashes and upstream treasury
122
- collect/redeem/assets/backing/position/debt operations without inventing wrappers.
123
- The pure example is `node examples/ufg-launch-config.ts` after building.
124
-
125
- ### UFG owned-fork check
126
-
127
- This explicitly authorized local-only integration uses the unlocked Anvil default
128
- account, no keys, and only public SDK launch APIs. In separate terminals:
129
-
130
- ```sh
131
- anvil --fork-url https://rpc.mainnet.chain.robinhood.com/ \
132
- --chain-id 4663 --port 8561 \
133
- --gas-limit 100000000 --silent
134
- pnpm build
135
- UFG_FORK_RPC_URL=http://127.0.0.1:8561 node test/integration/ufg-launch.fork.mjs
136
- ```
42
+ Do not hard-code a live profile count. Select the exact admitted profile ID and
43
+ its schema, terms, treasury, and dependency graph. For V4, permit at most **one
44
+ market per quote asset** across all hooks/fees; combine positions in that market.
45
+ An Abyss market can share the same quote. Oracle-enabled does not mean lending-approved.
137
46
 
138
- It validates the tuple against the deployed factory, mines bound-hook salts,
139
- executes Abyss QUOTE atomic, mixed Abyss QUOTE + UFG fixed staged (ERC20 funding/
140
- proxy approval), and UFG dynamic atomic launches. It calls each exact proxy
141
- command from the principal first, then checks Active, a nonzero `readUfgTreasury`
142
- and `proxy.launchConfigHash == hashUfgLaunchConfig`. Snapshot restoration makes
143
- the check repeatable. Stop Anvil afterward. Generic Anvil proves EVM contract
144
- behavior here, **not ArbOS compute/poster admission**: construction/read/mining/
145
- unsigned APIs plus actual fork `eth_call`/receipts are used, not a fake native
146
- Nitro planner certificate. Ordinary `pnpm test` never runs this funded check.
147
-
148
- ## 0.11.0 release: gas sizing, packing and prefix proof
149
-
150
- New atomic/activation envelopes containing HMM V2 markets add one forwarding target:
151
- `R = 4_250_000 + 500_000 * (hmmMarkets - 1)` gas (zero for non-HMM plans).
152
- For measured gross gas `G` (`maxUsedGas`, falling back to `gasUsed`), compute is
153
- `min(max(G + R, ceil(G * (10000 + headroomBps) / 10000)), computeCap)`.
154
- Begin/preparation/approval/cancel commands do not receive this reserve. Nitro poster
155
- gas is budgeted separately inside the same complete gas envelope. Optional padding
156
- and the reserve are targets, not refusal thresholds; exact validated replay proves
157
- the clamped envelope. There is no bisection or extra ordinary-path request.
158
-
159
- Staged planning greedily packs contiguous markets against the unpadded compute cap:
160
- HMM V2 costs 14.5M per market; other profiles cost a conservative 16M. At 32M,
161
- 1/2/3/5 HMM markets produce `[1]`/`[2]`/`[2,1]`/`[2,2,1]`.
162
- Only a packed measurement failure consuming at least 95% of its given gas splits
163
- that batch in half, regardless of its error selector. Low-gas semantic failures
164
- remain failures. `preparationBatchSize` is removed; inspect each preparation's
165
- `marketStart`/`marketCount`. The pure unsigned compiler still accepts explicit
166
- `preparationBatches`.
167
-
168
- Robinhood chain 4663 defaults `limits.maxSimulationGas` to **50_000_000n** aggregate
169
- gas **used**, not the sum of individual legal transaction envelopes. Override it
170
- for a provider with a different budget. The SDK measures and validates the longest
171
- estimated prefix that fits: begin 9.5M, preparations as above, activation 6M per
172
- market plus `R`, and small approvals/cancellation. A measurement failure after
173
- cumulative use reaches 95% of that cap defers the failed call and suffix.
174
- If even the immediate estimate cannot fit that budget, refusal first identifies
175
- any estimated per-transaction compute overflow (gas displayed in millions to one
176
- decimal), rather than suggesting that a larger RPC budget solves it. Estimates
177
- alone do not reject an envelope that can still be measured and exactly proved.
178
-
179
- `simulation.admitted`, `executionProof: "proved"` and `protocolFit: "proved"` cover
180
- only the successful proven prefix. **They do not promise eventual activation.**
181
- `simulation.deferredTransactionIds` explicitly lists unproved remaining steps
182
- (empty for full coverage); these commands carry no `gas`, `gasPrice`, `estimate`
183
- or `admission`. Call `buildNextTransaction` before each signature: canonical
184
- progress advances the proof window and gives the immediate next transaction exact
185
- gas. Reviewed envelopes receive only a direct exact replay of that immediate step,
186
- without new measurement, poster/price recommendations or suffix proof.
187
-
188
- `buySlippageBps` requires actual activation outputs. If activation is deferred,
189
- calibration refuses clearly; use a higher-budget native provider or preserve
190
- already committed minima by omitting recalibration. Outputs are never fabricated.
191
-
192
- ## 0.9.1 release: Abyss 0.01% fee tier
193
-
194
- `ABYSS_FEE_TIERS` now includes `{ feePips: 100, tickSpacing: 1, label: "0.01%" }`.
195
- Pass `fee: 100` to `deriveLaunchPoolRecipe` to derive spacing-one geometry without
196
- an explicit spacing override. Use `tickSpacing: 1` when passing that recipe to
197
- `deriveLaunchPositionRanges`; the permanent boundary tail reaches tick `887272`
198
- for launch token0 or `-887272` for launch token1. Abyss config1 lifecycle planning
199
- derives this spacing from the canonical catalog automatically.
200
-
201
- The Robinhood chain 4663 factory has enabled fee 100 with spacing 1. Other
202
- deployments must enable the tier before execution; local recipe construction is
203
- not evidence of live profile admission. Existing tiers and defaults are unchanged.
204
-
205
- ## 0.10.0 release: HMM V2 only
206
-
207
- The admitted October17 **HMM Fixed V2** and **HMM Dynamic V2** replace the SDK's
208
- earlier HMM offerings. Generic keys `KnownLifecycleProfile.V4HmmFixedPool`
209
- (`v4-hmm-fixed-pool`) and `V4HmmDynamicPool` (`v4-hmm-dynamic-pool`) now select V2.
210
- Versioned hook keys are `KnownLifecycleHook.HmmFixedPoolV2` / `HmmDynamicPoolV2`.
211
- These are not aliases for the non-HMM fixed/dynamic hooks; those APIs are unchanged.
212
-
213
- | Preset | Admitted V2 profile ID |
214
- | --- | --- |
215
- | HMM Fixed V2 (`hooks/hmm-fixed-v2`) | `0x45e957bcc7068350a65f5b4de5453b674d54d0f5eb2a88fc839da13197e80e98` |
216
- | HMM Dynamic V2 (`hooks/hmm-dynamic-v2`) | `0x8e500f203ace505072e209c5f077e11de6d071ac7f298cd75c0e05c6a55c36e6` |
47
+ <a id="deployed-infrastructure"></a>
48
+ [Deployment addresses](reference.md#deployed-infrastructure), [fee tiers](reference.md#choose-a-fee-tier),
49
+ and [config6 fields](reference.md#pool-bound-v4-version-6) are in the reference.
217
50
 
218
- Frozen adapters, envelopes, complete dependency graphs, creation code and admission
219
- fixtures preserve the V2 release and pinned registry readback at block 91,874,655.
220
- Each graph has its own collector/deployer, locker and hook deployer. Presets are
221
- construction data, not a current admission certificate: live
222
- `readLifecycleProfiles` and execution proof remain authoritative.
51
+ ## Price the launch
223
52
 
224
- Both require `lpFeePips: 0` and `developerFeeBps: 0`. Fixed charges `hookFeePips`
225
- as its static fee; Dynamic uses config6 minimum/maximum/sensitivity policy.
226
- Config6 requires a typed `hmmLaunchConfig`:
53
+ Choose supply and a whole-supply opening valuation (FDV):
227
54
 
228
- ```ts
229
- const hmmLaunchConfig = defaultHmmLaunchConfig({
230
- tokenBudget: marketTokenBudget, quoteAsset: "USDG", quoteDecimals: 6,
231
- // Illustrative caller choices, not SDK risk defaults:
232
- maxBidQuote: 100n * 10n ** 6n, // maximum 100 USDG of new/refreshed bid principal
233
- referenceDeviation: 0, // do not ask below or bid above the rounded reference
234
- });
235
- hmmLaunchConfig.strategyShareBps = 2500; // optional 25%; existing default is 50%
236
- hmmLaunchConfig.strategy.premiumDecaySeconds = 259200; // optional 72h; default off
237
- const config = encodePoolBoundV4LifecycleMarketConfig({
238
- ...reviewedV6Fields, lpFeePips: 0, developerFeeBps: 0, hmmLaunchConfig,
239
- });
55
+ ```text
56
+ FDV in USD = price per token in USD × total minted token supply
57
+ quote per token = target FDV in USD ÷ token supply ÷ quote USD price
240
58
  ```
241
59
 
242
- `HmmStrategyConfig` / `HmmLaunchConfig` are V2-only and mirror Solidity
243
- `HMMStrategyConfigV2` / `HMMLaunchConfigV2`. `encodeHmmLaunchTail` produces exactly
244
- `abi.encode(HMMLaunchConfigV2)`: **16 static words / 512 bytes**. The V6 codec
245
- appends the tail only for current HMM profile IDs. Missing tails, old 448-byte
246
- tails, extra words and noncanonical prefixes are rejected before RPC planning.
247
- Mining and recovery preserve the typed tail. The full launch-plan hash binds
248
- every tail byte, including both new guards.
249
-
250
- The hook constructor is still **one 20-word / 640-byte
251
- `PoolBoundHookParametersV2` tuple**. Do not append the HMM tail to creation code.
252
- The collector factory hashes only the canonical config6 prefix with zero hook
253
- salt for the hook's market-economics commitment; the auxiliary strategy settings
254
- are handed to the manager through the collector. Changing those settings changes
255
- the full plan commitment, but not the hook constructor's prefix-only commitment.
256
-
257
- `validateHmmLaunchConfig` enforces the factory's exact launch guards and Solidity
258
- field widths. Share is 0..10,000 bps. All 15 strategy fields are listed in wire order:
259
-
260
- | Field | Unit and accepted launch values |
261
- | --- | --- |
262
- | `bidWidth` | Tick spacings; positive int24 |
263
- | `askWidth` | Tick spacings; positive int24 |
264
- | `gap` | Tick spacings; nonnegative int24 |
265
- | `staleAfterSeconds` | Positive uint32 seconds |
266
- | `staleDisplacement` | Tick spacings; positive int24 |
267
- | `minQuoteDeploy` | Raw quote units; any uint256, including zero |
268
- | `minQuoteHarvest` | Raw quote units; positive uint256 |
269
- | `minTokenHarvest` | Raw launch-token units; positive, covered by both token caps |
270
- | `resalePremiumBps` | 0..5,000 bps |
271
- | `maxAsks` | 1..8 |
272
- | `maxAskTokenExposure` | Raw launch-token units; at least ask count and minimum token harvest, at most uint128 max |
273
- | `maxAcquiredInventory` | Raw launch-token units; at least minimum token harvest, at most uint128 max |
274
- | `premiumDecaySeconds` | Any uint32 seconds; zero disables decay |
275
- | `maxBidQuote` | Raw quote-principal ceiling for new/refreshed bids; positive and at most uint128 max |
276
- | `referenceDeviation` | Tick spacings; nonnegative int24 |
277
-
278
- For reference pool tick `R`, deviation `D` and spacing `S`, ask lower is at least
279
- `R - D*S` and bid upper is at most `R + D*S`, with side-appropriate grid rounding.
280
- The reference comes from the hook's truncated oracle, not an independent spot
281
- price supplied by the caller.
282
-
283
- The default helper **requires explicit `maxBidQuote` and `referenceDeviation`**;
284
- neither has a guessed risk default. All other defaults are preserved: 50% share,
285
- widths 5/5, gap 1, stale 86,400 seconds/displacement 5, premium 500 bps, 4 asks,
286
- decay off. Token thresholds are raw market-budget ratios: harvest budget/100,000,
287
- ask exposure budget/10, inventory budget. Too-small budgets producing zero harvest
288
- and budgets above uint128 are rejected. WETH quote deploy/harvest thresholds remain
289
- 0.0005/0.005 ETH; USDG is 1/10 six-decimal units. Catalog symbols/addresses work;
290
- explicit decimals must match. Other quotes require explicit `minQuoteDeploy` and
291
- `minQuoteHarvest`; no unsupported asset's USD price or units are inferred.
60
+ **1,000,000 tokens at $5,000 FDV = $0.005/token.** At an illustrative WETH price
61
+ of $2,486.49, that is about **0.00000201 WETH/token**. FDV is not cash raised or
62
+ quote deposited: the initial positions hold launch tokens only.
63
+ The SDK examples default to $5,000; the app defaults to $15,000. Neither number is
64
+ a recommendation or an anti-sniper guarantee. Compare the buyer's actual trade size.
292
65
 
293
- ### V2 fee funding and owner controls
66
+ ### Account for decimals and address order
294
67
 
295
- V2 reserves the strategy share **on each swap in both pool currencies, after the
296
- protocol treasury share**, rather than waiting for harvest. The remaining fees
297
- follow the normal fee-hub distribution. HMM itself does not burn fees and never
298
- touches the permanently locked launch positions.
299
-
300
- The browser-safe lifecycle and root exports include complete
301
- `hmmLiquidityManagerV2Abi`, `hmmFeeCollectorV2Abi`, `hmmFeeCollectorFactoryV2Abi`,
302
- `hmmFixedHookV2Abi` and `hmmDynamicHookV2Abi`. Use ordinary viem contract calls;
303
- the SDK adds no speculative transaction wrappers:
304
-
305
- - Read `strategyOwner()` and `liquidityManager()` on the collector. Authority
306
- follows the **current** fee owner from the canonical hub/fee-owner registry.
307
- - Call collector `setStrategyShareBps(uint16)` for 0..10,000 bps. It changes future
308
- swap reservations, not fees already reserved.
309
- - Read manager `strategyConfig()` for the complete 15-field tuple, then call
310
- `setStrategyConfig(HMMStrategyConfigV2)` to update every setting immediately.
311
- This queues a book refresh for the next maintenance pass. Lowering intake caps
312
- does not forcibly liquidate existing inventory; existing lots keep their stored
313
- floor and age. New premium settings apply to newly acquired lots.
314
- - Read `placementsPaused()` and call `setPlacementsPaused(bool)` on the manager.
315
- Pausing queues auxiliary-liquidity withdrawal on maintenance and stops new
316
- placements, not trading or fee collection. It does not withdraw locked launch LP.
317
-
318
- These writes require the current fee-owner wallet and explicit transaction
319
- authorization. Read-only planning never invokes them.
320
-
321
- ### Breaking migration from the earlier HMM SDK
322
-
323
- Rebuild unstarted HMM plans with the new preset IDs/adapters/graphs, supply both
324
- new guards, re-encode the 512-byte tail, and re-mine/re-prove the resulting plan.
325
- Do not pad old 448-byte payloads, reuse an old graph/salt, or relabel old bytes.
326
- The former `HmmFixedPoolV1` / `HmmDynamicPoolV1` enum members and old HMM metadata
327
- have no compatibility aliases. Existing onchain HMM V1 profiles/markets were not
328
- deregistered or upgraded by this SDK release; submitted legacy plans must not be
329
- mutated into V2 plans. Use tooling matching their original contract release.
330
-
331
- Independent Solidity ABI fixtures are regenerated with
332
- `node scripts/generate-hmm-fixtures.mjs` (Foundry `cast abi-encode` / `calldata`).
333
- To regenerate canonical V2 metadata/ABIs from preserved evidence without RPC or
334
- onchain writes, use `node --experimental-transform-types
335
- scripts/generate-hmm-presets.mjs release.json live.json`; both JSON files must be
336
- the exact V2 release and matching successful admission/pinned-readback records.
337
-
338
- ## 0.8.1 release
339
-
340
- Adds the Robinhood October11 Dynamic Fee Hook preset:
341
- `KnownLifecycleHook.DynamicFeePoolV1` and `KnownLifecycleProfile.V4DynamicFeePool`
342
- (`"v4-dynamic-fee-pool"`), profile
343
- `0x27f219872fe934a5464bc9b900028256122f215b40cbdb69ef5f88cd5f627ccb`. The fixed
344
- preset and example default are unchanged.
345
-
346
- ## 0.8.0 release
347
-
348
- This release adds unstarted-launch topology validation, boundary tails enabled by
349
- default, and the generalized `positionGaps` option replacing `staircaseBoundaries`.
350
- The TypeScript examples default to an unsigned dry run; `--broadcast` explicitly
351
- authorizes execution without an interactive hash prompt. `--resume` retains the
352
- original plan, receipt evidence, and metadata session instead of relaunching.
353
- The opt-in `LifecycleSessionCache` below reuses only the allowlisted static reads
354
- at the same canonical chain/block; execution proof and financial state stay live.
355
-
356
- ## 0.7.1 transaction-first construction
357
-
358
- Known chain/core/profile identities use frozen release construction metadata.
359
- `getKnownLifecycleDeployment`, `getKnownLifecycleProfile` and
360
- `listKnownLifecycleProfiles` are pure, browser-safe selectors. Profile selection
361
- accepts either `KnownLifecycleProfile` keys or exact profile IDs.
362
- `KnownLifecycleHook.FixedFeePoolV1` and `KnownLifecycleHook.DynamicFeePoolV1`
363
- identify the fixed hook and **Dynamic Fee Hook** respectively; both use the V2 constructor.
364
- Select the latter with `KnownLifecycleProfile.V4DynamicFeePool` (`"v4-dynamic-fee-pool"`).
365
- Unknown identities return no preset; explicit `readLifecycleProfiles` and unlisted
366
- profile construction retain live discovery/certification. Presets describe release
367
- availability, never current admission, and contain no `admitted` claim.
368
- The Robinhood presets retain the mined October8 fixed config6 graph and add the
369
- October11 zero-author-royalty Dynamic Fee Hook, each with its own exact artifact,
370
- review, terms, runtime pins, creation-code chunks and 20-word V2 constructor.
371
- Historical config5 codecs and 18-word V1 ABIs remain explicit for
372
- existing deployments, but October5 evidence is not relabeled or selected as the
373
- current preset. Explicit live discovery certifies unlisted deployments from actual
374
- pinned reads.
375
-
376
- `prepareAndPlanLifecycleLaunch` compiles the exact requested mode before native
377
- execution calibration. Known construction uses local commitments and hook
378
- creation code, mines salts once and compares every final constructor binding.
379
- Token prediction remains a genuine pinned factory read; advancing the block
380
- during asynchronous mining can require a second prediction. Current progress,
381
- receipts, nonce/confirmation gates, exact native proof and final canonical guards
382
- remain authoritative. Public discovery/constructor boundaries observe the current
383
- chain; policy callbacks are resolved freshly rather than cached as constant values.
384
-
385
- Optional `buySlippageBps` (integer 0–9999) protects **every ordered buy** from its
386
- actual diagnostic receipt output using `floor(output * (10000 - bps) / 10000)`.
387
- The diagnostic candidate is never admitted or returned for execution. Only the
388
- complete final protected commitment can pass measurement and validated replay.
389
- Protection is available only for an unstarted launch; omission preserves minima.
390
-
391
- `buildLaunchTransactions({ plan, mode, preparationBatches? })` is a pure unsigned
392
- compiler. Staged preparation counts must cover all markets exactly. Every command
393
- carries the full plan; `PlannedLaunch.totalCalldataBytes` sums exact remaining
394
- command bytes, including approvals. Splitting preparation cannot reduce this
395
- per-command calldata or partition mint/lock/all-buys/public-opening activation.
396
-
397
- Atomic and staged are explicit choices. Only proven compute/gas-envelope capacity
398
- can partition empty preparations. Semantic, opaque `DeploymentFailed`, source,
399
- provider-clamp and byte-cap refusals do not authorize retries or partitioning.
400
- Native explicit OOG recognizes simulator VM code `-32015` and compatible `-32000`,
401
- not near-cap gas consumption. Both activation receipt and event counts must match
402
- the committed positions. Failure DTOs expose safe classifications/numeric facts,
403
- not raw provider messages. Abort closes continuations, including unsupported adapters.
404
-
405
- `deriveLaunchPositionRanges` requires a source-defined Black Market
406
- `distributionPreset`: `"early-scarcity"`, `"staircase"` or `"smooth-ramp"`.
407
- Inventory weights, 64 active spacing steps and intentional gaps follow
408
- `black-market/apps/web/src/lib/launch.ts`. Early Scarcity widens sparse bands;
409
- Staircase inserts three-step gaps only between new inventory shelves; Smooth Ramp
410
- keeps gently narrowing bands contiguous. The SDK defaults to five positions and
411
- supports counts from 2 through 16. Arbitrary equal-tick fractions of the distance
412
- to a protocol boundary are not these presets.
413
- For Staircase and Early Scarcity, `positionGaps: "adjacent"` removes the empty
414
- intervals while retaining the source inventory weights and finite-band widths.
415
- The default `"source-gaps"` preserves their original topology. Smooth Ramp is
416
- always contiguous: omit `positionGaps` or use `"adjacent"`; explicitly supplying
417
- `"source-gaps"` is an error. Adjacent positions share an exact spacing-aligned tick;
418
- overlap is also valid for continuous price coverage.
419
- An empty gap can move the pool price without exchanging tokens in that interval.
420
-
421
-
422
- **Breaking default:** `extendFinalRangeToBoundary` now defaults to `true`.
423
- It replaces only the final band's far endpoint with the usable spacing-aligned
424
- protocol boundary, preserving every earlier band and the final band's near endpoint.
425
- Explicit `false` retains a finite final band, which does not satisfy default planner
426
- admission unless another funded position supplies the permanent tail.
427
- The complete finite preset must fit before this extension is applied.
428
- `boundaryTick` and `boundarySqrtPriceX96` always describe that finite protocol
429
- boundary; endpoint sqrt ratios are not exclusive swap-price guards.
430
-
431
- Pass the raw token budget for **one pool**. Each position exposes `inventoryBps`
432
- and its pre-mint `allocatedTokenAmount` cap: all but the last allocation floor
433
- their basis-point share, and the last receives the exact remaining budget.
434
- All caps and funded liquidities must be positive. `maxTokenAmount` equals the intended
435
- `allocatedTokenAmount`, including any mint-rounding residual; `tokenAmountMaximum`
436
- equals the pool budget. Both venues use the maximum exact budget-safe integer liquidity:
437
- one additional liquidity unit would exceed that position's allocated cap.
438
- `unspentTokenAmount` is the budget minus the canonical rounded-up expected mint
439
- debit, not an extra allocation or creator reserve. Do not add it to `tokenAmountMaximum`.
440
- Current admission requires position maxima to sum exactly to each market budget and
441
- market budgets to sum exactly to minted supply. The SDK planner and pure compiler
442
- enforce these equalities for every supported venue. The orchestrator burns every
443
- actual leftover before any buy. Positive int128 liquidity and V4 signed settlement
444
- limits remain enforced; arbitrary caller plans still need complete live admission.
445
-
446
- The existing ordering convention is `launchedTokenIsQuote: true` for launched
447
- token0. Gross liquidity must fit `maxLiquidityPerTick` at **each endpoint**;
448
- adjacent bands share their endpoint's gross capacity, while the sum of disjoint
449
- positions is not capped as if all began at launch. `openingActiveLiquidity`
450
- reflects the actual `[tickLower, tickUpper)` opening state. Token0 starts in the
451
- first band; token1 has zero opening-active liquidity until a downward quote buy
452
- crosses the opening upper endpoint. `buySideLiquidity` is only the first
453
- reachable band's liquidity, never the sum of all disjoint bands. Each position
454
- also exposes exact endpoint sqrt ratios for display valuation.
455
-
456
- ### Launch position-topology safety
457
-
458
- Every launch-created market must retain a **nonzero permanent tail** and continuous
459
- buy-side coverage from its opening price to that tail. A tail in another pool,
460
- including an optional Abyss allocation, cannot substitute for this market's tail.
461
- The tail ends at the furthest usable spacing-aligned protocol tick: the maximum
462
- tick when `launchedTokenIsQuote` is `true` (launch token0), otherwise the minimum.
463
- V4 uses the config's `tickSpacing`; Abyss config1 uses its canonical fee-tier spacing
464
- from `ABYSS_FEE_TIERS`, without changing the encoded config.
465
-
466
- `assertLaunchTopologyCoverage` is a pure validator exported by both
467
- `@black-market/sdk` and `@black-market/sdk/lifecycle`. It accepts
468
- `{ positions, openingTick, tickSpacing, launchedTokenIsQuote, allowInteriorGaps? }`.
469
- Positions carry `tickLower`, `tickUpper` and `maxTokenAmount` (or the Abyss config's
470
- `tokenAmountMaximum`). Derive the opening tick with exported
471
- `getTickAtSqrtRatio(marketSqrtPriceX96)`, not by rounding it to the spacing grid.
472
- The validator never changes the positions:
473
-
474
- - `TOPOLOGY_OPENING_GAP`: no band contains the opening tick on the buy side.
475
- Token0 containment is `[lower, upper)`; token1 is `(lower, upper]`, including
476
- the opening upper endpoint crossed by the first downward buy.
477
- - `TOPOLOGY_INTERIOR_GAP`: the sorted interval union leaves an uncovered interval
478
- between opening and the permanent tail. Exact adjacency and overlapping
479
- Orbit-style horizons both pass.
480
- - `MISSING_PERMANENT_TAIL`: no boundary-reaching band has a positive token cap.
481
- A finite final band or zero-allocation tail is not permanent liquidity.
482
- - `TOPOLOGY_OFF_GRID_TICK`: a band endpoint is not aligned to this market's spacing.
483
-
484
- The pure validator's `allowInteriorGaps: true` waives **only** interior gaps.
485
- `planLaunch` and `prepareAndPlanLifecycleLaunch` enforce the same checks for every
486
- market of an **unstarted launch**, using the pinned predicted token's ordering
487
- against that market's quote. Progress is read first: waiting/in-flight account or
488
- receipt state returns waiting without applying this creation policy. Started
489
- `Preparing`/`Ready` and terminal `Active`/`Cancelled` plans skip the topology gate.
490
- Their onchain commitments are immutable, so recovery must not retroactively
491
- refuse them for historical topology. Budget/supply, identity, confirmation,
492
- receipt and stateful execution checks still apply.
493
-
494
- | Local planning option | Effect |
495
- | --- | --- |
496
- | `allowTopologyGaps: true` | Allows interior gaps only; the funded permanent tail stays mandatory. |
497
- | `allowMissingPermanentTail: true` | Waives only the boundary-reaching, positive-allocation tail requirement. |
498
-
499
- Both options default to `false`. Even together they **never** waive opening
500
- containment or tick alignment. Staircase and Early Scarcity source gaps therefore
501
- require an explicit gap opt-out; choose `positionGaps: "adjacent"` for continuous
502
- admitted launches instead. These are validation choices, not an execution-proof
503
- bypass. They are retained on `PlannedLaunch` for fresh `simulateLaunchPlan` and
504
- `buildNextTransaction` admission while the launch remains unstarted. Started and
505
- terminal recovery does not require either opt-out. Persist explicit choices
506
- separately when restoring an unstarted saved plan. Budget/supply equalities, ABI
507
- encoding, hashes, calldata and stateful proof requirements are unchanged.
508
-
509
- No cross-review execution cache, caller certificate or execution-proof skip exists.
510
- Recovery and `buildNextTransaction` acquire fresh authority and retain separate
511
- immediate-submission-provider preflight. Construction/proof is not submission.
512
-
513
- ## Reviewed next envelopes
514
-
515
- `BuildNextTransactionOptions.reviewedTransaction` optionally supplies an untrusted
516
- previously reviewed immediate transaction. Omit it to construct a new envelope
517
- with the existing greater-of-recommendation-and-twice-pinned-base fee strategy.
518
- Supply it after review to validate that exact envelope, not to import cached
519
- admission or waive current-state proof.
520
-
521
- The SDK reconstructs the current unfinished commands from fresh canonical
522
- progress, funding/allowances and deployment/profile authority. It matches
523
- `id/kind/chainId/from/to/data/value` before using the reviewed gas or price.
524
- Preparation batching remains SDK-derived, not caller-selected. Malformed,
525
- wrong-step or changed envelopes refuse without returning executable calldata.
526
-
527
- The SDK directly validates only the immediate transaction at its exact held
528
- gas/price. It does not repeat measurement, quote a new gas price/poster budget or
529
- simulate future steps. Current protocol/transaction limits, native metering,
530
- postconditions and immediate actual-balance affordability still apply. Existing
531
- padding may be consumed; neither a new padding target nor a higher advisory quote
532
- alone invalidates a held envelope. Natural child base fees and payer balances are
533
- never overridden, and failed replay never raises held gas or switches backend.
534
-
535
- Approvals/resets, atomic launches, every staged next step and cancellation use
536
- this same contract. Cancellation retains its adapter-independent recovery policy.
537
- Fresh receipt/nonce/confirmation and final canonical checks remain mandatory.
538
- When supplied, `submissionClient` must estimate the exact held gas/price at
539
- `latest`; it still proves only immediate transport acceptance, not wallet identity.
540
- Returned estimates/admission are fresh and do not trust fields supplied with the
541
- reviewed transaction. Generic EVM external data-fee callbacks now receive the
542
- exact simulated `gas` and `gasPrice`.
543
-
544
-
545
-
546
- ## Opt-in static session fast path
547
-
548
- `createLifecycleSessionCache()` creates a caller-owned `LifecycleSessionCache`.
549
- Pass it as `LifecycleRpcClient.sessionCache` to retain the SDK's **explicit static
550
- read class** across `prepareAndPlanLifecycleLaunch`, `planLaunch`,
551
- `simulateLaunchPlan` and `buildNextTransaction`. Omit the option to preserve the
552
- default: only existing invocation-local fixed-state reuse, with no session events
553
- or cross-invocation observations. The SDK neither creates a global cache nor
554
- changes transport batching, selected mode or execution admission.
68
+ Let `q` be human quote units per launch token, `dQ` quote decimals, and `dL` launch
69
+ token decimals (18 for these recipes). The smaller numeric address is token0.
70
+ The helper's confusing `launchedTokenIsQuote: true` means **launch token is token0**.
71
+ Determine it from `BigInt(predictedToken) < BigInt(quoteAddress)`.
555
72
 
556
- ```ts
557
- import { createLifecycleSessionCache, planLaunch } from "@black-market/sdk/lifecycle";
558
-
559
- const sessionCache = createLifecycleSessionCache();
560
- const client = {
561
- ...publicClient,
562
- sessionCache,
563
- supportsReadBatching: true, // Only when this transport actually batches.
564
- onDiagnostic: (event) => { console.debug("[launch-sdk]", event); },
565
- };
566
-
567
- const first = await planLaunch({ client, plan, account, mode: "staged" });
568
- const second = await planLaunch({ client, plan, account, mode: "staged" });
569
- // At the same canonical head, the second call reuses matching static reads only.
570
- // Execution simulation, funding and canonical checks still run independently.
571
-
572
- sessionCache.block; // readonly { number: bigint, hash } or undefined
573
- sessionCache.invalidate(); // Wallet/chain switch, or each confirmed transaction.
574
- ```
73
+ | Launch-token order | Raw token1/token0 ratio | $0.005, 6-decimal quote, 18-decimal token |
74
+ | --- | --- | --- |
75
+ | token0 | `q × 10^(dQ-dL)` | `5 × 10^-15` |
76
+ | token1 | `1 / (q × 10^(dQ-dL))` | `2 × 10^14` |
575
77
 
576
- The retained class is deliberately narrow:
78
+ Before tick alignment, `sqrtPriceX96 = floor(sqrt(raw ratio) × 2^96)`.
79
+ `1n << 96n` means raw-unit parity, not a dollar price. Keep integer amounts as
80
+ `bigint`; parse display amounts with viem `parseUnits`, not floating-point arithmetic.
577
81
 
578
- | Static observations eligible for session reuse | Scope |
579
- | --- | --- |
580
- | Nitro capability getters and compute ceilings | Pinned `ArbSys.arbOSVersion`, `ArbGasInfo.getMaxTxGasLimit` / `getMaxBlockGasLimit` |
581
- | Nitro poster quotes | Pinned NodeInterface `gasEstimateL1Component`, including the exact payer, destination, calldata and value |
582
- | Frozen-profile registry certification | Profile/adapter registration (including dependency digest), topology, reviewed envelope, developer terms/cap and exact `requireEligible` request |
583
- | Reviewed immutable certification graph | Adapter metadata/dependency getters, graph runtime-code checks, locker/collector authorities and shared-hook constructor/runtime provenance |
584
- | Oracle configuration and factory presence | Pinned `oracleConfigs(configId)`, certified oracle-factory code and token-factory code-presence checks |
585
- | Hook deployer creation/constructor/runtime evidence | Creation-code hash, both chunk addresses/bytes, exact constructor metadata, initcode/CREATE2 comparisons and deployer-recorded runtime digests |
586
-
587
- Known construction presets continue to avoid discovery/getter mirrors they already
588
- omit; enabling a cache does not introduce extra certification reads. Unlisted
589
- profiles still use their normal frozen-envelope certification. Session hits retain
590
- private wire hex, not mutable public profile/deployment DTOs. Each consumer still
591
- decodes and checks its exact registration, graph, constructor and market bindings.
592
-
593
- **Never retained across invocations:** native/ERC20 balances, allowances,
594
- progress/market and core domain/prediction/binding reads, receipts/transactions,
595
- chain identity, head/latest/pending observations, account code/nonces,
596
- `eth_estimateGas`, `eth_simulateV1`, gas-price recommendations, funding-target
597
- admission, non-view calls, submission preflight and all write-adjacent calls.
598
- In particular the **native Nitro metering simulation probe is not cached**:
599
- its `eth_simulateV1` request remains separate from the static ArbSys/ArbGasInfo
600
- getters, and permissive measurement plus exact validated replay stay live.
601
- Existing invocation-local reuse for eligible fixed reads is unchanged.
602
-
603
- Each entry group owns one pinned block **number and hash**. Before session
604
- observations can be consumed, the invocation must read a fresh canonical head and
605
- chain identity. A changed head hash (including a same-height reorg), number or
606
- chain identity clears the old group before use. Exact
607
- wire parameters also participate in identity; changed caller, target, calldata,
608
- value, gas or constructor economics cannot hit another request's entry.
609
- Externally supplied pins without a fresh invocation-owned head bypass the session.
610
- Only successfully settled wire reads are shared; rejected requests, in-flight
611
- promises and obsolete completions after closure/invalidation are not retained.
612
- Final canonical/reorg and chain checks remain live and clear the session on
613
- identity failure.
614
-
615
- The caller-owned session is the cross-invocation identity boundary, not the
616
- reader object or `request` function. Recreated per-operation readers may share
617
- it while keeping their own transport cancellation signal. They still acquire
618
- fresh head/chain observations and run their own live requests; in-flight work
619
- and cancellation state are never shared. Do not route concurrent operation
620
- signals through one mutable global reader.
621
-
622
- The caller **must call `invalidate()` on wallet/chain switches and after every
623
- confirmed transaction**. It discards the pin and all memoized values immediately,
624
- including preventing older pending reads from repopulating the discarded group.
625
- It does not cancel an operation, freeze the chain or grant permission to submit
626
- a saved transaction. A new block causes fresh static reads; the cache is a
627
- same-snapshot acceleration, not a TTL or an assumption that facts never change.
628
-
629
- With `onDiagnostic`, each opted-in invocation emits a `session-cache` start and
630
- success/failure summary. `hits` counts static reads resolved from the session,
631
- `misses` counts new static reads, and `reuses` counts existing invocation-local
632
- static reuse. Summaries contain counts only: no cache keys, pins, RPC URLs,
633
- parameters, addresses, calldata, results or errors. Nested continuations share one
634
- summary, and listener failures remain isolated from financial behavior.
635
-
636
- Correctness follows from deterministic static observations at the **same exact
637
- canonical snapshot**, complete request identity and preserved live proof. The
638
- session never stores an admission certificate or replaces balances, receipt/nonce
639
- continuity, native capability simulation, execution replay or submission checks.
640
- Offline RPC-count regressions compare two sequential complete `planLaunch`
641
- operations with and without the option; they prove fewer repeated static reads,
642
- not a live end-to-end latency guarantee.
643
-
644
- Deterministic offline measurements (RPC messages, not HTTP batches):
645
-
646
- | Fixture, two sequential `planLaunch` calls at the same pin | Cache off: first / second | Cache on: first / second |
647
- | --- | --- | --- |
648
- | Admitted Nitro/Abyss staged plan, serial or batching transport | 33 / 33 | 33 / 22 |
649
- | Two-bound-market, two-creation-chunk V4 certification/constructor fixture | 64 / 64 | 64 / 19 |
650
-
651
- The first fixture performs the native Nitro probe, measurement and validated
652
- replay on **both** calls (three `eth_simulateV1` requests per call). The bound
653
- fixture deliberately refuses a smart account after completing read-only metadata;
654
- its savings prove static-read reuse, not economic execution. Both compare the
655
- complete returned plans for equality. Reproduce after `pnpm build` with
656
- `node --test test/lifecycle-read-pipeline.test.mjs test/lifecycle-execution.test.mjs`;
657
- the tests print the per-call counts and assert unchanged counts with the cache off.
658
-
659
- ## 0.6.3 certification and dependency-round reductions
660
-
661
- The public API, deployment authority and protocol admission rules are unchanged. With an
662
- actually batching source, selected-profile certification no longer waits for registry
663
- authority before reading the selected registrations, or for adapter registration before
664
- reading topology and applicable V4 envelope/terms. Eligibility overlaps graph certification.
665
- Private profile certification uses the registry's pinned `requireEligible` result and
666
- requires its returned implementation to match the registration. This covers discovery
667
- and standalone bound-hook metadata. Three duplicate implementation RPCs (`eth_getCode`,
668
- `core`, `dependencyDigest`) were removed: the registry already checks enabled records,
669
- exact profile/version/capability agreement, live runtime hash, core and live digest.
670
- The existing local V4 graph-digest comparison uses `registration.dependencyDigest`,
671
- whose equality to the live implementation digest is established by that same call.
672
-
673
- Abyss certification no longer re-reads `factory`, `CONFIG_SCHEMA`, `CONFIG_VERSION` or
674
- factory code. The registry certifies those fixed bindings before storing a profile;
675
- current eligibility pins the approved runtime and rechecks its live graph digest.
676
- SDK-side schema/version/topology and canonical chain/factory/variant checks still use
677
- the returned registration, including factory/venue agreement and no hook. A changed
678
- runtime or factory graph must fail authoritative eligibility, not an optional duplicate
679
- getter. Market resolution and both exact planning/simulation passes remain required.
680
-
681
- Source reasons in the reviewed protocol repository:
682
-
683
- - `contracts/src/launch/lifecycle/v2/LaunchImplementationRegistryV2.sol:285-296`
684
- implements current eligibility; `:88-97` certifies Abyss before storing it and
685
- `:153-174` prevents profile rebinding.
686
- - `contracts/src/launch/lifecycle/v2/LaunchCertificationV2.sol:79-102` certifies
687
- Abyss schema/version/capabilities, factory/code/canonical identity and graph digest.
688
- - `contracts/src/launch/lifecycle/v1/AbyssMarketAdapterV1.sol:75-76,150-167`
689
- makes the core/factory immutable and binds the live graph code/initcode hashes.
690
-
691
- V4 metadata, reviewed dependency code/getters, creation chunks and shared-root
692
- provenance remain independent pinned reads. Chunk addresses come from the frozen
693
- envelope only for scheduling; live deployer getters must still match them, including
694
- STOP prefixes, size/hash bounds, constructor creation hash, graph digest, runtime
695
- provenance and callback permissions.
696
-
697
- Planning reads swap-input admission and existing escrow balances alongside profile
698
- certification. A market's resolution uses its certified implementation while exact
699
- capability eligibility, selected-oracle validation and any bound-hook evidence settle.
700
- Their results must all agree before the resolved identity can authorize simulation.
701
-
702
- For batching-capable Nitro sources, the isolated ArbOS probe, NodeInterface poster
703
- quote and permissive exact-calldata measurement overlap. Probe isolation and its balance
704
- override do not enter the real execution sequence. Exact-gas validated replay waits for
705
- matching native metering, the poster budget and successful measurement. The redundant
706
- probe-only chain/hash recheck was removed: successful admission and simulation refusal
707
- still perform the enclosing live chain/canonical-block assertion. Natural child-block
708
- fees, uint64 gas bounds, real payer affordability, EIP-150 envelope discovery and
709
- postconditions are unchanged. Non-batching sources retain sequential measurement.
710
-
711
- No process-wide or cross-review cache, new read authority, public scope API, admission
712
- skip or write was introduced. Both opening-buy planning passes still choose fresh heads,
713
- retain live chain/pending/receipt observations and execute their own native proofs and
714
- validated replay. Only identical, complete pinned wire reads inside one invocation reuse
715
- observations.
716
-
717
- Offline regressions cover concurrent retained dependency responses, changed runtime,
718
- authority, graph digest and capability refusal through contract-modeled eligibility,
719
- canonical registry identity mismatch, build-next refusal after graph drift, V4
720
- expected-address evidence with mismatched live getters, overlapped measurement without
721
- premature replay, failed native evidence, serial/batched plan and fee equality, and
722
- retained final canonical checks. Obsolete duplicate-getter tests were removed.
723
- The released 0.6.3 SDK passed 133 offline tests with one opt-in real-AMM skip.
724
- Actual consumer-route timings are recorded by the integrating app, not a
725
- universal network-latency guarantee.
726
-
727
- ## 0.6.2 opt-in internal timing
728
-
729
- Initialize a lifecycle read client with optional `onDiagnostic(event)` to observe internal
730
- planning, profile certification, simulation context, Nitro probe, poster estimate, measurement,
731
- exact replay and envelope-discovery stages. Each stage reports start and completion/failure.
732
- RPC events distinguish queue admission, actual request start, settlement and invocation-local
733
- read reuse; `queueMs` is separate from request `durationMs`. `requestId` is a local numeric
734
- sequence, not a network or wallet identifier. Associate each client with your own local operation
735
- label when several reviews run concurrently.
82
+ ### Derive and display the aligned opening
83
+
84
+ Pricing helpers are imported from the **package root**, not `/lifecycle`.
85
+ Run this pure example with no wallet or RPC. It prices the full 1M supply while
86
+ allocating 500,000 tokens to this pool; the other half could fund a second pool.
87
+ The $1 six-decimal quote here is an arithmetic input, not a live price lookup.
736
88
 
737
89
  ```ts
738
- const client = {
739
- supportsReadBatching: true, // Only with an actually batching transport.
740
- request: (args) => publicClient.request(args),
741
- onDiagnostic: (event) => { console.debug("[launch-sdk]", event); },
742
- };
90
+ import { deriveLaunchPoolRecipe } from "@black-market/sdk";
91
+ import { formatUnits } from "viem";
92
+
93
+ const WAD = 10n ** 18n;
94
+ const supply = 1_000_000n * WAD;
95
+ for (const launchedTokenIsQuote of [true, false]) {
96
+ const r = deriveLaunchPoolRecipe({
97
+ pairedTokenDecimals: 6, pairedTokenUsdPriceX18: WAD,
98
+ targetMarketCapUsdX18: 5_000n * WAD,
99
+ launchedTokenIsQuote, fee: 3000, tickSpacing: 60,
100
+ supply, tokenBudget: supply / 2n,
101
+ });
102
+ const squared = r.launchSqrtPriceX96 ** 2n;
103
+ const fdvX18 = launchedTokenIsQuote
104
+ ? squared * supply * WAD / ((1n << 192n) * 10n ** 6n)
105
+ : (1n << 192n) * supply * WAD / (squared * 10n ** 6n);
106
+ console.log({
107
+ launchedTokenIsQuote, tick: r.launchTick,
108
+ sqrtPriceX96: r.launchSqrtPriceX96.toString(),
109
+ range: [r.tickLower, r.tickUpper], liquidity: r.liquidity.toString(),
110
+ alignedFdvUsd: formatUnits(fdvX18, 18),
111
+ });
112
+ }
743
113
  ```
744
114
 
745
- The SDK never enables console output or telemetry itself. Without a listener there are no
746
- diagnostic clocks or event allocations. Events omit RPC URLs, parameters/results, accounts,
747
- addresses, signatures, calldata and raw errors; RPC method names are allowlisted. Synchronous
748
- listener throws and asynchronous listener rejections are isolated from financial behavior.
749
- Successful timing settlement means the function returned, not that the returned plan/profile
750
- was admitted: inspect its normal typed admission result.
751
-
752
- Independent domain/progress reads now overlap after token prediction. Simulation context
753
- reads, Nitro/poster work and final chain/canonical observations also overlap where their inputs
754
- are independent, still within the existing transport-opted-in read bound. Serial transports,
755
- exact state/identity guards and both measurement and validated execution are retained.
756
- No total-launch latency claim is made from these scheduling changes.
757
-
758
- Verification: typecheck and the built offline suite passed **126 tests: 125 passing and one
759
- opt-in real-AMM skip**. Diagnostics regressions cover timing events, payload omission and
760
- throwing/rejecting observers without changing valid profile admission.
761
-
762
- ## 0.6.1 read-pipeline changes
763
-
764
- Existing standalone calls, deployment targets, economic commitments and admission
765
- requirements remain compatible. Invalid plan shapes are still refused before RPC work.
766
- The optional `LifecycleRpcClient.supportsReadBatching` transport hint enables independent
767
- getter dependency rounds with at most eight SDK-issued requests outstanding per invocation.
768
- Set it to `true` only when the supplied transport batches concurrent requests; the SDK
769
- does not select or configure a transport. Other clients retain serial requests, avoiding
770
- the measured concurrent-unbatched RPC regression. With batching enabled, initial head
771
- and chain reads overlap. Failed reads release their slots without rejecting unrelated
772
- queued callers or automatically retrying.
773
-
774
- A vanilla one-pool, one-quote, one-range plan selects only its committed profile IDs.
775
- Abyss-only profile selection no longer requests the V4-only
776
- `protocolMaximumDeveloperFeeBps` getter. A required V4 fee-limit read still fails the
777
- operation if unavailable. V4 oracle/deployer/terms work, extra token/position capabilities
778
- and swap-funding checks remain conditional on the actual committed plan. There is no
779
- weakened single-pool mode: adapter eligibility, live code hashes, authority/dependency
780
- bindings, schema/topology and applicable frozen terms remain mandatory.
781
-
782
- Without the optional static session fast path above, identical fixed-state reads share observations only within one planning invocation.
783
- Identity includes the original source client, pinned block number/hash and complete
784
- request parameters, including caller, target, calldata, value and gas context. Rejected
785
- reads are not retained. State changes, another caller/source/block or a subsequent public
786
- planning invocation cannot reuse those observations; mutated plans and obsolete
787
- completions are refused. There is no global cache or new public read-scope option.
788
-
789
- Chain checks, latest/head/pending observations, receipt reads and canonical block rechecks
790
- remain live. Wallet/provider preflight, non-view execution calls and stateful/fork RPC
791
- are not cached. Nitro metering probes, discovery and exact validated replay remain
792
- separate proofs. A protected opening-buy minimum changes the committed calldata and
793
- still requires a second full plan and stateful simulation against a fresh latest snapshot.
794
-
795
- Raw `request({method,params})` clients remain valid. JSON-RPC HTTP batching is optional
796
- and transport-owned; concurrent reads allow a configured batch scheduler to combine
797
- independent messages without changing `msg.sender` or the pinned state. No API planner
798
- service, transport fallback, signature or broadcast is introduced. The existing Node 20
799
- minimum and ES2022 target are unchanged; shipped scheduling does not require
800
- `Promise.withResolvers`.
801
-
802
- Verification on 2026-10-06: `pnpm typecheck` and `pnpm test` passed, including the build,
803
- with **123 tests: 122 passing, one opt-in real-AMM test skipped and zero failures**.
804
- Offline regressions exercise the vanilla
805
- Abyss path without V4 fee metadata, mandatory V4 read failures, bounded overlap and
806
- duplicate reads, exact combined funding, changed code/authority/terms, partial failures,
807
- chain drift/reorgs, source/caller/call-context separation, failed-slot release and
808
- obsolete results. These offline checks are not real-wallet or on-chain execution evidence.
809
-
810
- A live read-only comparison against installed 0.6.0 measured **one selected canonical
811
- Abyss profile certification only**, not whole planning, simulation or launch execution.
812
- Both versions used Node 24.11.1, viem 2.56.3 and identical pinned chain 4663 state at
813
- block 81455627, hash
814
- `0x66f20d4c04b1c2357e9cd711c984a83fc8913ab208449ff1a0c8289bf2854036`.
815
- Five runs per version and transport alternated version order; returned profiles were
816
- deep-equal, and the chain and canonical block were independently rechecked.
817
-
818
- | Transport | Version | Median / p95 (ms) | RPC messages | HTTP envelopes | Peak outstanding |
819
- | --- | --- | --- | --- | --- | --- |
820
- | Unbatched, default serial | 0.6.0 | 1496.10 / 1677.93 | 15 | 15 | 1 |
821
- | Unbatched, default serial | 0.6.1 | 1402.10 / 1414.52 | 14 | 14 | 1 |
822
- | Batched, explicit opt-in | 0.6.0 | 1503.38 / 1541.41 | 15 | 15 | 1 |
823
- | Batched, explicit opt-in | 0.6.1 | 1005.11 / 1219.87 | 14 | 10 | 3 |
824
-
825
- These scoped measurements do not establish end-to-end launch latency or a general
826
- provider guarantee. An earlier unbatched concurrency trial regressed from a 1150.64 ms
827
- baseline median to 3870.97 ms; explicit transport opt-in and default serial scheduling
828
- remove that scheduling choice for existing clients. For reproduction, keep the selected
829
- profile, pinned state, source, retry/batching settings and runtime equal; count RPC
830
- messages separately from HTTP envelopes and retain failures as failures. Do not log RPC
831
- URLs, accounts, calldata or provider messages, or substitute full-registry/complex-only
832
- timing for the selected-profile path.
833
-
834
- ## 0.6.0 admission changes
835
-
836
- Optional caller policy no longer substitutes for, or gates, actual sequential execution proof.
837
- Results distinguish `executionProof`, `protocolFit`, and `transportPreflight`. Robinhood
838
- admission reads pinned Nitro compute limits and poster budgets, then requires native exact
839
- validated replay. `buildNextTransaction` optionally preflights the immediate next envelope
840
- through `submissionClient`. Deployment addresses, lifecycle plan/schema domains, profile
841
- capabilities and explicit atomic/staged behavior are unchanged.
842
-
843
-
844
- ## Deployed infrastructure
845
-
846
- Robinhood mainnet (`4663`) selects the mined `pool-launch-v1` deployment captured at block
847
- `82880546` in `contracts/deployments/launch/pool-launch-v1/20261008T000141Z-e174ce6/manifest.json`.
848
- The October8 manifest records 22 successful receipts and canonical runtime evidence,
849
- with two adapters and five profiles at that deployment. The subsequent October11
850
- Dynamic Fee Hook admission adds a third adapter and sixth profile to the same
851
- core/registry: four canonical Abyss variants and fixed/dynamic pool-bound config6/V2.
852
- Neither offering deploys a shared V4 root. The fixed preset and example selection
853
- remain `0xf18140c2fb66a4202a0f22d62d6288bf7976db108de5381e5497aaed332a06f8`,
854
- with creation hash `0x11c4c7b1f265ed708b7d53ab828c89d237ca821333daa808f1913986c6f8f1ab`.
855
- The Dynamic Fee Hook profile is
856
- `0x27f219872fe934a5464bc9b900028256122f215b40cbdb69ef5f88cd5f627ccb`,
857
- with creation hash `0xd448a4117b78714cc4f96824356c6a90ec666449fde5c57e2b7bbe072f5e4a7a`.
858
- Its exact graph and terms are prepared in
859
- `contracts/deployments/launch/pool-hook-template-v1/20261011T033141Z-dynamic-fee-zero-royalty/admission.json`;
860
- that file is **unsigned preparation, not a mined receipt**. Registration is evidenced
861
- by the successful `forge-broadcast/AdmitPoolHookTemplateV1.s.sol/4663/admit-latest.json`
862
- receipt for `0x1928b13d1a6bf8826d08493a18c483fc7d244a3ec4100a7f8118f510a5db640c`.
863
- Creation bytes concatenate the preserved storage-child runtimes after stripping each
864
- first STOP byte, exactly as `PoolHookDeployerV1._initCode` does; constructor arguments
865
- are appended separately. An unpatched artifact runtime is not a deployed hook-instance runtime.
866
- October5 config5/V1 evidence remains historical, never a substitute for this graph.
867
-
868
- | SDK field | Mainnet address |
869
- | --- | --- |
870
- | `launchOrchestrator` | `0x91560876033d568d25CDe98C78c33ff8FC43962c` |
871
- | `launchImplementationRegistry` | `0xB2B0f9F36617810D67b8fC175153Aa10024C1358` |
872
- | `launchFeeOwnerRegistry` | `0x64b5ca1f21B8E84305b0e4D847924dca39Eb1fcb` |
873
-
874
- `getAddresses(4663)` exposes this graph; `robinhoodLaunchApplication` exposes the mined
875
- launch constants. Launch addresses on `31337` and `46631` remain zero unless configured.
876
- Address overrides are evaluated once on import: bare names win, then `VITE_`, then
877
- `NEXT_PUBLIC_`. Configure `LAUNCH_ORCHESTRATOR`, `LAUNCH_IMPLEMENTATION_REGISTRY`, and
878
- `LAUNCH_FEE_OWNER_REGISTRY` from the same deployment evidence.
879
-
880
- Canonical Abyss defaults are retained. DEX overrides apply on `31337` and `46631`, not
881
- mainnet. Lending mainnet defaults use `contracts/deployments/robinhood/replacement-infrastructure.json`;
882
- the workbench's existing lending endpoints follow that replacement. Lending overrides apply
883
- on `31337` (all three prefixes) and `46631` (`VITE_`/`NEXT_PUBLIC_`); liquidation-executor
884
- overrides use all three prefixes on every chain. Workbench chain RPC configuration reads
885
- `VITE_RPC_URL`, then `NEXT_PUBLIC_RPC_URL`, then `RPC_URL`; client factories prefer their explicit
886
- `rpcUrl`, then `RPC_URL`, then the chain endpoint. Select the chain explicitly in applications.
887
-
888
- Registry V2 has one `LaunchCertificationV2` child. V4 uses `profileEnvelope`, `profileId`, and
889
- signed six-argument `registerProfile`. Admin-only `registerAbyssProfile(uint8,registration)`
890
- independently certifies config-1 variants without an author envelope or royalty allocation.
891
- The admission EIP-712 domain is `Black Market Launch Registry`, version `2`.
892
-
893
- `LaunchBoundsV2` has five members: minimum/maximum tick spacing, maximum positions,
894
- maximum oracle cardinality, and fee-mode flags. LP and SharedV4 hook fees remain below
895
- 1,000,000 pips. Pool-bound minimum/maximum hook fees satisfy
896
- `0 <= minimumHookFeePips <= hookFeePips <= 1,000,000`; sensitivity is an explicit uint32.
897
- Creators also select a registered oracle configuration and per-pool external-liquidity policy.
898
- Author royalty ceilings are separate. Robinhood P1 `(1,4096)`, P2 `(6,4096)`, and P3 `(17,4096)`
899
- can use the same admitted profile; the selected oracle is checked at the pinned block.
900
- With `externalLiquidityDisabled=false`, third-party LP add/remove is allowed after opening.
901
- Permanent launch custody and pre-opening protection still apply.
902
-
903
- Config5 exports `fixedFeePoolHookV1Abi`, `poolHookDeployerV1Abi` and
904
- `poolFeeCollectorFactoryV1Abi` for the actual V1 wire contracts. Config6 exports
905
- `fixedFeePoolHookConfigV6Abi`, `poolHookDeployerConfigV6Abi` and
906
- `poolFeeCollectorFactoryConfigV6Abi`, including its 20-word constructor and
907
- `minimumHookFeePips()` / `feeSensitivityPipsSecondsPerTick()` getters. Existing
908
- config5 hooks are not queried for nonexistent V2 fee getters. Constructor encoding
909
- and initcode hashing require explicit `configVersion`; schema constants are
910
- `V4_POOL_BOUND_LIFECYCLE_CONFIG_SCHEMA_V5` and `_V6`, never an ambiguous alias.
911
- Generic shared contracts are `FixedFeeSharedHookV1`, `SharedHookDeployerV1`, and
912
- `SharedMarketAdapterV1`; they require their own admitted deployment graph.
913
- Other current ABI exports include `sharedMarketAdapterV1Abi`, `sharedHookDeployerV1Abi`,
914
- `poolMarketAdapterV1Abi`, `poolHookDeployerV1Abi`, and `poolFeeCollectorFactoryV1Abi`.
915
- Validation uses `validateV4LifecycleMarket`; the collector factory getter is
916
- `dependencyDigest(address)`. Identity domains are `black-market.launch-profile.v2`,
917
- `black-market.v4-dependencies.v2`, and `black-market.pool-bound-market-economics.v1`.
918
-
919
- ## Public operations
920
-
921
- | Operation | Contract |
922
- | --- | --- |
923
- | `readLifecycleProfiles({client, orchestrator, profileIds?, offset?, limit?})` | Bounded registry discovery, schema/topology dispatch, reviewed graph/terms certification and current registry admission. Default offset/limit is 0/100; explicit IDs can select a reviewed deployment's identities. |
924
- | `predictLifecycleToken({client, plan})` | Exact prediction from the explicit core. |
925
- | `prepareAndPlanLifecycleLaunch({client, account, plan, mode, buySlippageBps?, allowTopologyGaps?, allowMissingPermanentTail?, limits?, fork?, receipts?, confirmations?, signal?, onProgress?})` | Mines config6/V2 salts offchain, verifies per-market topology and exact final deployment bindings, and proves the selected-mode prefix under one fresh post-mining snapshot. Reports deferred steps; buy calibration requires activation coverage. No source-chain deployment or broadcast. |
926
- | `readPoolBoundHookDeployment({client, plan, marketIndex})` | Verifies typed factory, code chunks, constructor, normalized economics, initcode, salt, CREATE2 prediction and recorded provenance of existing code. Works with draft salts; permission bits are additionally required for executable predeployment. |
927
- | `buildPoolBoundHookDeploymentTransaction({client, plan, marketIndex})` | Optional permissionless typed `deploy(parameters,salt)` calldata. Does not create a token, register/initialize a pool, bind a collector or activate a launch. |
928
- | `planLaunch({client, account, plan, mode, allowTopologyGaps?, allowMissingPermanentTail?, limits?, fork?, receipts?, confirmations?, signal?})` | Independently fresh per-market topology/domain/funding/profile/progress/execution admission of the already-finalized commitment, simulating only the explicitly selected mode. |
929
- | `simulateLaunchPlan({client, planned, limits?, fork?, receipts?, confirmations?})` | Rereads canonical state and proves remaining work. Preserves receipt evidence, economics and selected mode. |
930
- | `readLaunchProgress({client, planned, receipts?, confirmations?})` | Confirmation-bound canonical state, latest head, receipt/replacement/reorg status, account-nonce safety and actual prepared market/live state. |
931
- | `buildNextTransaction({client, planned, reviewedTransaction?, submissionClient?, limits?, fork?, receipts?, confirmations?, action?})` | Rereads canonical progress and constraints, proves the immediate next transaction plus the fitting prefix, and returns one admitted envelope. With `reviewedTransaction`, directly replays only that matching immediate step at its exact held gas/price. Optional `submissionClient` adds read-only exact-envelope preflight. `action: "cancel"` retains adapter-independent recovery. Terminal launches return `undefined`. |
115
+ Output for the inputs above (raw integers except FDV):
932
116
 
933
- Clients may be viem public clients or raw `request({method,params})` RPC clients. Transport, wallet, signing and durable storage are application-owned. `PlannedLaunch` contains bigint values and live limit callbacks: persist `serializeLaunchPlan(plan)`, explicit execution mode, confirmation policy and receipt references, not the entire object.
934
-
935
- ## Commitment and integer representation
117
+ | Launch order | Tick | `sqrtPriceX96` | Range | Liquidity | Aligned FDV (USD) |
118
+ | --- | --- | --- | --- | --- | --- |
119
+ | token0 (`true`) | -329280 | `5610644101265567582035` | [-329280, 887220] | `35408142276778286` | 5014.946157970295405007 |
120
+ | token1 (`false`) | 329280 | `1118784514236214582089729076342544510` | [-887220, 329280] | `35408142276778286` | 5014.946157970295405007 |
936
121
 
937
- The economic tuple contains chain/core/creator/nonce; ERC20/ERC404 token and token-global reward configuration; sorted funding outputs and fee assets; ordered markets; ordered initial buys; deadline; and initial executor fee. Each market commits exact adapter/profile/quote/budget/version/config bytes. Each buy commits market index, quote input, minimum token output, recipient and Q64.96 price limit; repeated indices keep their order.
122
+ Tick spacing rounds the intended opening. Show the aligned FDV before signing;
123
+ use the actual quote/USD snapshot and actual predicted token orientation.
124
+ Always pass `supply`: the helper's omitted-supply default is **one billion** tokens,
125
+ not the runner's one million. `tokenBudget` changes depth, not whole-supply price.
938
126
 
939
- `hashLaunchPlan` is `keccak256(abi.encode(keccak256(bytes("BLACK_MARKET_LAUNCH_PLAN_V1")),plan))`. `hashLaunchIdentity` is `keccak256(abi.encode(chainId,orchestrator,creator,nonce))`. Token prediction depends on that identity and token configuration, not the economic plan hash, so the predicted token can appear in sorted fee assets without circularity. Economics cannot change after begin. Mode/batching are execution metadata outside the tuple.
127
+ ### Split inventory across pools and ranges
940
128
 
941
- Use bigint for uint256/uint160/uint128 values and decimal strings in portable JSON. `parseLaunchPlan` rejects lossy JSON numbers and out-of-width values. Amounts are base units, liquidity is uint128, and `1n << 96n` is raw-unit price ratio 1, not a display-unit price. The pure helpers `deriveLaunchPoolRecipe`, `estimateLaunchInitialBuy` and `deriveLaunchBuySqrtPriceLimitX96` do not prove fees, market admission or execution; derive actual minimum outputs from stateful simulation and review the resulting final commitment again.
129
+ Choose **one valuation**, then split token supply among pools. Each pool translates
130
+ that valuation using its quote's decimals and USD price. Two pools each priced at
131
+ $5,000 FDV still describe one $5,000 token valuation; their FDVs are not additive.
132
+ Less inventory at the same opening price means less depth.
942
133
 
943
- Bounds: 1–16 markets, 1–8 fee assets, at most 8 funding outputs, 64 ordered buys and 32 total positions. None guarantees gas feasibility. Reward-free ERC20 allows the positive uint256 supply domain; reward-enabled ERC20 is capped at `10^77`; ERC404 supply fits uint96. Current exported supply bounds preserve these exact numerical domains.
134
+ Start with the recipe's single range. For multiple bands, `deriveLaunchPositionRanges`
135
+ supports `early-scarcity`, `staircase`, and `smooth-ramp` via `distributionPreset`.
136
+ The app's Single range / Even spread / Layered / Higher ground choices are separate
137
+ app presets. This pure SDK example creates four adjacent Early Scarcity bands:
944
138
 
945
139
  ```ts
946
- import { parseLaunchPlan, serializeLaunchPlan, encodeLaunchPlan, hashLaunchPlan, hashLaunchIdentity } from "@black-market/sdk/lifecycle";
947
- const plan = parseLaunchPlan(savedPlanJson);
948
- const bytes = encodeLaunchPlan(plan);
949
- const commitment = hashLaunchPlan(plan);
950
- const identity = hashLaunchIdentity(plan);
951
- const portable = serializeLaunchPlan(plan);
140
+ import { deriveLaunchPoolRecipe, deriveLaunchPositionRanges } from "@black-market/sdk";
141
+
142
+ const budget = 500_000n * 10n ** 18n;
143
+ const recipe = deriveLaunchPoolRecipe({
144
+ pairedTokenDecimals: 6, pairedTokenUsdPriceX18: 10n ** 18n,
145
+ targetMarketCapUsdX18: 5_000n * 10n ** 18n,
146
+ launchedTokenIsQuote: true, fee: 3000,
147
+ supply: 1_000_000n * 10n ** 18n, tokenBudget: budget,
148
+ });
149
+ const ranges = deriveLaunchPositionRanges({
150
+ launchSqrtPriceX96: recipe.launchSqrtPriceX96,
151
+ launchedTokenIsQuote: true, tokenBudget: budget, tickSpacing: 60,
152
+ venue: "uniswap-v4", distributionPreset: "early-scarcity",
153
+ positionCount: 4, positionGaps: "adjacent",
154
+ });
155
+ console.table(ranges.positions.map((p) => ({
156
+ lower: p.tickLower, upper: p.tickUpper, bps: p.inventoryBps,
157
+ cap: p.maxTokenAmount.toString(),
158
+ })));
159
+ console.log("expected mint dust burned:", ranges.unspentTokenAmount.toString());
952
160
  ```
953
161
 
954
- ## Registry-driven profile selection and frozen consent
955
-
956
- The exact canonical registry getters are `profileCount`, `profileIds(offset,limit)`, `adapterCount`, `adapterIds(offset,limit)`, `profile`, `adapter`, `profileTopology` and `requireEligible`. There is no SDK template-ID allowlist and no invented `profiles()`/`isProfileEligible()` ABI. Schema plus certified topology dispatches supported implementations; an unsupported schema is reported separately from invalid/unsupported positions.
957
-
958
- `LifecycleProfile` contains `registration`, `adapter`, unchanged `topology`, `venueKind`, `admitted` and optional refusal `reason`. Reviewed V4 adds:
162
+ The returned raw caps and ticks are:
959
163
 
960
- - `envelope?: LaunchEnvelopeV2`: artifact/review/bounds/terms digests; topology, config/economic versions, capabilities, flags/callbacks, treasury policy, stable beneficiary/maximum rate, numerical bounds and the exact dependency graph.
961
- - `developerTerms?: {adapter, beneficiary, maximumDeveloperFeeBps, termsDigest, enabled}`.
962
- - `protocolMaximumDeveloperFeeBps?: number`.
164
+ | Range | Inventory bps | Token cap (18-decimal raw units) |
165
+ | --- | --- | --- |
166
+ | [-329280, -328920] | 366 | `18300000000000000000000` |
167
+ | [-328920, -328140] | 975 | `48750000000000000000000` |
168
+ | [-328140, -327000] | 2805 | `140250000000000000000000` |
169
+ | [-327000, 887220] | 5854 | `292700000000000000000000` |
963
170
 
964
- The profile identity uses domain `black-market.launch-profile.v2` and commits the four review/economic digests, topology/config/economic versions, stable beneficiary, maximum author rate and capabilities. It deliberately excludes instance addresses. `hashLifecycleProfile`, `encode/decodeLaunchEnvelope`, `encode/decode/hashLaunchBounds` and `hashLaunchDependencies` expose exact canonical encodings/domains. Graph dependency hashing includes chain/core/registry/registrar, live runtime commitments and exact code-chunk provenance; only the shared deployment salt is outside that economic dependency digest.
171
+ The expected mint dust burned is **6,401,491 raw units** (0.000000000006401491 tokens).
965
172
 
966
- Profile certification checks reviewed capabilities/bounds, adapter/core/graph runtime hashes, manager/oracle/locker/collector dependencies, typed hook deployer and STOP-prefixed creation-code chunks. Shared roots additionally require exact constructor/salt prediction and the deployer's recorded runtime. Bound roots require exact immutable constructor/key/economic commitment and recorded runtime when deployed. Address bits `0x1afc` under mask `0x3fff` are necessary but never sufficient. Runtime/initcode portability bounds remain 24,576/49,152 bytes; no compiler/code-size relaxation is assumed.
173
+ The inventory weights are **3.66% / 9.75% / 28.05% / 58.54%** of this pool's budget.
174
+ Use `positionGaps: "adjacent"` for default continuous planner admission; Early
175
+ Scarcity and Staircase otherwise preserve source gaps. Smooth Ramp is always adjacent.
176
+ The final band reaches the spacing-aligned protocol boundary by default.
177
+ Coverage must run continuously from opening to a funded permanent tail in **each** pool.
967
178
 
968
- Registry `authorizationDigest`, `beneficiaryNonces` and `registerProfile` ABIs expose canonical reviewed admission/author consent. Admission does not make a template name an SDK allowlist or bypass current `requireEligible`. Profile retirement prevents new/pending source binding; it does not rewrite frozen active source terms or disable stable-author fee claims.
179
+ Market budgets sum exactly to minted supply; position caps sum exactly to their
180
+ market budget. Actual integer mint debits may be lower. Every unused launch token
181
+ is **burned before opening buys**, not held as a creator reserve. Inspect material
182
+ burn separately from small rounding dust. Tail allocation is part of the budget.
183
+ At a token1 opening, active liquidity may be zero until the first downward swap
184
+ crosses the opening upper tick; inspect reachable buy-side liquidity too.
969
185
 
970
- ## Current venue codecs
186
+ ### Protect the opening buy
971
187
 
972
- ### Shared V4, version 4
188
+ Show estimated **tokens and share of supply**, quote spent, fees, and final marginal
189
+ price. A 0.00001 ETH smoke buy bought about 4.9 tokens out of 1M in recorded fixed-V4
190
+ examples; it does not predict the impact of a $100 or $1,000 buy.
191
+ A larger buy traverses ranges and can acquire a large share of supply.
973
192
 
974
- `encodeV4LifecycleMarketConfig` / `decodeV4LifecycleMarketConfig` accept only inner version 4. Market `configVersion` is 4. Exact schema:
193
+ `prepareAndPlanLifecycleLaunch({ buySlippageBps: 50 })` derives every ordered buy's
194
+ minimum from stateful diagnostic output, then proves the final protected commitment:
975
195
 
976
196
  ```text
977
- (uint16,uint24,int24,uint160,uint24,uint8,uint8,address,bool,bytes32,bytes32,bytes32,address,uint16,(int24,int24,uint128,bytes32,uint256)[])
197
+ minTokenOut = floor(simulatedTokenOut × (10000 - buySlippageBps) / 10000)
198
+ 50 bps: floor(simulatedTokenOut × 9950 / 10000)
978
199
  ```
979
200
 
980
- ### Pool-bound V4, version 6
201
+ This requires activation inside the simulated prefix. The option accepts 0–9,999
202
+ bps and applies only before begin; omitting it preserves your committed minima.
203
+ A 1-wei minimum is effectively unprotected. Never submit the diagnostic draft.
204
+ A Kyber conversion minimum protects ETH→quote output, not quote→launch-token output.
981
205
 
982
- `encodePoolBoundV4LifecycleMarketConfig` / `decodePoolBoundV4LifecycleMarketConfig` support explicit inner versions 5 and 6. The current October8 graph requires version 6 and matching market `configVersion`; historical version 5 requires its own graph and V1 constructor. Version 6 schema:
206
+ `deriveLaunchBuySqrtPriceLimitX96` is a separate, directional **marginal-price guard**.
207
+ Its `slippageBps` accepts 1–9,999 (default 50). It may stop a buy early; check partial
208
+ fills and refunds. It does not calculate minimum token output.
209
+ `estimateLaunchInitialBuy` is a pure single-position estimate, not a model of
210
+ multi-band traversal, prior buys, or hook execution. Use exact simulation for signing.
211
+ Final valuation uses the retained supply after burn and the observed post-buy price.
983
212
 
984
- ```text
985
- (uint16,uint24,int24,uint160,uint24,uint24,uint32,uint8,uint8,address,bool,bytes32,bytes32,bytes32,bytes32,address,uint16,(int24,int24,uint128,bytes32,uint256)[])
986
- ```
213
+ ## Build the plan
987
214
 
988
- Both preserve LP/hook fees, tick spacing, opening price, fee mode/denominator/treasury, external-liquidity policy, and canonical oracle identity. Pool-bound version6 inserts `uint24 minimumHookFeePips` and `uint32 feeSensitivityPipsSecondsPerTick` **immediately after `uint24 hookFeePips`**; `hookFeePips` remains the maximum. All other fields retain their order, including `hookSalt` immediately before `profileId`. Then come `profileId`, `termsDigest`, `developerBeneficiary`, explicit `developerFeeBps`, and positions. Every developer rate, including zero, is creator-selected. Copy terms from the selected profile's envelope. Inner/outer profile IDs must match; author identity is not the live payout. The creator rate cannot exceed either reviewed maximum or protocol ceiling.
989
-
990
- All three hook fee settings are independently chosen per pool and frozen at launch.
991
- One million pips is 100%, and 10,000 pips is 1%. `validatePoolBoundHookFees` validates
992
- integer `0 <= minimum <= maximum <= 1,000,000` and sensitivity `0..4,294,967,295`.
993
- The current reviewed `FixedFeePoolHookV1` uses a **fixed maximum-pips schedule**:
994
- each applicable hook fee is `floor(amount * hookFeePips / 1,000,000)`.
995
- Its base stores and exposes `minimumHookFeePips` and
996
- `feeSensitivityPipsSecondsPerTick`, but this concrete hook does not use them in fee
997
- calculation. Sensitivity's encoded units are fee-pips × seconds/tick; they do not
998
- imply an active oracle-driven fee formula. Registered oracle configuration still
999
- governs the pool's oracle behavior, not this hook's fixed fee rate.
1000
-
1001
- The registered **Dynamic Fee Hook** instead uses the oracle's upward quote-per-base
1002
- tick velocity: `minimum + floor(positiveTickChange * sensitivity / elapsedSeconds)`,
1003
- capped at `hookFeePips` (the maximum). Quiet/falling prices, zero elapsed time or zero
1004
- sensitivity use the minimum. Select its own preset/terms, set **`lpFeePips: 0`** and
1005
- explicitly encode all three config6 fee settings. Its reviewed author allocation is
1006
- **0 bps**, so `developerFeeBps` must be zero. Generic config6 codecs do not identify
1007
- fee behavior or certify constructor compatibility; live constructor/simulation
1008
- authority remains required. Fixed launch examples continue selecting the fixed preset.
215
+ For a complete wrapper, use [`launch-erc20-abyss.ts`](../examples/launch-erc20-abyss.ts).
216
+ For your own application, this function builds and prepares one market: 1M ERC20 supply,
217
+ Abyss QUOTE_ORACLE, 0.30% fee, one position, and a 0.00001 ETH native-wrap buy.
218
+ Pass a current WETH/USD-X18 price as `wethUsdPriceX18`. It reads and simulates, without signing.
219
+ The caller chooses `name` and `symbol`; keep API metadata identical to these fields.
1009
220
 
1010
221
  ```ts
1011
- const profile = profiles.find((row) => row.id === selectedProfileId);
1012
- if (!profile?.admitted || !profile.envelope || !profile.developerTerms) throw new Error("Selected profile is not reviewed/admitted");
1013
- const config = encodePoolBoundV4LifecycleMarketConfig({
1014
- version: 6, hookSalt: zeroHash, lpFeePips, tickSpacing, sqrtPriceX96,
1015
- hookFeePips: creatorMaximumFeePips, minimumHookFeePips: creatorMinimumFeePips,
1016
- feeSensitivityPipsSecondsPerTick: creatorSensitivityPipsSecondsPerTick, feeMode,
1017
- protocolFeeDenominator: profile.envelope.protocolFeeDenominator,
1018
- treasury: profile.envelope.protocolTreasury,
1019
- externalLiquidityDisabled: creatorSelectedExternalLiquidityDisabled,
1020
- oracleConfigId: creatorSelectedOracleConfigId,
1021
- profileId: profile.id, termsDigest: profile.developerTerms.termsDigest,
1022
- developerBeneficiary: profile.developerTerms.beneficiary,
1023
- developerFeeBps: creatorSelectedRate, positions,
1024
- });
222
+ import { randomBytes } from "node:crypto";
223
+ import { toHex, zeroAddress, type Address, type PublicClient } from "viem";
224
+ import {
225
+ deriveLaunchPoolRecipe, deriveLaunchBuySqrtPriceLimitX96,
226
+ encodeAbyssLifecycleMarketConfig, getAddresses, getKnownLifecycleProfile,
227
+ KnownLifecycleProfile, LAUNCH_ORACLE_CONFIG_ID, LifecycleFundingKind,
228
+ LifecycleRewardMode, LifecycleTokenKind, predictLifecycleToken,
229
+ prepareAndPlanLifecycleLaunch, readLifecycleProfiles, type LaunchPlanV1, type PlannedLaunch,
230
+ } from "@black-market/sdk";
231
+
232
+ export async function prepareOneMarketLaunch(
233
+ client: PublicClient, principal: Address, wethUsdPriceX18: bigint,
234
+ name: string, symbol: string,
235
+ ): Promise<PlannedLaunch> {
236
+ if (await client.getChainId() !== 4663) throw new Error("Wrong RPC chain");
237
+ const { launchOrchestrator: orchestrator, weth } = getAddresses(4663);
238
+ const preset = getKnownLifecycleProfile({
239
+ chainId: 4663, orchestrator, key: KnownLifecycleProfile.Abyss3,
240
+ });
241
+ if (!preset) throw new Error("Unknown deployment");
242
+ const [profile] = await readLifecycleProfiles({
243
+ client, orchestrator, profileIds: [preset.profile.id],
244
+ });
245
+ if (!profile?.admitted) throw new Error(profile?.reason ?? "Profile unavailable");
246
+ const supply = 1_000_000n * 10n ** 18n;
247
+ const buy = 10_000_000_000_000n;
248
+ const draft: LaunchPlanV1 = {
249
+ chainId: 4663n, orchestrator, creator: principal,
250
+ nonce: BigInt(toHex(randomBytes(32))),
251
+ token: {
252
+ kind: LifecycleTokenKind.ERC20, rewardMode: LifecycleRewardMode.None,
253
+ name, symbol, supply, nftUnit: 0n, metadataURI: "",
254
+ salt: toHex(randomBytes(32)), inventoryRecipient: principal, burnOnCancel: false,
255
+ },
256
+ funding: [], feeAssets: [], markets: [], buys: [],
257
+ deadline: BigInt(Math.floor(Date.now() / 1000)) + 3600n, executorFeeBps: 275,
258
+ };
259
+ const token = await predictLifecycleToken({ client, plan: draft });
260
+ const token0 = BigInt(token) < BigInt(weth);
261
+ const r = deriveLaunchPoolRecipe({
262
+ pairedTokenDecimals: 18, pairedTokenUsdPriceX18: wethUsdPriceX18,
263
+ targetMarketCapUsdX18: 5_000n * 10n ** 18n,
264
+ launchedTokenIsQuote: token0, fee: 3000, supply, tokenBudget: supply,
265
+ });
266
+ const plan: LaunchPlanV1 = {
267
+ ...draft,
268
+ funding: [{ asset: weth, amount: buy, kind: LifecycleFundingKind.NativeWrap,
269
+ inputAsset: weth, inputAmount: buy, target: zeroAddress, data: "0x" }],
270
+ feeAssets: [token, weth].sort((a, b) => BigInt(a) < BigInt(b) ? -1 : 1)
271
+ .map((asset) => ({ asset, ownerBps: 10000, rewardsBps: 0, burnBps: 0 })),
272
+ markets: [{ adapterId: profile.registration.adapterId, profileId: profile.id,
273
+ quoteAsset: weth, tokenBudget: supply, configVersion: 1,
274
+ config: encodeAbyssLifecycleMarketConfig({
275
+ profile: 3, fee: 3000, oracleConfigId: LAUNCH_ORACLE_CONFIG_ID,
276
+ openingSqrtPriceX96: r.launchSqrtPriceX96,
277
+ positions: [{ tickLower: r.tickLower, tickUpper: r.tickUpper,
278
+ liquidity: r.liquidity, tokenAmountMaximum: supply }],
279
+ }) }],
280
+ buys: [{ marketIndex: 0, quoteAmountIn: buy, minTokenOut: 1n,
281
+ recipient: principal, sqrtPriceLimitX96: deriveLaunchBuySqrtPriceLimitX96({
282
+ launchSqrtPriceX96: r.launchSqrtPriceX96, launchedTokenIsQuote: token0,
283
+ }) }],
284
+ };
285
+ return prepareAndPlanLifecycleLaunch({
286
+ client, account: principal, plan, mode: "atomic", buySlippageBps: 50,
287
+ });
288
+ }
1025
289
  ```
1026
290
 
1027
- `validateV4LifecycleMarket` checks profile/terms/topology/version, explicit author-rate ceilings and exact envelope bounds. Registry/adapter validation remains the execution authority. Positions must require **zero quote deposit** at the committed opening price in actual token orientation. Quotes fund ordered buys, not two-sided initial LP. At most one V4 market per quote is allowed across all templates/topologies/fees/salts; put that quote's positions into one market. Abyss may use the same quote.
1028
-
1029
- `validateV4LifecycleOracle({client,envelope,oracleConfigId,block})` checks the selected registered
1030
- oracle against the certified factory and `maximumOracleCardinality`. Profile discovery does
1031
- not choose an oracle or query a profile-pinned oracle ID.
1032
-
1033
- Abyss retains `encodeAbyssLifecycleMarketConfig` / `decodeAbyssLifecycleMarketConfig`: canonical pool profile, fee, oracle ID, opening price and ordered tick/liquidity/token-max positions. Quote/fee assets and direct ERC20/native-wrap funding are permissionless subject to actual code, transfers/balances/budgets and canonical bindings. Quote catalog metadata is not admission. Only swap-conversion inputs/targets use registry funding admission.
1034
-
1035
- ## Freeze and finalize bound economics
1036
-
1037
- Freeze chain/core/creator/nonce, token identity, sorted fee policies, all funding/budgets/positions/terms and ordered buys before mining. Create config6 with all three creator fee settings, the chosen bound profile's exact terms and a draft salt; never implicitly reinterpret a shared or historical config5 config.
291
+ The draft's `minTokenOut: 1n` is diagnostic. The function calls
292
+ `prepareAndPlanLifecycleLaunch` with `buySlippageBps: 50` and returns the final
293
+ protected plan. Token prediction uses core/creator/nonce/token configuration, so
294
+ the predicted token can enter sorted fee assets without a circular market-config hash.
295
+ `executorFeeBps: 275` matches the example runner: 2.75% of newly collected fees
296
+ goes to whoever calls `claimAndSplit()`. It is not charged to traders. Use 0 for none.
297
+
298
+ <a id="pool-bound-v4-version-6"></a>
299
+ For V4, use `encodePoolBoundV4LifecycleMarketConfig` with explicit `version: 6`,
300
+ all three hook-fee fields, selected oracle, external-liquidity policy, sorted fee
301
+ assets, positions, and the chosen profile's treasury/terms. A draft `hookSalt` is
302
+ finalized by `prepareAndPlanLifecycleLaunch`; freeze economics before mining.
303
+ [Config6 reference](reference.md#pool-bound-v4-version-6) describes fixed vs dynamic fees.
304
+
305
+ **Direct-core funding:** creator, payer, and refund recipient are the same address.
306
+ ERC20 inputs approve the core's `fundingEscrow`, not the core or router. Native
307
+ WETH buys use `NativeWrap`; other quotes can use admitted Kyber conversion, or be
308
+ funded directly as ERC20. Gas ETH is extra. [UFG](#launch-a-ufg-treasury) instead
309
+ uses a proxy creator, principal payer/refund recipient, and proxy approvals.
310
+
311
+ The wrappers set the plan deadline to **now + 3,600 seconds**. A Kyber conversion
312
+ uses **now + 300 seconds**, at most 1,200 seconds and never beyond plan expiry.
313
+ Staging can outlive the conversion even while the plan is valid. Do not edit a
314
+ saved deadline or replace committed conversion bytes after begin.
315
+
316
+ ## Prepare and simulate
317
+
318
+ | Mode | Transactions after any approvals | Choose when |
319
+ | --- | --- | --- |
320
+ | `atomic` | One transaction creates, prepares, mints, locks, buys, and opens. | The exact full launch fits the execution envelope. |
321
+ | `staged` | Begin → prepare batches → one activation that mints, locks, buys, and opens all markets. | Preparation needs separate transactions. |
1038
322
 
1039
- ```ts
1040
- const stop = new AbortController();
1041
- const finalized = await prepareAndPlanLifecycleLaunch({
1042
- client, account: draft.creator, plan: draft, mode: "staged", signal: stop.signal,
1043
- onProgress: ({marketIndex, attempts, predictedHook}) => console.log(marketIndex, attempts.toString(), predictedHook),
1044
- });
1045
- const saved = serializeLaunchPlan(finalized.plan);
1046
- if (!finalized.simulation.admitted) throw new Error(finalized.simulation.reason);
1047
- // Use finalized.progress and finalized.hookDeployments; do not immediately plan again.
1048
- const boundIndex = finalized.plan.markets.findIndex((market) => market.configVersion === 6);
1049
- const optionalPredeploy = await buildPoolBoundHookDeploymentTransaction({client, plan: finalized.plan, marketIndex: boundIndex});
1050
- // Inspect only; separately admit/authenticate/sign any optional predeployment.
1051
- ```
323
+ Staging is not rollback. A begun plan remains begun after its deadline until cancelled.
324
+ Activation cannot be split into separate position mints or buys.
325
+ The SDK never switches your requested mode or removes markets to make it fit.
1052
326
 
1053
- The collector supplies canonical normalized economic commitment and constructor metadata. `hashPoolBoundV4MarketCommitment` uses domain `black-market.pool-bound-market-economics.v1`: chain, core, registrar, predicted token, adapter/profile/quote/budget/version and encoded config hash with **only hookSalt zeroed**. Minimum, maximum and sensitivity, plus developer identity/digest/rate, remain committed. `PoolBoundHookParametersV2` and `poolBoundHookParametersV2Components` are the current constructor API; there is no V1 alias. The constructor freezes manager, registrar, oracle factory, core, locker, token/quote, fees/spacing/price/policy, economic commitment and position count. Salt is used verbatim by CREATE2, not as a constructor argument. Local/remote constructor equality and deployed immutable getters bind both new values.
1054
-
1055
- Mining is cancellable/nonblocking; `abort()` raises `AbortError` and does not cancel an onchain launch. The miner reuses the exact CREATE2 preimage's immutable prefix/suffix and checks all 14 permission bits before materializing rejected-address progress at bounded macrotask boundaries. Persist the returned finalized plan, not the draft. A salt-only change preserves normalized economics but changes final plan hash. Other changes invalidate initcode/mining; concurrent mutation is refused. Normal next-step review rechecks token factory and the captured `hookDeployments` baseline. Predeployment is permissionless, but code presence or self-reported getters alone cannot be adopted as provenance. It does not register/initialize/seal/open a market and does not eliminate real code-deposit/setup gas.
1056
-
1057
- ## Explicit execution mode and indivisible activation
1058
-
1059
- Every planning call requires `mode: "atomic" | "staged"`. Only that requested mode receives economic simulation; staged does not compute or expose an unused atomic attempt.
1060
-
1061
- - Atomic: after funding prerequisites, token creation, empty infrastructure, all mint/lock/buys/public activation are one transaction.
1062
- - Staged: prerequisites, `beginLaunch(Staged)`, greedily packed contiguous empty `prepareMarkets` batches, then one `activateLaunch`. A packed measurement failure using at least 95% of its given gas splits only that batch; low-gas semantic errors, byte caps and unlocated provider failures do not justify a split. Proof may cover only a prefix, with the unproved suffix explicitly deferred.
1063
- - Activation cannot split. If mint/lock/all buys/opening cannot fit, staged is also refused. No position/buy/minimum is omitted to manufacture success.
1064
-
1065
- No automatic atomic-to-staged fallback exists. Obtain separate consent to review the same commitment as staged. Both venues use token transfer restrictions plus activation-time canonical opening-state checks, not a pool gate. Manipulated opening price or preactivation liquidity fails verification; the token stays inactive and the launch remains Ready/cancellable. Failed activation rolls back every mint, seal, buy and opening together.
1066
-
1067
- ## Execution constraints and stateful simulation
1068
-
1069
- Optional `chainGasLimit`, `rpcGasLimit`, `accountGasLimit` and `maxCalldataBytes`
1070
- tighten admission. `maxSimulationGas` sets the aggregate used-gas proof-window
1071
- budget (default 50M on 4663, unknown on other chains). Missing policy stays explicit
1072
- in `limits.unknownExecutionConstraints`/`unknownConstraints`; no policy service is required.
1073
- A `LifecycleLimitSource` callback receives the canonical chain/account/core/block context.
1074
- Any supplied provenance must match it; stale/mismatched/malformed facts or a failed supplied
1075
- resolver reject rather than disappearing into optional uncertainty. Gas caps are positive
1076
- exact uint64 `bigint`; calldata is a positive safe integer byte count.
1077
- Reviewed gas envelopes are positive uint64 too; zero-metering observations cannot admit
1078
- a zero-gas transaction. Headroom calculations retain wide exact bigint intermediates.
1079
-
1080
- Headroom defaults to 1,500 bps, bounded 0–10,000; it clamps to current legal ceilings
1081
- instead of rejecting a fitting measured requirement. HMM V2 adds the forwarding
1082
- target documented above once on atomic/activation. Generic EVM admission uses the pinned
1083
- bounded header and supplied total-envelope restrictions plus actual validated RPC proof.
1084
- It reports an absent independent per-transaction cutoff honestly, without importing a
1085
- universal Ethereum cutoff. `executionGasCeiling` and `transactionGasCeiling` are equal on
1086
- generic EVM. The permissive measurement pass never admits a launch: replay must execute
1087
- the exact returned gas/fee envelope, every postcondition and all ordered buy bounds.
1088
- Fork discovery uses gross sequential estimates, not refund-reduced receipt gas as an
1089
- executable minimum. Explicit ceiling recovery preserves gasleft/EIP-150 validation and
1090
- carries that successfully replayed ceiling, never a smaller unproved envelope.
1091
-
1092
- Replay source, HTTP and malformed-response failures remain provisional with
1093
- `executionProof: "unavailable"` and do not retry economic execution. `AbortError`
1094
- propagates unchanged, including cancellation during supplied policy and Nitro getter
1095
- reads. Only explicit genuine native out-of-gas evidence may discover at most one
1096
- larger validated envelope within the same legal compute/total limits. Without returned
1097
- failing-call evidence, `failedTransactionId` stays absent; an opaque RPC error does
1098
- not invent a failing transaction or authorize preparation partitioning.
1099
- Returned failures retain the actual exact-replay prefix and its first failing
1100
- transaction, never a transaction ID borrowed from permissive measurement. For the
1101
- first failed preparation only, reviewed invariant errors may decode native
1102
- `error.data` when raw `returnData` is empty; nonempty raw bytes take precedence.
1103
- Unknown errors, `Panic`, generic `Error` and deployment wrappers do not establish
1104
- a deterministic preparation invariant or an out-of-gas cause.
1105
-
1106
- Sequential backends:
1107
-
1108
- 1. `eth_simulateV1`: ordered transactions with state carried between synthetic blocks, followed by exact validated replay.
1109
- 2. Generic EVM only: explicitly separate disposable loopback fork, with snapshot-isolated real transactions, pinned chain/header identity, canonical poststate/allowances and mandatory cleanup. Source RPC stays read-only.
1110
- 3. Neither: unavailable/provisional/unadmitted; disconnected dependent `eth_call` estimates cannot create an executable next transaction.
1111
-
1112
- Every proof completion rechecks the planned source chain ID and pinned block hash. A
1113
- changed source transport cannot retain admission merely by returning the same fork hash.
1114
-
1115
- ### Nitro compute and poster accounting
1116
-
1117
- For Robinhood `4663`, each pinned context reads ArbSys (`0x64`).`arbOSVersion` and ArbGasInfo
1118
- (`0x6c`).`getMaxTxGasLimit`/`getMaxBlockGasLimit`. ArbSys returns **55 + logical ArbOS version**;
1119
- the raw value must be at least 105, and `limits.arbOSVersion` reports logical version 50+.
1120
- The positive validated getter values establish `maxTxComputeGas`, `maxBlockComputeGas`
1121
- and their minimum `executionGasCeiling`. Failed reads never fall back to the huge Nitro
1122
- header or a hard-coded 32M. `limits.protocol` identifies `"nitro"` or `"evm"`.
1123
-
1124
- Nitro `transactionGasCeiling`, when present, is the minimum of supplied **complete**
1125
- chain/RPC/account gas-envelope restrictions. It is independent of the compute ceiling:
1126
- an admitted transaction's `gas` may exceed `executionGasCeiling` because poster gas is
1127
- not compute. NodeInterface (`0xc8`).`gasEstimateL1Component` is read at the same pinned
1128
- block with the exact sender, destination, calldata and value, including future dependent
1129
- steps whose execution cannot yet be independently estimated. It budgets poster gas only.
1130
- The buffered compute-discovery envelope plus buffered poster budget is carried into
1131
- exact validated replay; an approximate poster quote is never subtracted from total
1132
- gas use to certify exact compute fit.
1133
- Measurement waits for successful capability and candidate-specific poster evidence,
1134
- then uses the legal complete compute-plus-poster envelope. A packed preparation
1135
- failure consuming at least 95% may invalidate its cost estimate without claiming
1136
- its selector diagnoses OOG. Unsupported/transport evidence remains unavailable,
1137
- except an observed aggregate-budget exhaustion can preserve a successful prefix.
1138
-
1139
- A separate pinned validated simulation probes all three ArbOS/version/getter values
1140
- against the canonical observations. Only that isolated capability probe temporarily
1141
- overrides the probe account's balance, so probe-only fees cannot reject an exactly-funded
1142
- launch. Probe funding/nonce changes, steps and fees never enter real plan measurement,
1143
- replay, postconditions or affordability, which use actual payer state. The native Nitro
1144
- `eth_simulateV1` commit-mode ArbOS charging hook removes the **actual** poster charge and
1145
- holds excess compute gas before execution. Successful exact replay and postconditions,
1146
- not the quote or permissive pass, prove fit. Missing native metering or a mismatched
1147
- probe fails closed. Generic Anvil/Hardhat forks cannot certify Nitro execution.
1148
-
1149
- Transactions expose `estimate.executionFee`, optional external `dataFee`/`totalFee` and
1150
- `feeConfidence: "execution-and-data" | "execution-only"`. On Nitro, `posterGas`/`posterFee`
1151
- are the estimated buffered allocation **inside** `gasLimit`, `dataFeeIncludedInGas` is
1152
- true, `dataFee` is zero and `totalFee` equals `gasLimit * gasPrice`. Affordability counts
1153
- this complete envelope once; adding `posterFee` again would double-charge. A caller
1154
- `estimateDataFee` is therefore rejected on Nitro. On generic EVM it retains its original
1155
- semantics as a validated external fee added outside execution gas. Missing external fee
1156
- does not manufacture an all-in estimate. All observations and limits refresh for build-next.
1157
- For initial construction, the exact legacy gas-price envelope is the greater of
1158
- the fresh RPC recommendation and twice the pinned base fee. This avoids
1159
- underpriced submission when `eth_gasPrice` lags the block base fee. Reviewed
1160
- envelopes retain exact gas/price through direct immediate native validated replay
1161
- and immediate affordability, without repeating measurement or applying new padding.
1162
- The held price need not equal a new recommendation or twice the current base fee;
1163
- natural child fees and the actual submission estimate must accept it. Replay,
1164
- affordability and submission use that same price, without a base-fee override.
1165
- An aggregate budget selects the longest fitting estimated prefix; only when even
1166
- the immediate estimate does not fit is no proof possible. A definitive returned
1167
- failure from the final exact replay skips a later payer-balance lookup, while
1168
- retaining fee/estimate validation and final canonical checks. Successful replay
1169
- still requires current actual-balance affordability.
1170
-
1171
- Source references: [ArbSys version encoding](https://github.com/OffchainLabs/nitro-precompile-interfaces/blob/main/ArbSys.sol),
1172
- [ArbGasInfo compute getters](https://github.com/OffchainLabs/nitro-precompile-interfaces/blob/main/ArbGasInfo.sol),
1173
- [native simulate commit mode](https://github.com/OffchainLabs/go-ethereum/blob/51d716ccf8293072077feb9764d63d73f5e4ff20/internal/ethapi/simulate.go),
1174
- [ArbOS actual poster/compute charging](https://github.com/OffchainLabs/nitro/blob/ccc5c828d9ea22f161efc784bac4d4a6559b7e59/arbos/tx_processor.go),
1175
- and [NodeInterface poster estimate](https://github.com/OffchainLabs/nitro-contracts/blob/main/src/node-interface/NodeInterface.sol).
1176
- These describe backend semantics, not evidence of a live launch executed by this SDK release.
1177
-
1178
- Atomic/activation simulation `steps[].returnData` encodes the actual `LaunchReceiptV1` tuple. Controlled-fork output is reconstructed from matching real activation/buy receipt logs and canonical state, never geometric estimates or echoed request minima. `tokenOut` and `quoteSpent` are parallel arrays indexed by ordered buys.
327
+ | Operation | Options type / purpose |
328
+ | --- | --- |
329
+ | `prepareAndPlanLifecycleLaunch` | `PrepareAndPlanLifecycleLaunchOptions`: `client`, `account`, `plan`, `mode`; optional `ufg: { config }`, `buySlippageBps`, `fixedFinalEnvelope`, `limits`, `fork`, `receipts`, `confirmations`, `signal`, `onProgress`, topology opt-outs. |
330
+ | `planLaunch` | `PlanLaunchOptions`: same execution context, including optional `ufg: { config }` and `signal`; re-admits an already-finalized plan without changing salts/minima. |
331
+ | `simulateLaunchPlan` | `client`, `planned`; optional `limits`, `fork`, `receipts`, `confirmations`, `signal`. |
332
+ | `readLaunchProgress` | `client`, `planned`; optional `receipts`, `confirmations`, `signal`. |
333
+ | `buildNextTransaction` | Progress options, plus optional `action`, `limits`, `fork`, `submissionClient`, `reviewedTransaction`. |
334
+
335
+ `buildLaunchTransactions` compiles unsigned commands; it does not prove execution.
336
+ Persist `serializeLaunchPlan(planned.plan)`, mode, confirmation policy, receipt
337
+ references, and UFG config if applicable. `parseLaunchPlan` restores bigint values.
338
+ Do not serialize the entire `PlannedLaunch`, which can contain live limit callbacks.
339
+
340
+ Robinhood proof requires native Nitro `eth_simulateV1`. A generic Anvil/Hardhat fork
341
+ can exercise EVM behavior but cannot certify Nitro compute/poster accounting.
342
+ The default aggregate proof window is **50M used gas**, not a per-transaction ceiling.
343
+ Inspect `simulation.deferredTransactionIds` even when `simulation.admitted` is true:
344
+ admission can cover only a prefix. Deferred steps have no gas, price, or admission.
345
+ Buy-output calibration refuses a deferred activation. Start with one market; raise
346
+ `limits.maxSimulationGas` only with a provider that supports the larger window.
347
+
348
+ Review the returned final plan/hash, aligned price, burn, retained supply, fees,
349
+ ordered buy outputs/minima, funding, refunds, and complete gas envelope. A fresh
350
+ `buildNextTransaction` proves the immediate next step as canonical progress advances.
351
+ `submissionClient` adds read-only exact-envelope preflight on the sending RPC.
352
+
353
+ `planned.transactions[0]` already contains the immediate exact `from/to/data/value`,
354
+ `nonce`, `gas` and `gasPrice` when that step has admitted execution proof. There is
355
+ no need for another unreviewed build just to discover its envelope. The envelope
356
+ is reviewable and reusable; its block-bound admission is historical, not permission
357
+ to sign later. Pass it as `reviewedTransaction` for fresh immediate admission after
358
+ review or recovery. This is execution revalidation, not a second user review gate.
359
+ Reuse `planned.progress` for the same result's display instead of reading progress
360
+ independently beside the plan.
361
+
362
+ For an atomic caller with a chosen final gas envelope, prepare with
363
+ `fixedFinalEnvelope: { gas: 31_000_000n }`. This is an explicit chosen gas amount,
364
+ not a universal gas recommendation. `limits.accountGasLimit` remains only a ceiling.
365
+ The option accepts exactly one immediate atomic command with no approval prefix;
366
+ staged mode and approval-first shapes refuse it. Diagnostic and final protection
367
+ share one frozen fee recommendation. Supplying `gasPrice` holds that fee exactly;
368
+ it is never bumped to obtain admission. The final protected calldata still receives
369
+ one natural-child-fee validated replay at the exact chosen envelope. Failure never
370
+ enlarges it or switches mode.
371
+
372
+ On chain4663, independent Nitro getters, known factory prediction and known-core
373
+ head progress can share one Multicall3 read after pinned runtime attestation.
374
+ Poster estimates retain their original sender/value and are never aggregated.
375
+ One invocation owns the final fresh chain check and every distinct canonical
376
+ proof pin, including after optional sending-RPC estimation. Balances and economic
377
+ proofs are not shared across invocations.
378
+
379
+ ## Sign and confirm
380
+
381
+ Call `buildNextTransaction` before **every signature**, including approvals and
382
+ cancellation. For a reviewed command, pass `reviewedTransaction` so the SDK replays
383
+ the same gas/fee/nonce rather than rediscovering it. Send the exact returned
384
+ `from/to/data/value/nonce/gas/gasPrice` with a matching explicit viem chain.
385
+ `confirmations` defaults to 1; choose and retain your policy before begin.
386
+ SDK affordability is observed at block B, not at the provider's pending state.
387
+ Immediately before signing, the host must freshly compare pending nonce and
388
+ balance with this exact envelope and verify the active provider account/chain
389
+ after asynchronous work. Changed identity, nonce, intent or insufficient pending
390
+ funds blocks the signature; never overwrite the reviewed nonce or replay a write.
391
+ The following local-account loop signs only when explicitly called by your application.
392
+ Use a new owner-only journal path inside a private directory; on reload, restore the
393
+ original plan and receipt references before calling it. Journal records contain secrets.
1179
394
 
1180
395
  ```ts
1181
- const fork = createControlledLifecycleFork({
1182
- sourceRpcUrl: "http://127.0.0.1:18555", forkRpcUrl: "http://127.0.0.1:18556",
1183
- allowTransactions: true, impersonation: "anvil",
1184
- });
1185
- const planned = await planLaunch({client, account: plan.creator, plan, mode: "staged", limits, fork});
396
+ import { appendFile } from "node:fs/promises";
397
+ import {
398
+ keccak256, type Chain, type Hash, type LocalAccount, type PublicClient,
399
+ type Transport, type WalletClient,
400
+ } from "viem";
401
+ import {
402
+ buildNextTransaction, LifecyclePhase, readLaunchProgress, serializeLaunchPlan,
403
+ type LifecycleReceiptReference, type PlannedLaunch,
404
+ } from "@black-market/sdk";
405
+
406
+ export async function executeLaunch(
407
+ client: PublicClient, wallet: WalletClient<Transport, Chain, LocalAccount>,
408
+ planned: PlannedLaunch, receipts: LifecycleReceiptReference[], journalPath: string,
409
+ activationHash?: Hash,
410
+ ) {
411
+ const record = (value: unknown) => appendFile(journalPath,
412
+ JSON.stringify(value, (_, v) => typeof v === "bigint" ? v.toString() : v) + "\n",
413
+ { mode: 0o600 });
414
+ await record({ plan: serializeLaunchPlan(planned.plan), mode: planned.mode,
415
+ confirmations: planned.confirmations, ufg: planned.ufg, receipts });
416
+ for (;;) {
417
+ const next = await buildNextTransaction({
418
+ client, planned, receipts, submissionClient: client,
419
+ });
420
+ if (!next) {
421
+ const progress = await readLaunchProgress({ client, planned, receipts });
422
+ if (!progress.confirmationSafe || progress.canonical.phase !== LifecyclePhase.Active
423
+ || !progress.receipts.every((r) => r.status === "confirmed")
424
+ || !progress.markets.every((m) => m.live?.publicTrading) || !activationHash) {
425
+ throw new Error("Launch not confirmed Active; reconcile the journal");
426
+ }
427
+ return { progress, activationHash };
428
+ }
429
+ if (!next.admission?.admitted || next.gas === undefined || next.gasPrice === undefined) {
430
+ throw new Error("Immediate transaction is not admitted");
431
+ }
432
+ const [nonce, pendingBalance] = await Promise.all([
433
+ client.getTransactionCount({ address: next.from, blockTag: "pending" }),
434
+ client.getBalance({ address: next.from, blockTag: "pending" }),
435
+ ]);
436
+ const dataFee = next.estimate?.dataFee;
437
+ if (next.nonce === undefined || BigInt(nonce) !== next.nonce || dataFee === undefined
438
+ || pendingBalance < next.value + next.gas * next.gasPrice + dataFee) {
439
+ throw new Error("Pending nonce or complete affordability changed; revalidate");
440
+ }
441
+ const [address] = await wallet.getAddresses();
442
+ if (wallet.chain.id !== next.chainId || await wallet.getChainId() !== next.chainId
443
+ || await client.getChainId() !== next.chainId
444
+ || address?.toLowerCase() !== next.from.toLowerCase()
445
+ || wallet.account.address.toLowerCase() !== planned.account.toLowerCase()) {
446
+ throw new Error("Wallet account or chain changed");
447
+ }
448
+ const raw = await wallet.signTransaction({
449
+ chain: wallet.chain, account: wallet.account, nonce,
450
+ to: next.to, data: next.data, value: next.value, gas: next.gas, gasPrice: next.gasPrice,
451
+ });
452
+ const hash = keccak256(raw);
453
+ await record({ transactionId: next.id, kind: next.kind, hash, raw });
454
+ const sent = await client.sendRawTransaction({ serializedTransaction: raw });
455
+ if (sent !== hash) throw new Error("Submission hash mismatch; reconcile before continuing");
456
+ const receipt = await client.waitForTransactionReceipt({
457
+ hash, confirmations: planned.confirmations,
458
+ });
459
+ await record({ hash, receipt });
460
+ if (receipt.status !== "success" || receipt.transactionHash !== hash) {
461
+ throw new Error("Reverted or replaced transaction; reconcile before continuing");
462
+ }
463
+ receipts.push({ transactionHash: hash, confirmations: planned.confirmations,
464
+ observedBlockNumber: receipt.blockNumber, observedBlockHash: receipt.blockHash });
465
+ if (next.kind === "atomic" || next.kind === "activate") activationHash = hash;
466
+ await record({ receipts, activationHash });
467
+ }
468
+ }
1186
469
  ```
1187
470
 
1188
- The fork endpoint must be distinct/loopback, including localhost/IP aliases and effective port. Anvil reset/impersonation is default; disposable Hardhat may explicitly use its reset method/impersonation. Never pass the source client as a fork or use a live unlocked wallet as a disposable backend. Operators own exclusive fork access during generic EVM proof. Controlled-local execution does not prove wallet submission acceptance and cannot certify Nitro metering or imply bound topology is a gas optimization.
471
+ A send timeout is ambiguous: the hash may already be mined. Stop, query the saved
472
+ hash, and reconcile pending nonce/receipt state; do not automatically resend.
473
+ For an interactive review delay, pass the previously displayed command as
474
+ `reviewedTransaction` to a fresh `buildNextTransaction` before signing.
475
+ That directly replays the same immediate envelope at its held nonce, gas and gas price.
1189
476
 
1190
- ## Wallet submission and canonical recovery
477
+ Completion has **four checks**: receipt confirmed → markets active → metadata
478
+ published → API indexed. A successful transaction alone does not establish the last two.
1191
479
 
1192
- The SDK never signs. Before **every** approval/reset/core/predeploy/cancel signature, compare the wallet's configured chain (if any) and fresh connected `getChainId()` with the returned `next.chainId`. Failure/invalid identity is a refusal. Supply a matching explicit viem Chain descriptor; do not pass `chain: null` or disable wallet chain checks. Send exact `from/to/data/value/gas/gasPrice`, not a cached estimate or wallet batch.
480
+ ## Publish metadata
1193
481
 
1194
- Simulation and `next.admission` carry flat, independent proof outcomes:
1195
-
1196
- | Field | Values | Scope |
1197
- | --- | --- | --- |
1198
- | `executionProof` | `"proved"`, `"failed"`, `"unavailable"` | Stateful exact-envelope execution/postconditions for the proven prefix (or the immediate reviewed step), never measurement alone or an unproved suffix. |
1199
- | `protocolFit` | `"proved"`, `"failed"`, `"unknown"` | Applicable proven protocol and known tightening restrictions. Missing optional policy remains separately disclosed. |
1200
- | `transportPreflight` | `"not-requested"`, `"passed"`, `"failed"` | Immediate exact next-transaction acceptance estimate on the supplied submission RPC. |
1201
-
1202
- `admitted` means the proven prefix fits and is affordable, not that deferred
1203
- transactions have executed or that eventual activation is guaranteed. Always
1204
- inspect `simulation.deferredTransactionIds`. Admission reuses `blockNumber`,
1205
- `blockHash`, `account`, `chainId` and `limits`; signing uses only fresh build-next
1206
- admission. Plans never preflight future steps independently against nonexistent state.
482
+ Stage metadata **before chain signatures**, after you freeze the final plan. The
483
+ call order is `createUploadSession` → optional `putImage` → `completeUpload` →
484
+ confirm activation → `publishUploadSession`. Metadata-only sessions skip the image calls.
485
+ The following metadata-only function signs attribution and writes the API; call it
486
+ only with authorization. It works with a direct creator or the UFG principal.
1207
487
 
1208
488
  ```ts
1209
- const planned = await planLaunch({ client, account: plan.creator, plan, mode: "atomic" });
1210
- const submissionClient = {
1211
- request: ({ method, params }) => activeProvider.request({ method, params }),
1212
- };
1213
- const reviewedTransaction = await buildNextTransaction({ client, planned, receipts, submissionClient });
1214
- // Present this exact envelope for user review. After explicit confirmation:
1215
- if (reviewedTransaction !== undefined) {
1216
- const next = await buildNextTransaction({ client, planned, receipts, submissionClient, reviewedTransaction });
1217
- // Recheck active account/connector/chain and strict exact-envelope equality before signing next.
489
+ import { randomBytes, randomUUID } from "node:crypto";
490
+ import { appendFile } from "node:fs/promises";
491
+ import { toHex, zeroHash, type Chain, type LocalAccount, type Transport, type WalletClient } from "viem";
492
+ import {
493
+ canonicalLaunchMetadataHash, LaunchApiClient, launchAttributionTypes,
494
+ type LaunchSessionCreateMetadata, type PlannedLaunch,
495
+ } from "@black-market/sdk";
496
+
497
+ export async function stageMetadata(
498
+ wallet: WalletClient<Transport, Chain, LocalAccount>, planned: PlannedLaunch,
499
+ metadata: LaunchSessionCreateMetadata, journalPath: string,
500
+ ) {
501
+ if (wallet.account.address.toLowerCase() !== planned.account.toLowerCase()
502
+ || await wallet.getChainId() !== Number(planned.chainId)
503
+ || wallet.chain.id !== Number(planned.chainId)) throw new Error("Wrong signer domain");
504
+ if (metadata.name !== planned.plan.token.name || metadata.symbol !== planned.plan.token.symbol) {
505
+ throw new Error("Metadata differs from plan");
506
+ }
507
+ const record = (v: unknown) => appendFile(journalPath, JSON.stringify(v) + "\n", { mode: 0o600 });
508
+ const idempotencyKey = randomUUID();
509
+ const nonce = toHex(randomBytes(32));
510
+ const deadline = BigInt(Math.floor(Date.now() / 1000)) + 600n;
511
+ const signature = await wallet.signTypedData({
512
+ account: wallet.account,
513
+ domain: { name: "Abyss Launch Attribution", version: "1",
514
+ chainId: Number(planned.chainId), verifyingContract: planned.plan.orchestrator },
515
+ types: launchAttributionTypes, primaryType: "LaunchAttribution",
516
+ message: { chainId: planned.chainId, wallet: planned.account,
517
+ metadataHash: canonicalLaunchMetadataHash(metadata), imageSha256: zeroHash,
518
+ imageContentType: "", imageContentLength: 0n, idempotencyKey, nonce, deadline },
519
+ });
520
+ const body = { chainId: Number(planned.chainId), wallet: planned.account, metadata,
521
+ authorization: { nonce, deadline: deadline.toString(), signature } };
522
+ await record({ idempotencyKey, body });
523
+ const api = new LaunchApiClient({ baseUrl: "https://api.abyss.trading", retries: 1 });
524
+ const session = await api.createUploadSession(body, idempotencyKey);
525
+ await record({ session }); // Save the one-time capability before continuing.
526
+ if (!session.capability || session.status !== "ready_to_launch") {
527
+ throw new Error("Metadata session not ready");
528
+ }
529
+ return { api, sessionId: session.sessionId, capability: session.capability };
1218
530
  }
1219
531
  ```
1220
532
 
1221
- When supplied, `submissionClient` receives only read-only `eth_chainId` and
1222
- `eth_estimateGas` for the immediate next `from/to/data/value/gas/gasPrice` at `latest`.
1223
- Its positive estimate must fit the reviewed gas exactly; gas is never silently enlarged.
1224
- Chain identity is checked before and after estimation, and the source pinned hash/chain
1225
- is rechecked before returning. Success sets `next.admission.transportPreflight` to
1226
- `"passed"`. Refusal, unsupported RPC, malformed estimate, over-envelope estimate or
1227
- chain drift raises `SUBMISSION_PREFLIGHT_FAILED` without returning a transaction.
1228
- The error's `simulation` retains execution admission and its canonical context, changing
1229
- only transport outcome/reason to `"failed"`. Cancellation uses the same preflight.
1230
- Without a submission client, `"not-requested"` remains honest and no wallet RPC is implied.
1231
- Applications still own fresh active account/provider guards around asynchronous work;
1232
- a read-only preflight does not authenticate an active wallet or authorize a signature.
1233
-
1234
-
1235
- Persist transaction hashes immediately. After receipt, retain `observedBlockNumber`, `observedBlockHash`, confirmation policy and, for replacements, original `transactionHash` plus effective `replacementHash`. A same-nonce self-transfer is replacement-cancelled, not launch completion. Removed observed blocks are reorged; status zero is reverted; a missing receipt at the unchanged observed block remains pending. Unknown hashes do not invent reorgs.
533
+ Retain the capability separately: `getUploadSession`, `renewUploadUrl`, and
534
+ `completeUpload` responses do not reissue it. With an image, authorize its SHA-256,
535
+ content type, and byte length using `sha256Hex`; upload only the signed bytes to the
536
+ API-issued PUT URL/headers. Wait for verified image readiness before launching.
1236
537
 
1237
- `confirmations` is positive and defaults to 1. Progress anchors confirmed canonical and latest head hashes, compares confirmed/head/pending account nonces, and prevents ref-free reloads from advancing unconfirmed work. Pending receipts/state return no executable envelope (`RECEIPT_PENDING`/`UNCONFIRMED_STATE`). Reload the exact plan/mode/policy/receipt references; never trust a cached step counter. Market read failures are surfaced without removing adapter-independent cancellation.
538
+ Publish the **original atomic/activation transaction hash**, not an approval or begin
539
+ hash. Before publication, reread `readLaunchProgress` with all retained receipts;
540
+ require confirmation-safe Active, all markets public, and the activation confirmed at
541
+ its saved block number/hash. Call `publishUploadSession(chainId, sessionId, capability,
542
+ { chainId, transactionHash })` on the same `LaunchApiClient`.
1238
543
 
1239
- ## Funding and cancellation
1240
-
1241
- Creator/payer/refund recipient are the same address. ERC20 prerequisites approve immutable `fundingEscrow`, not core/adapters; amounts aggregate by input asset and insufficient nonzero allowances reset before exact approval. NativeWrap requires canonical wrapped-native binding and exact summed native value; there is no invented launch fee. Swap funding commits approved target/spender/code hash and exact input/output/calldata. Unrelated donations/other launches are not swept as refunds.
1242
-
1243
- Preparing/Ready cancellation uses the stored commitment after expiry/retirement without calling adapters or requiring new profile/funding admission. It refunds unspent external funding only. Wrapped-native refunds are wrapped ERC20, not original native gas funds. Cancelled token inventory remains inactive or burns according to the plan. Cancellation still needs current execution admission and nonce/receipt safety; Active/Cancelled are terminal and there is no postactivation creator pause.
1244
-
1245
- ```ts
1246
- const cancel = await buildNextTransaction({client, planned, receipts, limits, fork, action: "cancel"});
1247
- // Apply identical wallet-domain/exact-envelope/receipt handling before submission.
1248
- ```
544
+ Catch `LaunchPublishPending`: its `session` retains indexing state and `retryAfterMs`
545
+ tells you when to poll `getUploadSession` or retry the same publication. HTTP 202 is
546
+ pending indexing, not a deployment revert. A published/indexed response must match
547
+ the predicted token, activation hash, metadata, and image. An API/image failure after
548
+ Active calls for publication recovery, not another deployment.
1249
549
 
1250
- ## Stable author APIs and V3 fees
550
+ ## Recover or cancel
1251
551
 
1252
- `developerBeneficiary`/envelope `beneficiary` is stable `authorId`. Registry `authorPayout(authorId)` is mutable live routing/controller; zero means unknown. Registry admin or current payout/controller may change a known nonzero route with `setAuthorPayout`. Retirement never changes stable identity or blocks existing active developer credits.
552
+ Restore the exact saved plan with `parseLaunchPlan`, original mode, confirmations,
553
+ receipt references, and UFG config. Use `planLaunch` to re-admit it; obtain fresh
554
+ commands with `buildNextTransaction`. Never regenerate nonce, salts, or minima.
555
+ For runner launches use `--resume <original-run-dir>`; do not substitute a fresh run.
1253
556
 
1254
- | Helper | Inputs / semantics |
557
+ | Canonical phase / evidence | Action |
1255
558
  | --- | --- |
1256
- | `readLifecycleAuthor` | `{client,registry,authorId}` → canonical core/factory, known/live payout, hub count, pinned chain/block. |
1257
- | `readAuthorHubs` | Same + `offset?:bigint,limit?:bigint` (defaults 0/100), bounded **1..100**, offset ≤ total, actual hubs/nextOffset/total/cursorComplete. |
1258
- | `readDeveloperFees` | Same + `hub,assets?` → authenticated V3 hub, actual assets, claimable/reserved balances, frozen source terms/assets. Explicit unsupported assets are flagged, not relabeled supported. |
1259
- | `buildSetAuthorPayoutTransaction` | Same + `payout,account,chainId:bigint`; requires current controller/admin and known identity. |
1260
- | `buildClaimDeveloperFeesTransaction` | Same + `hub,asset,account,chainId:bigint`; permissionless fixed-live-payout direct claim. |
1261
- | `buildClaimDeveloperFeesPageTransaction` | Same + `offset:bigint,limit:bigint,assets?,account,chainId:bigint`; factory page **1..10**. |
1262
- | `decodeDeveloperClaimReceipt` | `{receipt,transaction}` using exact unsigned claim context; validates emitter/account/target/context/cursor/status/rows. |
1263
-
1264
- Unsigned objects carry exact `chainId/from/to/data/value`, with no execution default. Canonical membership is anchored to **registry.core → core.feeFactory → factory.isHub** before any hub self-report; selected registry/core/factory bindings are also checked. Page assets are sorted unique nonzero addresses, at most eight; `[]` means each hub's actual assets. Page cursor completion is independent of payment success: statuses **0 paid, 1 zero, 2 unsupported, 3 failed** remain distinct; preserve retryable failed rows even when the cursor reaches total. A successful direct receipt without `DeveloperFeesClaimed` is `unobserved`, not an invented zero payment.
1265
-
1266
- Choose an explicit page gas budget under the current account/RPC/chain limits, including every bounded hub/asset call plus factory overhead and EIP-150 headroom. `eth_estimateGas` success alone does not prove every row was attempted: the factory can complete the cursor while reporting failed rows under insufficient caller gas. Always decode and retry failed rows; never equate transaction success or cursor completion with payment success.
1267
-
1268
- Current ordinary fee preview/building uses public `lifecycleFeeHubAbi` with **no-argument `claimAndSplit()`**. Preview via viem `simulateContract`/`eth_call`, and build unsigned calldata with `encodeFunctionData`; neither guarantees execution. V3 freezes each source's adapter/profile/terms/stable author/maximum/selected rate. For each actual collected asset: executor bounty is deducted, owner/rewards/burn policy is split with canonical dust allocation, then developer fees are floor-rounded from each source's proportional contribution to the owner share. Developer fees do not reduce rewards/burn, do not reward donations and do not spend prior reserved credits. Owner and developer allocations are reserved before external payouts. `Distributed` includes `developerAmount`; hub reserved inventory is `reservedOwnerFees + reservedDeveloperFees`, not owner reserves alone.
1269
-
1270
- Current registered fee owners may update executor bounty up to 1,000 bps. Each no-arg harvest snapshots one rate for all sources, including already accrued source fees. Owner/reward/burn policy and frozen developer terms do not change; old owner credits remain with the credited owner. Owner `claimOwnerFees(asset,recipient)` withdraws reserved credits without harvesting/bounty. Developer `claimDeveloperFees(authorId,asset)` has no recipient argument and resolves live registry payout at execution.
1271
-
1272
- Lifecycle dividend tokens retain independent current-owner dividend bounty up to 1,000 bps, gross previews and net beneficiary payments with separate caller bounty; self-claims get full gross. Staking claims have no dividend payout bounty. Oracle genesis/cardinality/opening state is not lending-feed maturity, depth or recency approval; unrelated lending checks remain unchanged.
1273
-
1274
- ## Independent vectors and local proof
559
+ | None, no submitted hashes | Replan before signing; an expired unstarted plan needs a new explicit review. Runner resume stops for reconciliation. |
560
+ | Preparing / Ready, deadline valid | Resume the original plan's remaining preparations/activation. |
561
+ | Preparing / Ready, expired or retired | Request `buildNextTransaction({ client, planned, receipts, action: "cancel" })`; apply the same signing/receipt checks. |
562
+ | Active | Verify markets and original activation, then finish publication/claims. |
563
+ | Cancelled | Terminal; confirm refunds and inactive token state. |
564
+ | Pending / unconfirmed / replaced / reorged receipt | Reconcile the saved hash, inclusion, and nonce first; do not advance from a cached step counter. |
565
+
566
+ Preparing/Ready cancellation uses the committed plan without adapter calls or new
567
+ profile admission. It refunds **unspent external funding**; wrapped-native refunds
568
+ are WETH, not ETH gas. Token inventory stays inactive or burns per `burnOnCancel`.
569
+ Active liquidity cannot be cancelled or withdrawn, and there is no postlaunch creator pause.
570
+ `buildNextTransaction` returning `undefined` can mean Active **or Cancelled**; read phase.
571
+
572
+ | Symptom | Cause | Next action |
573
+ | --- | --- | --- |
574
+ | HTTP 403/429 on `eth_chainId`, `eth_getBalance`, `eth_simulateV1` | RPC throttling | Wait or use another RPC; reconcile and resume the same run. A 429 is not a revert. |
575
+ | Begun launch, deadline passed | Interrupted staged execution | Cancel as above; a fresh invocation cannot continue the original funds. |
576
+ | `eth_getLogs` range error | Genesis scan exceeds the 10M-block limit | Scan from core deployment block 82,880,313 in chunks of at most 10M. |
577
+ | `KYBER_UNAVAILABLE` / invalid WETH route | Trying to swap ETH→WETH | Use native wrapping; Kyber converts other quotes. |
578
+ | `INVALID_KYBER_INTENT` | Conversion deadline reused the plan deadline / exceeds 1,200 seconds | Use the separate 300-second conversion clock before committing. |
579
+ | `PLAN_NOT_ADMITTED` gas error | Saved envelope no longer fits | Rebuild the immediate envelope with `buildNextTransaction`. |
580
+ | `ECONOMIC_REVIEW_REQUIRED` hash mismatch | Review belongs to a different plan invocation | Review and admit the same saved plan. |
581
+ | `PUBLISH_TIMEOUT` / `API_INDEX_TIMEOUT` / image URL error after activation | Publication/indexing | Check Active; resume publication for the existing token. |
1275
582
 
1276
- `test/fixtures/launch-lifecycle-v1.json` is a portable **offline wire vector**, not a deployed launch. Its independent generator owns the literal canonical ABI tuple:
583
+ ## After launch
1277
584
 
1278
- ```sh
1279
- node examples/generate-lifecycle-commitment-fixture.ts test/fixtures/launch-lifecycle-v1.json
1280
- ```
585
+ Read the confirmed progress's `feeHub`, rewards module, and prepared market identities.
586
+ Locked launch positions are permanent; collecting fees never withdraws their principal.
1281
587
 
1282
- Build and run the offline unit suite with `pnpm test`, and deployed read-only discovery with
1283
- `pnpm examples`. Unit regressions cover commitment/identity/numerical domains,
1284
- reviewed terms/rate/bounds, salt normalization/mining, receipt recovery, and author
1285
- authentication/routing/page outcomes. Wire vectors remain
1286
- offline schema references; they are not a mainnet launch or an admitted deployment graph.
588
+ | Role | What to do |
589
+ | --- | --- |
590
+ | Anyone | Call fee hub `claimAndSplit()` to collect and split fees; receive the executor bounty if configured. |
591
+ | Fee owner | Read `claimableOwnerFees(owner, asset)`; call `claimOwnerFees(asset, recipient)` for credited fees. |
592
+ | Token holder | Claim staking rewards or dividends from the rewards module; the app has holder claim UI. |
593
+ | Hook author | Use `readDeveloperFees` and `buildClaimDeveloperFeesTransaction`; [author reference](reference.md#stable-author-apis-and-v3-fees). |
594
+ | UFG holder | Read the treasury with `readUfgTreasury(client, token)`; redeem through its contract. |
595
+
596
+ For ordinary owner fees, first simulate fee hub `claimAndSplit()` with
597
+ `lifecycleFeeHubAbi`, submit that approved transaction, and wait for confirmation.
598
+ Then read `claimableOwnerFees(owner, asset)`. Simulate and submit
599
+ `claimOwnerFees(asset, recipient)` from the credited owner account. The second call
600
+ withdraws credits without harvesting again. Check each receipt and `Distributed` /
601
+ `OwnerFeesClaimed` events; an outer success can still contain failed individual payouts.
602
+
603
+ <a id="stable-author-apis-and-v3-fees"></a>
604
+ V3 author royalties come from attributable owner fees **after** the executor bounty;
605
+ they do not reduce holder rewards or burn allocations. Author identity is stable,
606
+ while its registry payout can change. Owner credits remain with the credited owner.
607
+ The current fee owner may change executor bounty up to **1,000 bps**; policy fractions
608
+ and frozen author terms stay fixed. See the [full fee reference](reference.md#stable-author-apis-and-v3-fees).
609
+
610
+ <a id="0120-release-opt-in-ufg-treasuries"></a>
611
+ ## Launch a UFG treasury
612
+
613
+ UFG is an opt-in launch route, not another DEX venue. Creating one requires the SDK;
614
+ the app can display treasury launches but its creation route is not available here.
615
+ Start with the pure [`ufg-launch-config.ts`](../examples/ufg-launch-config.ts) example.
616
+ Neither the eleven wrappers nor the generic `examples/launch.ts` CLI supports UFG deployment.
617
+
618
+ 1. Choose reward-free **ERC20**, executor bounty **0**, treasury share **10,000 bps**,
619
+ exit fee **0–2,000 bps**, and **1–7 sorted unique reserve addresses**.
620
+ The reserve set must equal the market quote set and exclude the launch token.
621
+ 2. Before prediction, set creator to `UFG_LAUNCH_PROXY_ADDRESS` and nonce to
622
+ `ufgLaunchNonce(principal, nonce96)`. Keep inventory/buy recipients external to the proxy.
623
+ Recompute token orientation, ranges, and fees for that predicted token.
624
+ 3. Use canonical Abyss config1 **2 QUOTE / 3 QUOTE_ORACLE**, or UFG fixed/dynamic V4
625
+ config6 with `feeMode: 1` (QuoteOnly), LP fee 0, developer fee 0, and the identical
626
+ typed `ufgLaunchConfig` tail. HMM and ordinary V4 profiles cannot substitute.
627
+ 4. Build policies with `buildUfgFeeAssetPolicies(predictedToken, reserveAssets)`:
628
+ launch-token fees burn; reserve fees fund the treasury after protocol share.
629
+ 5. Call `prepareAndPlanLifecycleLaunch` or `planLaunch` with `account: principal`
630
+ and **`ufg: { config }`**. Every sender/payer/refund recipient stays the principal.
631
+ Approve the **proxy**, not the funding escrow, for non-native inputs.
632
+ 6. Preserve `PlannedLaunch.ufg` and restore the same config on recovery.
633
+ `readLaunchProgress` and `buildNextTransaction`, including cancellation, keep proxy routing.
634
+
635
+ `validateUfgLaunchPlan` checks offline restrictions. `buildUfgApprovals`,
636
+ `buildUfgLaunchTransactions`, and `buildUfgCancelTransaction` build unsigned commands;
637
+ use planner admission before signing. [UFG addresses/codecs](reference.md#deployed-infrastructure)
638
+ and `ufgBlackMarketTreasuryAbi` cover treasury reads, `collect()`, and redemption.
639
+
640
+ `readUfgTreasury(client, token)` returns the treasury address or `undefined`.
641
+ Holders burn tokens for a pro-rata share of **current collected reserves**, less
642
+ exit fee; this is not a guaranteed USD floor. Accrued pool fees are not collected
643
+ backing. Inspect collection failure events as well as receipt success; a fork check
644
+ needed at least 750k gas for `collect()`, not a universal mainnet gas limit.
645
+
646
+ <a id="0100-release-hmm-v2-only"></a>
647
+ ## Configure HMM V2
648
+
649
+ Select `V4HmmFixedPool` or `V4HmmDynamicPool`, with LP/developer fees **0**.
650
+ HMM adds auxiliary strategy liquidity; it cannot withdraw locked launch positions.
651
+ `defaultHmmLaunchConfig` requires explicit bid-principal and reference-price guards:
1287
652
 
1288
- `examples/launch.ts` reviews a saved plan without signatures or source-chain writes:
653
+ ```ts
654
+ import { defaultHmmLaunchConfig, encodeHmmLaunchTail } from "@black-market/sdk";
1289
655
 
1290
- ```sh
1291
- pnpm build
1292
- node examples/launch.ts --plan plan.json --rpc https://rpc.mainnet.chain.robinhood.com/ --mode staged --mine
656
+ const config = defaultHmmLaunchConfig({
657
+ tokenBudget: 500_000n * 10n ** 18n, quoteAsset: "USDG", quoteDecimals: 6,
658
+ maxBidQuote: 100n * 10n ** 6n, referenceDeviation: 0,
659
+ });
660
+ config.strategyShareBps = 2500;
661
+ config.strategy.premiumDecaySeconds = 259200;
662
+ console.log("tail bytes:", (encodeHmmLaunchTail(config).length - 2) / 2);
1293
663
  ```
1294
664
 
1295
- The saved plan must use that RPC's chain and current deployment. `--mine` finalizes bound
1296
- salts offchain; `--limits current-limits.json`, `--receipts receipts.json`, and `--confirmations N`
1297
- supply explicit execution/recovery evidence. The command never invents provider/account limits.
1298
- Without a sequential simulation backend and matching execution-limit provenance, its review
1299
- can be provisional and cannot produce an admitted next transaction.
1300
-
1301
- `examples/launch-lifecycle-smoke.ts manifest.json sdk-plans.json` executes Solidity-exported
1302
- plans only on explicitly authorized owned loopback fixtures. It exercises atomic/staged
1303
- modes, typed predeployment, ordered buys, permanent custody, canonical recovery, no-argument
1304
- V3 harvest, and authenticated author claims. `test/lifecycle-chain.test.mjs` exercises
1305
- real-graph refusals/reorg/cancellation with the same fixture environment. See
1306
- [example instructions](../examples/README.md#actual-owned-fixture-proof) for the explicit
1307
- local-execution opt-in and required files.
1308
-
1309
- ## Individual token creation examples
1310
-
1311
- Eleven independent `examples/launch-erc20-*.ts` / `launch-erc404-*.ts` files invoke
1312
- the shared `runLaunchExample(caseObject)` using valuation-driven economics with no
1313
- required CLI parameters or fixture files. **No mode flag means a dry run**:
1314
- the first output is `DRY RUN — no transactions signed or broadcast.`, followed by
1315
- the full economic review and retained planning artifacts. Successful planning exits
1316
- `0` without constructing a wallet signer, signing, broadcasting, uploading or publishing.
1317
- The final output line is the same case command with `--broadcast` appended:
1318
-
1319
- ```sh
1320
- node examples/launch-erc20-v4.ts
1321
- node examples/launch-erc20-v4.ts --broadcast
1322
- ```
665
+ These 100-USDG/zero-deviation/25% choices illustrate units, not recommended risk
666
+ settings. Put the typed config in config6 `hmmLaunchConfig`; the codec appends its
667
+ **512-byte** tail automatically. The [reference](reference.md#hmm-fields-and-migration)
668
+ lists all 15 strategy fields and the short HMM V1 migration note.
1323
669
 
1324
- **`--broadcast` authorizes full execution and intentionally skips manual
1325
- economic-review admission.** It prints/saves the same review and computed hash,
1326
- plans afresh against current state, then performs guarded signing, broadcast,
1327
- chain verification, image upload and publication. It does not execute the dry run's
1328
- saved plan. Unknown, duplicate or mixed flags fail fast with usage text.
1329
- Optional `--resume <recovery-dir>` continues the original committed launch unchanged,
1330
- without needing `--broadcast`. CLI dry-run/broadcast selection is independent of
1331
- the case's atomic/staged execution mode. The eight original cases remain; the three additions
1332
- (`launch-erc20-multi-quote.ts`, `launch-erc20-depth-v4.ts`, and
1333
- `launch-erc20-dividends-multiquote-mixed.ts`) await user live-run validation before
1334
- any replacement decision. See the [example guide](../examples/README.md#individual-token-creation)
1335
- for all one-line commands and setup-once working-directory `.env`.
1336
- Node 24+ loads `.env` once, with existing process environment taking precedence.
1337
- `LAUNCH_API_URL` and **exactly one** signer source are required before RPC/signing/API
1338
- work: `PRIVATE_KEY`, or `EXAMPLE_KEYSTORE_PATH` for an owner-only local V3
1339
- scrypt/AES-128-CTR JSON keystore. `EXAMPLE_KEYSTORE_PASSWORD` defaults to the empty
1340
- string; both/neither sources reject. The bounded keystore loader authenticates the
1341
- MAC and declared address before deriving the creator. There is no default key,
1342
- guessed metadata API or API-omission mode. Dry runs derive only the public creator
1343
- address (keystore decryption is allowed for this); local signing methods are constructed
1344
- only for broadcast or nonterminal resume execution. Broadcast mainnet writes are
1345
- irreversible. The official chain-4663 RPC is the default.
1346
-
1347
- The implementation imports public `@black-market/sdk`, resolves `getAddresses(4663)`,
1348
- derives the creator from the selected signer and discovers actual admitted profiles.
1349
- V4 requires bound topology/config `6` on a V2-constructor graph; Abyss requires exact
1350
- adapter `profileId(3)` QUOTE_ORACLE/config `1`. WETH verifies wrapped-native
1351
- binding/18 decimals and registered P1 oracle `(1,4096)`. USDG uses 6 decimals,
1352
- the reviewed `usdPeg` USD-X18 seed `1e18`, and a compatible registered oracle
1353
- discovered from pinned factory history/mapping, not an invented WETH oracle ID.
1354
- The eight original cases retain their token/reward/market/position/mode choices:
1355
- ERC20 `1_000_000e18` supply; ERC404 `10_000e18` supply / `100e18` NFT unit; selected
1356
- staking/dividends and token-only burn; one through five positions and ordered buys.
1357
- All cases now fund those buys through native-ETH Kyber conversion. Identity,
1358
- finalized salts and exact plans are saved before execution; refusal never changes
1359
- mode, drops positions/buys or overrides failed admission.
1360
- Pricing uses a USD 5000 target (optional exact `EXAMPLE_TARGET_MARKET_CAP_USD`),
1361
- actual total minted supply, predicted token order and each market's integer budget.
1362
- `deriveLaunchPoolRecipe` owns one-position pricing/liquidity; multi-position markets
1363
- use `deriveLaunchPositionRanges` with exact counts, `positionGaps: "adjacent"` and a
1364
- boundary final tail. Staircase remains the original multi-position default; the
1365
- four-position progressive Depth case uses Early Scarcity with adjacent bands and
1366
- increasing inventory allocations, not the separate historical eight-position
1367
- catalogue. Both venues use spacing 60/LP fee 3000. There is no 1:1 price,
1368
- fixed-liquidity reserve or arbitrary absolute range. All intended supply funds LP
1369
- except explicitly computed rounded-up mint-debit dust, which burns.
1370
-
1371
- Every opening buy defaults to **0.00001 native ETH** (`10000000000000` wei).
1372
- `EXAMPLE_BUY_AMOUNT_WEI` overrides the ETH input per buy, not the eventual quote
1373
- amount. The runner aggregates inputs by quote and obtains one fresh
1374
- `POST /api/v1/quote/kyber/build?chainId=4663` build per quote, using native
1375
- zero-address `tokenIn`, exact input, actual escrow sender/recipient, **50 bps**
1376
- conversion slippage and the same fixed **300-second launch-purpose deadline**
1377
- as the plan. `EXAMPLE_KYBER_API_URL` defaults to `LAUNCH_API_URL`; the app's public
1378
- trade default is `https://api.abyss.trading`, so a different service must be chosen
1379
- explicitly. The API owns route preview; preview GET bytes are not executable funding.
1380
-
1381
- Each row is `LifecycleFundingKind.Swap`, with native zero-address `inputAsset`,
1382
- the reviewed router target and original built calldata. The runner decodes the
1383
- reviewed `swap` entry point and binds currencies, escrow recipient, exact input,
1384
- minimum output, executor payload and flags. The API floor
1385
- `amountOutMinimum = floor(amountOut * 9950 / 10000)` becomes the quote budget;
1386
- ordered buys sharing a quote divide that budget, assigning the integer remainder
1387
- to the last buy. The creator needs native ETH for conversion **plus gas**, not
1388
- WETH/USDG wallet balances or ERC20 approvals. Registry target/spender/code-hash
1389
- admission is checked and rechecked before signatures. Any refusal stops unsigned
1390
- with retained evidence, never a native-wrap/direct-ERC20/separate-wallet-swap
1391
- fallback, route retry or rewritten deadline.
1392
-
1393
- Optional `EXAMPLE_TOKEN_NAME`, `EXAMPLE_TOKEN_SYMBOL` and
1394
- `EXAMPLE_TOKEN_DESCRIPTION` override each case's defaults while retaining the
1395
- existing normalized text and API/lifecycle length limits. Optional
1396
- `EXAMPLE_IMAGE_PATH` supplies actual PNG/JPEG/WebP artwork, bounded to 1 byte
1397
- through 5 MiB. Its exact SHA-256/content-type/length is part of the reviewed,
1398
- signed attribution; the runner follows the API-issued upload and completion flow.
1399
- Without that option, metadata-only sessions still require `image: null`.
1400
-
1401
- The indicative WETH USD seed comes only from the configured `LAUNCH_API_URL` token-prices
1402
- API: one uncached GET `/api/v1/token-prices?chainId=4663&limit=100`, with a 30-second
1403
- timeout, 1 MiB streamed body limit and no pagination, redirects or retries. The
1404
- reviewed WETH row must have chain 4663, ready metadata, 18 decimals, `priced: true`,
1405
- anchor source and direct WETH/Chainlink provenance with aggregator
1406
- `0x78F3556b67E17Df817D51Ef5a990cDaF09E8d3A9`. Price parsing requires an exact positive
1407
- uint256 decimal `priceUsdX18`. Missing, unpriced, mismatched or unavailable data
1408
- rejects without a manual or alternate-source fallback. API publication supplies
1409
- an indicative launch valuation, not executable swap, receipt or chain authority.
1410
- API origin, index/graph blocks, upstream timestamp/age and local retrieval time are
1411
- preserved and displayed; retrieval does not refresh the upstream observation.
1412
- There is no 15-minute contract-round gate or API-block canonicality requirement.
1413
- Public `economic-review.json` and exact private recovery precede the first signature.
1414
- Review includes quote/source/time, target, actual minted supply, exact price
1415
- orientation, all allocations/liquidity/fees,
1416
- expected retained supply and aligned opening valuations. The final admitted
1417
- activation simulation supplies each ordered buy's quote spent/token output,
1418
- all-in average USD/token and fee-plus-price-impact premium. This is not a claim
1419
- of simulated final marginal spot. Alignment and buys mean the eventual valuation
1420
- need not equal USD 5000; use actual retained supply and observed post-buy price.
1421
-
1422
- The computed review hash is provenance evidence, never an authorization input:
1423
- there is no interactive prompt or environment hash gate. `--broadcast` alone is
1424
- the explicit mode authorization, recorded in `economic-admission.json` as
1425
- `authorization: "--broadcast"` with the computed hash; this artifact is not another gate.
1426
- Before each attribution/transaction signature, the immutable review hash, saved
1427
- plan/supply/order/target and fresh RPC token prediction are rechecked, then the same
1428
- API source is reread. A changed exact price, API origin, chain, WETH/decimals or
1429
- direct-anchor identity/kind/source rejects without rewriting intent. Failed or
1430
- unpriced reads reject. Index/graph and observation/retrieval times may advance for
1431
- an unchanged exact price and source, without replacing the original review.
1432
- RPC still owns provider chain, balances, nonce, deadlines and native execution
1433
- proof. The saved deadline is checked again after asynchronous quote/authority
1434
- reads and before signatures/staging; expiry stops without extending it.
1435
- Partial execution never grants permission to replay or silently adopt different economics.
1436
- The current 0.7.1 V4 examples require the config6/V2 profile, freezing max `10000`,
1437
- min `1000` pips and sensitivity `7654321` fee-pips × seconds/tick. Its reviewed
1438
- `FixedFeePoolHookV1` charges the fixed maximum `10000` pips (1%) on applicable amounts;
1439
- minimum and sensitivity are stored constructor evidence, not a dynamic fee schedule.
1440
- Optional `NFT_BASE_URI` provides an ERC404 base URI you actually host; its default is
1441
- empty, with real NFT units/mirror behavior but no claim of hosted NFT metadata.
1442
-
1443
- The chain-4663 examples require native sequential Nitro `eth_simulateV1`; generic
1444
- Anvil/Hardhat simulation is not an ArbOS compute/poster proof. Source RPC requests
1445
- have a 120-second bound and are not retried. There are no invented default gas or
1446
- calldata caps. Explicit environment restrictions only tighten the complete envelope;
1447
- headroom defaults to 1,500 bps. SDK native pinned compute/poster reads and exact replay
1448
- remain mandatory, and build-next uses the same source RPC as the read-only submission
1449
- preflight before each local signature. Environment options are documented in the example guide.
1450
-
1451
- Execution rechecks chain/creator/destination and exact finalized calldata before each
1452
- send. Exact admitted envelopes are signed locally; computed hashes and unsigned
1453
- envelopes are saved **before** the single raw broadcast, and included receipts are
1454
- saved before status/provenance assertions. One canonical confirmation is required.
1455
- Actual verification covers Active state, factory/token runtime/name/symbol/kind/rewards,
1456
- ERC404 unit/URI/mirror, opened markets, sealed V4 custody/permanent Abyss lock terms,
1457
- fee policies and ordered activation buys.
1458
-
1459
- Actual SDK API metadata staging uses locally signed full-domain attribution.
1460
- Before **every** publication attempt and before final success, a fresh canonical
1461
- RPC barrier binds the original activation hash/receipt to the saved commitment
1462
- and Active state; a different later activation observation cannot replace it.
1463
- Only that original hash is published. Genuine pending-indexer HTTP `202` is
1464
- polled within 90 seconds respecting `Retry-After`; other failures are not retried.
1465
- Success requires an optimistic/final session and real indexed
1466
- `/api/v1/launches/<token>?chainId=4663` DTO with matching creator/token/hash/metadata.
1467
- Missing/incompatible indexing fails without manufacturing projections, skipping
1468
- API or launching another token.
1469
-
1470
- When `EXAMPLE_IMAGE_PATH` is set, metadata staging alone is insufficient:
1471
-
1472
- 1. The completed session must show the exact signed SHA-256/content-type/length
1473
- and a ready image.
1474
- 2. Publication must retain that descriptor and reference a ready public image.
1475
- 3. The indexed token must carry a non-null matching `imageUrl`, with matching
1476
- metadata type/length and a token-bound `imageKey`.
1477
- 4. One bounded, no-redirect GET of that URL must return **HTTP 200**, an image
1478
- content-type family and nonempty bytes. Byte-identical output must match the
1479
- upload's exact SHA-256/type/length. Different served bytes are recorded as an
1480
- inferred API/CDN transformation, proving only the image-family/nonempty-body
1481
- check, not byte identity. The canonical RPC barrier runs again after this GET.
1482
-
1483
- Any failed binding, HTTP/type/size/body check exits nonzero with retained evidence;
1484
- there is no image retry or metadata-only fallback. Without an image path, the
1485
- metadata-only null-image behavior remains.
1486
-
1487
- Every dry run or broadcast automatically creates fresh `launch-results/<timestamp-case-random>/`;
1488
- `--resume` appends recovery evidence in the original directory. Dry runs retain
1489
- all planning/review/recovery artifacts, but no admission, signed bytes, API session
1490
- or publication artifacts:
1491
-
1492
- | Artifact | Evidence |
1493
- | --- | --- |
1494
- | `events.jsonl` | Timestamped case/stage events, RPC/API errors, admission/simulation and actual chain observations. |
1495
- | `plan.json` | Redacted finalized plan for public inspection, not exact recovery authority. |
1496
- | `economic-review.json` | Public exact quotes, native Kyber inputs/calldata/minima/deadline, metadata/image descriptors, target, aligned minted/retained valuation, position budgets/debits/dust, fees, protected buys and simulated all-in execution prices; redacted, not recovery authority. |
1497
- | `economic-admission.json` | Broadcast-only mode authorization (`authorization: "--broadcast"`) and computed review hash as provenance, not supplied-hash admission. |
1498
- | `receipts.json` | Empty for a dry run; execution retains exact signed-envelope hashes pre-broadcast, followed by real receipts/references. |
1499
- | `result.json` | `black-market.launch-example-result.v1`, runMode, scope, status, failure stage and causal evidence. A successful dry run has `scope: "planning"` and `execution: "dry-run"`; it does not claim chain/API completion. |
1500
- | `published-token.json` | Actual indexed API representation. |
1501
- | `published-image.json` | Optional bounded public-image HTTP/type/length/SHA-256 verification, transformation qualification or failure evidence. |
1502
- | `private-recovery.json` | Mode `0600`; complete exact finalized `plan`, original `mode`/`confirmations` and immutable economic review/hash saved during planning. Broadcast adds immutable mode authorization/hash provenance, signed raw bytes, API signature and one-time capability retained across updates. Never share/commit. |
1503
-
1504
- Public artifacts preserve structured field names and redact sensitive values:
1505
- known keys/signatures/raw bytes/capabilities/auth values and endpoint secrets,
1506
- while retaining public calldata/hashes/revert bytes. `node examples/<case>.ts
1507
- --resume <recovery-dir>` reloads the owner-only exact plan, retained economic authorization
1508
- provenance, original mode/confirmations/restrictions and typed original inclusion references.
1509
- Fresh `planLaunch` (never preparation/mining) re-admits that commitment.
1510
- Canonical Preparing/Ready runs only the remaining build-next sequence; canonical
1511
- Active recovers the retained activation receipt and performs verification and
1512
- publication only. Missing/unconfirmed, moved/reorged or replaced receipt evidence
1513
- and unresolved signed hashes stop for read-only reconciliation, never replay.
1514
- The API session is refreshed while retaining its one-time capability. A pending
1515
- image requires the original `EXAMPLE_IMAGE_PATH` bytes and signed SHA-256/type/length;
1516
- only expired PUT responses (403/410/412) permit one upload-URL renewal/retry.
1517
- Existing chain verification and every publication canonicality barrier remain required.
1518
- Never reload redacted `plan.json` as the commitment. See
1519
- [recovery instructions](../examples/README.md#evidence-and-failure-recovery).
1520
- Configuration, admission, broadcast, publication and interruption failures exit
1521
- nonzero and preserve evidence. No source rollback, cancellation, broadcast retry or
1522
- relaunch occurs.
1523
- Inspect retained transaction hashes before deciding to run a new case after an
1524
- ambiguous send. `test/launch-example.test.mjs`, `test/launch-example-matrix.test.mjs`,
1525
- `test/launch-example-mode.test.mjs` and `test/launch-example-resume.test.mjs` cover offline
1526
- financial bounds, funding/rounding, quote/admission, signing/retention/config, default
1527
- dry-run/no-signing/banner/command, broadcast routing and resume boundaries,
1528
- not actual chain/API proof. The older independent whole-graph local
1529
- Solidity-exported proof remains separate.
1530
-
1531
- ## Real HTTP API integration
1532
-
1533
- The opt-in suites use `LaunchApiClient` and actual HTTP requests—not a mocked transport.
1534
- They do not start a service or provision storage. Configure an abyss-api service with:
1535
-
1536
- - `/api/v1/launch-upload-sessions` and its capability-protected read route;
1537
- - real application PostgreSQL storage with launch-upload-session migrations applied;
1538
- - an enabled request chain matching `LAUNCH_API_TEST_CHAIN_ID`;
1539
- - its current launch verifying contract matching `LAUNCH_API_TEST_ORCHESTRATOR`.
1540
-
1541
- For the actual abyss-api service, that verifying contract is the configured deployment
1542
- `contracts.launchFactory` field, which must contain the current orchestrator address.
1543
- The signed attribution domain is `Abyss Launch Attribution`, version `1`. Session signature
1544
- deadlines must be future timestamps at most 15 minutes away. Metadata hashes use canonical
1545
- NFC/trimmed text, normalized URLs, fixed field order, and null absent optional values.
1546
-
1547
- | Environment | Meaning |
1548
- | --- | --- |
1549
- | `LAUNCH_API_TEST_URL` | Required HTTP service root, e.g. `http://127.0.0.1:18763`; no implicit production target. |
1550
- | `LAUNCH_API_TEST_CHAIN_ID` | Defaults to `4663`; must be configured on the test service. |
1551
- | `LAUNCH_API_TEST_ORCHESTRATOR` | Defaults to `getAddresses(4663).launchOrchestrator` (October8 mined graph); must match the service's signing domain. |
1552
- | `LAUNCH_API_TEST_PRIVATE_KEY` | Required only for the explicit signed suite; disposable EOA key, never a production key. |
1553
-
1554
- ```sh
1555
- # Read-only capability/error boundaries; no key and no storage writes.
1556
- LAUNCH_API_TEST_URL=http://127.0.0.1:18763 pnpm test:api
1557
-
1558
- # Signed metadata-session writes, allowed only on an owned disposable loopback service.
1559
- LAUNCH_API_TEST_URL=http://127.0.0.1:18763 \
1560
- LAUNCH_API_TEST_PRIVATE_KEY="$DISPOSABLE_TEST_PRIVATE_KEY" pnpm test:api:write
1561
- ```
670
+ ### V2 fee funding and owner controls
1562
671
 
1563
- The write suite creates metadata-only sessions, reads persisted normalized metadata,
1564
- replays the byte-identical signed body without reissuing the one-time capability, refuses a
1565
- changed authorized payload under the same idempotency key, and checks capability isolation.
1566
- It also rejects wrong signers, wrong orchestrator/chain domains, altered metadata, a changed
1567
- idempotency key, expired authorization, and invalid signatures. The private key signs EIP-712
1568
- metadata only; it need not have a balance. Unique session/key/nonces permit repeat runs.
1569
- The disposable service operator owns database cleanup; the API has no session-deletion route.
1570
-
1571
- Neither suite needs an image store, indexed launch, confirmed transaction hash, or chain
1572
- write. Metadata sessions do not prove token deployment, image publication, or transaction
1573
- indexing. Image/upload/publication operations require those additional real prerequisites
1574
- and are not exercised here. Explicitly selected suites fail on missing URL/key, absent
1575
- storage/routes, an unreachable service, or incompatible signing configuration instead of
1576
- silently skipping. Ordinary `pnpm test` stays offline and does not select HTTP integration.
672
+ HMM V2 reserves its strategy share **on each swap in both currencies, after protocol
673
+ treasury share**. The remainder follows normal fee-hub distribution. HMM does not
674
+ burn strategy fees. Funded bids and maintenance events do not prove fills or profit.
675
+
676
+ Use `hmmFeeCollectorV2Abi` to read `strategyOwner()` / `liquidityManager()` and call
677
+ `setStrategyShareBps(0..10000)` as the current fee owner. Use
678
+ `hmmLiquidityManagerV2Abi` for `strategyConfig()`, `setStrategyConfig(...)`,
679
+ `placementsPaused()`, and `setPlacementsPaused(bool)`. Pausing stops new placements
680
+ and queues auxiliary withdrawal on maintenance, not trading or fee collection.
681
+ Lower intake caps do not liquidate existing lots; premium changes affect new lots.
682
+
683
+ For reference tick `R`, deviation `D`, and spacing `S`, ask lower is at least
684
+ `R - D*S`; bid upper is at most `R + D*S`, with grid rounding. A wider deviation
685
+ permits placements farther from the truncated oracle reference but weakens its
686
+ price bound. Choose guards against the actual pool; there is no universal safe setting.