@alleyboss/micropay-solana-x402-paywall 2.0.2 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -21,10 +21,13 @@ npm install @alleyboss/micropay-solana-x402-paywall @solana/web3.js
21
21
  | 💰 **SOL & USDC Payments** | Native SOL and SPL tokens (USDC, USDT) |
22
22
  | 🔐 **x402 Protocol** | HTTP 402 Payment Required standard |
23
23
  | 🔑 **JWT Sessions** | Secure unlock tracking with anti-replay |
24
- | �️ **Signature Store** | Prevent double-spend at app layer |
24
+ | 🛡️ **Signature Store** | Prevent double-spend at app layer |
25
25
  | 🔌 **Express & Next.js** | Zero-boilerplate middleware |
26
- | **Price Conversion** | USD↔SOL with multi-provider fallback |
26
+ | 💵 **Price Conversion** | USD↔SOL with multi-provider fallback |
27
27
  | 🌳 **Tree-Shakeable** | Import only what you need |
28
+ | 🔄 **RPC Fallback** | Automatic failover on RPC errors |
29
+ | ⚡ **Priority Fees** | Land transactions faster |
30
+ | 📦 **Versioned Tx** | Full v0 transaction support |
28
31
 
29
32
  ## 📦 Quick Example
30
33
 
@@ -79,20 +82,44 @@ import { getSolPrice, formatPriceDisplay, configurePricing } from '@alleyboss/mi
79
82
  import { withRetry } from '@alleyboss/micropay-solana-x402-paywall/utils';
80
83
  ```
81
84
 
82
- ## 🔥 New in v2.0
85
+ ## 🔥 New in v2.1.0
83
86
 
84
- - **SPL Token Support** — USDC, USDT, custom tokens
85
- - **Multi-Provider Pricing** — CoinCap Binance CoinGecko Kraken fallback
86
- - **Custom Price API** — `configurePricing({ customProvider: yourFn })`
87
- - **Express Middleware** — Works with Express, Fastify, Polka
88
- - **Signature Store** — Memory & Redis adapters for anti-replay
89
- - **Client Helpers** — Solana Pay URLs for QR codes
87
+ - **RPC Fallback Support** — Automatic failover on primary RPC failure (configurable, default: off)
88
+ - **Priority Fees** — Compute budget instructions for landing transactions faster (configurable, default: off)
89
+ - **Versioned Transactions** — Full v0 transaction support with lookup tables
90
+ - **TDD Test Suite** — Comprehensive tests with vitest (must pass before npm publish)
90
91
 
91
- ## 📚 Documentation
92
+ ```typescript
93
+ // RPC Fallback configuration
94
+ const config = {
95
+ network: 'mainnet-beta',
96
+ rpcUrl: 'https://primary-rpc.com',
97
+ enableFallback: true, // default: false
98
+ fallbackRpcUrls: [
99
+ 'https://fallback1.com',
100
+ 'https://fallback2.com',
101
+ ],
102
+ };
92
103
 
93
- **Full documentation, API reference, and examples:**
104
+ // Priority fees
105
+ import { createPriorityFeeInstructions, estimatePriorityFee } from '@alleyboss/micropay-solana-x402-paywall/solana';
94
106
 
95
- 👉 **[solana-x402-paywall.vercel.app/docs](https://solana-x402-paywall.vercel.app/docs)**
107
+ const instructions = createPriorityFeeInstructions({
108
+ enabled: true,
109
+ microLamports: 5000,
110
+ computeUnits: 200_000,
111
+ });
112
+
113
+ // Versioned transactions
114
+ import { buildVersionedTransaction } from '@alleyboss/micropay-solana-x402-paywall/solana';
115
+
116
+ const { transaction } = await buildVersionedTransaction({
117
+ connection,
118
+ payer: wallet.publicKey,
119
+ instructions: [transferIx],
120
+ priorityFee: { enabled: true },
121
+ });
122
+ ```
96
123
 
97
124
  ## 🛠️ RPC Providers
98
125
 
@@ -105,9 +132,19 @@ const config = {
105
132
  tatumApiKey: 'your-key',
106
133
  // Or custom (Helius, QuickNode, etc.)
107
134
  rpcUrl: 'https://your-rpc.com',
135
+ // Optional: enable fallback
136
+ enableFallback: true,
137
+ fallbackRpcUrls: ['https://backup.rpc.com'],
108
138
  };
109
139
  ```
110
140
 
141
+ ## 📚 Documentation
142
+
143
+ **Full documentation, API reference, and examples:**
144
+
145
+ 👉 **[solana-x402-paywall.vercel.app/docs](https://solana-x402-paywall.vercel.app/docs)**
146
+
111
147
  ## 📄 License
112
148
 
113
149
  MIT © AlleyBoss
150
+
@@ -0,0 +1,63 @@
1
+ import { Connection } from '@solana/web3.js';
2
+ import { S as SolanaNetwork } from './payment-CTxdtqmc.cjs';
3
+
4
+ /** Configuration for Solana client */
5
+ interface SolanaClientConfig {
6
+ /** Network to connect to */
7
+ network: SolanaNetwork;
8
+ /** Custom RPC URL (optional) */
9
+ rpcUrl?: string;
10
+ /** Tatum.io API key for RPC (optional) */
11
+ tatumApiKey?: string;
12
+ /** Enable RPC fallback on errors (default: false) */
13
+ enableFallback?: boolean;
14
+ /** Fallback RPC URLs to try on primary failure (optional) */
15
+ fallbackRpcUrls?: string[];
16
+ }
17
+ /** RPC connection with fallback support */
18
+ interface RpcConnectionWithFallback {
19
+ /** Primary connection */
20
+ connection: Connection;
21
+ /** Fallback connections (if configured) */
22
+ fallbacks: Connection[];
23
+ /** Whether fallback is enabled */
24
+ fallbackEnabled: boolean;
25
+ }
26
+ /**
27
+ * Get or create a Solana connection
28
+ * Uses singleton pattern with network-aware caching
29
+ */
30
+ declare function getConnection(config: SolanaClientConfig): Connection;
31
+ /**
32
+ * Get connection with fallback support
33
+ * Returns both primary and fallback connections for manual failover
34
+ */
35
+ declare function getConnectionWithFallback(config: SolanaClientConfig): RpcConnectionWithFallback;
36
+ /**
37
+ * Execute an RPC call with automatic fallback on failure
38
+ * Only used when fallback is enabled in config
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * const balance = await withFallback(
43
+ * config,
44
+ * (conn) => conn.getBalance(publicKey)
45
+ * );
46
+ * ```
47
+ */
48
+ declare function withFallback<T>(config: SolanaClientConfig, operation: (connection: Connection) => Promise<T>): Promise<T>;
49
+ /**
50
+ * Reset the cached connection
51
+ * Useful for testing or network switching
52
+ */
53
+ declare function resetConnection(): void;
54
+ /**
55
+ * Check if network is mainnet
56
+ */
57
+ declare function isMainnet(network: SolanaNetwork): boolean;
58
+ /**
59
+ * Convert Solana network to x402 network identifier
60
+ */
61
+ declare function toX402Network(network: SolanaNetwork): 'solana-devnet' | 'solana-mainnet';
62
+
63
+ export { type RpcConnectionWithFallback as R, type SolanaClientConfig as S, getConnectionWithFallback as a, getConnection as g, isMainnet as i, resetConnection as r, toX402Network as t, withFallback as w };
@@ -0,0 +1,63 @@
1
+ import { Connection } from '@solana/web3.js';
2
+ import { S as SolanaNetwork } from './payment-CTxdtqmc.js';
3
+
4
+ /** Configuration for Solana client */
5
+ interface SolanaClientConfig {
6
+ /** Network to connect to */
7
+ network: SolanaNetwork;
8
+ /** Custom RPC URL (optional) */
9
+ rpcUrl?: string;
10
+ /** Tatum.io API key for RPC (optional) */
11
+ tatumApiKey?: string;
12
+ /** Enable RPC fallback on errors (default: false) */
13
+ enableFallback?: boolean;
14
+ /** Fallback RPC URLs to try on primary failure (optional) */
15
+ fallbackRpcUrls?: string[];
16
+ }
17
+ /** RPC connection with fallback support */
18
+ interface RpcConnectionWithFallback {
19
+ /** Primary connection */
20
+ connection: Connection;
21
+ /** Fallback connections (if configured) */
22
+ fallbacks: Connection[];
23
+ /** Whether fallback is enabled */
24
+ fallbackEnabled: boolean;
25
+ }
26
+ /**
27
+ * Get or create a Solana connection
28
+ * Uses singleton pattern with network-aware caching
29
+ */
30
+ declare function getConnection(config: SolanaClientConfig): Connection;
31
+ /**
32
+ * Get connection with fallback support
33
+ * Returns both primary and fallback connections for manual failover
34
+ */
35
+ declare function getConnectionWithFallback(config: SolanaClientConfig): RpcConnectionWithFallback;
36
+ /**
37
+ * Execute an RPC call with automatic fallback on failure
38
+ * Only used when fallback is enabled in config
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * const balance = await withFallback(
43
+ * config,
44
+ * (conn) => conn.getBalance(publicKey)
45
+ * );
46
+ * ```
47
+ */
48
+ declare function withFallback<T>(config: SolanaClientConfig, operation: (connection: Connection) => Promise<T>): Promise<T>;
49
+ /**
50
+ * Reset the cached connection
51
+ * Useful for testing or network switching
52
+ */
53
+ declare function resetConnection(): void;
54
+ /**
55
+ * Check if network is mainnet
56
+ */
57
+ declare function isMainnet(network: SolanaNetwork): boolean;
58
+ /**
59
+ * Convert Solana network to x402 network identifier
60
+ */
61
+ declare function toX402Network(network: SolanaNetwork): 'solana-devnet' | 'solana-mainnet';
62
+
63
+ export { type RpcConnectionWithFallback as R, type SolanaClientConfig as S, getConnectionWithFallback as a, getConnection as g, isMainnet as i, resetConnection as r, toX402Network as t, withFallback as w };
package/dist/index.cjs CHANGED
@@ -15,6 +15,8 @@ var TOKEN_MINTS = {
15
15
  };
16
16
  var cachedConnection = null;
17
17
  var cachedNetwork = null;
18
+ var cachedFallbacks = [];
19
+ var cachedFallbackEnabled = false;
18
20
  function buildRpcUrl(config2) {
19
21
  const { network, rpcUrl, tatumApiKey } = config2;
20
22
  if (rpcUrl) {
@@ -29,19 +31,65 @@ function buildRpcUrl(config2) {
29
31
  }
30
32
  return web3_js.clusterApiUrl(network);
31
33
  }
34
+ function createConnection(rpcUrl) {
35
+ return new web3_js.Connection(rpcUrl, {
36
+ commitment: "confirmed",
37
+ confirmTransactionInitialTimeout: 6e4
38
+ });
39
+ }
32
40
  function getConnection(config2) {
33
41
  const { network } = config2;
34
42
  if (cachedConnection && cachedNetwork === network) {
35
43
  return cachedConnection;
36
44
  }
37
45
  const rpcUrl = buildRpcUrl(config2);
38
- cachedConnection = new web3_js.Connection(rpcUrl, {
39
- commitment: "confirmed",
40
- confirmTransactionInitialTimeout: 6e4
41
- });
46
+ cachedConnection = createConnection(rpcUrl);
42
47
  cachedNetwork = network;
48
+ cachedFallbackEnabled = config2.enableFallback ?? false;
49
+ cachedFallbacks = [];
50
+ if (cachedFallbackEnabled && config2.fallbackRpcUrls?.length) {
51
+ cachedFallbacks = config2.fallbackRpcUrls.map(createConnection);
52
+ }
43
53
  return cachedConnection;
44
54
  }
55
+ function getConnectionWithFallback(config2) {
56
+ const connection = getConnection(config2);
57
+ return {
58
+ connection,
59
+ fallbacks: cachedFallbacks,
60
+ fallbackEnabled: cachedFallbackEnabled
61
+ };
62
+ }
63
+ async function withFallback(config2, operation) {
64
+ const { connection, fallbacks, fallbackEnabled } = getConnectionWithFallback(config2);
65
+ try {
66
+ return await operation(connection);
67
+ } catch (error) {
68
+ if (!fallbackEnabled || fallbacks.length === 0) {
69
+ throw error;
70
+ }
71
+ if (!isRetryableError(error)) {
72
+ throw error;
73
+ }
74
+ for (let i = 0; i < fallbacks.length; i++) {
75
+ try {
76
+ return await operation(fallbacks[i]);
77
+ } catch (fallbackError) {
78
+ if (i === fallbacks.length - 1) {
79
+ throw fallbackError;
80
+ }
81
+ }
82
+ }
83
+ throw error;
84
+ }
85
+ }
86
+ function isRetryableError(error) {
87
+ if (error instanceof Error) {
88
+ const message = error.message.toLowerCase();
89
+ return message.includes("429") || message.includes("503") || message.includes("502") || message.includes("timeout") || message.includes("econnrefused") || message.includes("enotfound") || message.includes("rate limit");
90
+ }
91
+ return false;
92
+ }
45
93
  function resetConnection() {
46
94
  cachedConnection = null;
47
95
  cachedNetwork = null;
@@ -398,6 +446,88 @@ async function verifySPLPayment(params) {
398
446
  function isNativeAsset(asset) {
399
447
  return asset === "native";
400
448
  }
449
+ var DEFAULT_COMPUTE_UNITS = 2e5;
450
+ var DEFAULT_MICRO_LAMPORTS = 1e3;
451
+ function createPriorityFeeInstructions(config2 = {}) {
452
+ const { enabled = false, microLamports, computeUnits } = config2;
453
+ if (!enabled) {
454
+ return [];
455
+ }
456
+ const instructions = [];
457
+ const units = computeUnits ?? DEFAULT_COMPUTE_UNITS;
458
+ instructions.push(web3_js.ComputeBudgetProgram.setComputeUnitLimit({ units }));
459
+ const price = microLamports ?? DEFAULT_MICRO_LAMPORTS;
460
+ instructions.push(web3_js.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: price }));
461
+ return instructions;
462
+ }
463
+ async function estimatePriorityFee(connection, accounts = []) {
464
+ try {
465
+ const fees = await connection.getRecentPrioritizationFees({
466
+ lockedWritableAccounts: accounts
467
+ });
468
+ if (fees.length === 0) {
469
+ return DEFAULT_MICRO_LAMPORTS;
470
+ }
471
+ const sortedFees = fees.map((f) => f.prioritizationFee).filter((f) => f > 0).sort((a, b) => a - b);
472
+ if (sortedFees.length === 0) {
473
+ return DEFAULT_MICRO_LAMPORTS;
474
+ }
475
+ const medianIndex = Math.floor(sortedFees.length / 2);
476
+ return sortedFees[medianIndex];
477
+ } catch {
478
+ return DEFAULT_MICRO_LAMPORTS;
479
+ }
480
+ }
481
+ function calculatePriorityFeeCost(microLamportsPerCU, computeUnits) {
482
+ return Math.ceil(microLamportsPerCU * computeUnits / 1e6);
483
+ }
484
+ async function buildVersionedTransaction(config2) {
485
+ const {
486
+ connection,
487
+ payer,
488
+ instructions,
489
+ lookupTables = [],
490
+ priorityFee,
491
+ recentBlockhash
492
+ } = config2;
493
+ const priorityIxs = createPriorityFeeInstructions(priorityFee);
494
+ const allInstructions = [...priorityIxs, ...instructions];
495
+ let blockhash;
496
+ let lastValidBlockHeight;
497
+ if (recentBlockhash) {
498
+ blockhash = recentBlockhash;
499
+ const slot = await connection.getSlot();
500
+ lastValidBlockHeight = slot + 150;
501
+ } else {
502
+ const latestBlockhash = await connection.getLatestBlockhash("confirmed");
503
+ blockhash = latestBlockhash.blockhash;
504
+ lastValidBlockHeight = latestBlockhash.lastValidBlockHeight;
505
+ }
506
+ const message = new web3_js.TransactionMessage({
507
+ payerKey: payer,
508
+ recentBlockhash: blockhash,
509
+ instructions: allInstructions
510
+ }).compileToV0Message(lookupTables);
511
+ const transaction = new web3_js.VersionedTransaction(message);
512
+ return {
513
+ transaction,
514
+ blockhash,
515
+ lastValidBlockHeight
516
+ };
517
+ }
518
+ async function fetchLookupTables(connection, addresses) {
519
+ const tables = [];
520
+ for (const address of addresses) {
521
+ const result = await connection.getAddressLookupTable(address);
522
+ if (result.value) {
523
+ tables.push(result.value);
524
+ }
525
+ }
526
+ return tables;
527
+ }
528
+ function isVersionedTransaction(tx) {
529
+ return tx instanceof web3_js.VersionedTransaction;
530
+ }
401
531
  var MAX_ARTICLES_PER_SESSION = 100;
402
532
  var MIN_SECRET_LENGTH = 32;
403
533
  function getSecretKey(secret) {
@@ -1177,6 +1307,8 @@ exports.X402_HEADERS = X402_HEADERS;
1177
1307
  exports.addArticleToSession = addArticleToSession;
1178
1308
  exports.buildPaymentRequirement = buildPaymentRequirement;
1179
1309
  exports.buildSolanaPayUrl = buildSolanaPayUrl;
1310
+ exports.buildVersionedTransaction = buildVersionedTransaction;
1311
+ exports.calculatePriorityFeeCost = calculatePriorityFeeCost;
1180
1312
  exports.checkPaywallAccess = checkPaywallAccess;
1181
1313
  exports.clearPriceCache = clearPriceCache;
1182
1314
  exports.configurePricing = configurePricing;
@@ -1186,14 +1318,18 @@ exports.createMemoryStore = createMemoryStore;
1186
1318
  exports.createPaymentFlow = createPaymentFlow;
1187
1319
  exports.createPaymentReference = createPaymentReference;
1188
1320
  exports.createPaywallMiddleware = createPaywallMiddleware;
1321
+ exports.createPriorityFeeInstructions = createPriorityFeeInstructions;
1189
1322
  exports.createRedisStore = createRedisStore;
1190
1323
  exports.createSession = createSession;
1191
1324
  exports.decodePaymentRequired = decodePaymentRequired;
1192
1325
  exports.encodePaymentRequired = encodePaymentRequired;
1193
1326
  exports.encodePaymentResponse = encodePaymentResponse;
1327
+ exports.estimatePriorityFee = estimatePriorityFee;
1328
+ exports.fetchLookupTables = fetchLookupTables;
1194
1329
  exports.formatPriceDisplay = formatPriceDisplay;
1195
1330
  exports.formatPriceSync = formatPriceSync;
1196
1331
  exports.getConnection = getConnection;
1332
+ exports.getConnectionWithFallback = getConnectionWithFallback;
1197
1333
  exports.getProviders = getProviders;
1198
1334
  exports.getSolPrice = getSolPrice;
1199
1335
  exports.getTokenDecimals = getTokenDecimals;
@@ -1202,6 +1338,7 @@ exports.isArticleUnlocked = isArticleUnlocked;
1202
1338
  exports.isMainnet = isMainnet;
1203
1339
  exports.isNativeAsset = isNativeAsset;
1204
1340
  exports.isRetryableRPCError = isRetryableRPCError;
1341
+ exports.isVersionedTransaction = isVersionedTransaction;
1205
1342
  exports.lamportsToSol = lamportsToSol;
1206
1343
  exports.lamportsToUsd = lamportsToUsd;
1207
1344
  exports.parsePaymentHeader = parsePaymentHeader;
@@ -1215,6 +1352,7 @@ exports.verifyPayment = verifyPayment;
1215
1352
  exports.verifySPLPayment = verifySPLPayment;
1216
1353
  exports.verifyX402Payment = verifyX402Payment;
1217
1354
  exports.waitForConfirmation = waitForConfirmation;
1355
+ exports.withFallback = withFallback;
1218
1356
  exports.withPaywall = withPaywall;
1219
1357
  exports.withRetry = withRetry;
1220
1358
  //# sourceMappingURL=index.cjs.map