@oleary-labs/signet-sdk 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.
Files changed (95) hide show
  1. package/dist/admin.d.ts +38 -0
  2. package/dist/admin.d.ts.map +1 -0
  3. package/dist/admin.js +112 -0
  4. package/dist/admin.js.map +1 -0
  5. package/dist/authkey-session.d.ts +64 -0
  6. package/dist/authkey-session.d.ts.map +1 -0
  7. package/dist/authkey-session.js +164 -0
  8. package/dist/authkey-session.js.map +1 -0
  9. package/dist/bootstrap.d.ts +30 -0
  10. package/dist/bootstrap.d.ts.map +1 -0
  11. package/dist/bootstrap.js +60 -0
  12. package/dist/bootstrap.js.map +1 -0
  13. package/dist/bundler.d.ts +85 -0
  14. package/dist/bundler.d.ts.map +1 -0
  15. package/dist/bundler.js +160 -0
  16. package/dist/bundler.js.map +1 -0
  17. package/dist/delegate.d.ts +57 -0
  18. package/dist/delegate.d.ts.map +1 -0
  19. package/dist/delegate.js +111 -0
  20. package/dist/delegate.js.map +1 -0
  21. package/dist/frostVerify.d.ts +23 -0
  22. package/dist/frostVerify.d.ts.map +1 -0
  23. package/dist/frostVerify.js +69 -0
  24. package/dist/frostVerify.js.map +1 -0
  25. package/dist/index.d.ts +32 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +38 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/jwks.d.ts +28 -0
  30. package/dist/jwks.d.ts.map +1 -0
  31. package/dist/jwks.js +81 -0
  32. package/dist/jwks.js.map +1 -0
  33. package/dist/jwt.d.ts +27 -0
  34. package/dist/jwt.d.ts.map +1 -0
  35. package/dist/jwt.js +50 -0
  36. package/dist/jwt.js.map +1 -0
  37. package/dist/keygen.d.ts +26 -0
  38. package/dist/keygen.d.ts.map +1 -0
  39. package/dist/keygen.js +60 -0
  40. package/dist/keygen.js.map +1 -0
  41. package/dist/oauth.d.ts +34 -0
  42. package/dist/oauth.d.ts.map +1 -0
  43. package/dist/oauth.js +119 -0
  44. package/dist/oauth.js.map +1 -0
  45. package/dist/request.d.ts +42 -0
  46. package/dist/request.d.ts.map +1 -0
  47. package/dist/request.js +115 -0
  48. package/dist/request.js.map +1 -0
  49. package/dist/scopedSign.d.ts +82 -0
  50. package/dist/scopedSign.d.ts.map +1 -0
  51. package/dist/scopedSign.js +130 -0
  52. package/dist/scopedSign.js.map +1 -0
  53. package/dist/server-prover.d.ts +29 -0
  54. package/dist/server-prover.d.ts.map +1 -0
  55. package/dist/server-prover.js +54 -0
  56. package/dist/server-prover.js.map +1 -0
  57. package/dist/session.d.ts +14 -0
  58. package/dist/session.d.ts.map +1 -0
  59. package/dist/session.js +29 -0
  60. package/dist/session.js.map +1 -0
  61. package/dist/types.d.ts +56 -0
  62. package/dist/types.d.ts.map +1 -0
  63. package/dist/types.js +5 -0
  64. package/dist/types.js.map +1 -0
  65. package/dist/userop.d.ts +104 -0
  66. package/dist/userop.d.ts.map +1 -0
  67. package/dist/userop.js +212 -0
  68. package/dist/userop.js.map +1 -0
  69. package/dist/x402.d.ts +127 -0
  70. package/dist/x402.d.ts.map +1 -0
  71. package/dist/x402.js +167 -0
  72. package/dist/x402.js.map +1 -0
  73. package/package.json +64 -0
  74. package/src/admin.ts +178 -0
  75. package/src/authkey-session.ts +241 -0
  76. package/src/bootstrap.ts +106 -0
  77. package/src/bundler.ts +256 -0
  78. package/src/delegate.ts +163 -0
  79. package/src/frostVerify.ts +79 -0
  80. package/src/generate-inputs.ts +158 -0
  81. package/src/index.ts +43 -0
  82. package/src/jwks.ts +92 -0
  83. package/src/jwt.ts +74 -0
  84. package/src/keygen.ts +89 -0
  85. package/src/oauth.ts +157 -0
  86. package/src/partial-sha.ts +99 -0
  87. package/src/proof.ts +99 -0
  88. package/src/request.ts +174 -0
  89. package/src/scopedSign.ts +184 -0
  90. package/src/server-prover.ts +76 -0
  91. package/src/session.ts +33 -0
  92. package/src/types.ts +63 -0
  93. package/src/userop.ts +368 -0
  94. package/src/witness.ts +132 -0
  95. package/src/x402.ts +275 -0
package/src/x402.ts ADDED
@@ -0,0 +1,275 @@
1
+ /**
2
+ * x402 payment protocol client.
3
+ *
4
+ * Handles the HTTP 402 payment flow:
5
+ * 1. Parse `payment-required` header from 402 response
6
+ * 2. Build EIP-3009 TransferWithAuthorization typed data
7
+ * 3. Construct PaymentPayload with signature
8
+ * 4. Encode for `Payment-Signature` header
9
+ */
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // Types (matching x402 protocol spec)
13
+ // ---------------------------------------------------------------------------
14
+
15
+ export interface PaymentRequirements {
16
+ scheme: string;
17
+ network: string;
18
+ asset: string;
19
+ amount: string;
20
+ payTo: string;
21
+ maxTimeoutSeconds: number;
22
+ extra: Record<string, unknown>;
23
+ }
24
+
25
+ export interface PaymentRequired {
26
+ x402Version: number;
27
+ error?: string;
28
+ resource: {
29
+ url: string;
30
+ description?: string;
31
+ mimeType?: string;
32
+ };
33
+ accepts: PaymentRequirements[];
34
+ extensions?: Record<string, unknown>;
35
+ }
36
+
37
+ export interface PaymentPayload {
38
+ x402Version: number;
39
+ resource?: { url: string };
40
+ accepted: PaymentRequirements;
41
+ payload: Record<string, unknown>;
42
+ }
43
+
44
+ // ---------------------------------------------------------------------------
45
+ // Parse 402 response
46
+ // ---------------------------------------------------------------------------
47
+
48
+ /**
49
+ * Parse the `payment-required` header from a 402 response.
50
+ */
51
+ export function parsePaymentRequired(headerValue: string): PaymentRequired {
52
+ const json = atob(headerValue);
53
+ return JSON.parse(json);
54
+ }
55
+
56
+ /**
57
+ * Find an EVM payment option (Base USDC by default).
58
+ */
59
+ export function findEvmPaymentOption(
60
+ required: PaymentRequired,
61
+ preferredNetwork = "eip155:8453",
62
+ ): PaymentRequirements | null {
63
+ return required.accepts.find(
64
+ (a) => a.scheme === "exact" && a.network === preferredNetwork,
65
+ ) ?? null;
66
+ }
67
+
68
+ // ---------------------------------------------------------------------------
69
+ // Build EIP-3009 TransferWithAuthorization
70
+ // ---------------------------------------------------------------------------
71
+
72
+ /**
73
+ * Build EIP-712 typed data for TransferWithAuthorization (EIP-3009).
74
+ *
75
+ * @param from - The sender's Ethereum address (sub-key's address)
76
+ * @param payTo - Recipient address (from payment requirements)
77
+ * @param amount - Amount in smallest unit (e.g. "10000" for $0.01 USDC)
78
+ * @param asset - Token contract address
79
+ * @param chainId - Chain ID (e.g. 8453 for Base)
80
+ * @param tokenName - Token EIP-712 domain name (e.g. "USD Coin")
81
+ * @param tokenVersion - Token EIP-712 domain version (e.g. "2")
82
+ * @param validAfter - Unix timestamp (default: now - 60s)
83
+ * @param validBefore - Unix timestamp (default: now + 300s)
84
+ */
85
+ export function buildTransferAuthorization(
86
+ from: string,
87
+ payTo: string,
88
+ amount: string,
89
+ asset: string,
90
+ chainId: number,
91
+ tokenName: string,
92
+ tokenVersion: string,
93
+ validAfter?: number,
94
+ validBefore?: number,
95
+ ) {
96
+ const now = Math.floor(Date.now() / 1000);
97
+ const nonce = "0x" + Array.from(crypto.getRandomValues(new Uint8Array(32)))
98
+ .map((b) => b.toString(16).padStart(2, "0")).join("");
99
+
100
+ return {
101
+ domain: {
102
+ name: tokenName,
103
+ version: tokenVersion,
104
+ chainId,
105
+ verifyingContract: asset,
106
+ },
107
+ types: {
108
+ EIP712Domain: [
109
+ { name: "name", type: "string" },
110
+ { name: "version", type: "string" },
111
+ { name: "chainId", type: "uint256" },
112
+ { name: "verifyingContract", type: "address" },
113
+ ],
114
+ TransferWithAuthorization: [
115
+ { name: "from", type: "address" },
116
+ { name: "to", type: "address" },
117
+ { name: "value", type: "uint256" },
118
+ { name: "validAfter", type: "uint256" },
119
+ { name: "validBefore", type: "uint256" },
120
+ { name: "nonce", type: "bytes32" },
121
+ ],
122
+ },
123
+ primaryType: "TransferWithAuthorization" as const,
124
+ message: {
125
+ from,
126
+ to: payTo,
127
+ value: amount,
128
+ validAfter: String(validAfter ?? now - 60),
129
+ validBefore: String(validBefore ?? now + 300),
130
+ nonce,
131
+ },
132
+ };
133
+ }
134
+
135
+ // ---------------------------------------------------------------------------
136
+ // Build x402 PaymentPayload
137
+ // ---------------------------------------------------------------------------
138
+
139
+ /**
140
+ * Build the x402 PaymentPayload from a signed TransferWithAuthorization.
141
+ *
142
+ * @param accepted - The payment option we're fulfilling
143
+ * @param authorization - The TransferWithAuthorization message fields
144
+ * @param signature - The ECDSA signature (0x-prefixed, 65 bytes)
145
+ * @param resourceUrl - The URL of the resource being paid for
146
+ */
147
+ export function buildPaymentPayload(
148
+ accepted: PaymentRequirements,
149
+ authorization: {
150
+ from: string;
151
+ to: string;
152
+ value: string;
153
+ validAfter: string;
154
+ validBefore: string;
155
+ nonce: string;
156
+ },
157
+ signature: string,
158
+ resourceUrl?: string,
159
+ ): string {
160
+ const payload: PaymentPayload = {
161
+ x402Version: 2,
162
+ resource: resourceUrl ? { url: resourceUrl } : undefined,
163
+ accepted,
164
+ payload: {
165
+ signature,
166
+ authorization: {
167
+ from: authorization.from,
168
+ to: authorization.to,
169
+ value: authorization.value,
170
+ validAfter: authorization.validAfter,
171
+ validBefore: authorization.validBefore,
172
+ nonce: authorization.nonce,
173
+ },
174
+ },
175
+ };
176
+
177
+ // Base64-encode the payload for the Payment-Signature header
178
+ return btoa(JSON.stringify(payload));
179
+ }
180
+
181
+ // ---------------------------------------------------------------------------
182
+ // Full x402 fetch wrapper
183
+ // ---------------------------------------------------------------------------
184
+
185
+ export interface X402FetchOptions {
186
+ /** The sub-key's Ethereum address (from) */
187
+ signerAddress: string;
188
+ /** Preferred network (default: "eip155:8453" for Base) */
189
+ preferredNetwork?: string;
190
+ /** Called to sign the EIP-712 typed data. Returns the ECDSA signature. */
191
+ signTypedData: (typedData: ReturnType<typeof buildTransferAuthorization>) => Promise<string>;
192
+ }
193
+
194
+ /**
195
+ * Make an HTTP request with automatic x402 payment handling.
196
+ *
197
+ * If the server returns 402, automatically builds a payment authorization,
198
+ * signs it, and retries the request with the Payment-Signature header.
199
+ *
200
+ * @returns The successful response (after payment if needed)
201
+ */
202
+ export async function x402Fetch(
203
+ url: string,
204
+ init: RequestInit,
205
+ options: X402FetchOptions,
206
+ ): Promise<{ response: Response; paid: boolean; paymentDetails?: { amount: string; network: string; asset: string } }> {
207
+ // First attempt
208
+ const res = await fetch(url, init);
209
+
210
+ if (res.status !== 402) {
211
+ return { response: res, paid: false };
212
+ }
213
+
214
+ // Parse 402 payment requirements
215
+ const paymentHeader = res.headers.get("payment-required");
216
+ if (!paymentHeader) {
217
+ throw new Error("402 response missing payment-required header");
218
+ }
219
+
220
+ const required = parsePaymentRequired(paymentHeader);
221
+ const accepted = findEvmPaymentOption(required, options.preferredNetwork);
222
+ if (!accepted) {
223
+ throw new Error(`No compatible EVM payment option found (preferred: ${options.preferredNetwork ?? "eip155:8453"})`);
224
+ }
225
+
226
+ // Extract chain ID from network string (e.g. "eip155:8453" → 8453)
227
+ const chainId = parseInt(accepted.network.split(":")[1]);
228
+ const tokenName = (accepted.extra?.name as string) ?? "USD Coin";
229
+ const tokenVersion = (accepted.extra?.version as string) ?? "2";
230
+
231
+ // Build TransferWithAuthorization
232
+ const typedData = buildTransferAuthorization(
233
+ options.signerAddress,
234
+ accepted.payTo,
235
+ accepted.amount,
236
+ accepted.asset,
237
+ chainId,
238
+ tokenName,
239
+ tokenVersion,
240
+ );
241
+
242
+ // Sign via Signet
243
+ const signature = await options.signTypedData(typedData);
244
+
245
+ // Build payment payload
246
+ const paymentSignature = buildPaymentPayload(
247
+ accepted,
248
+ typedData.message,
249
+ signature,
250
+ required.resource.url,
251
+ );
252
+
253
+ // Retry with payment
254
+ const paidRes = await fetch(url, {
255
+ ...init,
256
+ headers: {
257
+ ...((): Record<string, string> => {
258
+ const h: Record<string, string> = {}
259
+ new Headers(init.headers).forEach((v, k) => { h[k] = v })
260
+ return h
261
+ })(),
262
+ "Payment-Signature": paymentSignature,
263
+ },
264
+ });
265
+
266
+ return {
267
+ response: paidRes,
268
+ paid: true,
269
+ paymentDetails: {
270
+ amount: accepted.amount,
271
+ network: accepted.network,
272
+ asset: accepted.asset,
273
+ },
274
+ };
275
+ }