@black-market/sdk 0.1.0 → 0.3.1
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 +78 -20
- package/dist/abyss.d.ts +4 -2748
- package/dist/abyss.d.ts.map +1 -1
- package/dist/abyss.js +3 -1029
- package/dist/abyss.js.map +1 -1
- package/dist/addresses.d.ts +3 -7
- package/dist/addresses.d.ts.map +1 -1
- package/dist/addresses.js +6 -21
- package/dist/addresses.js.map +1 -1
- package/dist/auction.d.ts +2 -2
- package/dist/auction.d.ts.map +1 -1
- package/dist/auction.js +2 -2
- package/dist/auction.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/launch-api.d.ts +151 -0
- package/dist/launch-api.d.ts.map +1 -0
- package/dist/launch-api.js +117 -0
- package/dist/launch-api.js.map +1 -0
- package/dist/launch.d.ts +23 -185
- package/dist/launch.d.ts.map +1 -1
- package/dist/launch.js +35 -272
- package/dist/launch.js.map +1 -1
- package/dist/lifecycle/abi.d.ts +6402 -0
- package/dist/lifecycle/abi.d.ts.map +1 -0
- package/dist/lifecycle/abi.js +331 -0
- package/dist/lifecycle/abi.js.map +1 -0
- package/dist/lifecycle/authors.d.ts +133 -0
- package/dist/lifecycle/authors.d.ts.map +1 -0
- package/dist/lifecycle/authors.js +217 -0
- package/dist/lifecycle/authors.js.map +1 -0
- package/dist/lifecycle/fork.d.ts +15 -0
- package/dist/lifecycle/fork.d.ts.map +1 -0
- package/dist/lifecycle/fork.js +36 -0
- package/dist/lifecycle/fork.js.map +1 -0
- package/dist/lifecycle/index.d.ts +10 -0
- package/dist/lifecycle/index.d.ts.map +1 -0
- package/dist/lifecycle/index.js +10 -0
- package/dist/lifecycle/index.js.map +1 -0
- package/dist/lifecycle/markets.d.ts +349 -0
- package/dist/lifecycle/markets.d.ts.map +1 -0
- package/dist/lifecycle/markets.js +184 -0
- package/dist/lifecycle/markets.js.map +1 -0
- package/dist/lifecycle/planner.d.ts +20 -0
- package/dist/lifecycle/planner.d.ts.map +1 -0
- package/dist/lifecycle/planner.js +478 -0
- package/dist/lifecycle/planner.js.map +1 -0
- package/dist/lifecycle/progress.d.ts +36 -0
- package/dist/lifecycle/progress.d.ts.map +1 -0
- package/dist/lifecycle/progress.js +441 -0
- package/dist/lifecycle/progress.js.map +1 -0
- package/dist/lifecycle/rpc.d.ts +17 -0
- package/dist/lifecycle/rpc.d.ts.map +1 -0
- package/dist/lifecycle/rpc.js +100 -0
- package/dist/lifecycle/rpc.js.map +1 -0
- package/dist/lifecycle/schema.d.ts +1096 -0
- package/dist/lifecycle/schema.d.ts.map +1 -0
- package/dist/lifecycle/schema.js +337 -0
- package/dist/lifecycle/schema.js.map +1 -0
- package/dist/lifecycle/simulation.d.ts +3 -0
- package/dist/lifecycle/simulation.d.ts.map +1 -0
- package/dist/lifecycle/simulation.js +393 -0
- package/dist/lifecycle/simulation.js.map +1 -0
- package/dist/lifecycle/types.d.ts +273 -0
- package/dist/lifecycle/types.d.ts.map +1 -0
- package/dist/lifecycle/types.js +11 -0
- package/dist/lifecycle/types.js.map +1 -0
- package/docs/launch-lifecycle.md +213 -0
- package/package.json +12 -3
- package/src/abyss.ts +3 -1059
- package/src/addresses.ts +9 -26
- package/src/auction.ts +2 -2
- package/src/index.ts +2 -0
- package/src/launch-api.ts +110 -0
- package/src/launch.ts +43 -411
- package/src/lifecycle/abi.ts +339 -0
- package/src/lifecycle/authors.ts +203 -0
- package/src/lifecycle/fork.ts +36 -0
- package/src/lifecycle/index.ts +9 -0
- package/src/lifecycle/markets.ts +204 -0
- package/src/lifecycle/planner.ts +393 -0
- package/src/lifecycle/progress.ts +367 -0
- package/src/lifecycle/rpc.ts +88 -0
- package/src/lifecycle/schema.ts +390 -0
- package/src/lifecycle/simulation.ts +329 -0
- 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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
137
|
-
|
|
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
|
-
|
|
165
|
-
|
|
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
|
-
/**
|
|
236
|
+
/** Lending/DEX defaults remain canonical; reviewed launches require explicit evidence. */
|
|
254
237
|
4663: {
|
|
255
238
|
...robinhoodAbyssInfrastructure,
|
|
256
|
-
...launchApplicationFromEnv(
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
@@ -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
|
+
}
|