@black-market/sdk 0.4.0 → 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.
@@ -2,42 +2,63 @@
2
2
 
3
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 registry/certification/adapter/config contracts under `contracts/src/launch/lifecycle/v2`, the typed hook deployers, and `contracts/src/launch/fees/v3` in the sibling protocol repository. Plan identity remains `LaunchPlanV1`; shared V4 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.
6
-
7
- The keystore-only `DeployPoolLaunchV1` deployment includes fixed pool-bound V4
8
- (`PoolHookDeployerV1`, `PoolMarketAdapterV1`, `PoolFeeCollectorFactoryV1`) and canonical Abyss:
9
- two adapters and five automatically admitted profiles.
10
- Registry V2 has one `LaunchCertificationV2` child and no arbitrary unsigned V4 endpoint.
11
- V4 uses `profileEnvelope`, `profileId` and signed six-argument `registerProfile`.
12
- Admin-only `registerAbyssProfile(uint8,registration)` independently certifies the four canonical
13
- config-1 variants; they have no author envelope or royalty allocation.
14
- Envelope/bounds/graph types and encoding helpers use `Launch` names.
15
- There are no old-name aliases. The admission EIP-712 domain is `Black Market Launch Registry`,
16
- version `2`; regenerate author consent after cutover. Market configuration tuple layouts remain
17
- unchanged; bounds encoding changes below require fresh profile identities and consent.
18
- Old deployment evidence is not new-graph proof.
19
-
20
- `LaunchBoundsV2` has five members beginning with `minimumTickSpacing`: tick-spacing bounds,
21
- maximum positions, maximum oracle cardinality and fee-mode flags. Former LP/hook fee ceilings
22
- and profile-level oracle/external-liquidity pins are removed. Creators independently choose
23
- valid LP/hook rates below 1,000,000 pips, including 15%, their registered oracle configuration,
24
- and their per-pool external-liquidity policy. Author royalty ceilings remain separate.
25
- Regenerate bounds digests, profile IDs and consent for the new tuple.
26
-
27
- Robinhood P1 `(1,4096)`, P2 `(6,4096)` and P3 `(17,4096)` are usable on the same admitted profile.
28
- Planning/preparation validates each selected oracle on the pinned factory at the pinned block;
29
- unregistered/invalid oracle parameters and excessive cardinality still fail. With
30
- `externalLiquidityDisabled=false`, third-party LP add/remove is allowed after opening.
31
- Permanent launch custody and pre-opening protection remain unchanged.
32
-
33
- Shared contracts are `FixedFeeSharedHookV1`, `SharedHookDeployerV1` and
34
- `SharedMarketAdapterV1`. ABI exports are `sharedMarketAdapterV1Abi`,
35
- `sharedHookDeployerV1Abi`, `poolMarketAdapterV1Abi`, `poolHookDeployerV1Abi` and
36
- `poolFeeCollectorFactoryV1Abi`; validation uses `validateV4LifecycleMarket`.
37
- The factory getter is `dependencyDigest(address)`. Identity domains are
38
- `black-market.launch-profile.v2`, `black-market.v4-dependencies.v2` and
39
- `black-market.pool-bound-market-economics.v1`. No old-name aliases remain; regenerate
40
- profile identities, commitments and author consent for these domain labels.
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**.
9
+
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`.
41
62
 
42
63
  ## Public operations
43
64
 
@@ -108,13 +129,13 @@ Registry `authorizationDigest`, `beneficiaryNonces` and `registerProfile` ABIs e
108
129
  (uint16,uint24,int24,uint160,uint24,uint8,uint8,address,bool,bytes32,bytes32,bytes32,bytes32,address,uint16,(int24,int24,uint128,bytes32,uint256)[])
109
130
  ```
110
131
 
111
- 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.
112
133
 
113
134
  ```ts
114
135
  const profile = profiles.find((row) => row.id === selectedProfileId);
115
136
  if (!profile?.admitted || !profile.envelope || !profile.developerTerms) throw new Error("Selected profile is not reviewed/admitted");
116
- const config = encodeV4LifecycleMarketConfig({
117
- version: 4, lpFeePips, tickSpacing, sqrtPriceX96, hookFeePips, feeMode,
137
+ const config = encodePoolBoundV4LifecycleMarketConfig({
138
+ version: 5, hookSalt: zeroHash, lpFeePips, tickSpacing, sqrtPriceX96, hookFeePips, feeMode,
118
139
  protocolFeeDenominator: profile.envelope.protocolFeeDenominator,
119
140
  treasury: profile.envelope.protocolTreasury,
120
141
  externalLiquidityDisabled: creatorSelectedExternalLiquidityDisabled,
@@ -238,6 +259,164 @@ Lifecycle dividend tokens retain independent current-owner dividend bounty up to
238
259
  node examples/generate-lifecycle-commitment-fixture.mjs test/fixtures/launch-lifecycle-v1.json
239
260
  ```
240
261
 
241
- 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
+ ```
242
408
 
243
- `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.4.0",
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);