@alleyboss/micropay-solana-x402-paywall 3.2.2 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -20,20 +20,48 @@ var LocalSvmFacilitator = class {
20
20
  * Get supported payment kinds
21
21
  * Mocking the response of the /supported endpoint
22
22
  */
23
- // Network Constants - CAIP-2 format for x402 v2
23
+ /**
24
+ * Network Constants - CAIP-2 format for x402 v2
25
+ * These match the official Solana chain IDs used by x402 protocol
26
+ */
24
27
  NETWORKS = {
25
28
  DEVNET: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
26
29
  MAINNET: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"
27
30
  };
28
- // Dummy fee payer address (System Program) - not used in local verification
29
- // but required by x402 protocol for supported kinds
31
+ /**
32
+ * DUMMY_FEE_PAYER Explanation (for auditors):
33
+ *
34
+ * In a HOSTED facilitator (like x402.org), the `feePayer` field in the
35
+ * SupportedResponse.extra specifies which address will pay transaction fees
36
+ * when the facilitator submits transactions on behalf of users.
37
+ *
38
+ * In LOCAL/SELF-SOVEREIGN mode (this implementation):
39
+ * - The USER pays their own transaction fees directly
40
+ * - The facilitator NEVER submits transactions - it only VERIFIES them
41
+ * - Therefore, no fee payer address is actually used
42
+ *
43
+ * However, the x402 protocol REQUIRES this field in the response schema.
44
+ * We use the System Program address (all 1s) as a placeholder because:
45
+ * 1. It's clearly not a real wallet (obvious placeholder)
46
+ * 2. It cannot receive funds or sign transactions
47
+ * 3. It signals to developers that fee paying is handled differently
48
+ *
49
+ * SECURITY NOTE: This is NOT a security risk because:
50
+ * - The fee payer is never used in verify() or settle() methods
51
+ * - Users sign and pay for their own transactions
52
+ * - The address is just a protocol-required placeholder
53
+ *
54
+ * @see https://docs.x402.org for protocol specification
55
+ */
30
56
  DUMMY_FEE_PAYER = "11111111111111111111111111111111";
31
57
  /**
32
58
  * Get supported payment kinds
33
59
  * Returns x402 v2 compatible response with CAIP-2 network identifiers
60
+ *
61
+ * NOTE: The feePayer in extra.feePayer is a placeholder only.
62
+ * In self-sovereign mode, users pay their own transaction fees.
34
63
  */
35
64
  async getSupported(_extensionKeys = []) {
36
- console.log("[LocalSvmFacilitator] getSupported called");
37
65
  const supported = {
38
66
  kinds: [
39
67
  {
@@ -51,10 +79,10 @@ var LocalSvmFacilitator = class {
51
79
  ],
52
80
  extensions: [],
53
81
  signers: {
82
+ // Placeholder - in self-sovereign mode, users are their own signers
54
83
  "solana:*": [this.DUMMY_FEE_PAYER]
55
84
  }
56
85
  };
57
- console.log("[LocalSvmFacilitator] Returning supported:", JSON.stringify(supported));
58
86
  return supported;
59
87
  }
60
88
  /**
@@ -69,6 +97,10 @@ var LocalSvmFacilitator = class {
69
97
  getSigners(_network) {
70
98
  return [];
71
99
  }
100
+ /**
101
+ * Enable debug logging (disable in production)
102
+ */
103
+ debug = process.env.NODE_ENV === "development";
72
104
  /**
73
105
  * Verify a payment on-chain
74
106
  */
@@ -81,57 +113,89 @@ var LocalSvmFacilitator = class {
81
113
  const payTo = requirements.payTo;
82
114
  const amountVal = requirements.amount || requirements.maxAmountRequired || "0";
83
115
  const requiredAmount = BigInt(amountVal);
84
- console.log(`[LocalSvmFacilitator] Verifying signature: ${signature}`);
85
- console.log(`[LocalSvmFacilitator] Requirements - Amount: ${requiredAmount}, PayTo: ${payTo}`);
86
- console.log(`[LocalSvmFacilitator] Full Requirements:`, JSON.stringify(requirements));
87
- const tx = await this.connection.getParsedTransaction(signature, {
88
- maxSupportedTransactionVersion: 0,
89
- commitment: "confirmed"
90
- });
116
+ if (this.debug) {
117
+ console.log(`[LocalSvmFacilitator] Verifying tx: ${signature.slice(0, 8)}...`);
118
+ }
119
+ const tx = await this.fetchTransactionWithRetry(signature, 3);
91
120
  if (!tx) {
92
- console.error("[LocalSvmFacilitator] Transaction not found or not confirmed");
93
121
  return { isValid: false, invalidReason: "Transaction not found or not confirmed" };
94
122
  }
95
- console.log("[LocalSvmFacilitator] Transaction found. Parsing instructions...");
96
123
  const instructions = tx.transaction.message.instructions;
97
124
  let paidAmount = 0n;
98
125
  let payer = void 0;
99
126
  for (const ix of instructions) {
100
127
  if ("program" in ix && ix.program === "system") {
101
128
  const parsed = ix.parsed;
102
- console.log(`[LocalSvmFacilitator] Inspecting IX:`, JSON.stringify(parsed));
103
- if (parsed.type === "transfer") {
104
- const info = parsed.info;
105
- console.log(`[LocalSvmFacilitator] Found transfer: ${info.lamports} lamports to ${info.destination}`);
106
- if (info.destination === payTo) {
107
- paidAmount += BigInt(info.lamports);
108
- if (!payer) payer = info.source;
129
+ if (parsed?.type === "transfer" && parsed.info?.destination === payTo) {
130
+ paidAmount += BigInt(parsed.info.lamports);
131
+ if (!payer) payer = parsed.info.source;
132
+ }
133
+ }
134
+ if ("program" in ix && (ix.program === "spl-token" || ix.program === "spl-token-2022")) {
135
+ const parsed = ix.parsed;
136
+ if (parsed?.type === "transferChecked" || parsed?.type === "transfer") {
137
+ if (this.debug) {
138
+ console.log(`[LocalSvmFacilitator] Found SPL transfer`);
109
139
  }
110
140
  }
111
141
  }
112
142
  }
113
- console.log(`[LocalSvmFacilitator] Total Paid Correctly: ${paidAmount}`);
114
143
  if (paidAmount >= requiredAmount) {
115
- console.log("[LocalSvmFacilitator] Verification SUCCESS");
144
+ if (this.debug) {
145
+ console.log(`[LocalSvmFacilitator] Verification SUCCESS for tx: ${signature.slice(0, 8)}...`);
146
+ }
116
147
  return {
117
148
  isValid: true,
118
149
  payer: payer || tx.transaction.message.accountKeys[0].pubkey.toBase58()
119
150
  };
120
151
  }
121
- console.error(`[LocalSvmFacilitator] Verification FAILED. Paid: ${paidAmount}, Required: ${requiredAmount}`);
122
152
  return {
123
153
  isValid: false,
124
- invalidReason: `Insufficient payment. Required: ${requiredAmount}, Found: ${paidAmount}`,
154
+ invalidReason: "Insufficient payment amount",
125
155
  payer
126
156
  };
127
157
  } catch (error) {
128
- console.error("[LocalSvmFacilitator] Verify error:", error);
158
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
159
+ if (this.debug) {
160
+ console.error("[LocalSvmFacilitator] Verify error:", errorMessage);
161
+ }
129
162
  throw new types.VerifyError(500, {
130
163
  isValid: false,
131
- invalidReason: error.message
164
+ invalidReason: errorMessage
132
165
  });
133
166
  }
134
167
  }
168
+ /**
169
+ * Fetch transaction with exponential backoff retry
170
+ */
171
+ async fetchTransactionWithRetry(signature, maxRetries = 3) {
172
+ let lastError;
173
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
174
+ try {
175
+ const tx = await this.connection.getParsedTransaction(signature, {
176
+ maxSupportedTransactionVersion: 0,
177
+ commitment: "confirmed"
178
+ });
179
+ if (tx) return tx;
180
+ if (attempt < maxRetries - 1) {
181
+ await this.sleep(Math.pow(2, attempt) * 1e3);
182
+ }
183
+ } catch (error) {
184
+ lastError = error instanceof Error ? error : new Error("RPC error");
185
+ if (attempt < maxRetries - 1) {
186
+ await this.sleep(Math.pow(2, attempt) * 1e3);
187
+ }
188
+ }
189
+ }
190
+ if (lastError) throw lastError;
191
+ return null;
192
+ }
193
+ /**
194
+ * Sleep helper
195
+ */
196
+ sleep(ms) {
197
+ return new Promise((resolve) => setTimeout(resolve, ms));
198
+ }
135
199
  /**
136
200
  * Settle a payment (not applicable for direct chain verification, usually)
137
201
  * But we must implement it. For 'exact', settlement is just verification + finality.
@@ -177,7 +241,6 @@ function createX402Middleware(config) {
177
241
  network: config.network === "mainnet-beta" ? "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp" : "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1"
178
242
  }
179
243
  };
180
- console.log("[createX402Middleware] Final Config:", JSON.stringify(finalConfig));
181
244
  return next.withX402(handler, finalConfig, server$2);
182
245
  };
183
246
  }
@@ -19,20 +19,48 @@ var LocalSvmFacilitator = class {
19
19
  * Get supported payment kinds
20
20
  * Mocking the response of the /supported endpoint
21
21
  */
22
- // Network Constants - CAIP-2 format for x402 v2
22
+ /**
23
+ * Network Constants - CAIP-2 format for x402 v2
24
+ * These match the official Solana chain IDs used by x402 protocol
25
+ */
23
26
  NETWORKS = {
24
27
  DEVNET: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
25
28
  MAINNET: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"
26
29
  };
27
- // Dummy fee payer address (System Program) - not used in local verification
28
- // but required by x402 protocol for supported kinds
30
+ /**
31
+ * DUMMY_FEE_PAYER Explanation (for auditors):
32
+ *
33
+ * In a HOSTED facilitator (like x402.org), the `feePayer` field in the
34
+ * SupportedResponse.extra specifies which address will pay transaction fees
35
+ * when the facilitator submits transactions on behalf of users.
36
+ *
37
+ * In LOCAL/SELF-SOVEREIGN mode (this implementation):
38
+ * - The USER pays their own transaction fees directly
39
+ * - The facilitator NEVER submits transactions - it only VERIFIES them
40
+ * - Therefore, no fee payer address is actually used
41
+ *
42
+ * However, the x402 protocol REQUIRES this field in the response schema.
43
+ * We use the System Program address (all 1s) as a placeholder because:
44
+ * 1. It's clearly not a real wallet (obvious placeholder)
45
+ * 2. It cannot receive funds or sign transactions
46
+ * 3. It signals to developers that fee paying is handled differently
47
+ *
48
+ * SECURITY NOTE: This is NOT a security risk because:
49
+ * - The fee payer is never used in verify() or settle() methods
50
+ * - Users sign and pay for their own transactions
51
+ * - The address is just a protocol-required placeholder
52
+ *
53
+ * @see https://docs.x402.org for protocol specification
54
+ */
29
55
  DUMMY_FEE_PAYER = "11111111111111111111111111111111";
30
56
  /**
31
57
  * Get supported payment kinds
32
58
  * Returns x402 v2 compatible response with CAIP-2 network identifiers
59
+ *
60
+ * NOTE: The feePayer in extra.feePayer is a placeholder only.
61
+ * In self-sovereign mode, users pay their own transaction fees.
33
62
  */
34
63
  async getSupported(_extensionKeys = []) {
35
- console.log("[LocalSvmFacilitator] getSupported called");
36
64
  const supported = {
37
65
  kinds: [
38
66
  {
@@ -50,10 +78,10 @@ var LocalSvmFacilitator = class {
50
78
  ],
51
79
  extensions: [],
52
80
  signers: {
81
+ // Placeholder - in self-sovereign mode, users are their own signers
53
82
  "solana:*": [this.DUMMY_FEE_PAYER]
54
83
  }
55
84
  };
56
- console.log("[LocalSvmFacilitator] Returning supported:", JSON.stringify(supported));
57
85
  return supported;
58
86
  }
59
87
  /**
@@ -68,6 +96,10 @@ var LocalSvmFacilitator = class {
68
96
  getSigners(_network) {
69
97
  return [];
70
98
  }
99
+ /**
100
+ * Enable debug logging (disable in production)
101
+ */
102
+ debug = process.env.NODE_ENV === "development";
71
103
  /**
72
104
  * Verify a payment on-chain
73
105
  */
@@ -80,57 +112,89 @@ var LocalSvmFacilitator = class {
80
112
  const payTo = requirements.payTo;
81
113
  const amountVal = requirements.amount || requirements.maxAmountRequired || "0";
82
114
  const requiredAmount = BigInt(amountVal);
83
- console.log(`[LocalSvmFacilitator] Verifying signature: ${signature}`);
84
- console.log(`[LocalSvmFacilitator] Requirements - Amount: ${requiredAmount}, PayTo: ${payTo}`);
85
- console.log(`[LocalSvmFacilitator] Full Requirements:`, JSON.stringify(requirements));
86
- const tx = await this.connection.getParsedTransaction(signature, {
87
- maxSupportedTransactionVersion: 0,
88
- commitment: "confirmed"
89
- });
115
+ if (this.debug) {
116
+ console.log(`[LocalSvmFacilitator] Verifying tx: ${signature.slice(0, 8)}...`);
117
+ }
118
+ const tx = await this.fetchTransactionWithRetry(signature, 3);
90
119
  if (!tx) {
91
- console.error("[LocalSvmFacilitator] Transaction not found or not confirmed");
92
120
  return { isValid: false, invalidReason: "Transaction not found or not confirmed" };
93
121
  }
94
- console.log("[LocalSvmFacilitator] Transaction found. Parsing instructions...");
95
122
  const instructions = tx.transaction.message.instructions;
96
123
  let paidAmount = 0n;
97
124
  let payer = void 0;
98
125
  for (const ix of instructions) {
99
126
  if ("program" in ix && ix.program === "system") {
100
127
  const parsed = ix.parsed;
101
- console.log(`[LocalSvmFacilitator] Inspecting IX:`, JSON.stringify(parsed));
102
- if (parsed.type === "transfer") {
103
- const info = parsed.info;
104
- console.log(`[LocalSvmFacilitator] Found transfer: ${info.lamports} lamports to ${info.destination}`);
105
- if (info.destination === payTo) {
106
- paidAmount += BigInt(info.lamports);
107
- if (!payer) payer = info.source;
128
+ if (parsed?.type === "transfer" && parsed.info?.destination === payTo) {
129
+ paidAmount += BigInt(parsed.info.lamports);
130
+ if (!payer) payer = parsed.info.source;
131
+ }
132
+ }
133
+ if ("program" in ix && (ix.program === "spl-token" || ix.program === "spl-token-2022")) {
134
+ const parsed = ix.parsed;
135
+ if (parsed?.type === "transferChecked" || parsed?.type === "transfer") {
136
+ if (this.debug) {
137
+ console.log(`[LocalSvmFacilitator] Found SPL transfer`);
108
138
  }
109
139
  }
110
140
  }
111
141
  }
112
- console.log(`[LocalSvmFacilitator] Total Paid Correctly: ${paidAmount}`);
113
142
  if (paidAmount >= requiredAmount) {
114
- console.log("[LocalSvmFacilitator] Verification SUCCESS");
143
+ if (this.debug) {
144
+ console.log(`[LocalSvmFacilitator] Verification SUCCESS for tx: ${signature.slice(0, 8)}...`);
145
+ }
115
146
  return {
116
147
  isValid: true,
117
148
  payer: payer || tx.transaction.message.accountKeys[0].pubkey.toBase58()
118
149
  };
119
150
  }
120
- console.error(`[LocalSvmFacilitator] Verification FAILED. Paid: ${paidAmount}, Required: ${requiredAmount}`);
121
151
  return {
122
152
  isValid: false,
123
- invalidReason: `Insufficient payment. Required: ${requiredAmount}, Found: ${paidAmount}`,
153
+ invalidReason: "Insufficient payment amount",
124
154
  payer
125
155
  };
126
156
  } catch (error) {
127
- console.error("[LocalSvmFacilitator] Verify error:", error);
157
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
158
+ if (this.debug) {
159
+ console.error("[LocalSvmFacilitator] Verify error:", errorMessage);
160
+ }
128
161
  throw new VerifyError(500, {
129
162
  isValid: false,
130
- invalidReason: error.message
163
+ invalidReason: errorMessage
131
164
  });
132
165
  }
133
166
  }
167
+ /**
168
+ * Fetch transaction with exponential backoff retry
169
+ */
170
+ async fetchTransactionWithRetry(signature, maxRetries = 3) {
171
+ let lastError;
172
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
173
+ try {
174
+ const tx = await this.connection.getParsedTransaction(signature, {
175
+ maxSupportedTransactionVersion: 0,
176
+ commitment: "confirmed"
177
+ });
178
+ if (tx) return tx;
179
+ if (attempt < maxRetries - 1) {
180
+ await this.sleep(Math.pow(2, attempt) * 1e3);
181
+ }
182
+ } catch (error) {
183
+ lastError = error instanceof Error ? error : new Error("RPC error");
184
+ if (attempt < maxRetries - 1) {
185
+ await this.sleep(Math.pow(2, attempt) * 1e3);
186
+ }
187
+ }
188
+ }
189
+ if (lastError) throw lastError;
190
+ return null;
191
+ }
192
+ /**
193
+ * Sleep helper
194
+ */
195
+ sleep(ms) {
196
+ return new Promise((resolve) => setTimeout(resolve, ms));
197
+ }
134
198
  /**
135
199
  * Settle a payment (not applicable for direct chain verification, usually)
136
200
  * But we must implement it. For 'exact', settlement is just verification + finality.
@@ -176,7 +240,6 @@ function createX402Middleware(config) {
176
240
  network: config.network === "mainnet-beta" ? "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp" : "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1"
177
241
  }
178
242
  };
179
- console.log("[createX402Middleware] Final Config:", JSON.stringify(finalConfig));
180
243
  return withX402$1(handler, finalConfig, server);
181
244
  };
182
245
  }
@@ -6,12 +6,10 @@ function lamportsToSol(lamports) {
6
6
  }
7
7
 
8
8
  // src/pricing/index.ts
9
- var cachedPrice = null;
10
- var config = {};
11
- var lastProviderIndex = -1;
9
+ var priceCache = null;
10
+ var currentConfig = {};
12
11
  function configurePricing(newConfig) {
13
- config = { ...config, ...newConfig };
14
- cachedPrice = null;
12
+ currentConfig = { ...currentConfig, ...newConfig };
15
13
  }
16
14
  var PROVIDERS = [
17
15
  {
@@ -51,56 +49,75 @@ async function fetchFromProvider(provider, timeout) {
51
49
  if (!price || price <= 0) {
52
50
  throw new Error("Invalid price");
53
51
  }
54
- return price;
52
+ return { price, source: provider.name };
55
53
  } finally {
56
54
  clearTimeout(timeoutId);
57
55
  }
58
56
  }
57
+ async function fetchPriceParallel(timeout) {
58
+ const promises = PROVIDERS.map(
59
+ (provider) => fetchFromProvider(provider, timeout).catch(() => null)
60
+ );
61
+ const results = await Promise.all(promises);
62
+ const validResult = results.find((r) => r !== null);
63
+ if (validResult) {
64
+ return validResult;
65
+ }
66
+ throw new Error("All providers failed");
67
+ }
68
+ async function fetchPriceSequential(timeout) {
69
+ for (const provider of PROVIDERS) {
70
+ try {
71
+ return await fetchFromProvider(provider, timeout);
72
+ } catch {
73
+ continue;
74
+ }
75
+ }
76
+ throw new Error("All providers failed");
77
+ }
59
78
  async function getSolPrice() {
60
- const cacheTTL = config.cacheTTL ?? 6e4;
61
- const timeout = config.timeout ?? 5e3;
62
- if (cachedPrice && Date.now() - cachedPrice.fetchedAt.getTime() < cacheTTL) {
63
- return cachedPrice;
79
+ const cacheTTL = currentConfig.cacheTTL ?? 6e4;
80
+ const timeout = currentConfig.timeout ?? 3e3;
81
+ const useParallel = currentConfig.parallelFetch ?? true;
82
+ const now = Date.now();
83
+ if (priceCache && now - priceCache.timestamp < cacheTTL) {
84
+ return priceCache.data;
64
85
  }
65
- if (config.customProvider) {
86
+ if (currentConfig.customProvider) {
66
87
  try {
67
- const price = await config.customProvider();
88
+ const price = await currentConfig.customProvider();
68
89
  if (price > 0) {
69
- cachedPrice = {
90
+ const data = {
70
91
  solPrice: price,
71
92
  fetchedAt: /* @__PURE__ */ new Date(),
72
93
  source: "custom"
73
94
  };
74
- return cachedPrice;
95
+ priceCache = { data, timestamp: now };
96
+ return data;
75
97
  }
76
98
  } catch {
77
99
  }
78
100
  }
79
- for (let i = 0; i < PROVIDERS.length; i++) {
80
- const idx = (lastProviderIndex + 1 + i) % PROVIDERS.length;
81
- const provider = PROVIDERS[idx];
82
- try {
83
- const price = await fetchFromProvider(provider, timeout);
84
- lastProviderIndex = idx;
85
- cachedPrice = {
86
- solPrice: price,
87
- fetchedAt: /* @__PURE__ */ new Date(),
88
- source: provider.name
101
+ try {
102
+ const result = useParallel ? await fetchPriceParallel(timeout) : await fetchPriceSequential(timeout);
103
+ const data = {
104
+ solPrice: result.price,
105
+ fetchedAt: /* @__PURE__ */ new Date(),
106
+ source: result.source
107
+ };
108
+ priceCache = { data, timestamp: now };
109
+ return data;
110
+ } catch {
111
+ if (priceCache) {
112
+ return {
113
+ ...priceCache.data,
114
+ source: `${priceCache.data.source} (stale)`
89
115
  };
90
- return cachedPrice;
91
- } catch {
92
- continue;
93
116
  }
117
+ throw new Error(
118
+ "Failed to fetch SOL price from all providers. Configure a custom provider or ensure network connectivity."
119
+ );
94
120
  }
95
- if (cachedPrice) {
96
- return {
97
- ...cachedPrice,
98
- source: `${cachedPrice.source} (stale)`
99
- };
100
- }
101
- throw new Error(
102
- "Failed to fetch SOL price from all providers. Configure a custom provider or ensure network connectivity."
103
- );
104
121
  }
105
122
  async function lamportsToUsd(lamports) {
106
123
  const { solPrice } = await getSolPrice();
@@ -128,8 +145,7 @@ function formatPriceSync(lamports, solPrice) {
128
145
  };
129
146
  }
130
147
  function clearPriceCache() {
131
- cachedPrice = null;
132
- lastProviderIndex = -1;
148
+ priceCache = null;
133
149
  }
134
150
  function getProviders() {
135
151
  return PROVIDERS.map((p) => ({ name: p.name, url: p.url }));
@@ -23,8 +23,10 @@ interface PriceConfig {
23
23
  customProvider?: CustomPriceProvider;
24
24
  /** Cache TTL in milliseconds (default: 60000) */
25
25
  cacheTTL?: number;
26
- /** Request timeout in milliseconds (default: 5000) */
26
+ /** Request timeout in milliseconds (default: 3000) */
27
27
  timeout?: number;
28
+ /** Use parallel fetching (default: true, faster but more network calls) */
29
+ parallelFetch?: boolean;
28
30
  }
29
31
  /**
30
32
  * Configure price fetching
@@ -39,19 +41,16 @@ interface PriceConfig {
39
41
  * },
40
42
  * });
41
43
  *
42
- * // Or just adjust cache TTL
43
- * configurePricing({ cacheTTL: 30000 }); // 30 seconds
44
+ * // Or adjust settings
45
+ * configurePricing({ cacheTTL: 30000, parallelFetch: true });
44
46
  * ```
45
47
  */
46
48
  declare function configurePricing(newConfig: PriceConfig): void;
47
49
  /**
48
- * Get SOL price with multi-provider fallback
50
+ * Get SOL price with multi-provider support
49
51
  *
50
- * Provider rotation order:
51
- * 1. CoinCap (primary)
52
- * 2. Binance (backup #1)
53
- * 3. CoinGecko (backup #2)
54
- * 4. Kraken (backup #3)
52
+ * Default behavior: Parallel fetch (all providers race, fastest wins)
53
+ * This is faster and more reliable in serverless environments.
55
54
  *
56
55
  * @example
57
56
  * ```typescript
@@ -23,8 +23,10 @@ interface PriceConfig {
23
23
  customProvider?: CustomPriceProvider;
24
24
  /** Cache TTL in milliseconds (default: 60000) */
25
25
  cacheTTL?: number;
26
- /** Request timeout in milliseconds (default: 5000) */
26
+ /** Request timeout in milliseconds (default: 3000) */
27
27
  timeout?: number;
28
+ /** Use parallel fetching (default: true, faster but more network calls) */
29
+ parallelFetch?: boolean;
28
30
  }
29
31
  /**
30
32
  * Configure price fetching
@@ -39,19 +41,16 @@ interface PriceConfig {
39
41
  * },
40
42
  * });
41
43
  *
42
- * // Or just adjust cache TTL
43
- * configurePricing({ cacheTTL: 30000 }); // 30 seconds
44
+ * // Or adjust settings
45
+ * configurePricing({ cacheTTL: 30000, parallelFetch: true });
44
46
  * ```
45
47
  */
46
48
  declare function configurePricing(newConfig: PriceConfig): void;
47
49
  /**
48
- * Get SOL price with multi-provider fallback
50
+ * Get SOL price with multi-provider support
49
51
  *
50
- * Provider rotation order:
51
- * 1. CoinCap (primary)
52
- * 2. Binance (backup #1)
53
- * 3. CoinGecko (backup #2)
54
- * 4. Kraken (backup #3)
52
+ * Default behavior: Parallel fetch (all providers race, fastest wins)
53
+ * This is faster and more reliable in serverless environments.
55
54
  *
56
55
  * @example
57
56
  * ```typescript