@forgedevv/shared 0.0.0-stage → 0.1.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/dist/api.d.ts ADDED
@@ -0,0 +1,429 @@
1
+ import { z } from 'zod';
2
+ /** Lamports serialized as a decimal string (bigint). 1 SOL = 1_000_000_000 lamports. */
3
+ export declare const Lamports: z.ZodString;
4
+ export type Lamports = z.infer<typeof Lamports>;
5
+ /** Raw token amount (smallest units) serialized as a decimal string. */
6
+ export declare const RawTokenAmount: z.ZodString;
7
+ /** Base58 ed25519 signature (transaction signature or signed message). */
8
+ export declare const Base58Signature: z.ZodString;
9
+ /** Serialized transaction, base64 encoded. */
10
+ export declare const Base64Transaction: z.ZodBase64;
11
+ export declare const Uuid: z.ZodUUID;
12
+ /** ISO 8601 date-time (UTC `Z` or with an offset). */
13
+ export declare const IsoDateTime: z.ZodISODateTime;
14
+ /** Path params of routes with `:id`. */
15
+ export declare const RouteIdParams: z.ZodObject<{
16
+ id: z.ZodUUID;
17
+ }, z.core.$strict>;
18
+ export type RouteIdParams = z.infer<typeof RouteIdParams>;
19
+ export declare const WEB_API_ROUTES: {
20
+ readonly authNonce: {
21
+ readonly method: "POST";
22
+ readonly path: "/api/auth/nonce";
23
+ };
24
+ readonly authVerify: {
25
+ readonly method: "POST";
26
+ readonly path: "/api/auth/verify";
27
+ };
28
+ readonly authSession: {
29
+ readonly method: "GET";
30
+ readonly path: "/api/auth/session";
31
+ };
32
+ readonly authLogout: {
33
+ readonly method: "POST";
34
+ readonly path: "/api/auth/logout";
35
+ };
36
+ readonly authSupabaseToken: {
37
+ readonly method: "GET";
38
+ readonly path: "/api/auth/supabase-token";
39
+ };
40
+ readonly gating: {
41
+ readonly method: "GET";
42
+ readonly path: "/api/gating";
43
+ };
44
+ readonly flags: {
45
+ readonly method: "GET";
46
+ readonly path: "/api/flags";
47
+ };
48
+ readonly chat: {
49
+ readonly method: "POST";
50
+ readonly path: "/api/chat";
51
+ };
52
+ readonly specConfirm: {
53
+ readonly method: "POST";
54
+ readonly path: "/api/spec/confirm";
55
+ };
56
+ readonly launchpadModifications: {
57
+ readonly method: "POST";
58
+ readonly path: "/api/launchpads/:id/modifications";
59
+ };
60
+ readonly paymentsQuote: {
61
+ readonly method: "POST";
62
+ readonly path: "/api/payments/quote";
63
+ };
64
+ readonly paymentsConfirm: {
65
+ readonly method: "POST";
66
+ readonly path: "/api/payments/confirm";
67
+ };
68
+ readonly jobApprove: {
69
+ readonly method: "POST";
70
+ readonly path: "/api/jobs/:id/approve";
71
+ };
72
+ readonly ownerTransactionGet: {
73
+ readonly method: "GET";
74
+ readonly path: "/api/jobs/:id/owner-transaction";
75
+ };
76
+ readonly ownerTransactionSubmit: {
77
+ readonly method: "POST";
78
+ readonly path: "/api/jobs/:id/owner-transaction";
79
+ };
80
+ readonly jobGet: {
81
+ readonly method: "GET";
82
+ readonly path: "/api/jobs/:id";
83
+ };
84
+ readonly launchpadsList: {
85
+ readonly method: "GET";
86
+ readonly path: "/api/launchpads";
87
+ };
88
+ readonly launchpadGet: {
89
+ readonly method: "GET";
90
+ readonly path: "/api/launchpads/:id";
91
+ };
92
+ readonly claimTransaction: {
93
+ readonly method: "GET";
94
+ readonly path: "/api/launchpads/:id/claim-transaction";
95
+ };
96
+ readonly launchpadReactivate: {
97
+ readonly method: "POST";
98
+ readonly path: "/api/launchpads/:id/reactivate";
99
+ };
100
+ readonly rpc: {
101
+ readonly method: "POST";
102
+ readonly path: "/api/rpc";
103
+ };
104
+ };
105
+ export declare const AuthNonceRequest: z.ZodObject<{
106
+ wallet: z.ZodString;
107
+ }, z.core.$strict>;
108
+ export type AuthNonceRequest = z.infer<typeof AuthNonceRequest>;
109
+ /**
110
+ * `nonce` is an opaque server-signed token (stateless). `message` is the exact text the wallet
111
+ * must sign; the client never builds it. The token is also set as an HttpOnly cookie.
112
+ */
113
+ export declare const AuthNonceResponse: z.ZodObject<{
114
+ nonce: z.ZodString;
115
+ message: z.ZodString;
116
+ expiresAt: z.ZodString;
117
+ }, z.core.$strip>;
118
+ export type AuthNonceResponse = z.infer<typeof AuthNonceResponse>;
119
+ export declare const AuthVerifyRequest: z.ZodObject<{
120
+ wallet: z.ZodString;
121
+ signature: z.ZodString;
122
+ nonce: z.ZodOptional<z.ZodString>;
123
+ }, z.core.$strict>;
124
+ export type AuthVerifyRequest = z.infer<typeof AuthVerifyRequest>;
125
+ /** The session. */
126
+ export declare const AuthVerifyResponse: z.ZodObject<{
127
+ wallet: z.ZodString;
128
+ }, z.core.$strip>;
129
+ export type AuthVerifyResponse = z.infer<typeof AuthVerifyResponse>;
130
+ export declare const AuthLogoutResponse: z.ZodObject<{
131
+ ok: z.ZodLiteral<true>;
132
+ }, z.core.$strip>;
133
+ export type AuthLogoutResponse = z.infer<typeof AuthLogoutResponse>;
134
+ export declare const SupabaseTokenResponse: z.ZodObject<{
135
+ accessToken: z.ZodString;
136
+ expiresAt: z.ZodString;
137
+ }, z.core.$strip>;
138
+ export type SupabaseTokenResponse = z.infer<typeof SupabaseTokenResponse>;
139
+ export declare const Session: z.ZodObject<{
140
+ wallet: z.ZodString;
141
+ }, z.core.$strip>;
142
+ export type Session = AuthVerifyResponse;
143
+ export declare const GatingResponse: z.ZodObject<{
144
+ ok: z.ZodBoolean;
145
+ required: z.ZodString;
146
+ balance: z.ZodString;
147
+ enabled: z.ZodBoolean;
148
+ }, z.core.$strip>;
149
+ export type GatingResponse = z.infer<typeof GatingResponse>;
150
+ export declare const FlagsResponse: z.ZodObject<{
151
+ signupsPaused: z.ZodBoolean;
152
+ }, z.core.$strip>;
153
+ export type FlagsResponse = z.infer<typeof FlagsResponse>;
154
+ export declare const ChatRequest: z.ZodObject<{
155
+ conversationId: z.ZodOptional<z.ZodUUID>;
156
+ launchpadId: z.ZodOptional<z.ZodUUID>;
157
+ message: z.ZodString;
158
+ }, z.core.$strict>;
159
+ export type ChatRequest = z.infer<typeof ChatRequest>;
160
+ /** Payload of the first stream event. */
161
+ export declare const ChatResponse: z.ZodObject<{
162
+ conversationId: z.ZodUUID;
163
+ }, z.core.$strip>;
164
+ export type ChatResponse = z.infer<typeof ChatResponse>;
165
+ export declare const SpecConfirmRequest: z.ZodObject<{
166
+ conversationId: z.ZodUUID;
167
+ spec: z.ZodObject<{
168
+ version: z.ZodLiteral<1>;
169
+ ownerWallet: z.ZodString;
170
+ name: z.ZodString;
171
+ slug: z.ZodString;
172
+ quote: z.ZodLiteral<"SOL">;
173
+ tradingFeeBps: z.ZodNumber;
174
+ coinCreatorSharePct: z.ZodDefault<z.ZodNumber>;
175
+ poolCreationFeeSol: z.ZodNumber;
176
+ antiSniper: z.ZodDefault<z.ZodBoolean>;
177
+ theme: z.ZodObject<{
178
+ primaryColor: z.ZodString;
179
+ accentColor: z.ZodString;
180
+ darkMode: z.ZodBoolean;
181
+ tagline: z.ZodString;
182
+ logoUrl: z.ZodOptional<z.ZodURL>;
183
+ designNotes: z.ZodString;
184
+ }, z.core.$strip>;
185
+ launchpadCoin: z.ZodObject<{
186
+ name: z.ZodString;
187
+ symbol: z.ZodString;
188
+ description: z.ZodString;
189
+ imageUrl: z.ZodURL;
190
+ firstBuySol: z.ZodNumber;
191
+ }, z.core.$strip>;
192
+ }, z.core.$strip>;
193
+ }, z.core.$strict>;
194
+ export type SpecConfirmRequest = z.input<typeof SpecConfirmRequest>;
195
+ export declare const SpecConfirmResponse: z.ZodObject<{
196
+ launchpadId: z.ZodUUID;
197
+ jobId: z.ZodUUID;
198
+ }, z.core.$strip>;
199
+ export type SpecConfirmResponse = z.infer<typeof SpecConfirmResponse>;
200
+ export declare const LaunchpadModificationRequest: z.ZodObject<{
201
+ conversationId: z.ZodUUID;
202
+ request: z.ZodString;
203
+ }, z.core.$strict>;
204
+ export type LaunchpadModificationRequest = z.infer<typeof LaunchpadModificationRequest>;
205
+ export declare const LaunchpadModificationResponse: z.ZodObject<{
206
+ jobId: z.ZodUUID;
207
+ }, z.core.$strip>;
208
+ export type LaunchpadModificationResponse = z.infer<typeof LaunchpadModificationResponse>;
209
+ export declare const PaymentsQuoteRequest: z.ZodObject<{
210
+ jobId: z.ZodUUID;
211
+ kind: z.ZodEnum<{
212
+ creation: "creation";
213
+ modification: "modification";
214
+ }>;
215
+ }, z.core.$strict>;
216
+ export type PaymentsQuoteRequest = z.infer<typeof PaymentsQuoteRequest>;
217
+ /** Quote as seen by the UI (docs/frontend/CONTRACT.md). */
218
+ export declare const Quote: z.ZodObject<{
219
+ paymentId: z.ZodUUID;
220
+ kind: z.ZodEnum<{
221
+ creation: "creation";
222
+ modification: "modification";
223
+ }>;
224
+ lamports: z.ZodString;
225
+ usdAmount: z.ZodNumber;
226
+ expiresAt: z.ZodISODateTime;
227
+ }, z.core.$strip>;
228
+ export type Quote = z.infer<typeof Quote>;
229
+ /**
230
+ * `transaction` is the unsigned payment transaction (base64). It is null when lamports = "0"
231
+ * (an included modification remains and the job goes straight to `paid`).
232
+ */
233
+ export declare const PaymentsQuoteResponse: z.ZodObject<{
234
+ paymentId: z.ZodUUID;
235
+ kind: z.ZodEnum<{
236
+ creation: "creation";
237
+ modification: "modification";
238
+ }>;
239
+ lamports: z.ZodString;
240
+ usdAmount: z.ZodNumber;
241
+ expiresAt: z.ZodISODateTime;
242
+ transaction: z.ZodNullable<z.ZodBase64>;
243
+ }, z.core.$strip>;
244
+ export type PaymentsQuoteResponse = z.infer<typeof PaymentsQuoteResponse>;
245
+ export declare const PaymentsConfirmRequest: z.ZodObject<{
246
+ paymentId: z.ZodUUID;
247
+ signature: z.ZodString;
248
+ }, z.core.$strict>;
249
+ export type PaymentsConfirmRequest = z.infer<typeof PaymentsConfirmRequest>;
250
+ export declare const PaymentsConfirmResponse: z.ZodObject<{
251
+ status: z.ZodEnum<{
252
+ refunded: "refunded";
253
+ quoted: "quoted";
254
+ confirmed: "confirmed";
255
+ expired: "expired";
256
+ }>;
257
+ }, z.core.$strip>;
258
+ export type PaymentsConfirmResponse = z.infer<typeof PaymentsConfirmResponse>;
259
+ export declare const JobApproveResponse: z.ZodObject<{
260
+ status: z.ZodEnum<{
261
+ spec_ready: "spec_ready";
262
+ paid: "paid";
263
+ building: "building";
264
+ preview_ready: "preview_ready";
265
+ approved: "approved";
266
+ onchain_setup: "onchain_setup";
267
+ awaiting_owner_signature: "awaiting_owner_signature";
268
+ owner_signed: "owner_signed";
269
+ deploying: "deploying";
270
+ live: "live";
271
+ failed: "failed";
272
+ refunded: "refunded";
273
+ }>;
274
+ }, z.core.$strip>;
275
+ export type JobApproveResponse = z.infer<typeof JobApproveResponse>;
276
+ /** Summary shown before the client signs the launch transaction. */
277
+ export declare const OwnerTransactionSummary: z.ZodObject<{
278
+ coinName: z.ZodString;
279
+ coinSymbol: z.ZodString;
280
+ firstBuyLamports: z.ZodString;
281
+ estimatedNetworkFeeLamports: z.ZodString;
282
+ ownerWallet: z.ZodString;
283
+ ownerRequiredBalanceLamports: z.ZodOptional<z.ZodString>;
284
+ }, z.core.$strip>;
285
+ export type OwnerTransactionSummary = z.infer<typeof OwnerTransactionSummary>;
286
+ /** Absolute https URL (the launchpad coin metadata URI). Kept exactly as sent (no normalization). */
287
+ export declare const HttpsUrl: z.ZodURL;
288
+ /**
289
+ * The accounts of the launch transaction, pinned by the browser (`verifyLaunchCoinTransaction` of
290
+ * @forge/core) before the owner signs: the launchpad coin config and mint (launchpad row), the
291
+ * FORGE creator wallet that creates the pool and holds the durable nonce, the durable nonce
292
+ * account, and the coin metadata URI written by the pool creation.
293
+ */
294
+ export declare const OwnerTransactionLaunch: z.ZodObject<{
295
+ config: z.ZodString;
296
+ mint: z.ZodString;
297
+ creator: z.ZodString;
298
+ nonceAccount: z.ZodString;
299
+ metadataUri: z.ZodURL;
300
+ }, z.core.$strip>;
301
+ export type OwnerTransactionLaunch = z.infer<typeof OwnerTransactionLaunch>;
302
+ export declare const OwnerTransactionGetResponse: z.ZodObject<{
303
+ transaction: z.ZodBase64;
304
+ summary: z.ZodObject<{
305
+ coinName: z.ZodString;
306
+ coinSymbol: z.ZodString;
307
+ firstBuyLamports: z.ZodString;
308
+ estimatedNetworkFeeLamports: z.ZodString;
309
+ ownerWallet: z.ZodString;
310
+ ownerRequiredBalanceLamports: z.ZodOptional<z.ZodString>;
311
+ }, z.core.$strip>;
312
+ simulation: z.ZodOptional<z.ZodEnum<{
313
+ failed: "failed";
314
+ ok: "ok";
315
+ unavailable: "unavailable";
316
+ }>>;
317
+ launch: z.ZodOptional<z.ZodObject<{
318
+ config: z.ZodString;
319
+ mint: z.ZodString;
320
+ creator: z.ZodString;
321
+ nonceAccount: z.ZodString;
322
+ metadataUri: z.ZodURL;
323
+ }, z.core.$strip>>;
324
+ }, z.core.$strip>;
325
+ export type OwnerTransactionGetResponse = z.infer<typeof OwnerTransactionGetResponse>;
326
+ export declare const OwnerTransactionSubmitRequest: z.ZodObject<{
327
+ signedTransaction: z.ZodBase64;
328
+ }, z.core.$strict>;
329
+ export type OwnerTransactionSubmitRequest = z.infer<typeof OwnerTransactionSubmitRequest>;
330
+ export declare const OwnerTransactionSubmitResponse: z.ZodObject<{
331
+ signature: z.ZodString;
332
+ }, z.core.$strip>;
333
+ export type OwnerTransactionSubmitResponse = z.infer<typeof OwnerTransactionSubmitResponse>;
334
+ export declare const ClaimTransactionSummary: z.ZodObject<{
335
+ pool: z.ZodString;
336
+ tradingFeesLamports: z.ZodString;
337
+ poolCreationFeeLamports: z.ZodString;
338
+ ownerCostLamports: z.ZodString;
339
+ remainingPools: z.ZodNumber;
340
+ }, z.core.$strip>;
341
+ export type ClaimTransactionSummary = z.infer<typeof ClaimTransactionSummary>;
342
+ export declare const ClaimTransactionResponse: z.ZodObject<{
343
+ transaction: z.ZodBase64;
344
+ summary: z.ZodOptional<z.ZodObject<{
345
+ pool: z.ZodString;
346
+ tradingFeesLamports: z.ZodString;
347
+ poolCreationFeeLamports: z.ZodString;
348
+ ownerCostLamports: z.ZodString;
349
+ remainingPools: z.ZodNumber;
350
+ }, z.core.$strip>>;
351
+ }, z.core.$strip>;
352
+ export type ClaimTransactionResponse = z.infer<typeof ClaimTransactionResponse>;
353
+ export declare const RpcRequest: z.ZodObject<{
354
+ jsonrpc: z.ZodLiteral<"2.0">;
355
+ id: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodNull]>;
356
+ method: z.ZodString;
357
+ params: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodUnknown>, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>;
358
+ }, z.core.$strict>;
359
+ export type RpcRequest = z.infer<typeof RpcRequest>;
360
+ export declare const RpcResponse: z.ZodObject<{
361
+ jsonrpc: z.ZodLiteral<"2.0">;
362
+ id: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodNull]>;
363
+ result: z.ZodOptional<z.ZodUnknown>;
364
+ error: z.ZodOptional<z.ZodObject<{
365
+ code: z.ZodNumber;
366
+ message: z.ZodString;
367
+ data: z.ZodOptional<z.ZodUnknown>;
368
+ }, z.core.$strip>>;
369
+ }, z.core.$strip>;
370
+ export type RpcResponse = z.infer<typeof RpcResponse>;
371
+ /** Hex HMAC-SHA256 computed with SIGNER_HMAC_SECRET over `signerSignedPayload(...)`. */
372
+ export declare const SIGNER_SIGNATURE_HEADER = "X-Forge-Signature";
373
+ /** Unix time in seconds, as a decimal string. */
374
+ export declare const SIGNER_TIMESTAMP_HEADER = "X-Forge-Timestamp";
375
+ /** Requests whose timestamp differs from the signer clock by more than this are rejected. */
376
+ export declare const SIGNER_MAX_CLOCK_SKEW_SECONDS = 60;
377
+ /**
378
+ * Exact string fed to HMAC-SHA256: `<timestamp>.<raw body>`. The timestamp is covered by the MAC
379
+ * so that a captured request cannot be replayed with a fresh timestamp.
380
+ */
381
+ export declare function signerSignedPayload(timestamp: string, rawBody: string): string;
382
+ export declare const SIGNER_ROUTES: {
383
+ readonly configs: {
384
+ readonly method: "POST";
385
+ readonly path: "/v1/configs";
386
+ };
387
+ readonly launchCoinPrepare: {
388
+ readonly method: "POST";
389
+ readonly path: "/v1/launch-coin/prepare";
390
+ };
391
+ readonly health: {
392
+ readonly method: "GET";
393
+ readonly path: "/v1/health";
394
+ };
395
+ };
396
+ export declare const SignerConfigsRequest: z.ZodObject<{
397
+ jobId: z.ZodUUID;
398
+ }, z.core.$strict>;
399
+ export type SignerConfigsRequest = z.infer<typeof SignerConfigsRequest>;
400
+ export declare const SignerConfigsResponse: z.ZodObject<{
401
+ launchpadConfig: z.ZodString;
402
+ launchpadCoinConfig: z.ZodString;
403
+ }, z.core.$strip>;
404
+ export type SignerConfigsResponse = z.infer<typeof SignerConfigsResponse>;
405
+ export declare const SignerLaunchPrepareRequest: z.ZodObject<{
406
+ jobId: z.ZodUUID;
407
+ }, z.core.$strict>;
408
+ export type SignerLaunchPrepareRequest = z.infer<typeof SignerLaunchPrepareRequest>;
409
+ export declare const SignerLaunchPrepareResponse: z.ZodObject<{
410
+ partiallySignedTx: z.ZodBase64;
411
+ mint: z.ZodString;
412
+ summary: z.ZodOptional<z.ZodObject<{
413
+ coinName: z.ZodString;
414
+ coinSymbol: z.ZodString;
415
+ firstBuyLamports: z.ZodString;
416
+ estimatedNetworkFeeLamports: z.ZodString;
417
+ ownerWallet: z.ZodString;
418
+ ownerRequiredBalanceLamports: z.ZodOptional<z.ZodString>;
419
+ }, z.core.$strip>>;
420
+ details: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
421
+ }, z.core.$strip>;
422
+ export type SignerLaunchPrepareResponse = z.infer<typeof SignerLaunchPrepareResponse>;
423
+ export declare const SignerHealthResponse: z.ZodObject<{
424
+ ok: z.ZodBoolean;
425
+ cluster: z.ZodOptional<z.ZodString>;
426
+ uptimeSeconds: z.ZodOptional<z.ZodNumber>;
427
+ jobs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
428
+ }, z.core.$strip>;
429
+ export type SignerHealthResponse = z.infer<typeof SignerHealthResponse>;
package/dist/api.js ADDED
@@ -0,0 +1,264 @@
1
+ import { z } from 'zod';
2
+ import { JobStatus } from './jobs.js';
3
+ import { PaymentKind, PaymentStatus } from './db.js';
4
+ import { LaunchpadSpec, SolanaAddress } from './spec.js';
5
+ /*
6
+ * Request/response schemas of the apps/web API (docs/INTERFACES.md §6) and of the internal
7
+ * builder -> signer API (§7). Request bodies are strict (unknown keys are rejected); responses
8
+ * strip unknown keys.
9
+ */
10
+ // ---------------------------------------------------------------------------
11
+ // Primitives
12
+ // ---------------------------------------------------------------------------
13
+ /** Lamports serialized as a decimal string (bigint). 1 SOL = 1_000_000_000 lamports. */
14
+ export const Lamports = z
15
+ .string()
16
+ .regex(/^\d+$/, { error: 'Lamports must be a decimal integer string' });
17
+ /** Raw token amount (smallest units) serialized as a decimal string. */
18
+ export const RawTokenAmount = z.string().regex(/^\d+$/);
19
+ /** Base58 ed25519 signature (transaction signature or signed message). */
20
+ export const Base58Signature = z.string().regex(/^[1-9A-HJ-NP-Za-km-z]{64,88}$/, {
21
+ error: 'Invalid base58 signature',
22
+ });
23
+ /** Serialized transaction, base64 encoded. */
24
+ export const Base64Transaction = z.base64().min(1);
25
+ export const Uuid = z.uuid();
26
+ /** ISO 8601 date-time (UTC `Z` or with an offset). */
27
+ export const IsoDateTime = z.iso.datetime({ offset: true });
28
+ /** Path params of routes with `:id`. */
29
+ export const RouteIdParams = z.strictObject({ id: Uuid });
30
+ // ---------------------------------------------------------------------------
31
+ // apps/web routes (§6)
32
+ // ---------------------------------------------------------------------------
33
+ export const WEB_API_ROUTES = {
34
+ authNonce: { method: 'POST', path: '/api/auth/nonce' },
35
+ authVerify: { method: 'POST', path: '/api/auth/verify' },
36
+ authSession: { method: 'GET', path: '/api/auth/session' },
37
+ authLogout: { method: 'POST', path: '/api/auth/logout' },
38
+ authSupabaseToken: { method: 'GET', path: '/api/auth/supabase-token' },
39
+ gating: { method: 'GET', path: '/api/gating' },
40
+ flags: { method: 'GET', path: '/api/flags' },
41
+ chat: { method: 'POST', path: '/api/chat' },
42
+ specConfirm: { method: 'POST', path: '/api/spec/confirm' },
43
+ launchpadModifications: { method: 'POST', path: '/api/launchpads/:id/modifications' },
44
+ paymentsQuote: { method: 'POST', path: '/api/payments/quote' },
45
+ paymentsConfirm: { method: 'POST', path: '/api/payments/confirm' },
46
+ jobApprove: { method: 'POST', path: '/api/jobs/:id/approve' },
47
+ ownerTransactionGet: { method: 'GET', path: '/api/jobs/:id/owner-transaction' },
48
+ ownerTransactionSubmit: { method: 'POST', path: '/api/jobs/:id/owner-transaction' },
49
+ jobGet: { method: 'GET', path: '/api/jobs/:id' },
50
+ launchpadsList: { method: 'GET', path: '/api/launchpads' },
51
+ launchpadGet: { method: 'GET', path: '/api/launchpads/:id' },
52
+ claimTransaction: { method: 'GET', path: '/api/launchpads/:id/claim-transaction' },
53
+ launchpadReactivate: { method: 'POST', path: '/api/launchpads/:id/reactivate' },
54
+ rpc: { method: 'POST', path: '/api/rpc' },
55
+ };
56
+ // POST /api/auth/nonce
57
+ export const AuthNonceRequest = z.strictObject({ wallet: SolanaAddress });
58
+ /**
59
+ * `nonce` is an opaque server-signed token (stateless). `message` is the exact text the wallet
60
+ * must sign; the client never builds it. The token is also set as an HttpOnly cookie.
61
+ */
62
+ export const AuthNonceResponse = z.object({
63
+ nonce: z.string().min(1),
64
+ message: z.string().min(1),
65
+ expiresAt: z.string().min(1),
66
+ });
67
+ // POST /api/auth/verify — signature of the message containing the nonce
68
+ export const AuthVerifyRequest = z.strictObject({
69
+ wallet: SolanaAddress,
70
+ signature: Base58Signature,
71
+ /** The token returned by /api/auth/nonce; optional because it is also sent as a cookie. */
72
+ nonce: z.string().min(1).optional(),
73
+ });
74
+ /** The session. */
75
+ export const AuthVerifyResponse = z.object({ wallet: SolanaAddress });
76
+ // POST /api/auth/logout
77
+ export const AuthLogoutResponse = z.object({ ok: z.literal(true) });
78
+ // GET /api/auth/supabase-token — short-lived JWT for Supabase RLS / Realtime (claim `wallet`)
79
+ export const SupabaseTokenResponse = z.object({
80
+ accessToken: z.string().min(1),
81
+ expiresAt: z.string().min(1),
82
+ });
83
+ export const Session = AuthVerifyResponse;
84
+ // GET /api/gating — FORGE_GATING_AMOUNT of mint FORGE_MINT. Without FORGE_MINT, gating is
85
+ // disabled: `enabled: false` and `ok: true`.
86
+ export const GatingResponse = z.object({
87
+ ok: z.boolean(),
88
+ /** Required raw amount (smallest units). */
89
+ required: RawTokenAmount,
90
+ /** Wallet raw balance (smallest units). */
91
+ balance: RawTokenAmount,
92
+ enabled: z.boolean(),
93
+ });
94
+ // GET /api/flags — public kill-switch state needed by the UI
95
+ export const FlagsResponse = z.object({ signupsPaused: z.boolean() });
96
+ // POST /api/chat — text/event-stream of ChatStreamEvent (web-client.ts); always starts with
97
+ // { type: 'conversation', conversationId } and ends with { type: 'done' }.
98
+ export const ChatRequest = z.strictObject({
99
+ conversationId: Uuid.optional(),
100
+ /** Set for a modification of an existing launchpad. */
101
+ launchpadId: Uuid.optional(),
102
+ message: z.string().min(1),
103
+ });
104
+ /** Payload of the first stream event. */
105
+ export const ChatResponse = z.object({ conversationId: Uuid });
106
+ // POST /api/spec/confirm
107
+ export const SpecConfirmRequest = z.strictObject({ conversationId: Uuid, spec: LaunchpadSpec });
108
+ export const SpecConfirmResponse = z.object({ launchpadId: Uuid, jobId: Uuid });
109
+ // POST /api/launchpads/:id/modifications
110
+ export const LaunchpadModificationRequest = z.strictObject({
111
+ conversationId: Uuid,
112
+ request: z.string().min(1),
113
+ });
114
+ export const LaunchpadModificationResponse = z.object({ jobId: Uuid });
115
+ // POST /api/payments/quote
116
+ export const PaymentsQuoteRequest = z.strictObject({ jobId: Uuid, kind: PaymentKind });
117
+ /** Quote as seen by the UI (docs/frontend/CONTRACT.md). */
118
+ export const Quote = z.object({
119
+ paymentId: Uuid,
120
+ kind: PaymentKind,
121
+ lamports: Lamports,
122
+ usdAmount: z.number().nonnegative(),
123
+ expiresAt: IsoDateTime,
124
+ });
125
+ /**
126
+ * `transaction` is the unsigned payment transaction (base64). It is null when lamports = "0"
127
+ * (an included modification remains and the job goes straight to `paid`).
128
+ */
129
+ export const PaymentsQuoteResponse = Quote.extend({ transaction: Base64Transaction.nullable() });
130
+ // POST /api/payments/confirm
131
+ export const PaymentsConfirmRequest = z.strictObject({
132
+ paymentId: Uuid,
133
+ signature: Base58Signature,
134
+ });
135
+ export const PaymentsConfirmResponse = z.object({ status: PaymentStatus });
136
+ // POST /api/jobs/:id/approve (no body)
137
+ export const JobApproveResponse = z.object({ status: JobStatus });
138
+ /** Summary shown before the client signs the launch transaction. */
139
+ export const OwnerTransactionSummary = z.object({
140
+ coinName: z.string(),
141
+ coinSymbol: z.string(),
142
+ firstBuyLamports: Lamports,
143
+ /** Everything the owner pays besides the first buy: network fee, rents, Metaplex fee, pool creation fee. */
144
+ estimatedNetworkFeeLamports: Lamports,
145
+ ownerWallet: SolanaAddress,
146
+ /**
147
+ * Balance the owner's wallet must hold before signing: first buy + everything above + the rent
148
+ * of the temporary WSOL account (created and closed within the transaction, so refunded, but
149
+ * held while it runs). Optional: absent when the server cannot compute it; older readers ignore it.
150
+ */
151
+ ownerRequiredBalanceLamports: Lamports.optional(),
152
+ });
153
+ /** Absolute https URL (the launchpad coin metadata URI). Kept exactly as sent (no normalization). */
154
+ export const HttpsUrl = z.url({ protocol: /^https$/, error: 'Must be an https URL' });
155
+ /**
156
+ * The accounts of the launch transaction, pinned by the browser (`verifyLaunchCoinTransaction` of
157
+ * @forge/core) before the owner signs: the launchpad coin config and mint (launchpad row), the
158
+ * FORGE creator wallet that creates the pool and holds the durable nonce, the durable nonce
159
+ * account, and the coin metadata URI written by the pool creation.
160
+ */
161
+ export const OwnerTransactionLaunch = z.object({
162
+ config: SolanaAddress,
163
+ mint: SolanaAddress,
164
+ creator: SolanaAddress,
165
+ nonceAccount: SolanaAddress,
166
+ metadataUri: HttpsUrl,
167
+ });
168
+ // GET /api/jobs/:id/owner-transaction — launch transaction partially signed by FORGE
169
+ export const OwnerTransactionGetResponse = z.object({
170
+ transaction: Base64Transaction,
171
+ /** Shown to the client before signing (INTERFACES §6). */
172
+ summary: OwnerTransactionSummary,
173
+ /** Whether the summary comes from a simulation of the exact message; when not `ok`, it is a lower bound. */
174
+ simulation: z.enum(['ok', 'failed', 'unavailable']).optional(),
175
+ /**
176
+ * The launch accounts the transaction must use. Optional in the schema so that older readers and
177
+ * writers still parse; the browser client refuses to sign a launch without it (fail closed).
178
+ */
179
+ launch: OwnerTransactionLaunch.optional(),
180
+ });
181
+ // POST /api/jobs/:id/owner-transaction
182
+ export const OwnerTransactionSubmitRequest = z.strictObject({
183
+ signedTransaction: Base64Transaction,
184
+ });
185
+ export const OwnerTransactionSubmitResponse = z.object({ signature: Base58Signature });
186
+ // GET /api/launchpads/:id/claim-transaction — client partner fee claim
187
+ export const ClaimTransactionSummary = z.object({
188
+ /** The pool this transaction claims (one pool per transaction). */
189
+ pool: SolanaAddress,
190
+ /** Partner trading fees claimed by this transaction. */
191
+ tradingFeesLamports: Lamports,
192
+ /** Partner pool creation fee claimed by this transaction (0 when none). */
193
+ poolCreationFeeLamports: Lamports,
194
+ /** What the owner pays to claim: network fee and any account rent. */
195
+ ownerCostLamports: Lamports,
196
+ /** Pools still holding claimable fees after this one. */
197
+ remainingPools: z.number().int().nonnegative(),
198
+ });
199
+ export const ClaimTransactionResponse = z.object({
200
+ transaction: Base64Transaction,
201
+ summary: ClaimTransactionSummary.optional(),
202
+ });
203
+ // POST /api/rpc — Helius relay (method allow-list and per-IP rate limit enforced by apps/web)
204
+ const JsonRpcId = z.union([z.string(), z.number(), z.null()]);
205
+ export const RpcRequest = z.strictObject({
206
+ jsonrpc: z.literal('2.0'),
207
+ id: JsonRpcId,
208
+ method: z.string().min(1),
209
+ params: z.union([z.array(z.unknown()), z.record(z.string(), z.unknown())]).optional(),
210
+ });
211
+ export const RpcResponse = z.object({
212
+ jsonrpc: z.literal('2.0'),
213
+ id: JsonRpcId,
214
+ result: z.unknown().optional(),
215
+ error: z
216
+ .object({ code: z.number().int(), message: z.string(), data: z.unknown().optional() })
217
+ .optional(),
218
+ });
219
+ // ---------------------------------------------------------------------------
220
+ // Internal builder -> signer API (§7)
221
+ // ---------------------------------------------------------------------------
222
+ /** Hex HMAC-SHA256 computed with SIGNER_HMAC_SECRET over `signerSignedPayload(...)`. */
223
+ export const SIGNER_SIGNATURE_HEADER = 'X-Forge-Signature';
224
+ /** Unix time in seconds, as a decimal string. */
225
+ export const SIGNER_TIMESTAMP_HEADER = 'X-Forge-Timestamp';
226
+ /** Requests whose timestamp differs from the signer clock by more than this are rejected. */
227
+ export const SIGNER_MAX_CLOCK_SKEW_SECONDS = 60;
228
+ /**
229
+ * Exact string fed to HMAC-SHA256: `<timestamp>.<raw body>`. The timestamp is covered by the MAC
230
+ * so that a captured request cannot be replayed with a fresh timestamp.
231
+ */
232
+ export function signerSignedPayload(timestamp, rawBody) {
233
+ return `${timestamp}.${rawBody}`;
234
+ }
235
+ export const SIGNER_ROUTES = {
236
+ configs: { method: 'POST', path: '/v1/configs' },
237
+ launchCoinPrepare: { method: 'POST', path: '/v1/launch-coin/prepare' },
238
+ health: { method: 'GET', path: '/v1/health' },
239
+ };
240
+ /*
241
+ * The signer never takes an address or an amount from the request: it re-reads everything in
242
+ * Supabase. Requests are therefore strict and carry only the job id. Both routes are idempotent.
243
+ */
244
+ // POST /v1/configs
245
+ export const SignerConfigsRequest = z.strictObject({ jobId: Uuid });
246
+ export const SignerConfigsResponse = z.object({
247
+ launchpadConfig: SolanaAddress,
248
+ launchpadCoinConfig: SolanaAddress,
249
+ });
250
+ // POST /v1/launch-coin/prepare — durable-nonce transaction signed by the creator wallet
251
+ export const SignerLaunchPrepareRequest = z.strictObject({ jobId: Uuid });
252
+ export const SignerLaunchPrepareResponse = z.object({
253
+ partiallySignedTx: Base64Transaction,
254
+ mint: SolanaAddress,
255
+ summary: OwnerTransactionSummary.optional(),
256
+ details: z.record(z.string(), z.unknown()).optional(),
257
+ });
258
+ // GET /v1/health
259
+ export const SignerHealthResponse = z.object({
260
+ ok: z.boolean(),
261
+ cluster: z.string().optional(),
262
+ uptimeSeconds: z.number().optional(),
263
+ jobs: z.record(z.string(), z.unknown()).optional(),
264
+ });