@alleyboss/micropay-solana-x402-paywall 2.3.1 → 3.0.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 (72) hide show
  1. package/README.md +64 -142
  2. package/dist/agent/index.cjs +1 -2
  3. package/dist/agent/index.cjs.map +1 -1
  4. package/dist/agent/index.d.cts +11 -2
  5. package/dist/agent/index.d.ts +11 -2
  6. package/dist/agent/index.js +1 -2
  7. package/dist/agent/index.js.map +1 -1
  8. package/dist/client/index.cjs +1 -1
  9. package/dist/client/index.cjs.map +1 -1
  10. package/dist/client/index.d.cts +10 -1
  11. package/dist/client/index.d.ts +10 -1
  12. package/dist/client/index.js +1 -1
  13. package/dist/client/index.js.map +1 -1
  14. package/dist/express/index.cjs +79 -0
  15. package/dist/express/index.cjs.map +1 -0
  16. package/dist/express/index.d.cts +40 -0
  17. package/dist/express/index.d.ts +40 -0
  18. package/dist/express/index.js +76 -0
  19. package/dist/express/index.js.map +1 -0
  20. package/dist/index.cjs +257 -1357
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +6 -12
  23. package/dist/index.d.ts +6 -12
  24. package/dist/index.js +239 -1319
  25. package/dist/index.js.map +1 -1
  26. package/dist/session/index.cjs.map +1 -1
  27. package/dist/session/index.d.cts +1 -1
  28. package/dist/session/index.d.ts +1 -1
  29. package/dist/session/index.js.map +1 -1
  30. package/dist/{session-D2IoWAWV.d.cts → types-BWYQMw03.d.cts} +1 -16
  31. package/dist/{session-D2IoWAWV.d.ts → types-BWYQMw03.d.ts} +1 -16
  32. package/package.json +29 -69
  33. package/dist/client-D-dteoJw.d.cts +0 -63
  34. package/dist/client-DfCIRrNG.d.ts +0 -63
  35. package/dist/memory-Daxkczti.d.cts +0 -29
  36. package/dist/memory-Daxkczti.d.ts +0 -29
  37. package/dist/middleware/index.cjs +0 -273
  38. package/dist/middleware/index.cjs.map +0 -1
  39. package/dist/middleware/index.d.cts +0 -91
  40. package/dist/middleware/index.d.ts +0 -91
  41. package/dist/middleware/index.js +0 -267
  42. package/dist/middleware/index.js.map +0 -1
  43. package/dist/nextjs-BDyOqGAq.d.cts +0 -81
  44. package/dist/nextjs-CbX8_9yK.d.ts +0 -81
  45. package/dist/payment-BGp7eMQl.d.cts +0 -103
  46. package/dist/payment-BGp7eMQl.d.ts +0 -103
  47. package/dist/priority-fees-C-OH4Trr.d.cts +0 -50
  48. package/dist/priority-fees-C-OH4Trr.d.ts +0 -50
  49. package/dist/solana/index.cjs +0 -589
  50. package/dist/solana/index.cjs.map +0 -1
  51. package/dist/solana/index.d.cts +0 -195
  52. package/dist/solana/index.d.ts +0 -195
  53. package/dist/solana/index.js +0 -567
  54. package/dist/solana/index.js.map +0 -1
  55. package/dist/store/index.cjs +0 -99
  56. package/dist/store/index.cjs.map +0 -1
  57. package/dist/store/index.d.cts +0 -38
  58. package/dist/store/index.d.ts +0 -38
  59. package/dist/store/index.js +0 -96
  60. package/dist/store/index.js.map +0 -1
  61. package/dist/utils/index.cjs +0 -68
  62. package/dist/utils/index.cjs.map +0 -1
  63. package/dist/utils/index.d.cts +0 -30
  64. package/dist/utils/index.d.ts +0 -30
  65. package/dist/utils/index.js +0 -65
  66. package/dist/utils/index.js.map +0 -1
  67. package/dist/x402/index.cjs +0 -387
  68. package/dist/x402/index.cjs.map +0 -1
  69. package/dist/x402/index.d.cts +0 -96
  70. package/dist/x402/index.d.ts +0 -96
  71. package/dist/x402/index.js +0 -375
  72. package/dist/x402/index.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,1151 +1,22 @@
1
- import { PublicKey, LAMPORTS_PER_SOL, ComputeBudgetProgram, TransactionMessage, VersionedTransaction, SystemProgram, Keypair, clusterApiUrl, Connection } from '@solana/web3.js';
1
+ export * from '@x402/core';
2
+ export * from '@x402/core/types';
3
+ export * from '@x402/core/client';
4
+ export * from '@x402/svm';
5
+ import { PublicKey, SystemProgram, LAMPORTS_PER_SOL, Keypair, TransactionMessage, VersionedTransaction, ComputeBudgetProgram } from '@solana/web3.js';
2
6
  import { SignJWT, jwtVerify } from 'jose';
3
7
  import { v4 } from 'uuid';
4
8
 
5
- // src/types/payment.ts
6
- var TOKEN_MINTS = {
7
- /** USDC on mainnet */
8
- USDC_MAINNET: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
9
- /** USDC on devnet */
10
- USDC_DEVNET: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
11
- /** USDT on mainnet */
12
- USDT_MAINNET: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"
13
- };
14
- var cachedConnection = null;
15
- var cachedNetwork = null;
16
- var cachedFallbacks = [];
17
- var cachedFallbackEnabled = false;
18
- function buildRpcUrl(config2) {
19
- const { network, rpcUrl, tatumApiKey } = config2;
20
- if (rpcUrl) {
21
- if (rpcUrl.includes("tatum.io") && tatumApiKey && !rpcUrl.includes(tatumApiKey)) {
22
- return rpcUrl.endsWith("/") ? `${rpcUrl}${tatumApiKey}` : `${rpcUrl}/${tatumApiKey}`;
23
- }
24
- return rpcUrl;
25
- }
26
- if (tatumApiKey) {
27
- const baseUrl = network === "mainnet-beta" ? "https://solana-mainnet.gateway.tatum.io" : "https://solana-devnet.gateway.tatum.io";
28
- return `${baseUrl}/${tatumApiKey}`;
29
- }
30
- return clusterApiUrl(network);
31
- }
32
- function createConnection(rpcUrl) {
33
- return new Connection(rpcUrl, {
34
- commitment: "confirmed",
35
- confirmTransactionInitialTimeout: 6e4
36
- });
37
- }
38
- function getConnection(config2) {
39
- const { network } = config2;
40
- if (cachedConnection && cachedNetwork === network) {
41
- return cachedConnection;
42
- }
43
- const rpcUrl = buildRpcUrl(config2);
44
- cachedConnection = createConnection(rpcUrl);
45
- cachedNetwork = network;
46
- cachedFallbackEnabled = config2.enableFallback ?? false;
47
- cachedFallbacks = [];
48
- if (cachedFallbackEnabled && config2.fallbackRpcUrls?.length) {
49
- cachedFallbacks = config2.fallbackRpcUrls.map(createConnection);
50
- }
51
- return cachedConnection;
52
- }
53
- function getConnectionWithFallback(config2) {
54
- const connection = getConnection(config2);
55
- return {
56
- connection,
57
- fallbacks: cachedFallbacks,
58
- fallbackEnabled: cachedFallbackEnabled
59
- };
60
- }
61
- async function withFallback(config2, operation) {
62
- const { connection, fallbacks, fallbackEnabled } = getConnectionWithFallback(config2);
63
- try {
64
- return await operation(connection);
65
- } catch (error) {
66
- if (!fallbackEnabled || fallbacks.length === 0) {
67
- throw error;
68
- }
69
- if (!isRetryableError(error)) {
70
- throw error;
71
- }
72
- for (let i = 0; i < fallbacks.length; i++) {
73
- try {
74
- return await operation(fallbacks[i]);
75
- } catch (fallbackError) {
76
- if (i === fallbacks.length - 1) {
77
- throw fallbackError;
78
- }
79
- }
80
- }
81
- throw error;
82
- }
83
- }
84
- function isRetryableError(error) {
85
- if (error instanceof Error) {
86
- const message = error.message.toLowerCase();
87
- return message.includes("429") || message.includes("503") || message.includes("502") || message.includes("timeout") || message.includes("econnrefused") || message.includes("enotfound") || message.includes("rate limit");
88
- }
89
- return false;
90
- }
91
- function resetConnection() {
92
- cachedConnection = null;
93
- cachedNetwork = null;
94
- }
95
- function isMainnet(network) {
96
- return network === "mainnet-beta";
97
- }
98
- function toX402Network(network) {
99
- return network === "mainnet-beta" ? "solana-mainnet" : "solana-devnet";
100
- }
101
- var SIGNATURE_REGEX = /^[1-9A-HJ-NP-Za-km-z]{87,88}$/;
102
- var WALLET_REGEX = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
103
- function isValidSignature(signature) {
104
- if (!signature || typeof signature !== "string") return false;
105
- return SIGNATURE_REGEX.test(signature);
106
- }
107
- function isValidWalletAddress(address) {
108
- if (!address || typeof address !== "string") return false;
109
- return WALLET_REGEX.test(address);
110
- }
111
- function parseSOLTransfer(transaction, expectedRecipient) {
112
- const instructions = transaction.transaction.message.instructions;
113
- for (const ix of instructions) {
114
- if ("parsed" in ix && ix.program === "system") {
115
- const parsed = ix.parsed;
116
- if (parsed.type === "transfer" && parsed.info.destination === expectedRecipient) {
117
- return {
118
- from: parsed.info.source,
119
- to: parsed.info.destination,
120
- amount: BigInt(parsed.info.lamports)
121
- };
122
- }
123
- }
124
- }
125
- if (transaction.meta?.innerInstructions) {
126
- for (const inner of transaction.meta.innerInstructions) {
127
- for (const ix of inner.instructions) {
128
- if ("parsed" in ix && ix.program === "system") {
129
- const parsed = ix.parsed;
130
- if (parsed.type === "transfer" && parsed.info.destination === expectedRecipient) {
131
- return {
132
- from: parsed.info.source,
133
- to: parsed.info.destination,
134
- amount: BigInt(parsed.info.lamports)
135
- };
136
- }
137
- }
138
- }
139
- }
140
- }
141
- return null;
142
- }
143
- async function verifyPayment(params) {
144
- const {
145
- signature,
146
- expectedRecipient,
147
- expectedAmount,
148
- maxAgeSeconds = 300,
149
- clientConfig,
150
- signatureStore
151
- } = params;
152
- if (!isValidSignature(signature)) {
153
- return { valid: false, confirmed: false, signature, error: "Invalid signature format" };
154
- }
155
- if (!isValidWalletAddress(expectedRecipient)) {
156
- return { valid: false, confirmed: false, signature, error: "Invalid recipient address" };
157
- }
158
- if (expectedAmount <= 0n) {
159
- return { valid: false, confirmed: false, signature, error: "Invalid expected amount" };
160
- }
161
- if (signatureStore) {
162
- const isUsed = await signatureStore.hasBeenUsed(signature);
163
- if (isUsed) {
164
- return { valid: false, confirmed: true, signature, error: "Signature already used" };
165
- }
166
- }
167
- const effectiveMaxAge = Math.min(Math.max(maxAgeSeconds, 60), 3600);
168
- const connection = getConnection(clientConfig);
169
- try {
170
- const transaction = await connection.getParsedTransaction(signature, {
171
- commitment: "confirmed",
172
- maxSupportedTransactionVersion: 0
173
- });
174
- if (!transaction) {
175
- return { valid: false, confirmed: false, signature, error: "Transaction not found" };
176
- }
177
- if (transaction.meta?.err) {
178
- return {
179
- valid: false,
180
- confirmed: true,
181
- signature,
182
- error: "Transaction failed on-chain"
183
- };
184
- }
185
- if (transaction.blockTime) {
186
- const now = Math.floor(Date.now() / 1e3);
187
- if (now - transaction.blockTime > effectiveMaxAge) {
188
- return { valid: false, confirmed: true, signature, error: "Transaction too old" };
189
- }
190
- if (transaction.blockTime > now + 60) {
191
- return { valid: false, confirmed: true, signature, error: "Invalid transaction time" };
192
- }
193
- }
194
- const transferDetails = parseSOLTransfer(transaction, expectedRecipient);
195
- if (!transferDetails) {
196
- return {
197
- valid: false,
198
- confirmed: true,
199
- signature,
200
- error: "No valid SOL transfer to recipient found"
201
- };
202
- }
203
- if (transferDetails.amount < expectedAmount) {
204
- return {
205
- valid: false,
206
- confirmed: true,
207
- signature,
208
- from: transferDetails.from,
209
- to: transferDetails.to,
210
- amount: transferDetails.amount,
211
- error: "Insufficient payment amount"
212
- };
213
- }
214
- return {
215
- valid: true,
216
- confirmed: true,
217
- signature,
218
- from: transferDetails.from,
219
- to: transferDetails.to,
220
- amount: transferDetails.amount,
221
- blockTime: transaction.blockTime ?? void 0,
222
- slot: transaction.slot
223
- };
224
- } catch (error) {
225
- return {
226
- valid: false,
227
- confirmed: false,
228
- signature,
229
- error: "Verification failed"
230
- };
231
- }
232
- }
233
- async function waitForConfirmation(signature, clientConfig) {
234
- if (!isValidSignature(signature)) {
235
- return { confirmed: false, error: "Invalid signature format" };
236
- }
237
- const connection = getConnection(clientConfig);
238
- try {
239
- const confirmation = await connection.confirmTransaction(signature, "confirmed");
240
- if (confirmation.value.err) {
241
- return { confirmed: false, error: "Transaction failed" };
242
- }
243
- return { confirmed: true, slot: confirmation.context?.slot };
244
- } catch {
245
- return { confirmed: false, error: "Confirmation timeout" };
246
- }
247
- }
248
- async function getWalletTransactions(walletAddress, clientConfig, limit = 20) {
249
- if (!isValidWalletAddress(walletAddress)) {
250
- return [];
251
- }
252
- const safeLimit = Math.min(Math.max(limit, 1), 100);
253
- const connection = getConnection(clientConfig);
254
- try {
255
- const pubkey = new PublicKey(walletAddress);
256
- const signatures = await connection.getSignaturesForAddress(pubkey, { limit: safeLimit });
257
- return signatures.map((sig) => ({
258
- signature: sig.signature,
259
- blockTime: sig.blockTime ?? void 0,
260
- slot: sig.slot
261
- }));
262
- } catch {
263
- return [];
264
- }
265
- }
266
- function lamportsToSol(lamports) {
267
- return Number(lamports) / LAMPORTS_PER_SOL;
268
- }
269
- function solToLamports(sol) {
270
- if (!Number.isFinite(sol) || sol < 0) {
271
- throw new Error("Invalid SOL amount");
272
- }
273
- return BigInt(Math.floor(sol * LAMPORTS_PER_SOL));
274
- }
275
- var SIGNATURE_REGEX2 = /^[1-9A-HJ-NP-Za-km-z]{87,88}$/;
276
- var WALLET_REGEX2 = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
277
- function resolveMintAddress(asset, network) {
278
- if (asset === "native") return null;
279
- if (asset === "usdc") {
280
- return network === "mainnet-beta" ? TOKEN_MINTS.USDC_MAINNET : TOKEN_MINTS.USDC_DEVNET;
281
- }
282
- if (asset === "usdt") {
283
- return TOKEN_MINTS.USDT_MAINNET;
284
- }
285
- if (typeof asset === "object" && "mint" in asset) {
286
- return asset.mint;
287
- }
288
- return null;
289
- }
290
- function getTokenDecimals(asset) {
291
- if (asset === "native") return 9;
292
- if (asset === "usdc" || asset === "usdt") return 6;
293
- if (typeof asset === "object" && "decimals" in asset) {
294
- return asset.decimals ?? 6;
295
- }
296
- return 6;
297
- }
298
- function parseSPLTransfer(transaction, expectedRecipient, expectedMint) {
299
- const instructions = transaction.transaction.message.instructions;
300
- for (const ix of instructions) {
301
- if ("parsed" in ix && (ix.program === "spl-token" || ix.program === "spl-token-2022")) {
302
- const parsed = ix.parsed;
303
- if (parsed.type === "transfer" || parsed.type === "transferChecked") {
304
- const amount = parsed.info.amount || parsed.info.tokenAmount?.amount;
305
- if (amount && parsed.info.destination) {
306
- return {
307
- from: parsed.info.authority || parsed.info.source || "",
308
- to: parsed.info.destination,
309
- amount: BigInt(amount),
310
- mint: parsed.info.mint || expectedMint
311
- };
312
- }
313
- }
314
- }
315
- }
316
- if (transaction.meta?.innerInstructions) {
317
- for (const inner of transaction.meta.innerInstructions) {
318
- for (const ix of inner.instructions) {
319
- if ("parsed" in ix && (ix.program === "spl-token" || ix.program === "spl-token-2022")) {
320
- const parsed = ix.parsed;
321
- if (parsed.type === "transfer" || parsed.type === "transferChecked") {
322
- const amount = parsed.info.amount || parsed.info.tokenAmount?.amount;
323
- if (amount) {
324
- return {
325
- from: parsed.info.authority || parsed.info.source || "",
326
- to: parsed.info.destination || "",
327
- amount: BigInt(amount),
328
- mint: parsed.info.mint || expectedMint
329
- };
330
- }
331
- }
332
- }
333
- }
334
- }
335
- }
336
- if (transaction.meta?.postTokenBalances && transaction.meta?.preTokenBalances) {
337
- const preBalances = transaction.meta.preTokenBalances;
338
- const postBalances = transaction.meta.postTokenBalances;
339
- for (const post of postBalances) {
340
- if (post.mint === expectedMint && post.owner === expectedRecipient) {
341
- const pre = preBalances.find(
342
- (p) => p.accountIndex === post.accountIndex
343
- );
344
- const preAmount = BigInt(pre?.uiTokenAmount?.amount || "0");
345
- const postAmount = BigInt(post.uiTokenAmount?.amount || "0");
346
- const transferred = postAmount - preAmount;
347
- if (transferred > 0n) {
348
- return {
349
- from: "",
350
- // Can't determine from balance changes
351
- to: expectedRecipient,
352
- amount: transferred,
353
- mint: expectedMint
354
- };
355
- }
356
- }
357
- }
358
- }
359
- return null;
360
- }
361
- async function verifySPLPayment(params) {
362
- const {
363
- signature,
364
- expectedRecipient,
365
- expectedAmount,
366
- asset,
367
- clientConfig,
368
- maxAgeSeconds = 300,
369
- signatureStore
370
- } = params;
371
- if (signatureStore) {
372
- const isUsed = await signatureStore.hasBeenUsed(signature);
373
- if (isUsed) {
374
- return { valid: false, confirmed: true, signature, error: "Signature already used" };
375
- }
376
- }
377
- if (!SIGNATURE_REGEX2.test(signature)) {
378
- return { valid: false, confirmed: false, signature, error: "Invalid signature format" };
379
- }
380
- if (!WALLET_REGEX2.test(expectedRecipient)) {
381
- return { valid: false, confirmed: false, signature, error: "Invalid recipient address" };
382
- }
383
- const mintAddress = resolveMintAddress(asset, clientConfig.network);
384
- if (!mintAddress) {
385
- return { valid: false, confirmed: false, signature, error: "Invalid asset configuration" };
386
- }
387
- if (expectedAmount <= 0n) {
388
- return { valid: false, confirmed: false, signature, error: "Invalid expected amount" };
389
- }
390
- const effectiveMaxAge = Math.min(Math.max(maxAgeSeconds, 60), 3600);
391
- const connection = getConnection(clientConfig);
392
- try {
393
- const transaction = await connection.getParsedTransaction(signature, {
394
- commitment: "confirmed",
395
- maxSupportedTransactionVersion: 0
396
- });
397
- if (!transaction) {
398
- return { valid: false, confirmed: false, signature, error: "Transaction not found" };
399
- }
400
- if (transaction.meta?.err) {
401
- return { valid: false, confirmed: true, signature, error: "Transaction failed on-chain" };
402
- }
403
- if (transaction.blockTime) {
404
- const now = Math.floor(Date.now() / 1e3);
405
- if (now - transaction.blockTime > effectiveMaxAge) {
406
- return { valid: false, confirmed: true, signature, error: "Transaction too old" };
407
- }
408
- if (transaction.blockTime > now + 60) {
409
- return { valid: false, confirmed: true, signature, error: "Invalid transaction time" };
410
- }
411
- }
412
- const transfer = parseSPLTransfer(transaction, expectedRecipient, mintAddress);
413
- if (!transfer) {
414
- return {
415
- valid: false,
416
- confirmed: true,
417
- signature,
418
- error: "No valid token transfer to recipient found"
419
- };
420
- }
421
- if (transfer.to) {
422
- try {
423
- const destinationInfo = await connection.getParsedAccountInfo(new PublicKey(transfer.to));
424
- const owner = destinationInfo.value?.data?.parsed?.info?.owner;
425
- if (owner && owner !== expectedRecipient) {
426
- return {
427
- valid: false,
428
- confirmed: true,
429
- signature,
430
- error: "Recipient mismatch: Token account not owned by merchant"
431
- };
432
- }
433
- } catch (e) {
434
- return {
435
- valid: false,
436
- confirmed: true,
437
- signature,
438
- error: "Could not verify token account owner"
439
- };
440
- }
441
- }
442
- if (transfer.mint !== mintAddress) {
443
- return {
444
- valid: false,
445
- confirmed: true,
446
- signature,
447
- error: "Token mint mismatch"
448
- };
449
- }
450
- if (transfer.amount < expectedAmount) {
451
- return {
452
- valid: false,
453
- confirmed: true,
454
- signature,
455
- from: transfer.from,
456
- to: transfer.to,
457
- mint: transfer.mint,
458
- amount: transfer.amount,
459
- error: "Insufficient payment amount"
460
- };
461
- }
462
- return {
463
- valid: true,
464
- confirmed: true,
465
- signature,
466
- from: transfer.from,
467
- to: transfer.to,
468
- mint: transfer.mint,
469
- amount: transfer.amount,
470
- blockTime: transaction.blockTime ?? void 0,
471
- slot: transaction.slot
472
- };
473
- } catch {
474
- return { valid: false, confirmed: false, signature, error: "Verification failed" };
475
- }
476
- }
477
- function isNativeAsset(asset) {
478
- return asset === "native";
479
- }
480
- var DEFAULT_COMPUTE_UNITS = 2e5;
481
- var DEFAULT_MICRO_LAMPORTS = 1e3;
482
- function createPriorityFeeInstructions(config2 = {}) {
483
- const { enabled = false, microLamports, computeUnits } = config2;
484
- if (!enabled) {
485
- return [];
486
- }
487
- const instructions = [];
488
- const units = computeUnits ?? DEFAULT_COMPUTE_UNITS;
489
- instructions.push(ComputeBudgetProgram.setComputeUnitLimit({ units }));
490
- const price = microLamports ?? DEFAULT_MICRO_LAMPORTS;
491
- instructions.push(ComputeBudgetProgram.setComputeUnitPrice({ microLamports: price }));
492
- return instructions;
493
- }
494
- async function estimatePriorityFee(connection, accounts = []) {
495
- try {
496
- const fees = await connection.getRecentPrioritizationFees({
497
- lockedWritableAccounts: accounts
498
- });
499
- if (fees.length === 0) {
500
- return DEFAULT_MICRO_LAMPORTS;
501
- }
502
- const sortedFees = fees.map((f) => f.prioritizationFee).filter((f) => f > 0).sort((a, b) => a - b);
503
- if (sortedFees.length === 0) {
504
- return DEFAULT_MICRO_LAMPORTS;
505
- }
506
- const medianIndex = Math.floor(sortedFees.length / 2);
507
- return sortedFees[medianIndex];
508
- } catch {
509
- return DEFAULT_MICRO_LAMPORTS;
510
- }
511
- }
512
- function calculatePriorityFeeCost(microLamportsPerCU, computeUnits) {
513
- return Math.ceil(microLamportsPerCU * computeUnits / 1e6);
514
- }
515
- async function buildVersionedTransaction(config2) {
516
- const {
517
- connection,
518
- payer,
519
- instructions,
520
- lookupTables = [],
521
- priorityFee,
522
- recentBlockhash
523
- } = config2;
524
- const priorityIxs = createPriorityFeeInstructions(priorityFee);
525
- const allInstructions = [...priorityIxs, ...instructions];
526
- let blockhash;
527
- let lastValidBlockHeight;
528
- if (recentBlockhash) {
529
- blockhash = recentBlockhash;
530
- const slot = await connection.getSlot();
531
- lastValidBlockHeight = slot + 150;
532
- } else {
533
- const latestBlockhash = await connection.getLatestBlockhash("confirmed");
534
- blockhash = latestBlockhash.blockhash;
535
- lastValidBlockHeight = latestBlockhash.lastValidBlockHeight;
536
- }
537
- const message = new TransactionMessage({
538
- payerKey: payer,
539
- recentBlockhash: blockhash,
540
- instructions: allInstructions
541
- }).compileToV0Message(lookupTables);
542
- const transaction = new VersionedTransaction(message);
543
- return {
544
- transaction,
545
- blockhash,
546
- lastValidBlockHeight
547
- };
548
- }
549
- async function fetchLookupTables(connection, addresses) {
550
- const tables = [];
551
- for (const address of addresses) {
552
- const result = await connection.getAddressLookupTable(address);
553
- if (result.value) {
554
- tables.push(result.value);
555
- }
556
- }
557
- return tables;
558
- }
559
- function isVersionedTransaction(tx) {
560
- return tx instanceof VersionedTransaction;
561
- }
562
- var MAX_ARTICLES_PER_SESSION = 100;
563
- var MIN_SECRET_LENGTH = 32;
564
- function getSecretKey(secret) {
565
- if (!secret || typeof secret !== "string") {
566
- throw new Error("Session secret is required");
567
- }
568
- if (secret.length < MIN_SECRET_LENGTH) {
569
- throw new Error(`Session secret must be at least ${MIN_SECRET_LENGTH} characters`);
570
- }
571
- return new TextEncoder().encode(secret);
572
- }
573
- function validateWalletAddress(address) {
574
- if (!address || typeof address !== "string") return false;
575
- const base58Regex = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
576
- return base58Regex.test(address);
577
- }
578
- function validateArticleId(articleId) {
579
- if (!articleId || typeof articleId !== "string") return false;
580
- if (articleId.length > 128) return false;
581
- const safeIdRegex = /^[a-zA-Z0-9_-]+$/;
582
- return safeIdRegex.test(articleId);
583
- }
584
- async function createSession(walletAddress, articleId, config2, siteWide = false) {
585
- if (!validateWalletAddress(walletAddress)) {
586
- throw new Error("Invalid wallet address format");
587
- }
588
- if (!validateArticleId(articleId)) {
589
- throw new Error("Invalid article ID format");
590
- }
591
- if (!config2.durationHours || config2.durationHours <= 0 || config2.durationHours > 720) {
592
- throw new Error("Session duration must be between 1 and 720 hours");
593
- }
594
- const sessionId = v4();
595
- const now = Math.floor(Date.now() / 1e3);
596
- const expiresAt = now + config2.durationHours * 3600;
597
- const session = {
598
- id: sessionId,
599
- walletAddress,
600
- unlockedArticles: [articleId],
601
- siteWideUnlock: Boolean(siteWide),
602
- createdAt: now,
603
- expiresAt
604
- };
605
- const payload = {
606
- sub: walletAddress,
607
- sid: sessionId,
608
- articles: session.unlockedArticles,
609
- siteWide: session.siteWideUnlock,
610
- iat: now,
611
- exp: expiresAt
612
- };
613
- const token = await new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime(`${config2.durationHours}h`).sign(getSecretKey(config2.secret));
614
- return { token, session };
615
- }
616
- async function validateSession(token, secret) {
617
- if (!token || typeof token !== "string") {
618
- return { valid: false, reason: "Invalid token format" };
619
- }
620
- try {
621
- const { payload } = await jwtVerify(token, getSecretKey(secret));
622
- const sessionPayload = payload;
623
- if (!sessionPayload.sub || !sessionPayload.sid || !sessionPayload.exp) {
624
- return { valid: false, reason: "Malformed session payload" };
625
- }
626
- const now = Math.floor(Date.now() / 1e3);
627
- if (sessionPayload.exp < now) {
628
- return { valid: false, reason: "Session expired" };
629
- }
630
- if (!validateWalletAddress(sessionPayload.sub)) {
631
- return { valid: false, reason: "Invalid session data" };
632
- }
633
- const session = {
634
- id: sessionPayload.sid,
635
- walletAddress: sessionPayload.sub,
636
- unlockedArticles: Array.isArray(sessionPayload.articles) ? sessionPayload.articles : [],
637
- siteWideUnlock: Boolean(sessionPayload.siteWide),
638
- createdAt: sessionPayload.iat ?? 0,
639
- expiresAt: sessionPayload.exp
640
- };
641
- return { valid: true, session };
642
- } catch (error) {
643
- return { valid: false, reason: "Invalid session" };
644
- }
645
- }
646
- async function addArticleToSession(token, articleId, secret) {
647
- if (!validateArticleId(articleId)) {
648
- return null;
649
- }
650
- const validation = await validateSession(token, secret);
651
- if (!validation.valid || !validation.session) {
652
- return null;
653
- }
654
- const session = validation.session;
655
- if (session.unlockedArticles.includes(articleId)) {
656
- return { token, session };
657
- }
658
- if (session.unlockedArticles.length >= MAX_ARTICLES_PER_SESSION) {
659
- return null;
660
- }
661
- const updatedArticles = [...session.unlockedArticles, articleId];
662
- const payload = {
663
- sub: session.walletAddress,
664
- sid: session.id,
665
- articles: updatedArticles,
666
- siteWide: session.siteWideUnlock,
667
- iat: session.createdAt,
668
- exp: session.expiresAt
669
- };
670
- const newToken = await new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).sign(getSecretKey(secret));
671
- return {
672
- token: newToken,
673
- session: { ...session, unlockedArticles: updatedArticles }
674
- };
675
- }
676
- async function isArticleUnlocked(token, articleId, secret) {
677
- if (!validateArticleId(articleId)) {
678
- return false;
679
- }
680
- const validation = await validateSession(token, secret);
681
- if (!validation.valid || !validation.session) {
682
- return false;
683
- }
684
- if (validation.session.siteWideUnlock) {
685
- return true;
686
- }
687
- return validation.session.unlockedArticles.includes(articleId);
688
- }
689
-
690
- // src/x402/config.ts
691
- var WALLET_REGEX3 = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
692
- function sanitizeDisplayString(str, maxLength = 200) {
693
- if (!str || typeof str !== "string") return "";
694
- return str.slice(0, maxLength).replace(/[<>"'&]/g, "");
695
- }
696
- function isValidUrl(url) {
697
- try {
698
- const parsed = new URL(url);
699
- return parsed.protocol === "http:" || parsed.protocol === "https:";
700
- } catch {
701
- return false;
702
- }
703
- }
704
- function buildPaymentRequirement(params) {
705
- if (!WALLET_REGEX3.test(params.creatorWallet)) {
706
- throw new Error("Invalid creator wallet address");
707
- }
708
- if (params.priceInLamports <= 0n) {
709
- throw new Error("Price must be positive");
710
- }
711
- if (!isValidUrl(params.resourceUrl)) {
712
- throw new Error("Invalid resource URL");
713
- }
714
- if (params.network !== "devnet" && params.network !== "mainnet-beta") {
715
- throw new Error("Invalid network");
716
- }
717
- const timeout = params.maxTimeoutSeconds ?? 300;
718
- if (timeout < 60 || timeout > 3600) {
719
- throw new Error("Timeout must be between 60 and 3600 seconds");
720
- }
721
- const x402Network = toX402Network(params.network);
722
- const safeTitle = sanitizeDisplayString(params.articleTitle, 200);
723
- const safeArticleId = sanitizeDisplayString(params.articleId, 128);
724
- return {
725
- scheme: "exact",
726
- network: x402Network,
727
- maxAmountRequired: params.priceInLamports.toString(),
728
- resource: params.resourceUrl,
729
- description: `Unlock: ${safeTitle}`,
730
- mimeType: "text/html",
731
- payTo: params.creatorWallet,
732
- maxTimeoutSeconds: timeout,
733
- asset: "native",
734
- extra: {
735
- name: safeTitle,
736
- articleId: safeArticleId
737
- }
738
- };
739
- }
740
- function encodePaymentRequired(requirement) {
741
- return Buffer.from(JSON.stringify(requirement)).toString("base64");
742
- }
743
- function decodePaymentRequired(encoded) {
744
- if (!encoded || typeof encoded !== "string") {
745
- throw new Error("Invalid encoded requirement");
746
- }
747
- if (encoded.length > 1e4) {
748
- throw new Error("Encoded requirement too large");
749
- }
750
- try {
751
- const decoded = Buffer.from(encoded, "base64").toString("utf-8");
752
- return JSON.parse(decoded);
753
- } catch {
754
- throw new Error("Failed to decode payment requirement");
755
- }
756
- }
757
- var X402_HEADERS = {
758
- PAYMENT_REQUIRED: "X-Payment-Required",
759
- PAYMENT: "X-Payment",
760
- PAYMENT_RESPONSE: "X-Payment-Response"
761
- };
762
- function create402ResponseBody(requirement) {
763
- const assetStr = typeof requirement.asset === "string" ? requirement.asset : requirement.asset.mint;
764
- return {
765
- error: "Payment Required",
766
- message: requirement.description,
767
- price: {
768
- amount: requirement.maxAmountRequired,
769
- asset: assetStr,
770
- network: requirement.network
771
- }
772
- };
773
- }
774
- function create402Headers(requirement) {
775
- const encoded = encodePaymentRequired(requirement);
776
- return {
777
- "Content-Type": "application/json",
778
- [X402_HEADERS.PAYMENT_REQUIRED]: encoded,
779
- "Access-Control-Expose-Headers": X402_HEADERS.PAYMENT_REQUIRED
780
- };
781
- }
782
-
783
- // src/x402/verification.ts
784
- var SIGNATURE_REGEX3 = /^[1-9A-HJ-NP-Za-km-z]{87,88}$/;
785
- async function verifyX402Payment(payload, requirement, clientConfig) {
786
- if (!payload || typeof payload !== "object") {
787
- return { valid: false, invalidReason: "Invalid payload" };
788
- }
789
- const signature = payload.payload?.signature;
790
- if (!signature || typeof signature !== "string") {
791
- return { valid: false, invalidReason: "Missing transaction signature" };
792
- }
793
- if (!SIGNATURE_REGEX3.test(signature)) {
794
- return { valid: false, invalidReason: "Invalid signature format" };
795
- }
796
- if (payload.x402Version !== 1) {
797
- return { valid: false, invalidReason: "Unsupported x402 version" };
798
- }
799
- if (payload.scheme !== "exact") {
800
- return { valid: false, invalidReason: "Unsupported payment scheme" };
801
- }
802
- if (payload.network !== requirement.network) {
803
- return {
804
- valid: false,
805
- invalidReason: `Network mismatch: expected ${requirement.network}`
806
- };
807
- }
808
- const walletRegex = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
809
- if (!walletRegex.test(requirement.payTo)) {
810
- return { valid: false, invalidReason: "Invalid recipient configuration" };
811
- }
812
- let expectedAmount;
813
- try {
814
- expectedAmount = BigInt(requirement.maxAmountRequired);
815
- if (expectedAmount <= 0n) {
816
- return { valid: false, invalidReason: "Invalid payment amount" };
817
- }
818
- } catch {
819
- return { valid: false, invalidReason: "Invalid payment amount format" };
820
- }
821
- const verification = await verifyPayment({
822
- signature,
823
- expectedRecipient: requirement.payTo,
824
- expectedAmount,
825
- maxAgeSeconds: requirement.maxTimeoutSeconds,
826
- clientConfig
827
- });
828
- if (!verification.valid) {
829
- return {
830
- valid: false,
831
- invalidReason: verification.error || "Transaction verification failed"
832
- };
833
- }
834
- return {
835
- valid: true,
836
- settled: verification.confirmed,
837
- from: verification.from,
838
- transaction: {
839
- signature: verification.signature,
840
- blockTime: verification.blockTime,
841
- slot: verification.slot
842
- }
843
- };
844
- }
845
- function parsePaymentHeader(header) {
846
- if (!header || typeof header !== "string") {
847
- return null;
848
- }
849
- if (header.length > 1e4) {
850
- return null;
851
- }
852
- try {
853
- const decoded = Buffer.from(header, "base64").toString("utf-8");
854
- const parsed = JSON.parse(decoded);
855
- if (!parsed || typeof parsed !== "object") {
856
- return null;
857
- }
858
- return parsed;
859
- } catch {
860
- return null;
861
- }
862
- }
863
- function encodePaymentRequirement(requirement) {
864
- return Buffer.from(JSON.stringify(requirement)).toString("base64");
865
- }
866
- function encodePaymentResponse(response) {
867
- return Buffer.from(JSON.stringify(response)).toString("base64");
868
- }
869
- function create402Response(requirement, body) {
870
- const headers = new Headers({
871
- "Content-Type": "application/json",
872
- "X-Payment-Required": encodePaymentRequirement(requirement)
873
- });
874
- const responseBody = body || {
875
- error: "Payment Required",
876
- message: "This resource requires payment to access",
877
- x402Version: 1,
878
- accepts: [requirement]
879
- };
880
- return new Response(JSON.stringify(responseBody), {
881
- status: 402,
882
- headers
883
- });
884
- }
885
-
886
- // src/store/memory.ts
887
- function createMemoryStore(options = {}) {
888
- const { cleanupInterval = 6e4 } = options;
889
- const store = /* @__PURE__ */ new Map();
890
- const cleanupTimer = setInterval(() => {
891
- const now = Date.now();
892
- for (const [key, record] of store.entries()) {
893
- if (record.expiresAt < now) {
894
- store.delete(key);
895
- }
896
- }
897
- }, cleanupInterval);
898
- return {
899
- async hasBeenUsed(signature) {
900
- const record = store.get(signature);
901
- if (!record) return false;
902
- if (record.expiresAt < Date.now()) {
903
- store.delete(signature);
904
- return false;
905
- }
906
- return true;
907
- },
908
- async markAsUsed(signature, resourceId, expiresAt) {
909
- store.set(signature, {
910
- resourceId,
911
- usedAt: Date.now(),
912
- expiresAt: expiresAt.getTime()
913
- });
914
- },
915
- async getUsage(signature) {
916
- const record = store.get(signature);
917
- if (!record) return null;
918
- if (record.expiresAt < Date.now()) {
919
- store.delete(signature);
920
- return null;
921
- }
922
- return {
923
- signature,
924
- resourceId: record.resourceId,
925
- usedAt: new Date(record.usedAt),
926
- expiresAt: new Date(record.expiresAt),
927
- walletAddress: record.walletAddress
928
- };
929
- },
930
- /** Stop cleanup timer (for graceful shutdown) */
931
- close() {
932
- clearInterval(cleanupTimer);
933
- store.clear();
934
- }
935
- };
936
- }
937
-
938
- // src/store/redis.ts
939
- function createRedisStore(options) {
940
- const { client, keyPrefix = "micropay:sig:" } = options;
941
- const buildKey = (signature) => `${keyPrefix}${signature}`;
942
- return {
943
- async hasBeenUsed(signature) {
944
- const exists = await client.exists(buildKey(signature));
945
- return exists > 0;
946
- },
947
- async markAsUsed(signature, resourceId, expiresAt) {
948
- const key = buildKey(signature);
949
- const ttl = Math.max(1, Math.floor((expiresAt.getTime() - Date.now()) / 1e3));
950
- const record = {
951
- signature,
952
- resourceId,
953
- usedAt: /* @__PURE__ */ new Date(),
954
- expiresAt
955
- };
956
- if (client.setex) {
957
- await client.setex(key, ttl, JSON.stringify(record));
958
- } else {
959
- await client.set(key, JSON.stringify(record), { EX: ttl });
960
- }
961
- },
962
- async getUsage(signature) {
963
- const data = await client.get(buildKey(signature));
964
- if (!data) return null;
965
- try {
966
- const record = JSON.parse(data);
967
- return {
968
- ...record,
969
- usedAt: new Date(record.usedAt),
970
- expiresAt: new Date(record.expiresAt)
971
- };
972
- } catch {
973
- return null;
974
- }
975
- }
976
- };
977
- }
978
-
979
- // src/middleware/nextjs.ts
980
- function matchesProtectedPath(path, patterns) {
981
- for (const pattern of patterns) {
982
- const regexPattern = pattern.replace(/\*\*/g, "{{DOUBLE_STAR}}").replace(/\*/g, "[^/]*").replace(/{{DOUBLE_STAR}}/g, ".*");
983
- const regex = new RegExp(`^${regexPattern}$`);
984
- if (regex.test(path)) {
985
- return true;
986
- }
987
- }
988
- return false;
989
- }
990
- async function checkPaywallAccess(path, sessionToken, config2) {
991
- if (!matchesProtectedPath(path, config2.protectedPaths)) {
992
- return { allowed: true };
993
- }
994
- if (!sessionToken) {
995
- return {
996
- allowed: false,
997
- reason: "No session token",
998
- requiresPayment: true
999
- };
1000
- }
1001
- const validation = await validateSession(sessionToken, config2.sessionSecret);
1002
- if (!validation.valid || !validation.session) {
1003
- return {
1004
- allowed: false,
1005
- reason: validation.reason || "Invalid session",
1006
- requiresPayment: true
1007
- };
1008
- }
1009
- return {
1010
- allowed: true,
1011
- session: validation.session
1012
- };
1013
- }
1014
- function createPaywallMiddleware(config2) {
1015
- const { cookieName = "x402_session" } = config2;
1016
- return async function middleware(request) {
1017
- const url = new URL(request.url);
1018
- const path = url.pathname;
1019
- const cookieHeader = request.headers.get("cookie") || "";
1020
- const cookies = Object.fromEntries(
1021
- cookieHeader.split(";").map((c) => {
1022
- const [key, ...vals] = c.trim().split("=");
1023
- return [key, vals.join("=")];
1024
- })
1025
- );
1026
- const sessionToken = cookies[cookieName];
1027
- const result = await checkPaywallAccess(path, sessionToken, config2);
1028
- if (!result.allowed && result.requiresPayment) {
1029
- const headers = {
1030
- "Content-Type": "application/json"
1031
- };
1032
- if (config2.paymentRequirement) {
1033
- const requirement = typeof config2.paymentRequirement === "function" ? config2.paymentRequirement(path) : config2.paymentRequirement;
1034
- headers["X-Payment-Required"] = encodePaymentRequirement(requirement);
1035
- }
1036
- const body = config2.custom402Response ? config2.custom402Response(path) : {
1037
- error: "Payment Required",
1038
- message: "This resource requires payment to access",
1039
- x402Version: 1,
1040
- path
1041
- };
1042
- return new Response(JSON.stringify(body), {
1043
- status: 402,
1044
- headers
1045
- });
1046
- }
1047
- return null;
1048
- };
1049
- }
1050
- function withPaywall(handler, options) {
1051
- const { sessionSecret, cookieName = "x402_session", articleId } = options;
1052
- return async function protectedHandler(request) {
1053
- const cookieHeader = request.headers.get("cookie") || "";
1054
- const cookies = Object.fromEntries(
1055
- cookieHeader.split(";").map((c) => {
1056
- const [key, ...vals] = c.trim().split("=");
1057
- return [key, vals.join("=")];
1058
- })
1059
- );
1060
- const sessionToken = cookies[cookieName];
1061
- if (!sessionToken) {
1062
- return new Response(
1063
- JSON.stringify({ error: "Payment Required", message: "No session token" }),
1064
- { status: 402, headers: { "Content-Type": "application/json" } }
1065
- );
1066
- }
1067
- const validation = await validateSession(sessionToken, sessionSecret);
1068
- if (!validation.valid || !validation.session) {
1069
- return new Response(
1070
- JSON.stringify({ error: "Payment Required", message: validation.reason }),
1071
- { status: 402, headers: { "Content-Type": "application/json" } }
1072
- );
1073
- }
1074
- if (articleId) {
1075
- const { session } = validation;
1076
- const hasAccess = session.siteWideUnlock || session.unlockedArticles.includes(articleId);
1077
- if (!hasAccess) {
1078
- return new Response(
1079
- JSON.stringify({ error: "Payment Required", message: "Article not unlocked" }),
1080
- { status: 402, headers: { "Content-Type": "application/json" } }
1081
- );
1082
- }
1083
- }
1084
- return handler(request, validation.session);
1085
- };
1086
- }
9
+ // src/index.ts
1087
10
 
1088
- // src/utils/retry.ts
1089
- function sleep(ms) {
1090
- return new Promise((resolve) => setTimeout(resolve, ms));
1091
- }
1092
- function calculateDelay(attempt, options) {
1093
- const { baseDelay, maxDelay, jitter } = options;
1094
- let delay = baseDelay * Math.pow(2, attempt);
1095
- delay = Math.min(delay, maxDelay);
1096
- if (jitter) {
1097
- const jitterAmount = delay * 0.25;
1098
- delay += Math.random() * jitterAmount * 2 - jitterAmount;
1099
- }
1100
- return Math.floor(delay);
1101
- }
1102
- async function withRetry(fn, options = {}) {
1103
- const {
1104
- maxAttempts = 3,
1105
- baseDelay = 500,
1106
- maxDelay = 1e4,
1107
- jitter = true,
1108
- retryOn = () => true
1109
- } = options;
1110
- let lastError;
1111
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
1112
- try {
1113
- return await fn();
1114
- } catch (error) {
1115
- lastError = error;
1116
- if (!retryOn(error)) {
1117
- throw error;
1118
- }
1119
- if (attempt < maxAttempts - 1) {
1120
- const delay = calculateDelay(attempt, {
1121
- baseDelay,
1122
- maxDelay,
1123
- jitter
1124
- });
1125
- await sleep(delay);
1126
- }
1127
- }
1128
- }
1129
- throw lastError;
1130
- }
1131
- function isRetryableRPCError(error) {
1132
- if (error instanceof Error) {
1133
- const message = error.message.toLowerCase();
1134
- if (message.includes("429") || message.includes("rate limit")) {
1135
- return true;
1136
- }
1137
- if (message.includes("timeout") || message.includes("econnreset")) {
1138
- return true;
1139
- }
1140
- if (message.includes("503") || message.includes("502") || message.includes("500")) {
1141
- return true;
1142
- }
1143
- if (message.includes("blockhash not found") || message.includes("slot skipped")) {
1144
- return true;
1145
- }
1146
- }
1147
- return false;
1148
- }
11
+ // src/client/types.ts
12
+ var TOKEN_MINTS = {
13
+ /** USDC on mainnet */
14
+ USDC_MAINNET: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
15
+ /** USDC on devnet */
16
+ USDC_DEVNET: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
17
+ /** USDT on mainnet */
18
+ USDT_MAINNET: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"
19
+ };
1149
20
 
1150
21
  // src/client/payment.ts
1151
22
  function buildSolanaPayUrl(params) {
@@ -1192,176 +63,95 @@ function createPaymentFlow(config2) {
1192
63
  getFormattedAmount: () => {
1193
64
  const symbol = asset === "native" ? "SOL" : asset === "usdc" ? "USDC" : asset === "usdt" ? "USDT" : "tokens";
1194
65
  return `${naturalAmount.toFixed(decimals > 6 ? 4 : 2)} ${symbol}`;
1195
- },
1196
- /** Generate Solana Pay URL for QR codes */
1197
- getSolanaPayUrl: (options = {}) => {
1198
- return buildSolanaPayUrl({
1199
- recipient: recipientWallet,
1200
- amount: naturalAmount,
1201
- splToken: mintAddress,
1202
- label: options.label,
1203
- reference: options.reference,
1204
- message: memo
1205
- });
1206
- },
1207
- /** Get the token mint address (undefined for native SOL) */
1208
- getMintAddress: () => mintAddress,
1209
- /** Check if this is a native SOL payment */
1210
- isNativePayment: () => asset === "native",
1211
- /** Get network information */
1212
- getNetworkInfo: () => ({
1213
- network,
1214
- isMainnet: network === "mainnet-beta",
1215
- explorerUrl: network === "mainnet-beta" ? "https://explorer.solana.com" : "https://explorer.solana.com?cluster=devnet"
1216
- }),
1217
- /** Build explorer URL for a transaction */
1218
- getExplorerUrl: (signature) => {
1219
- const baseUrl = "https://explorer.solana.com/tx";
1220
- const cluster = network === "mainnet-beta" ? "" : "?cluster=devnet";
1221
- return `${baseUrl}/${signature}${cluster}`;
1222
- }
1223
- };
1224
- }
1225
- function createPaymentReference() {
1226
- if (typeof crypto !== "undefined" && crypto.randomUUID) {
1227
- return crypto.randomUUID();
1228
- }
1229
- return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
1230
- }
1231
-
1232
- // src/pricing/index.ts
1233
- var cachedPrice = null;
1234
- var config = {};
1235
- var lastProviderIndex = -1;
1236
- function configurePricing(newConfig) {
1237
- config = { ...config, ...newConfig };
1238
- cachedPrice = null;
1239
- }
1240
- var PROVIDERS = [
1241
- {
1242
- name: "coincap",
1243
- url: "https://api.coincap.io/v2/assets/solana",
1244
- parse: (data) => parseFloat(data.data?.priceUsd || "0")
1245
- },
1246
- {
1247
- name: "binance",
1248
- url: "https://api.binance.com/api/v3/ticker/price?symbol=SOLUSDT",
1249
- parse: (data) => parseFloat(data.price || "0")
1250
- },
1251
- {
1252
- name: "coingecko",
1253
- url: "https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd",
1254
- parse: (data) => data.solana?.usd || 0
1255
- },
1256
- {
1257
- name: "kraken",
1258
- url: "https://api.kraken.com/0/public/Ticker?pair=SOLUSD",
1259
- parse: (data) => parseFloat(data.result?.SOLUSD?.c?.[0] || "0")
1260
- }
1261
- ];
1262
- async function fetchFromProvider(provider, timeout) {
1263
- const controller = new AbortController();
1264
- const timeoutId = setTimeout(() => controller.abort(), timeout);
1265
- try {
1266
- const response = await fetch(provider.url, {
1267
- headers: { "Accept": "application/json" },
1268
- signal: controller.signal
1269
- });
1270
- if (!response.ok) {
1271
- throw new Error(`HTTP ${response.status}`);
1272
- }
1273
- const data = await response.json();
1274
- const price = provider.parse(data);
1275
- if (!price || price <= 0) {
1276
- throw new Error("Invalid price");
1277
- }
1278
- return price;
1279
- } finally {
1280
- clearTimeout(timeoutId);
1281
- }
1282
- }
1283
- async function getSolPrice() {
1284
- const cacheTTL = config.cacheTTL ?? 6e4;
1285
- const timeout = config.timeout ?? 5e3;
1286
- if (cachedPrice && Date.now() - cachedPrice.fetchedAt.getTime() < cacheTTL) {
1287
- return cachedPrice;
1288
- }
1289
- if (config.customProvider) {
1290
- try {
1291
- const price = await config.customProvider();
1292
- if (price > 0) {
1293
- cachedPrice = {
1294
- solPrice: price,
1295
- fetchedAt: /* @__PURE__ */ new Date(),
1296
- source: "custom"
1297
- };
1298
- return cachedPrice;
1299
- }
1300
- } catch {
1301
- }
1302
- }
1303
- for (let i = 0; i < PROVIDERS.length; i++) {
1304
- const idx = (lastProviderIndex + 1 + i) % PROVIDERS.length;
1305
- const provider = PROVIDERS[idx];
1306
- try {
1307
- const price = await fetchFromProvider(provider, timeout);
1308
- lastProviderIndex = idx;
1309
- cachedPrice = {
1310
- solPrice: price,
1311
- fetchedAt: /* @__PURE__ */ new Date(),
1312
- source: provider.name
1313
- };
1314
- return cachedPrice;
1315
- } catch {
1316
- continue;
66
+ },
67
+ /** Generate Solana Pay URL for QR codes */
68
+ getSolanaPayUrl: (options = {}) => {
69
+ return buildSolanaPayUrl({
70
+ recipient: recipientWallet,
71
+ amount: naturalAmount,
72
+ splToken: mintAddress,
73
+ label: options.label,
74
+ reference: options.reference,
75
+ message: memo
76
+ });
77
+ },
78
+ /** Get the token mint address (undefined for native SOL) */
79
+ getMintAddress: () => mintAddress,
80
+ /** Check if this is a native SOL payment */
81
+ isNativePayment: () => asset === "native",
82
+ /** Get network information */
83
+ getNetworkInfo: () => ({
84
+ network,
85
+ isMainnet: network === "mainnet-beta",
86
+ explorerUrl: network === "mainnet-beta" ? "https://explorer.solana.com" : "https://explorer.solana.com?cluster=devnet"
87
+ }),
88
+ /** Build explorer URL for a transaction */
89
+ getExplorerUrl: (signature) => {
90
+ const baseUrl = "https://explorer.solana.com/tx";
91
+ const cluster = network === "mainnet-beta" ? "" : "?cluster=devnet";
92
+ return `${baseUrl}/${signature}${cluster}`;
1317
93
  }
1318
- }
1319
- if (cachedPrice) {
1320
- return {
1321
- ...cachedPrice,
1322
- source: `${cachedPrice.source} (stale)`
1323
- };
1324
- }
1325
- throw new Error(
1326
- "Failed to fetch SOL price from all providers. Configure a custom provider or ensure network connectivity."
1327
- );
1328
- }
1329
- async function lamportsToUsd(lamports) {
1330
- const { solPrice } = await getSolPrice();
1331
- const sol = Number(lamports) / 1e9;
1332
- return sol * solPrice;
94
+ };
1333
95
  }
1334
- async function usdToLamports(usd) {
1335
- const { solPrice } = await getSolPrice();
1336
- const sol = usd / solPrice;
1337
- return BigInt(Math.floor(sol * 1e9));
96
+ function createPaymentReference() {
97
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
98
+ return crypto.randomUUID();
99
+ }
100
+ return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
1338
101
  }
1339
- async function formatPriceDisplay(lamports) {
1340
- const { solPrice } = await getSolPrice();
1341
- const sol = Number(lamports) / 1e9;
1342
- const usd = sol * solPrice;
1343
- return `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`;
102
+ var DEFAULT_COMPUTE_UNITS = 2e5;
103
+ var DEFAULT_MICRO_LAMPORTS = 1e3;
104
+ function createPriorityFeeInstructions(config2 = {}) {
105
+ const { enabled = false, microLamports, computeUnits } = config2;
106
+ if (!enabled) {
107
+ return [];
108
+ }
109
+ const instructions = [];
110
+ const units = computeUnits ?? DEFAULT_COMPUTE_UNITS;
111
+ instructions.push(ComputeBudgetProgram.setComputeUnitLimit({ units }));
112
+ const price = microLamports ?? DEFAULT_MICRO_LAMPORTS;
113
+ instructions.push(ComputeBudgetProgram.setComputeUnitPrice({ microLamports: price }));
114
+ return instructions;
1344
115
  }
1345
- function formatPriceSync(lamports, solPrice) {
1346
- const sol = Number(lamports) / 1e9;
1347
- const usd = sol * solPrice;
116
+ async function buildVersionedTransaction(config2) {
117
+ const {
118
+ connection,
119
+ payer,
120
+ instructions,
121
+ priorityFee,
122
+ recentBlockhash
123
+ } = config2;
124
+ const priorityIxs = createPriorityFeeInstructions(priorityFee);
125
+ const allInstructions = [...priorityIxs, ...instructions];
126
+ let blockhash;
127
+ let lastValidBlockHeight;
128
+ if (recentBlockhash) {
129
+ blockhash = recentBlockhash;
130
+ const slot = await connection.getSlot();
131
+ lastValidBlockHeight = slot + 150;
132
+ } else {
133
+ const latestBlockhash = await connection.getLatestBlockhash("confirmed");
134
+ blockhash = latestBlockhash.blockhash;
135
+ lastValidBlockHeight = latestBlockhash.lastValidBlockHeight;
136
+ }
137
+ const message = new TransactionMessage({
138
+ payerKey: payer,
139
+ recentBlockhash: blockhash,
140
+ instructions: allInstructions
141
+ }).compileToV0Message([]);
142
+ const transaction = new VersionedTransaction(message);
1348
143
  return {
1349
- sol,
1350
- usd,
1351
- formatted: `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`
144
+ transaction,
145
+ blockhash,
146
+ lastValidBlockHeight
1352
147
  };
1353
148
  }
1354
- function clearPriceCache() {
1355
- cachedPrice = null;
1356
- lastProviderIndex = -1;
1357
- }
1358
- function getProviders() {
1359
- return PROVIDERS.map((p) => ({ name: p.name, url: p.url }));
1360
- }
1361
- var WALLET_REGEX4 = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
1362
- function isValidWalletAddress2(address) {
149
+
150
+ // src/agent/agentPayment.ts
151
+ var WALLET_REGEX = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
152
+ function isValidWalletAddress(address) {
1363
153
  if (!address || typeof address !== "string") return false;
1364
- return WALLET_REGEX4.test(address);
154
+ return WALLET_REGEX.test(address);
1365
155
  }
1366
156
  async function executeAgentPayment(params) {
1367
157
  const {
@@ -1372,7 +162,7 @@ async function executeAgentPayment(params) {
1372
162
  priorityFee,
1373
163
  confirmationTimeout = 6e4
1374
164
  } = params;
1375
- if (!isValidWalletAddress2(recipientAddress)) {
165
+ if (!isValidWalletAddress(recipientAddress)) {
1376
166
  return {
1377
167
  success: false,
1378
168
  error: "Invalid recipient address format"
@@ -1479,23 +269,23 @@ function generateAgentKeypair() {
1479
269
  };
1480
270
  }
1481
271
  var MAX_CREDITS = 1e3;
1482
- var MIN_SECRET_LENGTH2 = 32;
1483
- function getSecretKey2(secret) {
272
+ var MIN_SECRET_LENGTH = 32;
273
+ function getSecretKey(secret) {
1484
274
  if (!secret || typeof secret !== "string") {
1485
275
  throw new Error("Session secret is required");
1486
276
  }
1487
- if (secret.length < MIN_SECRET_LENGTH2) {
1488
- throw new Error(`Session secret must be at least ${MIN_SECRET_LENGTH2} characters`);
277
+ if (secret.length < MIN_SECRET_LENGTH) {
278
+ throw new Error(`Session secret must be at least ${MIN_SECRET_LENGTH} characters`);
1489
279
  }
1490
280
  return new TextEncoder().encode(secret);
1491
281
  }
1492
- function validateWalletAddress2(address) {
282
+ function validateWalletAddress(address) {
1493
283
  if (!address || typeof address !== "string") return false;
1494
284
  const base58Regex = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
1495
285
  return base58Regex.test(address);
1496
286
  }
1497
287
  async function createCreditSession(walletAddress, purchaseId, config2) {
1498
- if (!validateWalletAddress2(walletAddress)) {
288
+ if (!validateWalletAddress(walletAddress)) {
1499
289
  throw new Error("Invalid wallet address format");
1500
290
  }
1501
291
  if (config2.initialCredits <= 0 || config2.initialCredits > MAX_CREDITS) {
@@ -1530,7 +320,7 @@ async function createCreditSession(walletAddress, purchaseId, config2) {
1530
320
  iat: now,
1531
321
  exp: expiresAt
1532
322
  };
1533
- const token = await new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime(`${config2.durationHours}h`).sign(getSecretKey2(config2.secret));
323
+ const token = await new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime(`${config2.durationHours}h`).sign(getSecretKey(config2.secret));
1534
324
  return { token, session };
1535
325
  }
1536
326
  async function validateCreditSession(token, secret) {
@@ -1538,7 +328,7 @@ async function validateCreditSession(token, secret) {
1538
328
  return { valid: false, reason: "Invalid token format" };
1539
329
  }
1540
330
  try {
1541
- const { payload } = await jwtVerify(token, getSecretKey2(secret));
331
+ const { payload } = await jwtVerify(token, getSecretKey(secret));
1542
332
  const creditPayload = payload;
1543
333
  if (!creditPayload.sub || !creditPayload.sid || !creditPayload.exp) {
1544
334
  return { valid: false, reason: "Malformed session payload" };
@@ -1550,7 +340,7 @@ async function validateCreditSession(token, secret) {
1550
340
  if (creditPayload.bundleExpiry && creditPayload.bundleExpiry < now) {
1551
341
  return { valid: false, reason: "Bundle expired" };
1552
342
  }
1553
- if (!validateWalletAddress2(creditPayload.sub)) {
343
+ if (!validateWalletAddress(creditPayload.sub)) {
1554
344
  return { valid: false, reason: "Invalid session data" };
1555
345
  }
1556
346
  const session = {
@@ -1601,7 +391,7 @@ async function useCredit(token, secret, creditsToUse = 1) {
1601
391
  iat: session.createdAt,
1602
392
  exp: session.expiresAt
1603
393
  };
1604
- const newToken = await new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).sign(getSecretKey2(secret));
394
+ const newToken = await new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).sign(getSecretKey(secret));
1605
395
  return {
1606
396
  success: true,
1607
397
  remainingCredits: newCredits,
@@ -1629,7 +419,7 @@ async function addCredits(token, secret, creditsToAdd) {
1629
419
  iat: session.createdAt,
1630
420
  exp: session.expiresAt
1631
421
  };
1632
- const newToken = await new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).sign(getSecretKey2(secret));
422
+ const newToken = await new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).sign(getSecretKey(secret));
1633
423
  return {
1634
424
  success: true,
1635
425
  newToken,
@@ -1648,6 +438,136 @@ async function getRemainingCredits(token, secret) {
1648
438
  };
1649
439
  }
1650
440
 
1651
- export { TOKEN_MINTS, X402_HEADERS, addArticleToSession, addCredits, buildPaymentRequirement, buildSolanaPayUrl, buildVersionedTransaction, calculatePriorityFeeCost, checkPaywallAccess, clearPriceCache, configurePricing, create402Headers, create402Response, create402ResponseBody, createCreditSession, createMemoryStore, createPaymentFlow, createPaymentReference, createPaywallMiddleware, createPriorityFeeInstructions, createRedisStore, createSession, decodePaymentRequired, encodePaymentRequired, encodePaymentRequirement, encodePaymentResponse, estimatePriorityFee, executeAgentPayment, fetchLookupTables, formatPriceDisplay, formatPriceSync, generateAgentKeypair, getAgentBalance, getConnection, getConnectionWithFallback, getProviders, getRemainingCredits, getSolPrice, getTokenDecimals, getWalletTransactions, hasAgentSufficientBalance, isArticleUnlocked, isMainnet, isNativeAsset, isRetryableRPCError, isVersionedTransaction, keypairFromBase58, lamportsToSol, lamportsToUsd, parsePaymentHeader, resetConnection, resolveMintAddress, solToLamports, toX402Network, usdToLamports, useCredit, validateCreditSession, validateSession, verifyPayment, verifySPLPayment, verifyX402Payment, waitForConfirmation, withFallback, withPaywall, withRetry };
441
+ // src/pricing/index.ts
442
+ var cachedPrice = null;
443
+ var config = {};
444
+ var lastProviderIndex = -1;
445
+ function configurePricing(newConfig) {
446
+ config = { ...config, ...newConfig };
447
+ cachedPrice = null;
448
+ }
449
+ var PROVIDERS = [
450
+ {
451
+ name: "coincap",
452
+ url: "https://api.coincap.io/v2/assets/solana",
453
+ parse: (data) => parseFloat(data.data?.priceUsd || "0")
454
+ },
455
+ {
456
+ name: "binance",
457
+ url: "https://api.binance.com/api/v3/ticker/price?symbol=SOLUSDT",
458
+ parse: (data) => parseFloat(data.price || "0")
459
+ },
460
+ {
461
+ name: "coingecko",
462
+ url: "https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd",
463
+ parse: (data) => data.solana?.usd || 0
464
+ },
465
+ {
466
+ name: "kraken",
467
+ url: "https://api.kraken.com/0/public/Ticker?pair=SOLUSD",
468
+ parse: (data) => parseFloat(data.result?.SOLUSD?.c?.[0] || "0")
469
+ }
470
+ ];
471
+ async function fetchFromProvider(provider, timeout) {
472
+ const controller = new AbortController();
473
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
474
+ try {
475
+ const response = await fetch(provider.url, {
476
+ headers: { "Accept": "application/json" },
477
+ signal: controller.signal
478
+ });
479
+ if (!response.ok) {
480
+ throw new Error(`HTTP ${response.status}`);
481
+ }
482
+ const data = await response.json();
483
+ const price = provider.parse(data);
484
+ if (!price || price <= 0) {
485
+ throw new Error("Invalid price");
486
+ }
487
+ return price;
488
+ } finally {
489
+ clearTimeout(timeoutId);
490
+ }
491
+ }
492
+ async function getSolPrice() {
493
+ const cacheTTL = config.cacheTTL ?? 6e4;
494
+ const timeout = config.timeout ?? 5e3;
495
+ if (cachedPrice && Date.now() - cachedPrice.fetchedAt.getTime() < cacheTTL) {
496
+ return cachedPrice;
497
+ }
498
+ if (config.customProvider) {
499
+ try {
500
+ const price = await config.customProvider();
501
+ if (price > 0) {
502
+ cachedPrice = {
503
+ solPrice: price,
504
+ fetchedAt: /* @__PURE__ */ new Date(),
505
+ source: "custom"
506
+ };
507
+ return cachedPrice;
508
+ }
509
+ } catch {
510
+ }
511
+ }
512
+ for (let i = 0; i < PROVIDERS.length; i++) {
513
+ const idx = (lastProviderIndex + 1 + i) % PROVIDERS.length;
514
+ const provider = PROVIDERS[idx];
515
+ try {
516
+ const price = await fetchFromProvider(provider, timeout);
517
+ lastProviderIndex = idx;
518
+ cachedPrice = {
519
+ solPrice: price,
520
+ fetchedAt: /* @__PURE__ */ new Date(),
521
+ source: provider.name
522
+ };
523
+ return cachedPrice;
524
+ } catch {
525
+ continue;
526
+ }
527
+ }
528
+ if (cachedPrice) {
529
+ return {
530
+ ...cachedPrice,
531
+ source: `${cachedPrice.source} (stale)`
532
+ };
533
+ }
534
+ throw new Error(
535
+ "Failed to fetch SOL price from all providers. Configure a custom provider or ensure network connectivity."
536
+ );
537
+ }
538
+ async function lamportsToUsd(lamports) {
539
+ const { solPrice } = await getSolPrice();
540
+ const sol = Number(lamports) / 1e9;
541
+ return sol * solPrice;
542
+ }
543
+ async function usdToLamports(usd) {
544
+ const { solPrice } = await getSolPrice();
545
+ const sol = usd / solPrice;
546
+ return BigInt(Math.floor(sol * 1e9));
547
+ }
548
+ async function formatPriceDisplay(lamports) {
549
+ const { solPrice } = await getSolPrice();
550
+ const sol = Number(lamports) / 1e9;
551
+ const usd = sol * solPrice;
552
+ return `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`;
553
+ }
554
+ function formatPriceSync(lamports, solPrice) {
555
+ const sol = Number(lamports) / 1e9;
556
+ const usd = sol * solPrice;
557
+ return {
558
+ sol,
559
+ usd,
560
+ formatted: `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`
561
+ };
562
+ }
563
+ function clearPriceCache() {
564
+ cachedPrice = null;
565
+ lastProviderIndex = -1;
566
+ }
567
+ function getProviders() {
568
+ return PROVIDERS.map((p) => ({ name: p.name, url: p.url }));
569
+ }
570
+
571
+ export { addCredits, buildSolanaPayUrl, clearPriceCache, configurePricing, createCreditSession, createPaymentFlow, createPaymentReference, executeAgentPayment, formatPriceDisplay, formatPriceSync, generateAgentKeypair, getAgentBalance, getProviders, getRemainingCredits, getSolPrice, hasAgentSufficientBalance, keypairFromBase58, lamportsToUsd, usdToLamports, useCredit, validateCreditSession };
1652
572
  //# sourceMappingURL=index.js.map
1653
573
  //# sourceMappingURL=index.js.map