@relai-fi/x402 0.5.14 → 0.5.15

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.cjs CHANGED
@@ -301,6 +301,9 @@ function stripePayTo(stripeSecretKey, options) {
301
301
  }
302
302
  var USD_PRICE_CACHE_TTL_MS = 60 * 1e3;
303
303
  var usdPriceCache = /* @__PURE__ */ new Map();
304
+ var GECKOTERMINAL_NETWORK_BY_RELAI = {
305
+ polygon: "polygon_pos"
306
+ };
304
307
  var COINGECKO_ID_BY_SYMBOL = {
305
308
  WETH: "ethereum",
306
309
  WBTC: "bitcoin",
@@ -338,6 +341,42 @@ async function fetchUsdPriceFromCoinGecko(coinId) {
338
341
  });
339
342
  return usd;
340
343
  }
344
+ async function fetchUsdPriceFromGeckoTerminal(network, tokenAddress) {
345
+ const geckoNetwork = GECKOTERMINAL_NETWORK_BY_RELAI[network];
346
+ if (!geckoNetwork) {
347
+ throw new Error(`GeckoTerminal network not configured for ${network}`);
348
+ }
349
+ const normalizedAddress = String(tokenAddress || "").trim().toLowerCase();
350
+ if (!normalizedAddress) {
351
+ throw new Error("GeckoTerminal requires a token address");
352
+ }
353
+ const cacheKey = `gt:${geckoNetwork}:${normalizedAddress}`;
354
+ const now = Date.now();
355
+ const cached = usdPriceCache.get(cacheKey);
356
+ if (cached && cached.expiresAt > now) {
357
+ return cached.usd;
358
+ }
359
+ const url = `https://api.geckoterminal.com/api/v2/networks/${encodeURIComponent(geckoNetwork)}/tokens/${encodeURIComponent(normalizedAddress)}`;
360
+ const res = await fetch(url, {
361
+ method: "GET",
362
+ headers: {
363
+ Accept: "application/json"
364
+ }
365
+ });
366
+ if (!res.ok) {
367
+ throw new Error(`GeckoTerminal price request failed: HTTP ${res.status}`);
368
+ }
369
+ const payload = await res.json();
370
+ const usd = Number(payload?.data?.attributes?.price_usd);
371
+ if (!Number.isFinite(usd) || usd <= 0) {
372
+ throw new Error(`GeckoTerminal returned invalid usd price for ${normalizedAddress} on ${geckoNetwork}`);
373
+ }
374
+ usdPriceCache.set(cacheKey, {
375
+ usd,
376
+ expiresAt: now + USD_PRICE_CACHE_TTL_MS
377
+ });
378
+ return usd;
379
+ }
341
380
  async function resolveAmountAtomicFromUsd({
342
381
  priceUsd,
343
382
  token,
@@ -357,7 +396,22 @@ async function resolveAmountAtomicFromUsd({
357
396
  throw new Error(`No USD quote source configured for token ${symbol || token.address} on ${network}`);
358
397
  }
359
398
  const overrideEnv = process.env[`EVM_TOKEN_PRICE_${symbol}_USD`];
360
- const usdPerToken = overrideEnv && Number(overrideEnv) > 0 ? Number(overrideEnv) : await fetchUsdPriceFromCoinGecko(coinGeckoId);
399
+ let usdPerToken;
400
+ if (overrideEnv && Number(overrideEnv) > 0) {
401
+ usdPerToken = Number(overrideEnv);
402
+ } else {
403
+ try {
404
+ usdPerToken = await fetchUsdPriceFromCoinGecko(coinGeckoId);
405
+ } catch (coinGeckoError) {
406
+ try {
407
+ usdPerToken = await fetchUsdPriceFromGeckoTerminal(network, token.address);
408
+ } catch (geckoTerminalError) {
409
+ throw new Error(
410
+ `USD quote failed for ${symbol || token.address} on ${network}. CoinGecko: ${coinGeckoError instanceof Error ? coinGeckoError.message : String(coinGeckoError)}; GeckoTerminal: ${geckoTerminalError instanceof Error ? geckoTerminalError.message : String(geckoTerminalError)}`
411
+ );
412
+ }
413
+ }
414
+ }
361
415
  const tokenAmount = priceUsd / usdPerToken;
362
416
  const rawUnits = tokenAmount * Math.pow(10, decimals);
363
417
  if (!Number.isFinite(rawUnits) || rawUnits <= 0) {