@alleyboss/micropay-solana-x402-paywall 3.3.8 → 3.3.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -3,7 +3,6 @@ import { PaymentPayload, PaymentRequirements, VerifyResponse, SettleResponse, Su
3
3
  export * from '@x402/core/types';
4
4
  export * from '@x402/core/client';
5
5
  export * from '@x402/svm';
6
- export { PaymentFlowConfig, PaymentResult, PaymentStatus, PaywallConfig, PaywallState, SendPaymentParams, SolanaPayUrlParams, WalletAdapterInterface, buildSolanaPayUrl, createPaymentFlow, createPaymentReference, createX402AuthorizationHeader, sendSolanaPayment, useFormatPrice, useLamportsToUsd, useMicropay, usePaywallResource, usePricing } from './client/index.cjs';
7
6
  export { AgentPaymentResult, CreditSessionClaims, CreditSessionConfig, CreditSessionData, CreditValidation, ExecuteAgentPaymentParams, UseCreditResult, addCredits, createCreditSession, executeAgentPayment, generateAgentKeypair, getAgentBalance, getRemainingCredits, hasAgentSufficientBalance, keypairFromBase58, useCredit, validateCreditSession } from './agent/index.cjs';
8
7
  export { CustomPriceProvider, PriceConfig, PriceData, clearPriceCache, configurePricing, formatPriceDisplay, formatPriceSync, getProviders, getSolPrice, lamportsToSol, lamportsToUsd, usdToLamports } from './pricing/index.cjs';
9
8
  import '@solana/web3.js';
package/dist/index.d.ts CHANGED
@@ -3,7 +3,6 @@ import { PaymentPayload, PaymentRequirements, VerifyResponse, SettleResponse, Su
3
3
  export * from '@x402/core/types';
4
4
  export * from '@x402/core/client';
5
5
  export * from '@x402/svm';
6
- export { PaymentFlowConfig, PaymentResult, PaymentStatus, PaywallConfig, PaywallState, SendPaymentParams, SolanaPayUrlParams, WalletAdapterInterface, buildSolanaPayUrl, createPaymentFlow, createPaymentReference, createX402AuthorizationHeader, sendSolanaPayment, useFormatPrice, useLamportsToUsd, useMicropay, usePaywallResource, usePricing } from './client/index.js';
7
6
  export { AgentPaymentResult, CreditSessionClaims, CreditSessionConfig, CreditSessionData, CreditValidation, ExecuteAgentPaymentParams, UseCreditResult, addCredits, createCreditSession, executeAgentPayment, generateAgentKeypair, getAgentBalance, getRemainingCredits, hasAgentSufficientBalance, keypairFromBase58, useCredit, validateCreditSession } from './agent/index.js';
8
7
  export { CustomPriceProvider, PriceConfig, PriceData, clearPriceCache, configurePricing, formatPriceDisplay, formatPriceSync, getProviders, getSolPrice, lamportsToSol, lamportsToUsd, usdToLamports } from './pricing/index.js';
9
8
  import '@solana/web3.js';
package/dist/index.js CHANGED
@@ -3,526 +3,11 @@ import { VerifyError, SettleError } from '@x402/core/types';
3
3
  export * from '@x402/core/types';
4
4
  export * from '@x402/core/client';
5
5
  export * from '@x402/svm';
6
- import { decodePaymentRequiredHeader, encodePaymentSignatureHeader } from '@x402/core/http';
7
- import { PublicKey, Transaction, SystemProgram, LAMPORTS_PER_SOL, Connection, Keypair, TransactionMessage, VersionedTransaction, ComputeBudgetProgram } from '@solana/web3.js';
8
- import { useState, useCallback, useEffect, useRef } from 'react';
6
+ import { Connection, PublicKey, SystemProgram, LAMPORTS_PER_SOL, Keypair, TransactionMessage, VersionedTransaction, ComputeBudgetProgram } from '@solana/web3.js';
9
7
  import bs58 from 'bs58';
10
8
  import { SignJWT, jwtVerify } from 'jose';
11
9
 
12
10
  // src/index.ts
13
-
14
- // src/client/types.ts
15
- var TOKEN_MINTS = {
16
- /** USDC on mainnet */
17
- USDC_MAINNET: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
18
- /** USDC on devnet */
19
- USDC_DEVNET: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
20
- /** USDT on mainnet */
21
- USDT_MAINNET: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"
22
- };
23
-
24
- // src/client/payment.ts
25
- function buildSolanaPayUrl(params) {
26
- const { recipient, amount, splToken, reference, label, message } = params;
27
- const url = new URL(`solana:${recipient}`);
28
- if (amount !== void 0) {
29
- url.searchParams.set("amount", amount.toString());
30
- }
31
- if (splToken) {
32
- url.searchParams.set("spl-token", splToken);
33
- }
34
- if (reference) {
35
- url.searchParams.set("reference", reference);
36
- }
37
- if (label) {
38
- url.searchParams.set("label", label);
39
- }
40
- if (message) {
41
- url.searchParams.set("message", message);
42
- }
43
- return url.toString();
44
- }
45
- function createPaymentFlow(config) {
46
- const { network, recipientWallet, amount, asset = "native", memo } = config;
47
- let decimals = 9;
48
- let mintAddress;
49
- if (asset === "usdc") {
50
- decimals = 6;
51
- mintAddress = network === "mainnet-beta" ? TOKEN_MINTS.USDC_MAINNET : TOKEN_MINTS.USDC_DEVNET;
52
- } else if (asset === "usdt") {
53
- decimals = 6;
54
- mintAddress = TOKEN_MINTS.USDT_MAINNET;
55
- } else if (typeof asset === "object" && "mint" in asset) {
56
- decimals = asset.decimals ?? 6;
57
- mintAddress = asset.mint;
58
- }
59
- const naturalAmount = Number(amount) / Math.pow(10, decimals);
60
- return {
61
- /** Get the payment configuration */
62
- getConfig: () => ({ ...config }),
63
- /** Get amount in natural display units (e.g., 0.01 SOL) */
64
- getDisplayAmount: () => naturalAmount,
65
- /** Get amount formatted with symbol */
66
- getFormattedAmount: () => {
67
- const symbol = asset === "native" ? "SOL" : asset === "usdc" ? "USDC" : asset === "usdt" ? "USDT" : "tokens";
68
- return `${naturalAmount.toFixed(decimals > 6 ? 4 : 2)} ${symbol}`;
69
- },
70
- /** Generate Solana Pay URL for QR codes */
71
- getSolanaPayUrl: (options = {}) => {
72
- return buildSolanaPayUrl({
73
- recipient: recipientWallet,
74
- amount: naturalAmount,
75
- splToken: mintAddress,
76
- label: options.label,
77
- reference: options.reference,
78
- message: memo
79
- });
80
- },
81
- /** Get the token mint address (undefined for native SOL) */
82
- getMintAddress: () => mintAddress,
83
- /** Check if this is a native SOL payment */
84
- isNativePayment: () => asset === "native",
85
- /** Get network information */
86
- getNetworkInfo: () => ({
87
- network,
88
- isMainnet: network === "mainnet-beta",
89
- explorerUrl: network === "mainnet-beta" ? "https://explorer.solana.com" : "https://explorer.solana.com?cluster=devnet"
90
- }),
91
- /** Build explorer URL for a transaction */
92
- getExplorerUrl: (signature) => {
93
- const baseUrl = "https://explorer.solana.com/tx";
94
- const cluster = network === "mainnet-beta" ? "" : "?cluster=devnet";
95
- return `${baseUrl}/${signature}${cluster}`;
96
- }
97
- };
98
- }
99
- function createPaymentReference() {
100
- if (typeof crypto !== "undefined" && crypto.randomUUID) {
101
- return crypto.randomUUID();
102
- }
103
- return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
104
- }
105
- function createX402AuthorizationHeader(signature, paymentRequiredHeader) {
106
- const cleanHeader = paymentRequiredHeader.replace(/^[Xx]402\s+/, "");
107
- const required = decodePaymentRequiredHeader(cleanHeader);
108
- const accepts = Array.isArray(required.accepts) ? required.accepts[0] : required.accepts;
109
- const payload = {
110
- accepted: accepts,
111
- client: {
112
- scheme: accepts.scheme,
113
- // TypeScript knows this exists on PaymentRequirements
114
- network: accepts.network
115
- },
116
- payment: {
117
- signature
118
- }
119
- };
120
- const token = encodePaymentSignatureHeader(payload);
121
- return `x402 ${token}`;
122
- }
123
- async function sendSolanaPayment({
124
- connection,
125
- wallet,
126
- recipientAddress,
127
- amount,
128
- // memo, // TODO: Add memo support to transaction
129
- commitment = "confirmed"
130
- }) {
131
- if (!wallet.publicKey) {
132
- throw new Error("Wallet not connected");
133
- }
134
- if (amount <= 0n) {
135
- throw new Error("Amount must be greater than 0");
136
- }
137
- try {
138
- const recipientPubkey = new PublicKey(recipientAddress);
139
- const transaction = new Transaction().add(
140
- SystemProgram.transfer({
141
- fromPubkey: wallet.publicKey,
142
- toPubkey: recipientPubkey,
143
- lamports: amount
144
- })
145
- );
146
- const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash(commitment);
147
- transaction.recentBlockhash = blockhash;
148
- transaction.feePayer = wallet.publicKey;
149
- const signature = await wallet.sendTransaction(transaction, connection);
150
- await connection.confirmTransaction({
151
- signature,
152
- blockhash,
153
- lastValidBlockHeight
154
- }, commitment);
155
- return {
156
- signature,
157
- amountSol: Number(amount) / LAMPORTS_PER_SOL
158
- };
159
- } catch (error) {
160
- console.error("Payment failed:", error);
161
- throw new Error(error.message || "Payment failed");
162
- }
163
- }
164
- function usePaywallResource({
165
- url,
166
- connection,
167
- wallet
168
- }) {
169
- const [data, setData] = useState(null);
170
- const [isLocked, setIsLocked] = useState(true);
171
- const [isLoading, setIsLoading] = useState(true);
172
- const [error, setError] = useState(null);
173
- const [paymentHeader, setPaymentHeader] = useState(null);
174
- const [price, setPrice] = useState();
175
- const [recipient, setRecipient] = useState();
176
- const fetchData = useCallback(async (authHeader) => {
177
- setIsLoading(true);
178
- setError(null);
179
- try {
180
- const headers = {
181
- "Content-Type": "application/json"
182
- };
183
- if (authHeader) {
184
- headers["Authorization"] = authHeader;
185
- }
186
- const res = await fetch(url, {
187
- headers,
188
- credentials: "include"
189
- // Ensure cookies are sent/received
190
- });
191
- if (res.status === 402) {
192
- const wwwAuth = res.headers.get("WWW-Authenticate") || res.headers.get("Payment-Required");
193
- const errorReason = res.headers.get("X-Payment-Error");
194
- console.log("[usePaywallResource] 402 Response. Header:", wwwAuth, "Error:", errorReason);
195
- if (authHeader && errorReason) {
196
- console.error("[usePaywallResource] Verification Failed:", errorReason);
197
- setError(`Verification Failed: ${errorReason}`);
198
- }
199
- if (wwwAuth) {
200
- setPaymentHeader(wwwAuth);
201
- try {
202
- const { decodePaymentRequiredHeader: decodePaymentRequiredHeader2 } = await import('@x402/core/http');
203
- const cleanHeader = wwwAuth.replace(/^[Xx]402\s+/, "");
204
- const decoded = decodePaymentRequiredHeader2(cleanHeader);
205
- console.log("[usePaywallResource] Decoded header:", decoded);
206
- const accepts = Array.isArray(decoded.accepts) ? decoded.accepts[0] : decoded.accepts;
207
- console.log("[usePaywallResource] Accepts:", accepts);
208
- if (accepts) {
209
- const amountStr = accepts.amount || accepts.price || accepts.maxAmountRequired || "0";
210
- setPrice(BigInt(amountStr));
211
- setRecipient(accepts.payTo);
212
- console.log("[usePaywallResource] Set price:", amountStr, "recipient:", accepts.payTo);
213
- } else {
214
- console.warn("[usePaywallResource] No accepts found in header");
215
- }
216
- } catch (e) {
217
- console.warn("[usePaywallResource] Failed to parse x402 header:", e);
218
- }
219
- } else {
220
- console.warn("[usePaywallResource] 402 response missing WWW-Authenticate header");
221
- }
222
- setIsLocked(true);
223
- setData(null);
224
- } else if (res.ok) {
225
- const json = await res.json();
226
- setData(json);
227
- setIsLocked(false);
228
- } else {
229
- throw new Error(`Request failed with status ${res.status}`);
230
- }
231
- } catch (err) {
232
- setError(err.message || "Failed to fetch resource");
233
- } finally {
234
- setIsLoading(false);
235
- }
236
- }, [url]);
237
- useEffect(() => {
238
- fetchData();
239
- }, [fetchData]);
240
- const unlock = useCallback(async () => {
241
- if (!paymentHeader || !price || !recipient) {
242
- setError("Missing payment requirements");
243
- return;
244
- }
245
- setIsLoading(true);
246
- try {
247
- const { signature } = await sendSolanaPayment({
248
- connection,
249
- wallet,
250
- recipientAddress: recipient,
251
- amount: price
252
- });
253
- const authHeader = createX402AuthorizationHeader(signature, paymentHeader);
254
- await fetchData(authHeader);
255
- } catch (err) {
256
- console.error("Unlock failed", err);
257
- setError(err.message || "Payment/Unlock failed");
258
- setIsLoading(false);
259
- }
260
- }, [connection, wallet, paymentHeader, price, recipient, fetchData]);
261
- return {
262
- data,
263
- isLocked,
264
- isLoading,
265
- price,
266
- recipient,
267
- error,
268
- unlock
269
- };
270
- }
271
-
272
- // src/pricing/utils.ts
273
- function lamportsToSol(lamports) {
274
- return Number(lamports) / 1e9;
275
- }
276
-
277
- // src/pricing/index.ts
278
- var priceCache = null;
279
- var currentConfig = {};
280
- function configurePricing(newConfig) {
281
- currentConfig = { ...currentConfig, ...newConfig };
282
- }
283
- var PROVIDERS = [
284
- {
285
- name: "coincap",
286
- url: "https://api.coincap.io/v2/assets/solana",
287
- parse: (data) => parseFloat(data.data?.priceUsd || "0")
288
- },
289
- {
290
- name: "binance",
291
- url: "https://api.binance.com/api/v3/ticker/price?symbol=SOLUSDT",
292
- parse: (data) => parseFloat(data.price || "0")
293
- },
294
- {
295
- name: "coingecko",
296
- url: "https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd",
297
- parse: (data) => data.solana?.usd || 0
298
- },
299
- {
300
- name: "kraken",
301
- url: "https://api.kraken.com/0/public/Ticker?pair=SOLUSD",
302
- parse: (data) => parseFloat(data.result?.SOLUSD?.c?.[0] || "0")
303
- }
304
- ];
305
- async function fetchFromProvider(provider, timeout) {
306
- const controller = new AbortController();
307
- const timeoutId = setTimeout(() => controller.abort(), timeout);
308
- try {
309
- const response = await fetch(provider.url, {
310
- headers: { "Accept": "application/json" },
311
- signal: controller.signal
312
- });
313
- if (!response.ok) {
314
- throw new Error(`HTTP ${response.status}`);
315
- }
316
- const data = await response.json();
317
- const price = provider.parse(data);
318
- if (!price || price <= 0) {
319
- throw new Error("Invalid price");
320
- }
321
- return { price, source: provider.name };
322
- } finally {
323
- clearTimeout(timeoutId);
324
- }
325
- }
326
- async function fetchPriceParallel(timeout) {
327
- const promises = PROVIDERS.map(
328
- (provider) => fetchFromProvider(provider, timeout).catch(() => null)
329
- );
330
- const results = await Promise.all(promises);
331
- const validResult = results.find((r) => r !== null);
332
- if (validResult) {
333
- return validResult;
334
- }
335
- throw new Error("All providers failed");
336
- }
337
- async function fetchPriceSequential(timeout) {
338
- for (const provider of PROVIDERS) {
339
- try {
340
- return await fetchFromProvider(provider, timeout);
341
- } catch {
342
- continue;
343
- }
344
- }
345
- throw new Error("All providers failed");
346
- }
347
- async function getSolPrice() {
348
- const cacheTTL = currentConfig.cacheTTL ?? 6e4;
349
- const timeout = currentConfig.timeout ?? 3e3;
350
- const useParallel = currentConfig.parallelFetch ?? true;
351
- const now = Date.now();
352
- if (priceCache && now - priceCache.timestamp < cacheTTL) {
353
- return priceCache.data;
354
- }
355
- if (currentConfig.customProvider) {
356
- try {
357
- const price = await currentConfig.customProvider();
358
- if (price > 0) {
359
- const data = {
360
- solPrice: price,
361
- fetchedAt: /* @__PURE__ */ new Date(),
362
- source: "custom"
363
- };
364
- priceCache = { data, timestamp: now };
365
- return data;
366
- }
367
- } catch {
368
- }
369
- }
370
- try {
371
- const result = useParallel ? await fetchPriceParallel(timeout) : await fetchPriceSequential(timeout);
372
- const data = {
373
- solPrice: result.price,
374
- fetchedAt: /* @__PURE__ */ new Date(),
375
- source: result.source
376
- };
377
- priceCache = { data, timestamp: now };
378
- return data;
379
- } catch {
380
- if (priceCache) {
381
- return {
382
- ...priceCache.data,
383
- source: `${priceCache.data.source} (stale)`
384
- };
385
- }
386
- throw new Error(
387
- "Failed to fetch SOL price from all providers. Configure a custom provider or ensure network connectivity."
388
- );
389
- }
390
- }
391
- async function lamportsToUsd(lamports) {
392
- const { solPrice } = await getSolPrice();
393
- const sol = Number(lamports) / 1e9;
394
- return sol * solPrice;
395
- }
396
- async function usdToLamports(usd) {
397
- const { solPrice } = await getSolPrice();
398
- const sol = usd / solPrice;
399
- return BigInt(Math.floor(sol * 1e9));
400
- }
401
- async function formatPriceDisplay(lamports) {
402
- const { solPrice } = await getSolPrice();
403
- const sol = Number(lamports) / 1e9;
404
- const usd = sol * solPrice;
405
- return `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`;
406
- }
407
- function formatPriceSync(lamports, solPrice) {
408
- const sol = Number(lamports) / 1e9;
409
- const usd = sol * solPrice;
410
- return {
411
- sol,
412
- usd,
413
- formatted: `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`
414
- };
415
- }
416
- function clearPriceCache() {
417
- priceCache = null;
418
- }
419
- function getProviders() {
420
- return PROVIDERS.map((p) => ({ name: p.name, url: p.url }));
421
- }
422
-
423
- // src/client/hooks.ts
424
- function usePricing(refreshIntervalMs = 6e4) {
425
- const [priceData, setPriceData] = useState(null);
426
- const [isLoading, setIsLoading] = useState(true);
427
- const [error, setError] = useState(null);
428
- const intervalRef = useRef(null);
429
- const fetchPrice = useCallback(async () => {
430
- try {
431
- setIsLoading(true);
432
- setError(null);
433
- const data = await getSolPrice();
434
- setPriceData(data);
435
- } catch (err) {
436
- setError(err instanceof Error ? err.message : "Failed to fetch price");
437
- } finally {
438
- setIsLoading(false);
439
- }
440
- }, []);
441
- useEffect(() => {
442
- fetchPrice();
443
- if (refreshIntervalMs > 0) {
444
- intervalRef.current = setInterval(fetchPrice, refreshIntervalMs);
445
- }
446
- return () => {
447
- if (intervalRef.current) {
448
- clearInterval(intervalRef.current);
449
- }
450
- };
451
- }, [fetchPrice, refreshIntervalMs]);
452
- return {
453
- solPrice: priceData?.solPrice ?? null,
454
- source: priceData?.source ?? null,
455
- fetchedAt: priceData?.fetchedAt ?? null,
456
- isLoading,
457
- error,
458
- refresh: fetchPrice
459
- };
460
- }
461
- function useLamportsToUsd(lamports) {
462
- const { solPrice, isLoading } = usePricing();
463
- if (isLoading || !solPrice || lamports === null) {
464
- return { usd: null, formatted: null, isLoading };
465
- }
466
- const lamportsBigInt = typeof lamports === "number" ? BigInt(lamports) : lamports;
467
- const sol = Number(lamportsBigInt) / 1e9;
468
- const usd = sol * solPrice;
469
- return {
470
- usd,
471
- formatted: `$${usd.toFixed(2)}`,
472
- isLoading: false
473
- };
474
- }
475
- function useMicropay() {
476
- const [status, setStatus] = useState("idle");
477
- const [error, setError] = useState(null);
478
- const [signature, setSignature] = useState(null);
479
- const reset = useCallback(() => {
480
- setStatus("idle");
481
- setError(null);
482
- setSignature(null);
483
- }, []);
484
- const pay = useCallback(async (_options) => {
485
- setStatus("pending");
486
- setError(null);
487
- try {
488
- throw new Error(
489
- "useMicropay requires implementation of onSign/onSend callbacks. See documentation for wallet adapter integration."
490
- );
491
- } catch (err) {
492
- const errorMessage = err instanceof Error ? err.message : "Payment failed";
493
- setError(errorMessage);
494
- setStatus("error");
495
- return { success: false, error: errorMessage };
496
- }
497
- }, []);
498
- return {
499
- status,
500
- error,
501
- signature,
502
- pay,
503
- reset
504
- };
505
- }
506
- function useFormatPrice(lamports) {
507
- const { solPrice, isLoading } = usePricing();
508
- if (isLoading || !solPrice || lamports === null) {
509
- return {
510
- sol: null,
511
- usd: null,
512
- formatted: "Loading...",
513
- isLoading
514
- };
515
- }
516
- const lamportsBigInt = typeof lamports === "number" ? BigInt(lamports) : lamports;
517
- const sol = Number(lamportsBigInt) / 1e9;
518
- const usd = sol * solPrice;
519
- return {
520
- sol,
521
- usd,
522
- formatted: `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`,
523
- isLoading: false
524
- };
525
- }
526
11
  var LocalSvmFacilitator = class {
527
12
  scheme = "exact";
528
13
  caipFamily = "solana:*";
@@ -1075,4 +560,155 @@ async function getRemainingCredits(token, secret) {
1075
560
  };
1076
561
  }
1077
562
 
1078
- export { LocalSvmFacilitator, addCredits, buildSolanaPayUrl, clearPriceCache, configurePricing, createCreditSession, createPaymentFlow, createPaymentReference, createX402AuthorizationHeader, executeAgentPayment, formatPriceDisplay, formatPriceSync, generateAgentKeypair, getAgentBalance, getProviders, getRemainingCredits, getSolPrice, hasAgentSufficientBalance, keypairFromBase58, lamportsToSol, lamportsToUsd, sendSolanaPayment, usdToLamports, useCredit, useFormatPrice, useLamportsToUsd, useMicropay, usePaywallResource, usePricing, validateCreditSession };
563
+ // src/pricing/utils.ts
564
+ function lamportsToSol(lamports) {
565
+ return Number(lamports) / 1e9;
566
+ }
567
+
568
+ // src/pricing/index.ts
569
+ var priceCache = null;
570
+ var currentConfig = {};
571
+ function configurePricing(newConfig) {
572
+ currentConfig = { ...currentConfig, ...newConfig };
573
+ }
574
+ var PROVIDERS = [
575
+ {
576
+ name: "coincap",
577
+ url: "https://api.coincap.io/v2/assets/solana",
578
+ parse: (data) => parseFloat(data.data?.priceUsd || "0")
579
+ },
580
+ {
581
+ name: "binance",
582
+ url: "https://api.binance.com/api/v3/ticker/price?symbol=SOLUSDT",
583
+ parse: (data) => parseFloat(data.price || "0")
584
+ },
585
+ {
586
+ name: "coingecko",
587
+ url: "https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd",
588
+ parse: (data) => data.solana?.usd || 0
589
+ },
590
+ {
591
+ name: "kraken",
592
+ url: "https://api.kraken.com/0/public/Ticker?pair=SOLUSD",
593
+ parse: (data) => parseFloat(data.result?.SOLUSD?.c?.[0] || "0")
594
+ }
595
+ ];
596
+ async function fetchFromProvider(provider, timeout) {
597
+ const controller = new AbortController();
598
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
599
+ try {
600
+ const response = await fetch(provider.url, {
601
+ headers: { "Accept": "application/json" },
602
+ signal: controller.signal
603
+ });
604
+ if (!response.ok) {
605
+ throw new Error(`HTTP ${response.status}`);
606
+ }
607
+ const data = await response.json();
608
+ const price = provider.parse(data);
609
+ if (!price || price <= 0) {
610
+ throw new Error("Invalid price");
611
+ }
612
+ return { price, source: provider.name };
613
+ } finally {
614
+ clearTimeout(timeoutId);
615
+ }
616
+ }
617
+ async function fetchPriceParallel(timeout) {
618
+ const promises = PROVIDERS.map(
619
+ (provider) => fetchFromProvider(provider, timeout).catch(() => null)
620
+ );
621
+ const results = await Promise.all(promises);
622
+ const validResult = results.find((r) => r !== null);
623
+ if (validResult) {
624
+ return validResult;
625
+ }
626
+ throw new Error("All providers failed");
627
+ }
628
+ async function fetchPriceSequential(timeout) {
629
+ for (const provider of PROVIDERS) {
630
+ try {
631
+ return await fetchFromProvider(provider, timeout);
632
+ } catch {
633
+ continue;
634
+ }
635
+ }
636
+ throw new Error("All providers failed");
637
+ }
638
+ async function getSolPrice() {
639
+ const cacheTTL = currentConfig.cacheTTL ?? 6e4;
640
+ const timeout = currentConfig.timeout ?? 3e3;
641
+ const useParallel = currentConfig.parallelFetch ?? true;
642
+ const now = Date.now();
643
+ if (priceCache && now - priceCache.timestamp < cacheTTL) {
644
+ return priceCache.data;
645
+ }
646
+ if (currentConfig.customProvider) {
647
+ try {
648
+ const price = await currentConfig.customProvider();
649
+ if (price > 0) {
650
+ const data = {
651
+ solPrice: price,
652
+ fetchedAt: /* @__PURE__ */ new Date(),
653
+ source: "custom"
654
+ };
655
+ priceCache = { data, timestamp: now };
656
+ return data;
657
+ }
658
+ } catch {
659
+ }
660
+ }
661
+ try {
662
+ const result = useParallel ? await fetchPriceParallel(timeout) : await fetchPriceSequential(timeout);
663
+ const data = {
664
+ solPrice: result.price,
665
+ fetchedAt: /* @__PURE__ */ new Date(),
666
+ source: result.source
667
+ };
668
+ priceCache = { data, timestamp: now };
669
+ return data;
670
+ } catch {
671
+ if (priceCache) {
672
+ return {
673
+ ...priceCache.data,
674
+ source: `${priceCache.data.source} (stale)`
675
+ };
676
+ }
677
+ throw new Error(
678
+ "Failed to fetch SOL price from all providers. Configure a custom provider or ensure network connectivity."
679
+ );
680
+ }
681
+ }
682
+ async function lamportsToUsd(lamports) {
683
+ const { solPrice } = await getSolPrice();
684
+ const sol = Number(lamports) / 1e9;
685
+ return sol * solPrice;
686
+ }
687
+ async function usdToLamports(usd) {
688
+ const { solPrice } = await getSolPrice();
689
+ const sol = usd / solPrice;
690
+ return BigInt(Math.floor(sol * 1e9));
691
+ }
692
+ async function formatPriceDisplay(lamports) {
693
+ const { solPrice } = await getSolPrice();
694
+ const sol = Number(lamports) / 1e9;
695
+ const usd = sol * solPrice;
696
+ return `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`;
697
+ }
698
+ function formatPriceSync(lamports, solPrice) {
699
+ const sol = Number(lamports) / 1e9;
700
+ const usd = sol * solPrice;
701
+ return {
702
+ sol,
703
+ usd,
704
+ formatted: `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`
705
+ };
706
+ }
707
+ function clearPriceCache() {
708
+ priceCache = null;
709
+ }
710
+ function getProviders() {
711
+ return PROVIDERS.map((p) => ({ name: p.name, url: p.url }));
712
+ }
713
+
714
+ export { LocalSvmFacilitator, addCredits, clearPriceCache, configurePricing, createCreditSession, executeAgentPayment, formatPriceDisplay, formatPriceSync, generateAgentKeypair, getAgentBalance, getProviders, getRemainingCredits, getSolPrice, hasAgentSufficientBalance, keypairFromBase58, lamportsToSol, lamportsToUsd, usdToLamports, useCredit, validateCreditSession };