@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.
- package/README.md +102 -129
- package/assets/splash.png +0 -0
- package/dist/addresses.d.ts +3 -1
- package/dist/addresses.d.ts.map +1 -1
- package/dist/addresses.js +23 -17
- package/dist/addresses.js.map +1 -1
- package/dist/client.js +2 -2
- package/dist/client.js.map +1 -1
- package/dist/launch-api.d.ts +2 -1
- package/dist/launch-api.d.ts.map +1 -1
- package/dist/launch-api.js +2 -1
- package/dist/launch-api.js.map +1 -1
- package/dist/launch.d.ts +1 -1
- package/dist/launch.d.ts.map +1 -1
- package/dist/launch.js +1 -1
- package/dist/launch.js.map +1 -1
- package/dist/lifecycle/abi.d.ts +1191 -248
- package/dist/lifecycle/abi.d.ts.map +1 -1
- package/dist/lifecycle/abi.js +119 -21
- package/dist/lifecycle/abi.js.map +1 -1
- package/dist/lifecycle/markets.d.ts +1 -1
- package/dist/lifecycle/markets.d.ts.map +1 -1
- package/dist/lifecycle/markets.js +10 -8
- package/dist/lifecycle/markets.js.map +1 -1
- package/dist/lifecycle/planner.d.ts.map +1 -1
- package/dist/lifecycle/planner.js +8 -5
- package/dist/lifecycle/planner.js.map +1 -1
- package/dist/lifecycle/progress.d.ts +8 -1
- package/dist/lifecycle/progress.d.ts.map +1 -1
- package/dist/lifecycle/progress.js +37 -29
- package/dist/lifecycle/progress.js.map +1 -1
- package/dist/lifecycle/schema.d.ts +3 -32
- package/dist/lifecycle/schema.d.ts.map +1 -1
- package/dist/lifecycle/schema.js +6 -9
- package/dist/lifecycle/schema.js.map +1 -1
- package/docs/launch-lifecycle.md +230 -21
- package/package.json +8 -3
- package/src/addresses.ts +25 -18
- package/src/client.ts +2 -2
- package/src/launch-api.ts +3 -2
- package/src/launch.ts +1 -1
- package/src/lifecycle/abi.ts +120 -21
- package/src/lifecycle/markets.ts +9 -8
- package/src/lifecycle/planner.ts +7 -4
- package/src/lifecycle/progress.ts +39 -28
- package/src/lifecycle/schema.ts +7 -11
package/docs/launch-lifecycle.md
CHANGED
|
@@ -1,17 +1,64 @@
|
|
|
1
|
-
#
|
|
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
|
|
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
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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.
|
|
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
|
|
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 =
|
|
91
|
-
version:
|
|
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:
|
|
95
|
-
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
|
-
`
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
"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
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
188
|
+
/** Robinhood-mainnet fork lending defaults; launch deployment remains explicit. */
|
|
182
189
|
const workbenchDefaults: ProtocolAddresses = {
|
|
183
190
|
...robinhoodAbyssInfrastructure,
|
|
184
191
|
...launchApplicationFromEnv(zeroLaunchApplication),
|
|
185
|
-
lendingPool: "
|
|
186
|
-
addressesProvider: "
|
|
187
|
-
dataProvider: "
|
|
192
|
+
lendingPool: "0x5D8878b145904425C598f12EB8eD550985369a82",
|
|
193
|
+
addressesProvider: "0x892faB533E8D04135D902F94974e45dB48C17697",
|
|
194
|
+
dataProvider: "0x1f3faA42C1D5cC330f6BD0242B9a56d611bdC78a",
|
|
188
195
|
uiPoolDataProvider: zero,
|
|
189
196
|
walletBalanceProvider: zero,
|
|
190
|
-
aaveOracle: "
|
|
197
|
+
aaveOracle: "0x6837B3cF5d959d01e07bf6DaB53f562877BF7d53",
|
|
191
198
|
liquidationExecutor: zero,
|
|
192
|
-
protocolVault: "
|
|
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
|
-
/**
|
|
243
|
+
/** Current mined launch, canonical Abyss, and replacement lending infrastructure. */
|
|
237
244
|
4663: {
|
|
238
245
|
...robinhoodAbyssInfrastructure,
|
|
239
|
-
...launchApplicationFromEnv(
|
|
240
|
-
lendingPool: "
|
|
241
|
-
addressesProvider: "
|
|
242
|
-
dataProvider: "
|
|
243
|
-
uiPoolDataProvider: "
|
|
244
|
-
walletBalanceProvider: "
|
|
245
|
-
aaveOracle: "
|
|
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: "
|
|
258
|
+
protocolVault: "0x83Ec5DbFEd6d972be89df88d3654EA2c70Fa2FB3",
|
|
252
259
|
ethUsdFeed: ROBINHOOD_ETH_USD,
|
|
253
260
|
weth: ROBINHOOD_WETH,
|
|
254
|
-
wethGateway: "
|
|
255
|
-
lens: "
|
|
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 ??
|
|
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 ??
|
|
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>;
|
|
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
|
-
|
|
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
|
|
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;
|