@black-market/sdk 0.1.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +92 -20
  2. package/dist/abyss.d.ts +4 -2748
  3. package/dist/abyss.d.ts.map +1 -1
  4. package/dist/abyss.js +3 -1029
  5. package/dist/abyss.js.map +1 -1
  6. package/dist/addresses.d.ts +3 -7
  7. package/dist/addresses.d.ts.map +1 -1
  8. package/dist/addresses.js +6 -21
  9. package/dist/addresses.js.map +1 -1
  10. package/dist/auction.d.ts +2 -2
  11. package/dist/auction.d.ts.map +1 -1
  12. package/dist/auction.js +2 -2
  13. package/dist/auction.js.map +1 -1
  14. package/dist/index.d.ts +2 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +2 -0
  17. package/dist/index.js.map +1 -1
  18. package/dist/launch-api.d.ts +151 -0
  19. package/dist/launch-api.d.ts.map +1 -0
  20. package/dist/launch-api.js +117 -0
  21. package/dist/launch-api.js.map +1 -0
  22. package/dist/launch.d.ts +23 -185
  23. package/dist/launch.d.ts.map +1 -1
  24. package/dist/launch.js +35 -272
  25. package/dist/launch.js.map +1 -1
  26. package/dist/lifecycle/abi.d.ts +6391 -0
  27. package/dist/lifecycle/abi.d.ts.map +1 -0
  28. package/dist/lifecycle/abi.js +335 -0
  29. package/dist/lifecycle/abi.js.map +1 -0
  30. package/dist/lifecycle/authors.d.ts +133 -0
  31. package/dist/lifecycle/authors.d.ts.map +1 -0
  32. package/dist/lifecycle/authors.js +217 -0
  33. package/dist/lifecycle/authors.js.map +1 -0
  34. package/dist/lifecycle/fork.d.ts +15 -0
  35. package/dist/lifecycle/fork.d.ts.map +1 -0
  36. package/dist/lifecycle/fork.js +36 -0
  37. package/dist/lifecycle/fork.js.map +1 -0
  38. package/dist/lifecycle/index.d.ts +10 -0
  39. package/dist/lifecycle/index.d.ts.map +1 -0
  40. package/dist/lifecycle/index.js +10 -0
  41. package/dist/lifecycle/index.js.map +1 -0
  42. package/dist/lifecycle/markets.d.ts +349 -0
  43. package/dist/lifecycle/markets.d.ts.map +1 -0
  44. package/dist/lifecycle/markets.js +186 -0
  45. package/dist/lifecycle/markets.js.map +1 -0
  46. package/dist/lifecycle/planner.d.ts +20 -0
  47. package/dist/lifecycle/planner.d.ts.map +1 -0
  48. package/dist/lifecycle/planner.js +481 -0
  49. package/dist/lifecycle/planner.js.map +1 -0
  50. package/dist/lifecycle/progress.d.ts +43 -0
  51. package/dist/lifecycle/progress.d.ts.map +1 -0
  52. package/dist/lifecycle/progress.js +449 -0
  53. package/dist/lifecycle/progress.js.map +1 -0
  54. package/dist/lifecycle/rpc.d.ts +17 -0
  55. package/dist/lifecycle/rpc.d.ts.map +1 -0
  56. package/dist/lifecycle/rpc.js +100 -0
  57. package/dist/lifecycle/rpc.js.map +1 -0
  58. package/dist/lifecycle/schema.d.ts +1068 -0
  59. package/dist/lifecycle/schema.d.ts.map +1 -0
  60. package/dist/lifecycle/schema.js +335 -0
  61. package/dist/lifecycle/schema.js.map +1 -0
  62. package/dist/lifecycle/simulation.d.ts +3 -0
  63. package/dist/lifecycle/simulation.d.ts.map +1 -0
  64. package/dist/lifecycle/simulation.js +393 -0
  65. package/dist/lifecycle/simulation.js.map +1 -0
  66. package/dist/lifecycle/types.d.ts +273 -0
  67. package/dist/lifecycle/types.d.ts.map +1 -0
  68. package/dist/lifecycle/types.js +11 -0
  69. package/dist/lifecycle/types.js.map +1 -0
  70. package/docs/launch-lifecycle.md +243 -0
  71. package/package.json +12 -3
  72. package/src/abyss.ts +3 -1059
  73. package/src/addresses.ts +9 -26
  74. package/src/auction.ts +2 -2
  75. package/src/index.ts +2 -0
  76. package/src/launch-api.ts +110 -0
  77. package/src/launch.ts +43 -411
  78. package/src/lifecycle/abi.ts +343 -0
  79. package/src/lifecycle/authors.ts +203 -0
  80. package/src/lifecycle/fork.ts +36 -0
  81. package/src/lifecycle/index.ts +9 -0
  82. package/src/lifecycle/markets.ts +205 -0
  83. package/src/lifecycle/planner.ts +396 -0
  84. package/src/lifecycle/progress.ts +378 -0
  85. package/src/lifecycle/rpc.ts +88 -0
  86. package/src/lifecycle/schema.ts +387 -0
  87. package/src/lifecycle/simulation.ts +329 -0
  88. package/src/lifecycle/types.ts +132 -0
package/src/addresses.ts CHANGED
@@ -20,11 +20,9 @@ export type AbyssInfrastructureAddresses = {
20
20
  };
21
21
 
22
22
  export type LaunchApplicationAddresses = {
23
- launchTokenFactory: Address;
24
- launchCoordinator: Address;
25
- launchFactory: Address;
26
- launchTemplateRegistry: Address;
27
- launchModuleFactory: Address;
23
+ /** No reviewed deployment is selected implicitly; set these from exact deployment evidence. */
24
+ launchOrchestrator: Address;
25
+ launchImplementationRegistry: Address;
28
26
  launchFeeOwnerRegistry: Address;
29
27
  };
30
28
 
@@ -133,11 +131,8 @@ function launchApplicationFromEnv(
133
131
  envAddr([name, `VITE_${name}`, `NEXT_PUBLIC_${name}`], defaultAddress);
134
132
 
135
133
  return {
136
- launchTokenFactory: address("LAUNCH_TOKEN_FACTORY", fallback.launchTokenFactory),
137
- launchCoordinator: address("LAUNCH_COORDINATOR", fallback.launchCoordinator),
138
- launchFactory: address("LAUNCH_FACTORY", fallback.launchFactory),
139
- launchTemplateRegistry: address("LAUNCH_TEMPLATE_REGISTRY", fallback.launchTemplateRegistry),
140
- launchModuleFactory: address("LAUNCH_MODULE_FACTORY", fallback.launchModuleFactory),
134
+ launchOrchestrator: address("LAUNCH_ORCHESTRATOR", fallback.launchOrchestrator),
135
+ launchImplementationRegistry: address("LAUNCH_IMPLEMENTATION_REGISTRY", fallback.launchImplementationRegistry),
141
136
  launchFeeOwnerRegistry: address("LAUNCH_FEE_OWNER_REGISTRY", fallback.launchFeeOwnerRegistry),
142
137
  };
143
138
  }
@@ -161,11 +156,8 @@ const zeroAbyssInfrastructure: AbyssInfrastructureAddresses = {
161
156
  };
162
157
 
163
158
  const zeroLaunchApplication: LaunchApplicationAddresses = {
164
- launchTokenFactory: zero,
165
- launchCoordinator: zero,
166
- launchFactory: zero,
167
- launchTemplateRegistry: zero,
168
- launchModuleFactory: zero,
159
+ launchOrchestrator: zero,
160
+ launchImplementationRegistry: zero,
169
161
  launchFeeOwnerRegistry: zero,
170
162
  };
171
163
 
@@ -185,15 +177,6 @@ export const robinhoodAbyssInfrastructure: AbyssInfrastructureAddresses = {
185
177
  abyssFeeRouter: "0x2c3B1b6fe0EDa8e10C0445567b47e66E825B34cd",
186
178
  };
187
179
 
188
- /** Captured replacement: contracts/deployments/launch/robinhood-mainnet-replacement-deployment-record.json. */
189
- export const robinhoodLaunchApplication: LaunchApplicationAddresses = {
190
- launchTokenFactory: "0x7B6F6efb4536F579223423e31d9036D996bc90F0",
191
- launchCoordinator: "0xE98A82202D794A7836316971e7ACf61F260f8399",
192
- launchFactory: "0xAf3FdC499b3717EBE8aD51B66bA78Cb083552351",
193
- launchTemplateRegistry: "0x5C22c6e02bAC8225eed64629447868bAA5774186",
194
- launchModuleFactory: "0xb2D9988592eE245a36290A0992505ca0474434f2",
195
- launchFeeOwnerRegistry: "0xdD3756269Db20F2a60055b0FE8c101FAd12525f9",
196
- };
197
180
 
198
181
  /** Workbench deployment snapshot — override via env when redeploying. */
199
182
  const workbenchDefaults: ProtocolAddresses = {
@@ -250,10 +233,10 @@ export const addresses: Record<SupportedChainId, ProtocolAddresses> = {
250
233
  tokenVesting: envAddr(["TOKEN_VESTING", "VITE_TOKEN_VESTING", "NEXT_PUBLIC_TOKEN_VESTING"]),
251
234
  faucet: envAddr(["FAUCET", "VITE_FAUCET", "NEXT_PUBLIC_FAUCET"]),
252
235
  },
253
- /** Captured replacement launch defaults; environment variables may override them. */
236
+ /** Lending/DEX defaults remain canonical; reviewed launches require explicit evidence. */
254
237
  4663: {
255
238
  ...robinhoodAbyssInfrastructure,
256
- ...launchApplicationFromEnv(robinhoodLaunchApplication),
239
+ ...launchApplicationFromEnv(zeroLaunchApplication),
257
240
  lendingPool: "0x5b8F732A4F7a62D642070bb49255d6C434A76766",
258
241
  addressesProvider: "0xaaD329d0Da03C8c00E8460b5b208D0A21A48C9df",
259
242
  dataProvider: "0x3B097A7899DF433552B8428E774964661b53C193",
package/src/auction.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { type Address, zeroAddress } from "viem";
2
2
 
3
- /** Fixed supply used by Atomic launches. */
3
+ /** Default display-recipe supply; reviewed lifecycle plans commit their own supply. */
4
4
  export const AUCTION_SUPPLY = 1_000_000_000n * 10n ** 18n;
5
5
  export const AUCTION_SUPPLY_WHOLE = 1_000_000_000;
6
6
 
@@ -62,7 +62,7 @@ function rwa(id: AuctionQuoteId, name: string, address: Address): AuctionQuoteOp
62
62
  return { id, symbol: id, name, address, decimals: 18, usdPeg: false };
63
63
  }
64
64
 
65
- /** Supported paired assets for Atomic launches: native ETH, wrapped WETH, USDG, then catalog stocks. */
65
+ /** Display catalog only, not an admission allowlist; native ETH is a funding option, not an ERC20 market quote. */
66
66
  export const AUCTION_QUOTE_OPTIONS: AuctionQuoteOption[] = [
67
67
  { id: "ETH", symbol: "ETH", name: "Ether", address: zeroAddress, decimals: 18, usdPeg: false },
68
68
  { id: "WETH", symbol: "WETH", name: "Wrapped Ether", address: ROBINHOOD_WETH, decimals: 18, usdPeg: false },
package/src/index.ts CHANGED
@@ -6,3 +6,5 @@ export * from "./client.js";
6
6
  export * from "./format.js";
7
7
  export * from "./launch.js";
8
8
  export * from "./live.js";
9
+ export * from "./launch-api.js";
10
+ export * from "./lifecycle/index.js";
@@ -0,0 +1,110 @@
1
+ import { keccak256, stringToHex, type Address, type Hash, type Hex } from "viem";
2
+
3
+ export const DEFAULT_LAUNCH_API_URL = "https://api.abyss.trading";
4
+ export const LAUNCH_IMAGE_CONTENT_TYPES = ["image/png", "image/jpeg", "image/webp"] as const;
5
+ export type LaunchImageContentType = (typeof LAUNCH_IMAGE_CONTENT_TYPES)[number];
6
+ export const MAX_LAUNCH_IMAGE_BYTES = 5 * 1024 * 1024;
7
+ export type LaunchSessionMetadata = { name: string; symbol: string; description?: string; websiteUrl?: string; twitterUrl?: string; telegramUrl?: string; discordUrl?: string; imageKey?: string };
8
+ /** Metadata accepted by a new upload session. imageKey is assigned by the API, never supplied here. */
9
+ export type LaunchSessionCreateMetadata = {
10
+ name: string; symbol: string; description: string; websiteUrl?: string; twitterUrl?: string; telegramUrl?: string; discordUrl?: string;
11
+ };
12
+ export type LaunchSessionImageDescriptor = { sha256: Hex; contentType: LaunchImageContentType; contentLength: number };
13
+ export type LaunchAttributionAuthorization = { nonce: Hex; deadline: string; signature: Hex };
14
+ export type LaunchSessionCreateRequest = { chainId: number; wallet: Address; metadata: LaunchSessionCreateMetadata; image?: LaunchSessionImageDescriptor; authorization: LaunchAttributionAuthorization };
15
+ export type LaunchSessionPublishRequest = { chainId: number; transactionHash: Hash };
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 };
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
+ 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
+
21
+ export class LaunchApiError extends Error {
22
+ readonly status: number;
23
+ readonly code: LaunchApiErrorCode;
24
+ readonly retryAfterMs?: number;
25
+ constructor(status: number, code: LaunchApiErrorCode, message: string, retryAfterMs?: number) {
26
+ super(message); this.name = "LaunchApiError"; this.status = status; this.code = code; this.retryAfterMs = retryAfterMs;
27
+ }
28
+ }
29
+ export class LaunchPublishPending extends Error {
30
+ readonly session: LaunchSessionResponse;
31
+ readonly retryAfterMs: number;
32
+ constructor(session: LaunchSessionResponse, retryAfterMs: number) {
33
+ super("Launch transaction is confirmed but indexing is still pending."); this.name = "LaunchPublishPending"; this.session = session; this.retryAfterMs = retryAfterMs;
34
+ }
35
+ }
36
+ export type LaunchApiClientOptions = { baseUrl?: string; fetch?: typeof globalThis.fetch; retries?: number };
37
+
38
+ /** Optional client for the web app's signed metadata/upload/publication lifecycle. */
39
+ export class LaunchApiClient {
40
+ readonly baseUrl: string;
41
+ readonly fetch: typeof globalThis.fetch;
42
+ readonly retries: number;
43
+ constructor(options: LaunchApiClientOptions = {}) {
44
+ this.baseUrl = (options.baseUrl ?? DEFAULT_LAUNCH_API_URL).replace(/\/+$/, "");
45
+ this.fetch = options.fetch ?? globalThis.fetch;
46
+ if (this.fetch === undefined) throw new Error("A fetch implementation is required for LaunchApiClient");
47
+ this.retries = options.retries ?? 3;
48
+ }
49
+ async createUploadSession(body: LaunchSessionCreateRequest, idempotencyKey: string): Promise<LaunchSessionResponse> {
50
+ return this.request("POST", "/api/v1/launch-upload-sessions", body.chainId, { body, idempotencyKey });
51
+ }
52
+ async getUploadSession(chainId: number, sessionId: string, capability: string): Promise<LaunchSessionResponse> {
53
+ return this.request("GET", `/api/v1/launch-upload-sessions/${encodeURIComponent(sessionId)}`, chainId, { capability });
54
+ }
55
+ async renewUploadUrl(chainId: number, sessionId: string, capability: string): Promise<LaunchSessionResponse> {
56
+ return this.request("POST", `/api/v1/launch-upload-sessions/${encodeURIComponent(sessionId)}/upload-url`, chainId, { body: {}, capability });
57
+ }
58
+ async completeUpload(chainId: number, sessionId: string, capability: string): Promise<LaunchSessionResponse> {
59
+ return this.request("POST", `/api/v1/launch-upload-sessions/${encodeURIComponent(sessionId)}/upload/complete`, chainId, { body: {}, capability });
60
+ }
61
+ async publishUploadSession(chainId: number, sessionId: string, capability: string, body: LaunchSessionPublishRequest): Promise<LaunchSessionResponse> {
62
+ return this.request("POST", `/api/v1/launch-upload-sessions/${encodeURIComponent(sessionId)}/publish`, chainId, { body, capability, pendingOnAccepted: true });
63
+ }
64
+ async putImage(upload: LaunchSessionDirectUpload, body: Blob): Promise<void> {
65
+ const response = await this.fetch(upload.url, { method: "PUT", headers: upload.headers, body });
66
+ if (!response.ok) throw new LaunchApiError(response.status, "UPLOAD_CONFLICT", `Launch image upload failed with status ${response.status}.`);
67
+ }
68
+ private async request<T>(method: "GET" | "POST" | "PUT", path: string, chainId: number, options: { body?: unknown; capability?: string; idempotencyKey?: string; pendingOnAccepted?: boolean }): Promise<T> {
69
+ const url = new URL(path, `${this.baseUrl}/`); url.searchParams.set("chainId", String(chainId));
70
+ const headers: Record<string, string> = {};
71
+ if (options.body !== undefined) headers["Content-Type"] = "application/json";
72
+ if (options.capability !== undefined) headers.Authorization = `Launch-Upload-Capability ${options.capability}`;
73
+ if (options.idempotencyKey !== undefined) headers["Idempotency-Key"] = options.idempotencyKey;
74
+ let lastError: unknown;
75
+ for (let attempt = 0; attempt < this.retries; attempt += 1) {
76
+ try {
77
+ const response = await this.fetch(url, { method, headers, body: options.body === undefined ? undefined : JSON.stringify(options.body) });
78
+ const retryAfterMs = Math.max(250, Number(response.headers.get("Retry-After")) * 1000 || 0) || undefined;
79
+ if (response.ok) {
80
+ const value = await response.json() as T;
81
+ if (response.status === 202 && options.pendingOnAccepted) throw new LaunchPublishPending(value as LaunchSessionResponse, retryAfterMs ?? 5_000);
82
+ return value;
83
+ }
84
+ let code: LaunchApiErrorCode = "INVALID_REQUEST";
85
+ let message = `Launch API request failed with status ${response.status}.`;
86
+ try { const value = await response.json() as { code?: unknown; error?: unknown }; if (typeof value.code === "string") code = value.code; if (typeof value.error === "string") message = value.error; } catch { /* response body is optional */ }
87
+ const error = new LaunchApiError(response.status, code, message, retryAfterMs);
88
+ if (![429, 502, 503, 504].includes(response.status) || attempt + 1 === this.retries) throw error;
89
+ lastError = error;
90
+ } catch (error) {
91
+ if (error instanceof LaunchPublishPending || error instanceof LaunchApiError && ![429, 502, 503, 504].includes(error.status)) throw error;
92
+ lastError = error;
93
+ }
94
+ await new Promise<void>((resolve) => globalThis.setTimeout(resolve, 750 * 2 ** attempt));
95
+ }
96
+ throw lastError instanceof LaunchApiError ? lastError : new LaunchApiError(0, "RPC_UNAVAILABLE", lastError instanceof Error ? lastError.message : "The launch API is unreachable.");
97
+ }
98
+ }
99
+ export const launchAttributionTypes = { LaunchAttribution: [
100
+ { name: "chainId", type: "uint256" }, { name: "wallet", type: "address" }, { name: "metadataHash", type: "bytes32" },
101
+ { name: "imageSha256", type: "bytes32" }, { name: "imageContentType", type: "string" }, { name: "imageContentLength", type: "uint256" },
102
+ { name: "idempotencyKey", type: "string" }, { name: "nonce", type: "bytes32" }, { name: "deadline", type: "uint256" },
103
+ ] } as const;
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 })));
106
+ }
107
+ export async function sha256Hex(bytes: ArrayBuffer): Promise<Hex> {
108
+ const digest = await crypto.subtle.digest("SHA-256", bytes);
109
+ return `0x${Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("")}` as Hex;
110
+ }