@black-market/sdk 0.3.1 → 0.5.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.
Files changed (46) hide show
  1. package/README.md +102 -129
  2. package/assets/splash.png +0 -0
  3. package/dist/addresses.d.ts +3 -1
  4. package/dist/addresses.d.ts.map +1 -1
  5. package/dist/addresses.js +23 -17
  6. package/dist/addresses.js.map +1 -1
  7. package/dist/client.js +2 -2
  8. package/dist/client.js.map +1 -1
  9. package/dist/launch-api.d.ts +2 -1
  10. package/dist/launch-api.d.ts.map +1 -1
  11. package/dist/launch-api.js +2 -1
  12. package/dist/launch-api.js.map +1 -1
  13. package/dist/launch.d.ts +1 -1
  14. package/dist/launch.d.ts.map +1 -1
  15. package/dist/launch.js +1 -1
  16. package/dist/launch.js.map +1 -1
  17. package/dist/lifecycle/abi.d.ts +1191 -248
  18. package/dist/lifecycle/abi.d.ts.map +1 -1
  19. package/dist/lifecycle/abi.js +119 -21
  20. package/dist/lifecycle/abi.js.map +1 -1
  21. package/dist/lifecycle/markets.d.ts +1 -1
  22. package/dist/lifecycle/markets.d.ts.map +1 -1
  23. package/dist/lifecycle/markets.js +10 -8
  24. package/dist/lifecycle/markets.js.map +1 -1
  25. package/dist/lifecycle/planner.d.ts.map +1 -1
  26. package/dist/lifecycle/planner.js +8 -5
  27. package/dist/lifecycle/planner.js.map +1 -1
  28. package/dist/lifecycle/progress.d.ts +8 -1
  29. package/dist/lifecycle/progress.d.ts.map +1 -1
  30. package/dist/lifecycle/progress.js +37 -29
  31. package/dist/lifecycle/progress.js.map +1 -1
  32. package/dist/lifecycle/schema.d.ts +3 -32
  33. package/dist/lifecycle/schema.d.ts.map +1 -1
  34. package/dist/lifecycle/schema.js +6 -9
  35. package/dist/lifecycle/schema.js.map +1 -1
  36. package/docs/launch-lifecycle.md +230 -21
  37. package/package.json +8 -3
  38. package/src/addresses.ts +25 -18
  39. package/src/client.ts +2 -2
  40. package/src/launch-api.ts +3 -2
  41. package/src/launch.ts +1 -1
  42. package/src/lifecycle/abi.ts +120 -21
  43. package/src/lifecycle/markets.ts +9 -8
  44. package/src/lifecycle/planner.ts +7 -4
  45. package/src/lifecycle/progress.ts +39 -28
  46. package/src/lifecycle/schema.ts +7 -11
@@ -1,17 +1,64 @@
1
- # Reviewed launch lifecycle API
1
+ # Launch lifecycle API
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 reviewed 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
+ `@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.
4
4
 
5
- The canonical authorities are the reviewed registry/certification/adapter/config contracts under `contracts/src/launch/lifecycle/v2`, the reviewed typed hook deployers, and `contracts/src/launch/fees/v3` in the sibling protocol repository. Plan identity remains `LaunchPlanV1`; shared V4 now accepts **config 4 only**, bound V4 **config 5 only**, and Abyss keeps its canonical schema. Old Atomic/Unified request encoders, template catalogs and old V4 profile/adapter constants have been removed. Immutable old contracts/pools and unrelated DEX/lending functions are not upgraded or rebound.
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 5**,
8
+ canonical Abyss uses **config 1**, and generic current shared-V4 support uses **config 4**.
6
9
 
7
- The clean pool-only deployment uses `DeployPoolLaunchV1`, `PoolHookDeployerV1`,
8
- `PoolMarketAdapterV1`, `PoolFeeCollectorFactoryV1` and `LaunchCertificationV2`.
9
- Registry V2 has no legacy certification child or unsigned admission endpoint.
10
- Use `profileEnvelope`, `profileId` and signed `registerProfile`; envelope/bounds/graph
11
- types and encoding helpers now use `Launch` names without the `Reviewed` prefix.
12
- There are no old-name aliases. The admission EIP-712 domain is `Black Market Launch Registry`,
13
- version `2`; regenerate author consent after cutover. Existing profile/dependency hash
14
- preimages and config tuples remain unchanged. Old deployment evidence is not new-graph proof.
10
+ ## Deployed infrastructure
11
+
12
+ Robinhood mainnet (`4663`) selects the mined `pool-launch-v1` deployment captured at block
13
+ `80548181` in `contracts/deployments/launch/pool-launch-v1/20261005T053747Z-b5a7b88/manifest.json`.
14
+ The manifest records successful receipts and canonical runtime evidence. The graph has two
15
+ adapters and five admitted profiles: one fixed pool-bound V4 profile and four canonical Abyss
16
+ variants. It does not deploy a shared V4 root.
17
+
18
+ | SDK field | Mainnet address |
19
+ | --- | --- |
20
+ | `launchOrchestrator` | `0xb75CBD17b9aecb7305B4DFcDa69595F783341c0E` |
21
+ | `launchImplementationRegistry` | `0xaa8a410709B79cBA6F118F1be1FF568877A3B8Ee` |
22
+ | `launchFeeOwnerRegistry` | `0x15778Aad08e12D458B2848F035860e2a8c2a0725` |
23
+
24
+ `getAddresses(4663)` exposes this graph; `robinhoodLaunchApplication` exposes the mined
25
+ launch constants. Launch addresses on `31337` and `46631` remain zero unless configured.
26
+ Address overrides are evaluated once on import: bare names win, then `VITE_`, then
27
+ `NEXT_PUBLIC_`. Configure `LAUNCH_ORCHESTRATOR`, `LAUNCH_IMPLEMENTATION_REGISTRY`, and
28
+ `LAUNCH_FEE_OWNER_REGISTRY` from the same deployment evidence.
29
+
30
+ Canonical Abyss defaults are retained. DEX overrides apply on `31337` and `46631`, not
31
+ mainnet. Lending mainnet defaults use `contracts/deployments/robinhood/replacement-infrastructure.json`;
32
+ the workbench's existing lending endpoints follow that replacement. Lending overrides apply
33
+ on `31337` (all three prefixes) and `46631` (`VITE_`/`NEXT_PUBLIC_`); liquidation-executor
34
+ overrides use all three prefixes on every chain. Workbench chain RPC configuration reads
35
+ `VITE_RPC_URL`, then `NEXT_PUBLIC_RPC_URL`, then `RPC_URL`; client factories prefer their explicit
36
+ `rpcUrl`, then `RPC_URL`, then the chain endpoint. Select the chain explicitly in applications.
37
+
38
+ Registry V2 has one `LaunchCertificationV2` child. V4 uses `profileEnvelope`, `profileId`, and
39
+ signed six-argument `registerProfile`. Admin-only `registerAbyssProfile(uint8,registration)`
40
+ independently certifies config-1 variants without an author envelope or royalty allocation.
41
+ The admission EIP-712 domain is `Black Market Launch Registry`, version `2`.
42
+
43
+ `LaunchBoundsV2` has five members: minimum/maximum tick spacing, maximum positions,
44
+ maximum oracle cardinality, and fee-mode flags. Creators select valid LP/hook rates below
45
+ 1,000,000 pips, a registered oracle configuration, and per-pool external-liquidity policy.
46
+ Author royalty ceilings are separate. Robinhood P1 `(1,4096)`, P2 `(6,4096)`, and P3 `(17,4096)`
47
+ can use the same admitted profile; the selected oracle is checked at the pinned block.
48
+ With `externalLiquidityDisabled=false`, third-party LP add/remove is allowed after opening.
49
+ Permanent launch custody and pre-opening protection still apply.
50
+
51
+ The deployed pool stack uses `FixedFeePoolHookV1`, `PoolHookDeployerV1`,
52
+ `PoolMarketAdapterV1`, and `PoolFeeCollectorFactoryV1`. The complete `fixedFeePoolHookV1Abi`
53
+ includes the exact **one-tuple, 18-field constructor**, callbacks, events, and errors from the
54
+ mined artifact. Its fixed scalar fee calculation does not imply a scalar-argument constructor.
55
+ Generic shared contracts are `FixedFeeSharedHookV1`, `SharedHookDeployerV1`, and
56
+ `SharedMarketAdapterV1`; they require their own admitted deployment graph.
57
+ Other current ABI exports include `sharedMarketAdapterV1Abi`, `sharedHookDeployerV1Abi`,
58
+ `poolMarketAdapterV1Abi`, `poolHookDeployerV1Abi`, and `poolFeeCollectorFactoryV1Abi`.
59
+ Validation uses `validateV4LifecycleMarket`; the collector factory getter is
60
+ `dependencyDigest(address)`. Identity domains are `black-market.launch-profile.v2`,
61
+ `black-market.v4-dependencies.v2`, and `black-market.pool-bound-market-economics.v1`.
15
62
 
16
63
  ## Public operations
17
64
 
@@ -58,7 +105,7 @@ The exact canonical registry getters are `profileCount`, `profileIds(offset,limi
58
105
  - `developerTerms?: {adapter, beneficiary, maximumDeveloperFeeBps, termsDigest, enabled}`.
59
106
  - `protocolMaximumDeveloperFeeBps?: number`.
60
107
 
61
- The profile identity uses domain `black-market.reviewed-launch-profile.v2` and commits the four review/economic digests, topology/config/economic versions, stable beneficiary, maximum 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.
108
+ 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.
62
109
 
63
110
  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.
64
111
 
@@ -82,24 +129,28 @@ Registry `authorizationDigest`, `beneficiaryNonces` and `registerProfile` ABIs e
82
129
  (uint16,uint24,int24,uint160,uint24,uint8,uint8,address,bool,bytes32,bytes32,bytes32,bytes32,address,uint16,(int24,int24,uint128,bytes32,uint256)[])
83
130
  ```
84
131
 
85
- Both preserve LP/hook fees, tick spacing, opening price, fee mode/denominator/treasury, external-liquidity policy and canonical oracle identity. Then come `profileId`, `termsDigest`, `developerBeneficiary` and explicit `developerFeeBps`, followed by positions. Version 5 inserts `hookSalt` immediately before `profileId`. There is no conversion from config 2/3 and no SDK default developer rate, including when the chosen rate is zero. Copy terms from the selected profile, not another shared/bound template'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.
132
+ Both preserve LP/hook fees, tick spacing, opening price, fee mode/denominator/treasury, external-liquidity policy, and canonical oracle identity. Then come `profileId`, `termsDigest`, `developerBeneficiary`, and explicit `developerFeeBps`, followed by positions. Version 5 inserts `hookSalt` immediately before `profileId`. 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.
86
133
 
87
134
  ```ts
88
135
  const profile = profiles.find((row) => row.id === selectedProfileId);
89
136
  if (!profile?.admitted || !profile.envelope || !profile.developerTerms) throw new Error("Selected profile is not reviewed/admitted");
90
- const config = encodeV4LifecycleMarketConfig({
91
- version: 4, lpFeePips, tickSpacing, sqrtPriceX96, hookFeePips, feeMode,
137
+ const config = encodePoolBoundV4LifecycleMarketConfig({
138
+ version: 5, hookSalt: zeroHash, lpFeePips, tickSpacing, sqrtPriceX96, hookFeePips, feeMode,
92
139
  protocolFeeDenominator: profile.envelope.protocolFeeDenominator,
93
140
  treasury: profile.envelope.protocolTreasury,
94
- externalLiquidityDisabled: profile.envelope.bounds.externalLiquidityDisabled,
95
- oracleConfigId: profile.envelope.bounds.oracleConfigId,
141
+ externalLiquidityDisabled: creatorSelectedExternalLiquidityDisabled,
142
+ oracleConfigId: creatorSelectedOracleConfigId,
96
143
  profileId: profile.id, termsDigest: profile.developerTerms.termsDigest,
97
144
  developerBeneficiary: profile.developerTerms.beneficiary,
98
145
  developerFeeBps: creatorSelectedRate, positions,
99
146
  });
100
147
  ```
101
148
 
102
- `validateReviewedV4LifecycleMarket` checks profile/terms/topology/version, explicit 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.
149
+ `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.
150
+
151
+ `validateV4LifecycleOracle({client,envelope,oracleConfigId,block})` checks the selected registered
152
+ oracle against the certified factory and `maximumOracleCardinality`. Profile discovery does
153
+ not choose an oracle or query a profile-pinned oracle ID.
103
154
 
104
155
  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.
105
156
 
@@ -119,7 +170,7 @@ const optionalPredeploy = await buildPoolBoundHookDeploymentTransaction({client,
119
170
  // Inspect only; separately admit/authenticate/sign any optional predeployment.
120
171
  ```
121
172
 
122
- The reviewed collector supplies canonical normalized economic commitment and constructor metadata. `hashPoolBoundV4MarketCommitment` uses domain `black-market.reviewed-pool-bound-market-economics.v1`: chain, core, registrar, predicted token, adapter/profile/quote/budget/version and encoded config hash with **only hookSalt zeroed**. Developer identity/digest/rate remain committed. The 18-word 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.
173
+ 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**. Developer identity/digest/rate remain committed. The 18-word 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.
123
174
 
124
175
  Mining is cancellable/nonblocking; `abort()` raises `AbortError` and does not cancel an onchain launch. 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.
125
176
 
@@ -208,6 +259,164 @@ Lifecycle dividend tokens retain independent current-owner dividend bounty up to
208
259
  node examples/generate-lifecycle-commitment-fixture.mjs test/fixtures/launch-lifecycle-v1.json
209
260
  ```
210
261
 
211
- After concurrent source changes settle, build/test the package and run `examples/quickstart.mjs`. Meaningful regressions cover commitment/identity/numerical domains, retired codecs, reviewed terms/rate/bounds, salt normalization/mining/cancellation, receipt recovery and author authentication/routing/page outcomes. Retired wording/selector/source-wiring tests are removed rather than re-pinned.
262
+ Build and run the offline unit suite with `pnpm test`, and deployed read-only discovery with
263
+ `pnpm examples`. Unit regressions cover commitment/identity/numerical domains,
264
+ reviewed terms/rate/bounds, salt normalization/mining, receipt recovery, and author
265
+ authentication/routing/page outcomes. Wire vectors remain
266
+ offline schema references; they are not a mainnet launch or an admitted deployment graph.
267
+
268
+ `examples/launch.mjs` reviews a saved plan without signatures or source-chain writes:
269
+
270
+ ```sh
271
+ pnpm build
272
+ node examples/launch.mjs --plan plan.json --rpc https://rpc.mainnet.chain.robinhood.com/ --mode staged --mine
273
+ ```
274
+
275
+ The saved plan must use that RPC's chain and current deployment. `--mine` finalizes bound
276
+ salts offchain; `--limits current-limits.json`, `--receipts receipts.json`, and `--confirmations N`
277
+ supply explicit execution/recovery evidence. The command never invents provider/account limits.
278
+ Without a sequential simulation backend and matching execution-limit provenance, its review
279
+ can be provisional and cannot produce an admitted next transaction.
280
+
281
+ `examples/launch-lifecycle-smoke.mjs manifest.json sdk-plans.json` executes Solidity-exported
282
+ plans only on explicitly authorized owned loopback fixtures. It exercises atomic/staged
283
+ modes, typed predeployment, ordered buys, permanent custody, canonical recovery, no-argument
284
+ V3 harvest, and authenticated author claims. `test/lifecycle-chain.test.mjs` exercises
285
+ real-graph refusals/reorg/cancellation with the same fixture environment. See
286
+ [example instructions](../examples/README.md#actual-owned-fixture-proof) for the explicit
287
+ local-execution opt-in and required files.
288
+
289
+ ## Individual token creation smoke
290
+
291
+ `pnpm smoke:launch` builds the SDK and runs `examples/smoke-launch.mjs` for **one**
292
+ case from an explicit `black-market.launch-smoke-fixture.v1` file. The
293
+ [shared local setup and eight case commands](../../black-market/docs/sdk-launch-smoke.md)
294
+ create separate owned loopback execution/simulation Anvil instances on chain `4663`
295
+ using the current deployed graph, not a replacement launcher. Use a real loopback
296
+ API with application storage and an indexer configured for that same execution chain.
297
+ No default live endpoint, private key, source-chain snapshot rollback or production
298
+ write is used. The older whole-suite Solidity-exported proof remains separate.
299
+
300
+ ```sh
301
+ pnpm smoke:launch --help
302
+ pnpm smoke:launch --fixture /tmp/sdk-smoke/fixture.json --list
303
+
304
+ # Default plan-only: registry/provenance reads, token prediction and offchain salt mining.
305
+ pnpm smoke:launch --fixture /tmp/sdk-smoke/fixture.json \
306
+ --case erc20-staking-v4 --output /tmp/node-staking-plan
307
+
308
+ # Actual metadata signature/session, launch, custody/buy checks, publication and token GET.
309
+ pnpm smoke:launch --fixture /tmp/sdk-smoke/fixture.json \
310
+ --case erc20-staking-v4 --output /tmp/node-staking-launch \
311
+ --execute --api-url http://127.0.0.1:18763 --publish-timeout-seconds 90
312
+
313
+ # API omitted by explicit choice, not a full end-to-end pass.
314
+ pnpm smoke:launch --fixture /tmp/sdk-smoke/fixture.json \
315
+ --case erc404-abyss --output /tmp/node-erc404-chain --execute --chain-only
316
+ ```
317
+
318
+ `--list` reads only the fixture catalogue, with no RPC, signature or write. A default
319
+ run persists the finalized portable `plan.json` and reports **“Plan prepared; launch
320
+ not executed”**, `scope: "plan-only"`, `execution: "not-run"` and API `not-run`.
321
+ Only `--execute` permits SDK `planLaunch`/controlled-fork simulation, metadata signing,
322
+ session creation and `eth_sendTransaction` from the fixture's unlocked Anvil creator.
323
+ Every endpoint must be an explicit loopback root and the fixture must declare
324
+ `fixtureOnly: true`; execution/simulation ports must differ. An API endpoint can come
325
+ from `fixture.apiUrl` or `--api-url`, never the production SDK default.
326
+ Local execution and instrumented simulation RPC HTTP calls have a bounded
327
+ 120-second per-request timeout to accommodate cold nested-fork state reads; they
328
+ are not retried. API requests retain their separate timeout and publication deadline.
329
+
330
+ All eight cases cover ERC20/ERC404, staking/dividends, token-only burn, atomic/staged
331
+ execution, V4/Abyss/mixed markets and one through five permanent positions. Profiles
332
+ are registry-discovered: V4 must be admitted bound topology/config `5`; Abyss must be
333
+ the adapter's exact `profileId(3)` QUOTE_ORACLE/config `1`, not the first Abyss entry.
334
+ The runner uses exact frozen V4 treasury/denominator/author terms, explicit developer
335
+ rate `0`, measured local gas/calldata caps, current-block deadline and NativeWrap
336
+ funding. ERC20 supply is `1_000_000e18`; ERC404 supply is `10_000e18` with `100e18`
337
+ NFT units. It finalizes hook salts and saves the random identity before any write,
338
+ never switching mode, dropping positions/buys or changing economics to obtain admission.
339
+
340
+ Execution rechecks chain/creator/destination and exact finalized calldata before each
341
+ send. Submitted envelopes/hashes are persisted **before** receipt polling; included
342
+ receipts are preserved **before** status/provenance assertions and require one canonical
343
+ confirmation. Verification reads actual Active state, token runtime/name/symbol/factory
344
+ kind/reward binding, ERC404 unit/URI/mirror, canonical opened markets, permanent V4 sealed
345
+ custody/Abyss lock terms, fee policies and ordered activation buys.
346
+
347
+ The API session uses the SDK attribution schema with full EIP-712 domain and the same
348
+ name/symbol as the token, signed via owned Anvil `eth_signTypedData_v4`. Only the actual
349
+ atomic/activate transaction hash is published. Genuine `LaunchPublishPending` HTTP `202`
350
+ responses are polled within the configured deadline, respecting `Retry-After`; all other
351
+ errors fail immediately. Success additionally requires an actual optimistic/final session
352
+ matching that token/hash and an actual `/api/v1/launches/<token>?chainId=4663` representation
353
+ with matching creator, provenance and metadata. An API that cannot index `LaunchActivated`
354
+ must fail honestly; the runner does not manufacture projections or claim chain-only as
355
+ end-to-end success.
356
+
357
+ Every run requires a fresh exclusive `--output` directory (its parent must exist):
358
+
359
+ | Artifact | Evidence |
360
+ | --- | --- |
361
+ | `events.jsonl` | Timestamped SDK/case/stage events, RPC/API errors, simulation backend/steps/gas, timing and canonical observations. |
362
+ | `plan.json` | Full finalized SDK portable economic plan, written before execution. |
363
+ | `receipts.json` | Every submitted exact envelope/hash and subsequently observed receipt/reference, including failures. |
364
+ | `result.json` | `black-market.launch-smoke-result.v1`, scope, terminal passed/failed stage, chain/API observations, causal stacks and revert data. |
365
+ | `private-recovery.json` | Mode `0600`; the only file retaining API authorization signature and one-time capability. Do not share or commit it. |
366
+
367
+ Public artifacts recursively redact signatures, keys, capability/auth values and URL
368
+ credentials/query secrets while retaining public transaction calldata, hashes, selector
369
+ and revert bytes. Profile/draft/identity/planning/progress files provide additional
370
+ redacted context. Configuration, admission, transaction, publication and interruption
371
+ failures exit nonzero and preserve available evidence. The source chain is **not**
372
+ reverted, cancelled or relaunched after failure; only the SDK's separate simulation fork
373
+ restores its isolated simulation snapshots. Recover from stored evidence before deciding
374
+ to run a new case. Runner diagnostic unit tests are `test/smoke-launch.test.mjs`; they do
375
+ not replace actual fixture/API execution.
376
+
377
+ ## Real HTTP API integration
378
+
379
+ The opt-in suites use `LaunchApiClient` and actual HTTP requests—not a mocked transport.
380
+ They do not start a service or provision storage. Configure an abyss-api service with:
381
+
382
+ - `/api/v1/launch-upload-sessions` and its capability-protected read route;
383
+ - real application PostgreSQL storage with launch-upload-session migrations applied;
384
+ - an enabled request chain matching `LAUNCH_API_TEST_CHAIN_ID`;
385
+ - its current launch verifying contract matching `LAUNCH_API_TEST_ORCHESTRATOR`.
386
+
387
+ For the actual abyss-api service, that verifying contract is the configured deployment
388
+ `contracts.launchFactory` field, which must contain the current orchestrator address.
389
+ The signed attribution domain is `Abyss Launch Attribution`, version `1`. Session signature
390
+ deadlines must be future timestamps at most 15 minutes away. Metadata hashes use canonical
391
+ NFC/trimmed text, normalized URLs, fixed field order, and null absent optional values.
392
+
393
+ | Environment | Meaning |
394
+ | --- | --- |
395
+ | `LAUNCH_API_TEST_URL` | Required HTTP service root, e.g. `http://127.0.0.1:18763`; no implicit production target. |
396
+ | `LAUNCH_API_TEST_CHAIN_ID` | Defaults to `4663`; must be configured on the test service. |
397
+ | `LAUNCH_API_TEST_ORCHESTRATOR` | Defaults to `0xb75CBD17b9aecb7305B4DFcDa69595F783341c0E`; must match the service's signing domain. |
398
+ | `LAUNCH_API_TEST_PRIVATE_KEY` | Required only for the explicit signed suite; disposable EOA key, never a production key. |
399
+
400
+ ```sh
401
+ # Read-only capability/error boundaries; no key and no storage writes.
402
+ LAUNCH_API_TEST_URL=http://127.0.0.1:18763 pnpm test:api
403
+
404
+ # Signed metadata-session writes, allowed only on an owned disposable loopback service.
405
+ LAUNCH_API_TEST_URL=http://127.0.0.1:18763 \
406
+ LAUNCH_API_TEST_PRIVATE_KEY="$DISPOSABLE_TEST_PRIVATE_KEY" pnpm test:api:write
407
+ ```
212
408
 
213
- `examples/launch-lifecycle-smoke.mjs manifest.json sdk-plans.json` executes current Solidity-exported `plans` on explicitly authorized owned loopback fixtures, with distinct fork and reviewed controlled-local operator limits. It exercises explicit atomic/staged modes, mined typed predeployment, actual ordered buys/permanent custody, canonical recovery, no-arg V3 harvest and authenticated author page claims with actual payout balance checks. `test/lifecycle-chain.test.mjs` adds real-graph refusal/reorg/cancellation boundaries when the same fixture environment is provided. See [the current evidence index](../../black-market/docs/10-4-audit/README.md) for exercised results and limits; this guide makes no live broadcast/UI/provider-ceiling claim.
409
+ The write suite creates metadata-only sessions, reads persisted normalized metadata,
410
+ replays the byte-identical signed body without reissuing the one-time capability, refuses a
411
+ changed authorized payload under the same idempotency key, and checks capability isolation.
412
+ It also rejects wrong signers, wrong orchestrator/chain domains, altered metadata, a changed
413
+ idempotency key, expired authorization, and invalid signatures. The private key signs EIP-712
414
+ metadata only; it need not have a balance. Unique session/key/nonces permit repeat runs.
415
+ The disposable service operator owns database cleanup; the API has no session-deletion route.
416
+
417
+ Neither suite needs an image store, indexed launch, confirmed transaction hash, or chain
418
+ write. Metadata sessions do not prove token deployment, image publication, or transaction
419
+ indexing. Image/upload/publication operations require those additional real prerequisites
420
+ and are not exercised here. Explicitly selected suites fail on missing URL/key, absent
421
+ storage/routes, an unreachable service, or incompatible signing configuration instead of
422
+ silently skipping. Ordinary `pnpm test` stays offline and does not select HTTP integration.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@black-market/sdk",
3
- "version": "0.3.1",
3
+ "version": "0.5.0",
4
4
  "description": "Canonical TypeScript SDK for Black Market on Robinhood Chain (reviewed multi-market launches, Abyss DEX, lending).",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -23,7 +23,8 @@
23
23
  "files": [
24
24
  "dist",
25
25
  "src",
26
- "docs"
26
+ "docs",
27
+ "assets"
27
28
  ],
28
29
  "sideEffects": false,
29
30
  "engines": {
@@ -39,8 +40,12 @@
39
40
  "dev": "tsc -p tsconfig.json --watch",
40
41
  "examples": "pnpm build && node examples/quickstart.mjs",
41
42
  "lint": "tsc -p tsconfig.json --noEmit",
43
+ "smoke:launch": "pnpm build && node examples/smoke-launch.mjs",
42
44
  "prepublishOnly": "pnpm build",
43
- "test": "pnpm build && node --test test/*.test.mjs",
45
+ "test": "pnpm test:unit",
46
+ "test:unit": "pnpm build && node --test test/*.test.mjs",
47
+ "test:api": "pnpm build && node --test test/integration/launch-api.read.test.mjs",
48
+ "test:api:write": "pnpm build && node --test test/integration/launch-api.sessions.test.mjs",
44
49
  "typecheck": "tsc -p tsconfig.json --noEmit"
45
50
  },
46
51
  "dependencies": {
package/src/addresses.ts CHANGED
@@ -20,7 +20,7 @@ export type AbyssInfrastructureAddresses = {
20
20
  };
21
21
 
22
22
  export type LaunchApplicationAddresses = {
23
- /** No reviewed deployment is selected implicitly; set these from exact deployment evidence. */
23
+ /** Mined mainnet deployment; local chains require explicit environment configuration. */
24
24
  launchOrchestrator: Address;
25
25
  launchImplementationRegistry: Address;
26
26
  launchFeeOwnerRegistry: Address;
@@ -177,19 +177,26 @@ export const robinhoodAbyssInfrastructure: AbyssInfrastructureAddresses = {
177
177
  abyssFeeRouter: "0x2c3B1b6fe0EDa8e10C0445567b47e66E825B34cd",
178
178
  };
179
179
 
180
+ /** Mined pool-launch-v1 deployment, 20261005T053747Z-b5a7b88 (manifest.json). */
181
+ export const robinhoodLaunchApplication: LaunchApplicationAddresses = {
182
+ launchOrchestrator: "0xb75CBD17b9aecb7305B4DFcDa69595F783341c0E",
183
+ launchImplementationRegistry: "0xaa8a410709B79cBA6F118F1be1FF568877A3B8Ee",
184
+ launchFeeOwnerRegistry: "0x15778Aad08e12D458B2848F035860e2a8c2a0725",
185
+ };
186
+
180
187
 
181
- /** Workbench deployment snapshot — override via env when redeploying. */
188
+ /** Robinhood-mainnet fork lending defaults; launch deployment remains explicit. */
182
189
  const workbenchDefaults: ProtocolAddresses = {
183
190
  ...robinhoodAbyssInfrastructure,
184
191
  ...launchApplicationFromEnv(zeroLaunchApplication),
185
- lendingPool: "0xe1576c5CF12F670911BEd5Cc0AEDBcD4E5E9550c",
186
- addressesProvider: "0x9Fcca440F19c62CDF7f973eB6DDF218B15d4C71D",
187
- dataProvider: "0x79E8AB29Ff79805025c9462a2f2F12e9A496f81d",
192
+ lendingPool: "0x5D8878b145904425C598f12EB8eD550985369a82",
193
+ addressesProvider: "0x892faB533E8D04135D902F94974e45dB48C17697",
194
+ dataProvider: "0x1f3faA42C1D5cC330f6BD0242B9a56d611bdC78a",
188
195
  uiPoolDataProvider: zero,
189
196
  walletBalanceProvider: zero,
190
- aaveOracle: "0x9c65f85425c619A6cB6D29fF8d57ef696323d188",
197
+ aaveOracle: "0x6837B3cF5d959d01e07bf6DaB53f562877BF7d53",
191
198
  liquidationExecutor: zero,
192
- protocolVault: "0xAe120F0df055428E45b264E7794A18c54a2a3fAF",
199
+ protocolVault: "0x83Ec5DbFEd6d972be89df88d3654EA2c70Fa2FB3",
193
200
  ethUsdFeed: ROBINHOOD_ETH_USD,
194
201
  weth: ROBINHOOD_WETH,
195
202
  wethGateway: zero,
@@ -233,26 +240,26 @@ export const addresses: Record<SupportedChainId, ProtocolAddresses> = {
233
240
  tokenVesting: envAddr(["TOKEN_VESTING", "VITE_TOKEN_VESTING", "NEXT_PUBLIC_TOKEN_VESTING"]),
234
241
  faucet: envAddr(["FAUCET", "VITE_FAUCET", "NEXT_PUBLIC_FAUCET"]),
235
242
  },
236
- /** Lending/DEX defaults remain canonical; reviewed launches require explicit evidence. */
243
+ /** Current mined launch, canonical Abyss, and replacement lending infrastructure. */
237
244
  4663: {
238
245
  ...robinhoodAbyssInfrastructure,
239
- ...launchApplicationFromEnv(zeroLaunchApplication),
240
- lendingPool: "0x5b8F732A4F7a62D642070bb49255d6C434A76766",
241
- addressesProvider: "0xaaD329d0Da03C8c00E8460b5b208D0A21A48C9df",
242
- dataProvider: "0x3B097A7899DF433552B8428E774964661b53C193",
243
- uiPoolDataProvider: "0x2F35A64c7E7cBc0c05A1A1C3e2F3952E103fD5b8",
244
- walletBalanceProvider: "0x833BB152212DD5d2d6C7b0501aB38efb9602AD8B",
245
- aaveOracle: "0xb9441f8D3Eda65Ac7cbb6b542b5345c98067e5Fb",
246
+ ...launchApplicationFromEnv(robinhoodLaunchApplication),
247
+ lendingPool: "0x5D8878b145904425C598f12EB8eD550985369a82",
248
+ addressesProvider: "0x892faB533E8D04135D902F94974e45dB48C17697",
249
+ dataProvider: "0x1f3faA42C1D5cC330f6BD0242B9a56d611bdC78a",
250
+ uiPoolDataProvider: "0x02D2CA3bBbBaBD3C25bEDD4bE0eE6E5885C4D152",
251
+ walletBalanceProvider: "0xBbb5D81123C3d514456974e9Fe6C7C8d7a0E4E2A",
252
+ aaveOracle: "0x6837B3cF5d959d01e07bf6DaB53f562877BF7d53",
246
253
  liquidationExecutor: envAddr([
247
254
  "LIQUIDATION_EXECUTOR",
248
255
  "VITE_LIQUIDATION_EXECUTOR",
249
256
  "NEXT_PUBLIC_LIQUIDATION_EXECUTOR",
250
257
  ]),
251
- protocolVault: "0x961981916AB6575C3af3eeCecbf6A3b7Fad7C9e1",
258
+ protocolVault: "0x83Ec5DbFEd6d972be89df88d3654EA2c70Fa2FB3",
252
259
  ethUsdFeed: ROBINHOOD_ETH_USD,
253
260
  weth: ROBINHOOD_WETH,
254
- wethGateway: "0xAbc9E3B20e8773536BDCa5ba28C619762C8e0570",
255
- lens: "0x82FA6e601F48d64b4f163Eb47D6001f2d5040f9B",
261
+ wethGateway: "0xa16aB7646267327cB26dD3533526309cDe676d9d",
262
+ lens: "0xB56079f966597CB9edaE27c589F8190f4dCD12df",
256
263
  tokenVesting: zero,
257
264
  faucet: zero,
258
265
  },
package/src/client.ts CHANGED
@@ -20,7 +20,7 @@ export function createProtocolPublicClient(opts?: {
20
20
  }): PublicClient {
21
21
  const chainId = opts?.chainId ?? 31337;
22
22
  const chain = chains[chainId] ?? foundry;
23
- const transport = http(opts?.rpcUrl ?? process.env.RPC_URL ?? "http://127.0.0.1:8545");
23
+ const transport = http(opts?.rpcUrl ?? process.env.RPC_URL ?? chain.rpcUrls.default.http[0]);
24
24
  return createPublicClient({ chain, transport });
25
25
  }
26
26
 
@@ -31,7 +31,7 @@ export function createProtocolWalletClient(opts: {
31
31
  }): WalletClient {
32
32
  const chainId = opts.chainId ?? 31337;
33
33
  const chain: Chain = chains[chainId] ?? foundry;
34
- const transport: Transport = http(opts.rpcUrl ?? process.env.RPC_URL ?? "http://127.0.0.1:8545");
34
+ const transport: Transport = http(opts.rpcUrl ?? process.env.RPC_URL ?? chain.rpcUrls.default.http[0]);
35
35
  const account: Account = privateKeyToAccount(opts.privateKey);
36
36
  return createWalletClient({ account, chain, transport });
37
37
  }
package/src/launch-api.ts CHANGED
@@ -14,7 +14,7 @@ export type LaunchAttributionAuthorization = { nonce: Hex; deadline: string; sig
14
14
  export type LaunchSessionCreateRequest = { chainId: number; wallet: Address; metadata: LaunchSessionCreateMetadata; image?: LaunchSessionImageDescriptor; authorization: LaunchAttributionAuthorization };
15
15
  export type LaunchSessionPublishRequest = { chainId: number; transactionHash: Hash };
16
16
  export type LaunchSessionStatus = "awaiting_upload" | "verifying_image" | "ready_to_launch" | "transaction_submitted" | "awaiting_indexer" | "optimistic" | "final" | "reorged" | "expired" | "rejected";
17
- export type LaunchSessionDirectUpload = { url: string; headers: Record<string, string>; expiresAt: string };
17
+ export type LaunchSessionDirectUpload = { url: string; method: "PUT"; headers: Record<string, string>; expiresInSeconds: number };
18
18
  export type LaunchSessionResponse = { sessionId: string; chainId: number; wallet: Address; status: LaunchSessionStatus; metadata: { name: string; symbol: string; description: string | null; websiteUrl: string | null; twitterUrl: string | null; telegramUrl: string | null; discordUrl: string | null }; image: { sha256: Hex; contentType: LaunchImageContentType; contentLength: number; width: number | null; height: number | null } | null; transactionHash?: Hash | null; token?: Address | null; canonicalStatus: "pending" | "optimistic" | "final" | "reorged"; metadataStatus: "queued" | "ready" | "needs_owner_action" | "none"; imageStatus: "none" | "upload_pending" | "verifying" | "ready" | "rejected"; retryable: boolean; createdAt: string; updatedAt: string; sessionExpiresAt: string; publishedAt?: string; upload?: LaunchSessionDirectUpload; capability?: string; imageUrl?: string };
19
19
  export type LaunchApiErrorCode = "INVALID_REQUEST" | "INVALID_ADDRESS" | "INVALID_IDEMPOTENCY_KEY" | "INVALID_CAPABILITY" | "UNSUPPORTED_CHAIN" | "NOT_FOUND" | "IMAGE_INVALID" | "SESSION_STATE_CONFLICT" | "SESSION_EXPIRED" | "IDEMPOTENCY_CONFLICT" | "UPLOAD_CONFLICT" | "RATE_LIMITED" | "OVERLOADED" | "RPC_UNAVAILABLE" | "RPC_TIMEOUT" | "INDEXER_UNAVAILABLE" | "APP_STORAGE_UNAVAILABLE" | "IMAGE_STORAGE_UNAVAILABLE" | "TX_REVERTED" | "TX_PROVENANCE_MISMATCH" | "TX_METADATA_MISMATCH" | (string & {});
20
20
 
@@ -102,7 +102,8 @@ export const launchAttributionTypes = { LaunchAttribution: [
102
102
  { name: "idempotencyKey", type: "string" }, { name: "nonce", type: "bytes32" }, { name: "deadline", type: "uint256" },
103
103
  ] } as const;
104
104
  export function canonicalLaunchMetadataHash(metadata: LaunchSessionMetadata): Hex {
105
- return keccak256(stringToHex(JSON.stringify({ name: metadata.name.normalize("NFC").trim(), symbol: metadata.symbol.normalize("NFC").trim(), description: (metadata.description ?? "").normalize("NFC").trim(), websiteUrl: metadata.websiteUrl ?? null, twitterUrl: metadata.twitterUrl ?? null, telegramUrl: metadata.telegramUrl ?? null, discordUrl: metadata.discordUrl ?? null, imageKey: metadata.imageKey ?? null })));
105
+ const url = (value: string | undefined) => value === undefined ? null : new URL(value.normalize("NFC").trim()).toString();
106
+ return keccak256(stringToHex(JSON.stringify({ name: metadata.name.normalize("NFC").trim(), symbol: metadata.symbol.normalize("NFC").trim(), description: (metadata.description ?? "").normalize("NFC").trim(), websiteUrl: url(metadata.websiteUrl), twitterUrl: url(metadata.twitterUrl), telegramUrl: url(metadata.telegramUrl), discordUrl: url(metadata.discordUrl), imageKey: metadata.imageKey ?? null })));
106
107
  }
107
108
  export async function sha256Hex(bytes: ArrayBuffer): Promise<Hex> {
108
109
  const digest = await crypto.subtle.digest("SHA-256", bytes);
package/src/launch.ts CHANGED
@@ -3,7 +3,7 @@ import { AUCTION_SUPPLY } from "./auction.js";
3
3
 
4
4
  export const LAUNCH_REWARD_DURATION = 7 * 24 * 60 * 60;
5
5
 
6
- /** Canonical Abyss oracle ID; reviewed V4 uses the selected envelope's exact ID. */
6
+ /** Canonical P3 oracle default; reviewed V4 markets may select any valid registered oracle within their cardinality bounds. */
7
7
  export const LAUNCH_ORACLE_CONFIG_ID =
8
8
  "0xc0e9bed88d70a13fd3ab31451fefdd073b7266e838aee0ad1c236c8c9eff855d" as const;
9
9
  export const LAUNCH_DEADLINE_SECONDS = 20 * 60;