@alleyboss/micropay-solana-x402-paywall 3.2.1 → 3.3.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.
- package/README.md +30 -0
- package/dist/agent/index.cjs +2 -2
- package/dist/agent/index.d.cts +1 -1
- package/dist/agent/index.d.ts +1 -1
- package/dist/agent/index.js +2 -2
- package/dist/client/index.cjs +220 -0
- package/dist/client/index.d.cts +101 -1
- package/dist/client/index.d.ts +101 -1
- package/dist/client/index.js +218 -2
- package/dist/index.cjs +278 -154
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +276 -156
- package/dist/next/index.cjs +86 -37
- package/dist/next/index.js +86 -37
- package/dist/pricing/index.cjs +54 -38
- package/dist/pricing/index.d.cts +8 -9
- package/dist/pricing/index.d.ts +8 -9
- package/dist/pricing/index.js +54 -38
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ export * from '@x402/core/client';
|
|
|
4
4
|
export * from '@x402/svm';
|
|
5
5
|
import { decodePaymentRequiredHeader, encodePaymentSignatureHeader } from '@x402/core/http';
|
|
6
6
|
import { PublicKey, Transaction, SystemProgram, LAMPORTS_PER_SOL, Keypair, TransactionMessage, VersionedTransaction, ComputeBudgetProgram } from '@solana/web3.js';
|
|
7
|
-
import { useState, useCallback, useEffect } from 'react';
|
|
7
|
+
import { useState, useCallback, useEffect, useRef } from 'react';
|
|
8
8
|
import bs58 from 'bs58';
|
|
9
9
|
import { SignJWT, jwtVerify } from 'jose';
|
|
10
10
|
|
|
@@ -41,8 +41,8 @@ function buildSolanaPayUrl(params) {
|
|
|
41
41
|
}
|
|
42
42
|
return url.toString();
|
|
43
43
|
}
|
|
44
|
-
function createPaymentFlow(
|
|
45
|
-
const { network, recipientWallet, amount, asset = "native", memo } =
|
|
44
|
+
function createPaymentFlow(config) {
|
|
45
|
+
const { network, recipientWallet, amount, asset = "native", memo } = config;
|
|
46
46
|
let decimals = 9;
|
|
47
47
|
let mintAddress;
|
|
48
48
|
if (asset === "usdc") {
|
|
@@ -58,7 +58,7 @@ function createPaymentFlow(config2) {
|
|
|
58
58
|
const naturalAmount = Number(amount) / Math.pow(10, decimals);
|
|
59
59
|
return {
|
|
60
60
|
/** Get the payment configuration */
|
|
61
|
-
getConfig: () => ({ ...
|
|
61
|
+
getConfig: () => ({ ...config }),
|
|
62
62
|
/** Get amount in natural display units (e.g., 0.01 SOL) */
|
|
63
63
|
getDisplayAmount: () => naturalAmount,
|
|
64
64
|
/** Get amount formatted with symbol */
|
|
@@ -246,10 +246,265 @@ function usePaywallResource({
|
|
|
246
246
|
unlock
|
|
247
247
|
};
|
|
248
248
|
}
|
|
249
|
+
|
|
250
|
+
// src/pricing/utils.ts
|
|
251
|
+
function lamportsToSol(lamports) {
|
|
252
|
+
return Number(lamports) / 1e9;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// src/pricing/index.ts
|
|
256
|
+
var priceCache = null;
|
|
257
|
+
var currentConfig = {};
|
|
258
|
+
function configurePricing(newConfig) {
|
|
259
|
+
currentConfig = { ...currentConfig, ...newConfig };
|
|
260
|
+
}
|
|
261
|
+
var PROVIDERS = [
|
|
262
|
+
{
|
|
263
|
+
name: "coincap",
|
|
264
|
+
url: "https://api.coincap.io/v2/assets/solana",
|
|
265
|
+
parse: (data) => parseFloat(data.data?.priceUsd || "0")
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
name: "binance",
|
|
269
|
+
url: "https://api.binance.com/api/v3/ticker/price?symbol=SOLUSDT",
|
|
270
|
+
parse: (data) => parseFloat(data.price || "0")
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
name: "coingecko",
|
|
274
|
+
url: "https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd",
|
|
275
|
+
parse: (data) => data.solana?.usd || 0
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
name: "kraken",
|
|
279
|
+
url: "https://api.kraken.com/0/public/Ticker?pair=SOLUSD",
|
|
280
|
+
parse: (data) => parseFloat(data.result?.SOLUSD?.c?.[0] || "0")
|
|
281
|
+
}
|
|
282
|
+
];
|
|
283
|
+
async function fetchFromProvider(provider, timeout) {
|
|
284
|
+
const controller = new AbortController();
|
|
285
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
286
|
+
try {
|
|
287
|
+
const response = await fetch(provider.url, {
|
|
288
|
+
headers: { "Accept": "application/json" },
|
|
289
|
+
signal: controller.signal
|
|
290
|
+
});
|
|
291
|
+
if (!response.ok) {
|
|
292
|
+
throw new Error(`HTTP ${response.status}`);
|
|
293
|
+
}
|
|
294
|
+
const data = await response.json();
|
|
295
|
+
const price = provider.parse(data);
|
|
296
|
+
if (!price || price <= 0) {
|
|
297
|
+
throw new Error("Invalid price");
|
|
298
|
+
}
|
|
299
|
+
return { price, source: provider.name };
|
|
300
|
+
} finally {
|
|
301
|
+
clearTimeout(timeoutId);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
async function fetchPriceParallel(timeout) {
|
|
305
|
+
const promises = PROVIDERS.map(
|
|
306
|
+
(provider) => fetchFromProvider(provider, timeout).catch(() => null)
|
|
307
|
+
);
|
|
308
|
+
const results = await Promise.all(promises);
|
|
309
|
+
const validResult = results.find((r) => r !== null);
|
|
310
|
+
if (validResult) {
|
|
311
|
+
return validResult;
|
|
312
|
+
}
|
|
313
|
+
throw new Error("All providers failed");
|
|
314
|
+
}
|
|
315
|
+
async function fetchPriceSequential(timeout) {
|
|
316
|
+
for (const provider of PROVIDERS) {
|
|
317
|
+
try {
|
|
318
|
+
return await fetchFromProvider(provider, timeout);
|
|
319
|
+
} catch {
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
throw new Error("All providers failed");
|
|
324
|
+
}
|
|
325
|
+
async function getSolPrice() {
|
|
326
|
+
const cacheTTL = currentConfig.cacheTTL ?? 6e4;
|
|
327
|
+
const timeout = currentConfig.timeout ?? 3e3;
|
|
328
|
+
const useParallel = currentConfig.parallelFetch ?? true;
|
|
329
|
+
const now = Date.now();
|
|
330
|
+
if (priceCache && now - priceCache.timestamp < cacheTTL) {
|
|
331
|
+
return priceCache.data;
|
|
332
|
+
}
|
|
333
|
+
if (currentConfig.customProvider) {
|
|
334
|
+
try {
|
|
335
|
+
const price = await currentConfig.customProvider();
|
|
336
|
+
if (price > 0) {
|
|
337
|
+
const data = {
|
|
338
|
+
solPrice: price,
|
|
339
|
+
fetchedAt: /* @__PURE__ */ new Date(),
|
|
340
|
+
source: "custom"
|
|
341
|
+
};
|
|
342
|
+
priceCache = { data, timestamp: now };
|
|
343
|
+
return data;
|
|
344
|
+
}
|
|
345
|
+
} catch {
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
try {
|
|
349
|
+
const result = useParallel ? await fetchPriceParallel(timeout) : await fetchPriceSequential(timeout);
|
|
350
|
+
const data = {
|
|
351
|
+
solPrice: result.price,
|
|
352
|
+
fetchedAt: /* @__PURE__ */ new Date(),
|
|
353
|
+
source: result.source
|
|
354
|
+
};
|
|
355
|
+
priceCache = { data, timestamp: now };
|
|
356
|
+
return data;
|
|
357
|
+
} catch {
|
|
358
|
+
if (priceCache) {
|
|
359
|
+
return {
|
|
360
|
+
...priceCache.data,
|
|
361
|
+
source: `${priceCache.data.source} (stale)`
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
throw new Error(
|
|
365
|
+
"Failed to fetch SOL price from all providers. Configure a custom provider or ensure network connectivity."
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
async function lamportsToUsd(lamports) {
|
|
370
|
+
const { solPrice } = await getSolPrice();
|
|
371
|
+
const sol = Number(lamports) / 1e9;
|
|
372
|
+
return sol * solPrice;
|
|
373
|
+
}
|
|
374
|
+
async function usdToLamports(usd) {
|
|
375
|
+
const { solPrice } = await getSolPrice();
|
|
376
|
+
const sol = usd / solPrice;
|
|
377
|
+
return BigInt(Math.floor(sol * 1e9));
|
|
378
|
+
}
|
|
379
|
+
async function formatPriceDisplay(lamports) {
|
|
380
|
+
const { solPrice } = await getSolPrice();
|
|
381
|
+
const sol = Number(lamports) / 1e9;
|
|
382
|
+
const usd = sol * solPrice;
|
|
383
|
+
return `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`;
|
|
384
|
+
}
|
|
385
|
+
function formatPriceSync(lamports, solPrice) {
|
|
386
|
+
const sol = Number(lamports) / 1e9;
|
|
387
|
+
const usd = sol * solPrice;
|
|
388
|
+
return {
|
|
389
|
+
sol,
|
|
390
|
+
usd,
|
|
391
|
+
formatted: `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
function clearPriceCache() {
|
|
395
|
+
priceCache = null;
|
|
396
|
+
}
|
|
397
|
+
function getProviders() {
|
|
398
|
+
return PROVIDERS.map((p) => ({ name: p.name, url: p.url }));
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// src/client/hooks.ts
|
|
402
|
+
function usePricing(refreshIntervalMs = 6e4) {
|
|
403
|
+
const [priceData, setPriceData] = useState(null);
|
|
404
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
405
|
+
const [error, setError] = useState(null);
|
|
406
|
+
const intervalRef = useRef(null);
|
|
407
|
+
const fetchPrice = useCallback(async () => {
|
|
408
|
+
try {
|
|
409
|
+
setIsLoading(true);
|
|
410
|
+
setError(null);
|
|
411
|
+
const data = await getSolPrice();
|
|
412
|
+
setPriceData(data);
|
|
413
|
+
} catch (err) {
|
|
414
|
+
setError(err instanceof Error ? err.message : "Failed to fetch price");
|
|
415
|
+
} finally {
|
|
416
|
+
setIsLoading(false);
|
|
417
|
+
}
|
|
418
|
+
}, []);
|
|
419
|
+
useEffect(() => {
|
|
420
|
+
fetchPrice();
|
|
421
|
+
if (refreshIntervalMs > 0) {
|
|
422
|
+
intervalRef.current = setInterval(fetchPrice, refreshIntervalMs);
|
|
423
|
+
}
|
|
424
|
+
return () => {
|
|
425
|
+
if (intervalRef.current) {
|
|
426
|
+
clearInterval(intervalRef.current);
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
}, [fetchPrice, refreshIntervalMs]);
|
|
430
|
+
return {
|
|
431
|
+
solPrice: priceData?.solPrice ?? null,
|
|
432
|
+
source: priceData?.source ?? null,
|
|
433
|
+
fetchedAt: priceData?.fetchedAt ?? null,
|
|
434
|
+
isLoading,
|
|
435
|
+
error,
|
|
436
|
+
refresh: fetchPrice
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
function useLamportsToUsd(lamports) {
|
|
440
|
+
const { solPrice, isLoading } = usePricing();
|
|
441
|
+
if (isLoading || !solPrice || lamports === null) {
|
|
442
|
+
return { usd: null, formatted: null, isLoading };
|
|
443
|
+
}
|
|
444
|
+
const lamportsBigInt = typeof lamports === "number" ? BigInt(lamports) : lamports;
|
|
445
|
+
const sol = Number(lamportsBigInt) / 1e9;
|
|
446
|
+
const usd = sol * solPrice;
|
|
447
|
+
return {
|
|
448
|
+
usd,
|
|
449
|
+
formatted: `$${usd.toFixed(2)}`,
|
|
450
|
+
isLoading: false
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
function useMicropay() {
|
|
454
|
+
const [status, setStatus] = useState("idle");
|
|
455
|
+
const [error, setError] = useState(null);
|
|
456
|
+
const [signature, setSignature] = useState(null);
|
|
457
|
+
const reset = useCallback(() => {
|
|
458
|
+
setStatus("idle");
|
|
459
|
+
setError(null);
|
|
460
|
+
setSignature(null);
|
|
461
|
+
}, []);
|
|
462
|
+
const pay = useCallback(async (_options) => {
|
|
463
|
+
setStatus("pending");
|
|
464
|
+
setError(null);
|
|
465
|
+
try {
|
|
466
|
+
throw new Error(
|
|
467
|
+
"useMicropay requires implementation of onSign/onSend callbacks. See documentation for wallet adapter integration."
|
|
468
|
+
);
|
|
469
|
+
} catch (err) {
|
|
470
|
+
const errorMessage = err instanceof Error ? err.message : "Payment failed";
|
|
471
|
+
setError(errorMessage);
|
|
472
|
+
setStatus("error");
|
|
473
|
+
return { success: false, error: errorMessage };
|
|
474
|
+
}
|
|
475
|
+
}, []);
|
|
476
|
+
return {
|
|
477
|
+
status,
|
|
478
|
+
error,
|
|
479
|
+
signature,
|
|
480
|
+
pay,
|
|
481
|
+
reset
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
function useFormatPrice(lamports) {
|
|
485
|
+
const { solPrice, isLoading } = usePricing();
|
|
486
|
+
if (isLoading || !solPrice || lamports === null) {
|
|
487
|
+
return {
|
|
488
|
+
sol: null,
|
|
489
|
+
usd: null,
|
|
490
|
+
formatted: "Loading...",
|
|
491
|
+
isLoading
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
const lamportsBigInt = typeof lamports === "number" ? BigInt(lamports) : lamports;
|
|
495
|
+
const sol = Number(lamportsBigInt) / 1e9;
|
|
496
|
+
const usd = sol * solPrice;
|
|
497
|
+
return {
|
|
498
|
+
sol,
|
|
499
|
+
usd,
|
|
500
|
+
formatted: `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`,
|
|
501
|
+
isLoading: false
|
|
502
|
+
};
|
|
503
|
+
}
|
|
249
504
|
var DEFAULT_COMPUTE_UNITS = 2e5;
|
|
250
505
|
var DEFAULT_MICRO_LAMPORTS = 1e3;
|
|
251
|
-
function createPriorityFeeInstructions(
|
|
252
|
-
const { enabled = false, microLamports, computeUnits } =
|
|
506
|
+
function createPriorityFeeInstructions(config = {}) {
|
|
507
|
+
const { enabled = false, microLamports, computeUnits } = config;
|
|
253
508
|
if (!enabled) {
|
|
254
509
|
return [];
|
|
255
510
|
}
|
|
@@ -260,14 +515,14 @@ function createPriorityFeeInstructions(config2 = {}) {
|
|
|
260
515
|
instructions.push(ComputeBudgetProgram.setComputeUnitPrice({ microLamports: price }));
|
|
261
516
|
return instructions;
|
|
262
517
|
}
|
|
263
|
-
async function buildVersionedTransaction(
|
|
518
|
+
async function buildVersionedTransaction(config) {
|
|
264
519
|
const {
|
|
265
520
|
connection,
|
|
266
521
|
payer,
|
|
267
522
|
instructions,
|
|
268
523
|
priorityFee,
|
|
269
524
|
recentBlockhash
|
|
270
|
-
} =
|
|
525
|
+
} = config;
|
|
271
526
|
const priorityIxs = createPriorityFeeInstructions(priorityFee);
|
|
272
527
|
const allInstructions = [...priorityIxs, ...instructions];
|
|
273
528
|
let blockhash;
|
|
@@ -383,9 +638,9 @@ async function getAgentBalance(connection, agentKeypair) {
|
|
|
383
638
|
balanceSol: balance / LAMPORTS_PER_SOL
|
|
384
639
|
};
|
|
385
640
|
}
|
|
386
|
-
async function hasAgentSufficientBalance(connection, agentKeypair, requiredLamports) {
|
|
641
|
+
async function hasAgentSufficientBalance(connection, agentKeypair, requiredLamports, feeBufferLamports = 5000000n) {
|
|
387
642
|
const { balance } = await getAgentBalance(connection, agentKeypair);
|
|
388
|
-
const totalRequired = requiredLamports +
|
|
643
|
+
const totalRequired = requiredLamports + feeBufferLamports;
|
|
389
644
|
return {
|
|
390
645
|
sufficient: balance >= totalRequired,
|
|
391
646
|
balance,
|
|
@@ -434,20 +689,20 @@ function validateWalletAddress(address) {
|
|
|
434
689
|
const base58Regex = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
435
690
|
return base58Regex.test(address);
|
|
436
691
|
}
|
|
437
|
-
async function createCreditSession(walletAddress, purchaseId,
|
|
692
|
+
async function createCreditSession(walletAddress, purchaseId, config) {
|
|
438
693
|
if (!validateWalletAddress(walletAddress)) {
|
|
439
694
|
throw new Error("Invalid wallet address format");
|
|
440
695
|
}
|
|
441
|
-
if (
|
|
696
|
+
if (config.initialCredits <= 0 || config.initialCredits > MAX_CREDITS) {
|
|
442
697
|
throw new Error(`Credits must be between 1 and ${MAX_CREDITS}`);
|
|
443
698
|
}
|
|
444
|
-
if (!
|
|
699
|
+
if (!config.durationHours || config.durationHours <= 0 || config.durationHours > 8760) {
|
|
445
700
|
throw new Error("Session duration must be between 1 and 8760 hours (1 year)");
|
|
446
701
|
}
|
|
447
702
|
const sessionId = uuidv4();
|
|
448
703
|
const now = Math.floor(Date.now() / 1e3);
|
|
449
|
-
const expiresAt = now +
|
|
450
|
-
const bundleExpiry =
|
|
704
|
+
const expiresAt = now + config.durationHours * 3600;
|
|
705
|
+
const bundleExpiry = config.bundleExpiryHours ? now + config.bundleExpiryHours * 3600 : expiresAt;
|
|
451
706
|
const session = {
|
|
452
707
|
id: sessionId,
|
|
453
708
|
walletAddress,
|
|
@@ -455,22 +710,22 @@ async function createCreditSession(walletAddress, purchaseId, config2) {
|
|
|
455
710
|
siteWideUnlock: false,
|
|
456
711
|
createdAt: now,
|
|
457
712
|
expiresAt,
|
|
458
|
-
credits:
|
|
713
|
+
credits: config.initialCredits,
|
|
459
714
|
bundleExpiry,
|
|
460
|
-
bundleType:
|
|
715
|
+
bundleType: config.bundleType
|
|
461
716
|
};
|
|
462
717
|
const payload = {
|
|
463
718
|
sub: walletAddress,
|
|
464
719
|
sid: sessionId,
|
|
465
720
|
articles: session.unlockedArticles,
|
|
466
721
|
siteWide: false,
|
|
467
|
-
credits:
|
|
722
|
+
credits: config.initialCredits,
|
|
468
723
|
bundleExpiry,
|
|
469
|
-
bundleType:
|
|
724
|
+
bundleType: config.bundleType,
|
|
470
725
|
iat: now,
|
|
471
726
|
exp: expiresAt
|
|
472
727
|
};
|
|
473
|
-
const token = await new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime(`${
|
|
728
|
+
const token = await new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime(`${config.durationHours}h`).sign(getSecretKey(config.secret));
|
|
474
729
|
return { token, session };
|
|
475
730
|
}
|
|
476
731
|
async function validateCreditSession(token, secret) {
|
|
@@ -588,139 +843,4 @@ async function getRemainingCredits(token, secret) {
|
|
|
588
843
|
};
|
|
589
844
|
}
|
|
590
845
|
|
|
591
|
-
|
|
592
|
-
function lamportsToSol(lamports) {
|
|
593
|
-
return Number(lamports) / 1e9;
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
// src/pricing/index.ts
|
|
597
|
-
var cachedPrice = null;
|
|
598
|
-
var config = {};
|
|
599
|
-
var lastProviderIndex = -1;
|
|
600
|
-
function configurePricing(newConfig) {
|
|
601
|
-
config = { ...config, ...newConfig };
|
|
602
|
-
cachedPrice = null;
|
|
603
|
-
}
|
|
604
|
-
var PROVIDERS = [
|
|
605
|
-
{
|
|
606
|
-
name: "coincap",
|
|
607
|
-
url: "https://api.coincap.io/v2/assets/solana",
|
|
608
|
-
parse: (data) => parseFloat(data.data?.priceUsd || "0")
|
|
609
|
-
},
|
|
610
|
-
{
|
|
611
|
-
name: "binance",
|
|
612
|
-
url: "https://api.binance.com/api/v3/ticker/price?symbol=SOLUSDT",
|
|
613
|
-
parse: (data) => parseFloat(data.price || "0")
|
|
614
|
-
},
|
|
615
|
-
{
|
|
616
|
-
name: "coingecko",
|
|
617
|
-
url: "https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd",
|
|
618
|
-
parse: (data) => data.solana?.usd || 0
|
|
619
|
-
},
|
|
620
|
-
{
|
|
621
|
-
name: "kraken",
|
|
622
|
-
url: "https://api.kraken.com/0/public/Ticker?pair=SOLUSD",
|
|
623
|
-
parse: (data) => parseFloat(data.result?.SOLUSD?.c?.[0] || "0")
|
|
624
|
-
}
|
|
625
|
-
];
|
|
626
|
-
async function fetchFromProvider(provider, timeout) {
|
|
627
|
-
const controller = new AbortController();
|
|
628
|
-
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
629
|
-
try {
|
|
630
|
-
const response = await fetch(provider.url, {
|
|
631
|
-
headers: { "Accept": "application/json" },
|
|
632
|
-
signal: controller.signal
|
|
633
|
-
});
|
|
634
|
-
if (!response.ok) {
|
|
635
|
-
throw new Error(`HTTP ${response.status}`);
|
|
636
|
-
}
|
|
637
|
-
const data = await response.json();
|
|
638
|
-
const price = provider.parse(data);
|
|
639
|
-
if (!price || price <= 0) {
|
|
640
|
-
throw new Error("Invalid price");
|
|
641
|
-
}
|
|
642
|
-
return price;
|
|
643
|
-
} finally {
|
|
644
|
-
clearTimeout(timeoutId);
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
async function getSolPrice() {
|
|
648
|
-
const cacheTTL = config.cacheTTL ?? 6e4;
|
|
649
|
-
const timeout = config.timeout ?? 5e3;
|
|
650
|
-
if (cachedPrice && Date.now() - cachedPrice.fetchedAt.getTime() < cacheTTL) {
|
|
651
|
-
return cachedPrice;
|
|
652
|
-
}
|
|
653
|
-
if (config.customProvider) {
|
|
654
|
-
try {
|
|
655
|
-
const price = await config.customProvider();
|
|
656
|
-
if (price > 0) {
|
|
657
|
-
cachedPrice = {
|
|
658
|
-
solPrice: price,
|
|
659
|
-
fetchedAt: /* @__PURE__ */ new Date(),
|
|
660
|
-
source: "custom"
|
|
661
|
-
};
|
|
662
|
-
return cachedPrice;
|
|
663
|
-
}
|
|
664
|
-
} catch {
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
for (let i = 0; i < PROVIDERS.length; i++) {
|
|
668
|
-
const idx = (lastProviderIndex + 1 + i) % PROVIDERS.length;
|
|
669
|
-
const provider = PROVIDERS[idx];
|
|
670
|
-
try {
|
|
671
|
-
const price = await fetchFromProvider(provider, timeout);
|
|
672
|
-
lastProviderIndex = idx;
|
|
673
|
-
cachedPrice = {
|
|
674
|
-
solPrice: price,
|
|
675
|
-
fetchedAt: /* @__PURE__ */ new Date(),
|
|
676
|
-
source: provider.name
|
|
677
|
-
};
|
|
678
|
-
return cachedPrice;
|
|
679
|
-
} catch {
|
|
680
|
-
continue;
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
if (cachedPrice) {
|
|
684
|
-
return {
|
|
685
|
-
...cachedPrice,
|
|
686
|
-
source: `${cachedPrice.source} (stale)`
|
|
687
|
-
};
|
|
688
|
-
}
|
|
689
|
-
throw new Error(
|
|
690
|
-
"Failed to fetch SOL price from all providers. Configure a custom provider or ensure network connectivity."
|
|
691
|
-
);
|
|
692
|
-
}
|
|
693
|
-
async function lamportsToUsd(lamports) {
|
|
694
|
-
const { solPrice } = await getSolPrice();
|
|
695
|
-
const sol = Number(lamports) / 1e9;
|
|
696
|
-
return sol * solPrice;
|
|
697
|
-
}
|
|
698
|
-
async function usdToLamports(usd) {
|
|
699
|
-
const { solPrice } = await getSolPrice();
|
|
700
|
-
const sol = usd / solPrice;
|
|
701
|
-
return BigInt(Math.floor(sol * 1e9));
|
|
702
|
-
}
|
|
703
|
-
async function formatPriceDisplay(lamports) {
|
|
704
|
-
const { solPrice } = await getSolPrice();
|
|
705
|
-
const sol = Number(lamports) / 1e9;
|
|
706
|
-
const usd = sol * solPrice;
|
|
707
|
-
return `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`;
|
|
708
|
-
}
|
|
709
|
-
function formatPriceSync(lamports, solPrice) {
|
|
710
|
-
const sol = Number(lamports) / 1e9;
|
|
711
|
-
const usd = sol * solPrice;
|
|
712
|
-
return {
|
|
713
|
-
sol,
|
|
714
|
-
usd,
|
|
715
|
-
formatted: `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`
|
|
716
|
-
};
|
|
717
|
-
}
|
|
718
|
-
function clearPriceCache() {
|
|
719
|
-
cachedPrice = null;
|
|
720
|
-
lastProviderIndex = -1;
|
|
721
|
-
}
|
|
722
|
-
function getProviders() {
|
|
723
|
-
return PROVIDERS.map((p) => ({ name: p.name, url: p.url }));
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
export { addCredits, buildSolanaPayUrl, clearPriceCache, configurePricing, createCreditSession, createPaymentFlow, createPaymentReference, createX402AuthorizationHeader, executeAgentPayment, formatPriceDisplay, formatPriceSync, generateAgentKeypair, getAgentBalance, getProviders, getRemainingCredits, getSolPrice, hasAgentSufficientBalance, keypairFromBase58, lamportsToSol, lamportsToUsd, sendSolanaPayment, usdToLamports, useCredit, usePaywallResource, validateCreditSession };
|
|
846
|
+
export { 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 };
|