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

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.
@@ -2,43 +2,100 @@
2
2
 
3
3
  // src/pricing/index.ts
4
4
  var cachedPrice = null;
5
- var CACHE_TTL_MS = 6e4;
6
- async function getSolPrice() {
7
- if (cachedPrice && Date.now() - cachedPrice.fetchedAt.getTime() < CACHE_TTL_MS) {
8
- return cachedPrice;
5
+ var config = {};
6
+ var lastProviderIndex = -1;
7
+ function configurePricing(newConfig) {
8
+ config = { ...config, ...newConfig };
9
+ cachedPrice = null;
10
+ }
11
+ var PROVIDERS = [
12
+ {
13
+ name: "coincap",
14
+ url: "https://api.coincap.io/v2/assets/solana",
15
+ parse: (data) => parseFloat(data.data?.priceUsd || "0")
16
+ },
17
+ {
18
+ name: "binance",
19
+ url: "https://api.binance.com/api/v3/ticker/price?symbol=SOLUSDT",
20
+ parse: (data) => parseFloat(data.price || "0")
21
+ },
22
+ {
23
+ name: "coingecko",
24
+ url: "https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd",
25
+ parse: (data) => data.solana?.usd || 0
26
+ },
27
+ {
28
+ name: "kraken",
29
+ url: "https://api.kraken.com/0/public/Ticker?pair=SOLUSD",
30
+ parse: (data) => parseFloat(data.result?.SOLUSD?.c?.[0] || "0")
9
31
  }
32
+ ];
33
+ async function fetchFromProvider(provider, timeout) {
34
+ const controller = new AbortController();
35
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
10
36
  try {
11
- const response = await fetch(
12
- "https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd",
13
- {
14
- headers: { "Accept": "application/json" },
15
- signal: AbortSignal.timeout(5e3)
16
- }
17
- );
37
+ const response = await fetch(provider.url, {
38
+ headers: { "Accept": "application/json" },
39
+ signal: controller.signal
40
+ });
18
41
  if (!response.ok) {
19
- throw new Error(`Price fetch failed: ${response.status}`);
42
+ throw new Error(`HTTP ${response.status}`);
20
43
  }
21
44
  const data = await response.json();
22
- if (!data.solana?.usd) {
23
- throw new Error("Invalid price response");
45
+ const price = provider.parse(data);
46
+ if (!price || price <= 0) {
47
+ throw new Error("Invalid price");
24
48
  }
25
- cachedPrice = {
26
- solPrice: data.solana.usd,
27
- fetchedAt: /* @__PURE__ */ new Date(),
28
- source: "coingecko"
29
- };
49
+ return price;
50
+ } finally {
51
+ clearTimeout(timeoutId);
52
+ }
53
+ }
54
+ async function getSolPrice() {
55
+ const cacheTTL = config.cacheTTL ?? 6e4;
56
+ const timeout = config.timeout ?? 5e3;
57
+ if (cachedPrice && Date.now() - cachedPrice.fetchedAt.getTime() < cacheTTL) {
30
58
  return cachedPrice;
31
- } catch (error) {
32
- if (cachedPrice) {
59
+ }
60
+ if (config.customProvider) {
61
+ try {
62
+ const price = await config.customProvider();
63
+ if (price > 0) {
64
+ cachedPrice = {
65
+ solPrice: price,
66
+ fetchedAt: /* @__PURE__ */ new Date(),
67
+ source: "custom"
68
+ };
69
+ return cachedPrice;
70
+ }
71
+ } catch {
72
+ }
73
+ }
74
+ for (let i = 0; i < PROVIDERS.length; i++) {
75
+ const idx = (lastProviderIndex + 1 + i) % PROVIDERS.length;
76
+ const provider = PROVIDERS[idx];
77
+ try {
78
+ const price = await fetchFromProvider(provider, timeout);
79
+ lastProviderIndex = idx;
80
+ cachedPrice = {
81
+ solPrice: price,
82
+ fetchedAt: /* @__PURE__ */ new Date(),
83
+ source: provider.name
84
+ };
33
85
  return cachedPrice;
86
+ } catch {
87
+ continue;
34
88
  }
35
- return {
36
- solPrice: 150,
37
- // Fallback price
38
- fetchedAt: /* @__PURE__ */ new Date(),
39
- source: "fallback"
40
- };
41
89
  }
90
+ if (cachedPrice) {
91
+ return cachedPrice;
92
+ }
93
+ return {
94
+ solPrice: 150,
95
+ // Reasonable fallback
96
+ fetchedAt: /* @__PURE__ */ new Date(),
97
+ source: "fallback"
98
+ };
42
99
  }
43
100
  async function lamportsToUsd(lamports) {
44
101
  const { solPrice } = await getSolPrice();
@@ -67,11 +124,17 @@ function formatPriceSync(lamports, solPrice) {
67
124
  }
68
125
  function clearPriceCache() {
69
126
  cachedPrice = null;
127
+ lastProviderIndex = -1;
128
+ }
129
+ function getProviders() {
130
+ return PROVIDERS.map((p) => ({ name: p.name, url: p.url }));
70
131
  }
71
132
 
72
133
  exports.clearPriceCache = clearPriceCache;
134
+ exports.configurePricing = configurePricing;
73
135
  exports.formatPriceDisplay = formatPriceDisplay;
74
136
  exports.formatPriceSync = formatPriceSync;
137
+ exports.getProviders = getProviders;
75
138
  exports.getSolPrice = getSolPrice;
76
139
  exports.lamportsToUsd = lamportsToUsd;
77
140
  exports.usdToLamports = usdToLamports;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/pricing/index.ts"],"names":[],"mappings":";;;AAkBA,IAAI,WAAA,GAAgC,IAAA;AACpC,IAAM,YAAA,GAAe,GAAA;AAYrB,eAAsB,WAAA,GAAkC;AAEpD,EAAA,IAAI,WAAA,IAAe,KAAK,GAAA,EAAI,GAAI,YAAY,SAAA,CAAU,OAAA,KAAY,YAAA,EAAc;AAC5E,IAAA,OAAO,WAAA;AAAA,EACX;AAEA,EAAA,IAAI;AAEA,IAAA,MAAM,WAAW,MAAM,KAAA;AAAA,MACnB,4EAAA;AAAA,MACA;AAAA,QACI,OAAA,EAAS,EAAE,QAAA,EAAU,kBAAA,EAAmB;AAAA,QACxC,MAAA,EAAQ,WAAA,CAAY,OAAA,CAAQ,GAAI;AAAA;AACpC,KACJ;AAEA,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AACd,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,oBAAA,EAAuB,QAAA,CAAS,MAAM,CAAA,CAAE,CAAA;AAAA,IAC5D;AAEA,IAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AAEjC,IAAA,IAAI,CAAC,IAAA,CAAK,MAAA,EAAQ,GAAA,EAAK;AACnB,MAAA,MAAM,IAAI,MAAM,wBAAwB,CAAA;AAAA,IAC5C;AAEA,IAAA,WAAA,GAAc;AAAA,MACV,QAAA,EAAU,KAAK,MAAA,CAAO,GAAA;AAAA,MACtB,SAAA,sBAAe,IAAA,EAAK;AAAA,MACpB,MAAA,EAAQ;AAAA,KACZ;AAEA,IAAA,OAAO,WAAA;AAAA,EACX,SAAS,KAAA,EAAO;AAEZ,IAAA,IAAI,WAAA,EAAa;AACb,MAAA,OAAO,WAAA;AAAA,IACX;AAGA,IAAA,OAAO;AAAA,MACH,QAAA,EAAU,GAAA;AAAA;AAAA,MACV,SAAA,sBAAe,IAAA,EAAK;AAAA,MACpB,MAAA,EAAQ;AAAA,KACZ;AAAA,EACJ;AACJ;AAWA,eAAsB,cAAc,QAAA,EAAmC;AACnE,EAAA,MAAM,EAAE,QAAA,EAAS,GAAI,MAAM,WAAA,EAAY;AACvC,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,QAAQ,CAAA,GAAI,GAAA;AAC/B,EAAA,OAAO,GAAA,GAAM,QAAA;AACjB;AAWA,eAAsB,cAAc,GAAA,EAA8B;AAC9D,EAAA,MAAM,EAAE,QAAA,EAAS,GAAI,MAAM,WAAA,EAAY;AACvC,EAAA,MAAM,MAAM,GAAA,GAAM,QAAA;AAClB,EAAA,OAAO,MAAA,CAAO,IAAA,CAAK,KAAA,CAAM,GAAA,GAAM,GAAa,CAAC,CAAA;AACjD;AAWA,eAAsB,mBAAmB,QAAA,EAAmC;AACxE,EAAA,MAAM,EAAE,QAAA,EAAS,GAAI,MAAM,WAAA,EAAY;AACvC,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,QAAQ,CAAA,GAAI,GAAA;AAC/B,EAAA,MAAM,MAAM,GAAA,GAAM,QAAA;AAElB,EAAA,OAAO,CAAA,EAAG,IAAI,OAAA,CAAQ,CAAC,CAAC,CAAA,QAAA,EAAW,GAAA,CAAI,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAA,CAAA;AACrD;AAMO,SAAS,eAAA,CAAgB,UAAkB,QAAA,EAIhD;AACE,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,QAAQ,CAAA,GAAI,GAAA;AAC/B,EAAA,MAAM,MAAM,GAAA,GAAM,QAAA;AAElB,EAAA,OAAO;AAAA,IACH,GAAA;AAAA,IACA,GAAA;AAAA,IACA,SAAA,EAAW,CAAA,EAAG,GAAA,CAAI,OAAA,CAAQ,CAAC,CAAC,CAAA,QAAA,EAAW,GAAA,CAAI,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAA;AAAA,GACzD;AACJ;AAKO,SAAS,eAAA,GAAwB;AACpC,EAAA,WAAA,GAAc,IAAA;AAClB","file":"index.cjs","sourcesContent":["// Price Conversion Helpers\n// Fetch SOL price and convert between USD/SOL\n\n/**\n * Price data from API\n */\nexport interface PriceData {\n /** SOL price in USD */\n solPrice: number;\n /** Timestamp of price fetch */\n fetchedAt: Date;\n /** Source of price data */\n source: string;\n}\n\n/**\n * Cached price data\n */\nlet cachedPrice: PriceData | null = null;\nconst CACHE_TTL_MS = 60000; // 1 minute cache\n\n/**\n * Fetch current SOL price from CoinGecko\n * Includes caching to avoid rate limits\n * \n * @example\n * ```typescript\n * const { solPrice } = await getSolPrice();\n * console.log(`SOL is $${solPrice}`);\n * ```\n */\nexport async function getSolPrice(): Promise<PriceData> {\n // Return cached price if still valid\n if (cachedPrice && Date.now() - cachedPrice.fetchedAt.getTime() < CACHE_TTL_MS) {\n return cachedPrice;\n }\n\n try {\n // CoinGecko free API (no key required, 10-30 calls/min)\n const response = await fetch(\n 'https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd',\n {\n headers: { 'Accept': 'application/json' },\n signal: AbortSignal.timeout(5000),\n }\n );\n\n if (!response.ok) {\n throw new Error(`Price fetch failed: ${response.status}`);\n }\n\n const data = await response.json() as { solana?: { usd?: number } };\n\n if (!data.solana?.usd) {\n throw new Error('Invalid price response');\n }\n\n cachedPrice = {\n solPrice: data.solana.usd,\n fetchedAt: new Date(),\n source: 'coingecko',\n };\n\n return cachedPrice;\n } catch (error) {\n // If fetch fails and we have stale cache, use it\n if (cachedPrice) {\n return cachedPrice;\n }\n\n // Fallback to a reasonable estimate (update periodically)\n return {\n solPrice: 150, // Fallback price\n fetchedAt: new Date(),\n source: 'fallback',\n };\n }\n}\n\n/**\n * Convert lamports to USD\n * \n * @example\n * ```typescript\n * const usd = await lamportsToUsd(10000000n); // 0.01 SOL\n * console.log(`$${usd.toFixed(2)}`);\n * ```\n */\nexport async function lamportsToUsd(lamports: bigint): Promise<number> {\n const { solPrice } = await getSolPrice();\n const sol = Number(lamports) / 1_000_000_000; // LAMPORTS_PER_SOL\n return sol * solPrice;\n}\n\n/**\n * Convert USD to lamports\n * \n * @example\n * ```typescript\n * const lamports = await usdToLamports(1.50); // $1.50\n * console.log(`${lamports} lamports`);\n * ```\n */\nexport async function usdToLamports(usd: number): Promise<bigint> {\n const { solPrice } = await getSolPrice();\n const sol = usd / solPrice;\n return BigInt(Math.floor(sol * 1_000_000_000));\n}\n\n/**\n * Format a price for display with both SOL and USD\n * \n * @example\n * ```typescript\n * const display = await formatPriceDisplay(10000000n);\n * // Returns: \"0.01 SOL (~$1.50)\"\n * ```\n */\nexport async function formatPriceDisplay(lamports: bigint): Promise<string> {\n const { solPrice } = await getSolPrice();\n const sol = Number(lamports) / 1_000_000_000;\n const usd = sol * solPrice;\n\n return `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`;\n}\n\n/**\n * Synchronous price formatting (requires pre-fetched price)\n * Use this when you've already fetched the price\n */\nexport function formatPriceSync(lamports: bigint, solPrice: number): {\n sol: number;\n usd: number;\n formatted: string;\n} {\n const sol = Number(lamports) / 1_000_000_000;\n const usd = sol * solPrice;\n\n return {\n sol,\n usd,\n formatted: `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`,\n };\n}\n\n/**\n * Clear the price cache (for testing or manual refresh)\n */\nexport function clearPriceCache(): void {\n cachedPrice = null;\n}\n"]}
1
+ {"version":3,"sources":["../../src/pricing/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["// Price Conversion Helpers\n// Multi-provider SOL price fetching with fallback rotation\n\n/**\n * Price data from API\n */\nexport interface PriceData {\n /** SOL price in USD */\n solPrice: number;\n /** Timestamp of price fetch */\n fetchedAt: Date;\n /** Source of price data */\n source: string;\n}\n\n/**\n * Custom price provider function type\n */\nexport type CustomPriceProvider = () => Promise<number>;\n\n/**\n * Price provider configuration\n */\nexport interface PriceConfig {\n /** Custom price provider (if set, skips built-in providers) */\n customProvider?: CustomPriceProvider;\n /** Cache TTL in milliseconds (default: 60000) */\n cacheTTL?: number;\n /** Request timeout in milliseconds (default: 5000) */\n timeout?: number;\n}\n\n// Cached price data\nlet cachedPrice: PriceData | null = null;\nlet config: PriceConfig = {};\nlet lastProviderIndex = -1;\n\n/**\n * Configure price fetching\n * \n * @example\n * ```typescript\n * // Use custom API\n * configurePricing({\n * customProvider: async () => {\n * const res = await fetch('https://my-api.com/sol-price');\n * return (await res.json()).price;\n * },\n * });\n * \n * // Or just adjust cache TTL\n * configurePricing({ cacheTTL: 30000 }); // 30 seconds\n * ```\n */\nexport function configurePricing(newConfig: PriceConfig): void {\n config = { ...config, ...newConfig };\n cachedPrice = null; // Clear cache on config change\n}\n\n/**\n * Built-in price providers with reliability rotation\n */\nconst PROVIDERS = [\n {\n name: 'coincap',\n url: 'https://api.coincap.io/v2/assets/solana',\n parse: (data: { data?: { priceUsd?: string } }) => parseFloat(data.data?.priceUsd || '0'),\n },\n {\n name: 'binance',\n url: 'https://api.binance.com/api/v3/ticker/price?symbol=SOLUSDT',\n parse: (data: { price?: string }) => parseFloat(data.price || '0'),\n },\n {\n name: 'coingecko',\n url: 'https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd',\n parse: (data: { solana?: { usd?: number } }) => data.solana?.usd || 0,\n },\n {\n name: 'kraken',\n url: 'https://api.kraken.com/0/public/Ticker?pair=SOLUSD',\n parse: (data: { result?: { SOLUSD?: { c?: string[] } } }) =>\n parseFloat(data.result?.SOLUSD?.c?.[0] || '0'),\n },\n];\n\n/**\n * Fetch price from a single provider\n */\nasync function fetchFromProvider(\n provider: typeof PROVIDERS[0],\n timeout: number\n): Promise<number> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(provider.url, {\n headers: { 'Accept': 'application/json' },\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n\n const data = await response.json() as Record<string, unknown>;\n const price = provider.parse(data as Parameters<typeof provider.parse>[0]);\n\n if (!price || price <= 0) {\n throw new Error('Invalid price');\n }\n\n return price;\n } finally {\n clearTimeout(timeoutId);\n }\n}\n\n/**\n * Get SOL price with multi-provider fallback\n * \n * Provider rotation order:\n * 1. CoinCap (primary)\n * 2. Binance (backup #1)\n * 3. CoinGecko (backup #2)\n * 4. Kraken (backup #3)\n * \n * @example\n * ```typescript\n * const { solPrice, source } = await getSolPrice();\n * console.log(`SOL is $${solPrice} (from ${source})`);\n * ```\n */\nexport async function getSolPrice(): Promise<PriceData> {\n const cacheTTL = config.cacheTTL ?? 60000;\n const timeout = config.timeout ?? 5000;\n\n // Return cached price if valid\n if (cachedPrice && Date.now() - cachedPrice.fetchedAt.getTime() < cacheTTL) {\n return cachedPrice;\n }\n\n // Try custom provider first if configured\n if (config.customProvider) {\n try {\n const price = await config.customProvider();\n if (price > 0) {\n cachedPrice = {\n solPrice: price,\n fetchedAt: new Date(),\n source: 'custom',\n };\n return cachedPrice;\n }\n } catch {\n // Fall through to built-in providers\n }\n }\n\n // Try providers in rotation, starting after the last successful one\n for (let i = 0; i < PROVIDERS.length; i++) {\n const idx = (lastProviderIndex + 1 + i) % PROVIDERS.length;\n const provider = PROVIDERS[idx];\n\n try {\n const price = await fetchFromProvider(provider, timeout);\n lastProviderIndex = idx;\n\n cachedPrice = {\n solPrice: price,\n fetchedAt: new Date(),\n source: provider.name,\n };\n return cachedPrice;\n } catch {\n // Try next provider\n continue;\n }\n }\n\n // All providers failed, use stale cache or fallback\n if (cachedPrice) {\n return cachedPrice;\n }\n\n // Last resort fallback\n return {\n solPrice: 150, // Reasonable fallback\n fetchedAt: new Date(),\n source: 'fallback',\n };\n}\n\n/**\n * Convert lamports to USD\n * \n * @example\n * ```typescript\n * const usd = await lamportsToUsd(10_000_000n); // 0.01 SOL\n * console.log(`$${usd.toFixed(2)}`);\n * ```\n */\nexport async function lamportsToUsd(lamports: bigint): Promise<number> {\n const { solPrice } = await getSolPrice();\n const sol = Number(lamports) / 1_000_000_000;\n return sol * solPrice;\n}\n\n/**\n * Convert USD to lamports\n * \n * @example\n * ```typescript\n * const lamports = await usdToLamports(1.50); // $1.50\n * console.log(`${lamports} lamports`);\n * ```\n */\nexport async function usdToLamports(usd: number): Promise<bigint> {\n const { solPrice } = await getSolPrice();\n const sol = usd / solPrice;\n return BigInt(Math.floor(sol * 1_000_000_000));\n}\n\n/**\n * Format a price for display with both SOL and USD\n * \n * @example\n * ```typescript\n * const display = await formatPriceDisplay(10_000_000n);\n * // Returns: \"0.0100 SOL (~$1.50)\"\n * ```\n */\nexport async function formatPriceDisplay(lamports: bigint): Promise<string> {\n const { solPrice } = await getSolPrice();\n const sol = Number(lamports) / 1_000_000_000;\n const usd = sol * solPrice;\n\n return `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`;\n}\n\n/**\n * Synchronous price formatting (requires pre-fetched price)\n */\nexport function formatPriceSync(lamports: bigint, solPrice: number): {\n sol: number;\n usd: number;\n formatted: string;\n} {\n const sol = Number(lamports) / 1_000_000_000;\n const usd = sol * solPrice;\n\n return {\n sol,\n usd,\n formatted: `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`,\n };\n}\n\n/**\n * Clear the price cache (for testing or manual refresh)\n */\nexport function clearPriceCache(): void {\n cachedPrice = null;\n lastProviderIndex = -1;\n}\n\n/**\n * Get list of available built-in providers\n */\nexport function getProviders(): { name: string; url: string }[] {\n return PROVIDERS.map(p => ({ name: p.name, url: p.url }));\n}\n"]}
@@ -10,13 +10,51 @@ interface PriceData {
10
10
  source: string;
11
11
  }
12
12
  /**
13
- * Fetch current SOL price from CoinGecko
14
- * Includes caching to avoid rate limits
13
+ * Custom price provider function type
14
+ */
15
+ type CustomPriceProvider = () => Promise<number>;
16
+ /**
17
+ * Price provider configuration
18
+ */
19
+ interface PriceConfig {
20
+ /** Custom price provider (if set, skips built-in providers) */
21
+ customProvider?: CustomPriceProvider;
22
+ /** Cache TTL in milliseconds (default: 60000) */
23
+ cacheTTL?: number;
24
+ /** Request timeout in milliseconds (default: 5000) */
25
+ timeout?: number;
26
+ }
27
+ /**
28
+ * Configure price fetching
15
29
  *
16
30
  * @example
17
31
  * ```typescript
18
- * const { solPrice } = await getSolPrice();
19
- * console.log(`SOL is $${solPrice}`);
32
+ * // Use custom API
33
+ * configurePricing({
34
+ * customProvider: async () => {
35
+ * const res = await fetch('https://my-api.com/sol-price');
36
+ * return (await res.json()).price;
37
+ * },
38
+ * });
39
+ *
40
+ * // Or just adjust cache TTL
41
+ * configurePricing({ cacheTTL: 30000 }); // 30 seconds
42
+ * ```
43
+ */
44
+ declare function configurePricing(newConfig: PriceConfig): void;
45
+ /**
46
+ * Get SOL price with multi-provider fallback
47
+ *
48
+ * Provider rotation order:
49
+ * 1. CoinCap (primary)
50
+ * 2. Binance (backup #1)
51
+ * 3. CoinGecko (backup #2)
52
+ * 4. Kraken (backup #3)
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * const { solPrice, source } = await getSolPrice();
57
+ * console.log(`SOL is $${solPrice} (from ${source})`);
20
58
  * ```
21
59
  */
22
60
  declare function getSolPrice(): Promise<PriceData>;
@@ -25,7 +63,7 @@ declare function getSolPrice(): Promise<PriceData>;
25
63
  *
26
64
  * @example
27
65
  * ```typescript
28
- * const usd = await lamportsToUsd(10000000n); // 0.01 SOL
66
+ * const usd = await lamportsToUsd(10_000_000n); // 0.01 SOL
29
67
  * console.log(`$${usd.toFixed(2)}`);
30
68
  * ```
31
69
  */
@@ -45,14 +83,13 @@ declare function usdToLamports(usd: number): Promise<bigint>;
45
83
  *
46
84
  * @example
47
85
  * ```typescript
48
- * const display = await formatPriceDisplay(10000000n);
49
- * // Returns: "0.01 SOL (~$1.50)"
86
+ * const display = await formatPriceDisplay(10_000_000n);
87
+ * // Returns: "0.0100 SOL (~$1.50)"
50
88
  * ```
51
89
  */
52
90
  declare function formatPriceDisplay(lamports: bigint): Promise<string>;
53
91
  /**
54
92
  * Synchronous price formatting (requires pre-fetched price)
55
- * Use this when you've already fetched the price
56
93
  */
57
94
  declare function formatPriceSync(lamports: bigint, solPrice: number): {
58
95
  sol: number;
@@ -63,5 +100,12 @@ declare function formatPriceSync(lamports: bigint, solPrice: number): {
63
100
  * Clear the price cache (for testing or manual refresh)
64
101
  */
65
102
  declare function clearPriceCache(): void;
103
+ /**
104
+ * Get list of available built-in providers
105
+ */
106
+ declare function getProviders(): {
107
+ name: string;
108
+ url: string;
109
+ }[];
66
110
 
67
- export { type PriceData, clearPriceCache, formatPriceDisplay, formatPriceSync, getSolPrice, lamportsToUsd, usdToLamports };
111
+ export { type CustomPriceProvider, type PriceConfig, type PriceData, clearPriceCache, configurePricing, formatPriceDisplay, formatPriceSync, getProviders, getSolPrice, lamportsToUsd, usdToLamports };
@@ -10,13 +10,51 @@ interface PriceData {
10
10
  source: string;
11
11
  }
12
12
  /**
13
- * Fetch current SOL price from CoinGecko
14
- * Includes caching to avoid rate limits
13
+ * Custom price provider function type
14
+ */
15
+ type CustomPriceProvider = () => Promise<number>;
16
+ /**
17
+ * Price provider configuration
18
+ */
19
+ interface PriceConfig {
20
+ /** Custom price provider (if set, skips built-in providers) */
21
+ customProvider?: CustomPriceProvider;
22
+ /** Cache TTL in milliseconds (default: 60000) */
23
+ cacheTTL?: number;
24
+ /** Request timeout in milliseconds (default: 5000) */
25
+ timeout?: number;
26
+ }
27
+ /**
28
+ * Configure price fetching
15
29
  *
16
30
  * @example
17
31
  * ```typescript
18
- * const { solPrice } = await getSolPrice();
19
- * console.log(`SOL is $${solPrice}`);
32
+ * // Use custom API
33
+ * configurePricing({
34
+ * customProvider: async () => {
35
+ * const res = await fetch('https://my-api.com/sol-price');
36
+ * return (await res.json()).price;
37
+ * },
38
+ * });
39
+ *
40
+ * // Or just adjust cache TTL
41
+ * configurePricing({ cacheTTL: 30000 }); // 30 seconds
42
+ * ```
43
+ */
44
+ declare function configurePricing(newConfig: PriceConfig): void;
45
+ /**
46
+ * Get SOL price with multi-provider fallback
47
+ *
48
+ * Provider rotation order:
49
+ * 1. CoinCap (primary)
50
+ * 2. Binance (backup #1)
51
+ * 3. CoinGecko (backup #2)
52
+ * 4. Kraken (backup #3)
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * const { solPrice, source } = await getSolPrice();
57
+ * console.log(`SOL is $${solPrice} (from ${source})`);
20
58
  * ```
21
59
  */
22
60
  declare function getSolPrice(): Promise<PriceData>;
@@ -25,7 +63,7 @@ declare function getSolPrice(): Promise<PriceData>;
25
63
  *
26
64
  * @example
27
65
  * ```typescript
28
- * const usd = await lamportsToUsd(10000000n); // 0.01 SOL
66
+ * const usd = await lamportsToUsd(10_000_000n); // 0.01 SOL
29
67
  * console.log(`$${usd.toFixed(2)}`);
30
68
  * ```
31
69
  */
@@ -45,14 +83,13 @@ declare function usdToLamports(usd: number): Promise<bigint>;
45
83
  *
46
84
  * @example
47
85
  * ```typescript
48
- * const display = await formatPriceDisplay(10000000n);
49
- * // Returns: "0.01 SOL (~$1.50)"
86
+ * const display = await formatPriceDisplay(10_000_000n);
87
+ * // Returns: "0.0100 SOL (~$1.50)"
50
88
  * ```
51
89
  */
52
90
  declare function formatPriceDisplay(lamports: bigint): Promise<string>;
53
91
  /**
54
92
  * Synchronous price formatting (requires pre-fetched price)
55
- * Use this when you've already fetched the price
56
93
  */
57
94
  declare function formatPriceSync(lamports: bigint, solPrice: number): {
58
95
  sol: number;
@@ -63,5 +100,12 @@ declare function formatPriceSync(lamports: bigint, solPrice: number): {
63
100
  * Clear the price cache (for testing or manual refresh)
64
101
  */
65
102
  declare function clearPriceCache(): void;
103
+ /**
104
+ * Get list of available built-in providers
105
+ */
106
+ declare function getProviders(): {
107
+ name: string;
108
+ url: string;
109
+ }[];
66
110
 
67
- export { type PriceData, clearPriceCache, formatPriceDisplay, formatPriceSync, getSolPrice, lamportsToUsd, usdToLamports };
111
+ export { type CustomPriceProvider, type PriceConfig, type PriceData, clearPriceCache, configurePricing, formatPriceDisplay, formatPriceSync, getProviders, getSolPrice, lamportsToUsd, usdToLamports };
@@ -1,42 +1,99 @@
1
1
  // src/pricing/index.ts
2
2
  var cachedPrice = null;
3
- var CACHE_TTL_MS = 6e4;
4
- async function getSolPrice() {
5
- if (cachedPrice && Date.now() - cachedPrice.fetchedAt.getTime() < CACHE_TTL_MS) {
6
- return cachedPrice;
3
+ var config = {};
4
+ var lastProviderIndex = -1;
5
+ function configurePricing(newConfig) {
6
+ config = { ...config, ...newConfig };
7
+ cachedPrice = null;
8
+ }
9
+ var PROVIDERS = [
10
+ {
11
+ name: "coincap",
12
+ url: "https://api.coincap.io/v2/assets/solana",
13
+ parse: (data) => parseFloat(data.data?.priceUsd || "0")
14
+ },
15
+ {
16
+ name: "binance",
17
+ url: "https://api.binance.com/api/v3/ticker/price?symbol=SOLUSDT",
18
+ parse: (data) => parseFloat(data.price || "0")
19
+ },
20
+ {
21
+ name: "coingecko",
22
+ url: "https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd",
23
+ parse: (data) => data.solana?.usd || 0
24
+ },
25
+ {
26
+ name: "kraken",
27
+ url: "https://api.kraken.com/0/public/Ticker?pair=SOLUSD",
28
+ parse: (data) => parseFloat(data.result?.SOLUSD?.c?.[0] || "0")
7
29
  }
30
+ ];
31
+ async function fetchFromProvider(provider, timeout) {
32
+ const controller = new AbortController();
33
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
8
34
  try {
9
- const response = await fetch(
10
- "https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd",
11
- {
12
- headers: { "Accept": "application/json" },
13
- signal: AbortSignal.timeout(5e3)
14
- }
15
- );
35
+ const response = await fetch(provider.url, {
36
+ headers: { "Accept": "application/json" },
37
+ signal: controller.signal
38
+ });
16
39
  if (!response.ok) {
17
- throw new Error(`Price fetch failed: ${response.status}`);
40
+ throw new Error(`HTTP ${response.status}`);
18
41
  }
19
42
  const data = await response.json();
20
- if (!data.solana?.usd) {
21
- throw new Error("Invalid price response");
43
+ const price = provider.parse(data);
44
+ if (!price || price <= 0) {
45
+ throw new Error("Invalid price");
22
46
  }
23
- cachedPrice = {
24
- solPrice: data.solana.usd,
25
- fetchedAt: /* @__PURE__ */ new Date(),
26
- source: "coingecko"
27
- };
47
+ return price;
48
+ } finally {
49
+ clearTimeout(timeoutId);
50
+ }
51
+ }
52
+ async function getSolPrice() {
53
+ const cacheTTL = config.cacheTTL ?? 6e4;
54
+ const timeout = config.timeout ?? 5e3;
55
+ if (cachedPrice && Date.now() - cachedPrice.fetchedAt.getTime() < cacheTTL) {
28
56
  return cachedPrice;
29
- } catch (error) {
30
- if (cachedPrice) {
57
+ }
58
+ if (config.customProvider) {
59
+ try {
60
+ const price = await config.customProvider();
61
+ if (price > 0) {
62
+ cachedPrice = {
63
+ solPrice: price,
64
+ fetchedAt: /* @__PURE__ */ new Date(),
65
+ source: "custom"
66
+ };
67
+ return cachedPrice;
68
+ }
69
+ } catch {
70
+ }
71
+ }
72
+ for (let i = 0; i < PROVIDERS.length; i++) {
73
+ const idx = (lastProviderIndex + 1 + i) % PROVIDERS.length;
74
+ const provider = PROVIDERS[idx];
75
+ try {
76
+ const price = await fetchFromProvider(provider, timeout);
77
+ lastProviderIndex = idx;
78
+ cachedPrice = {
79
+ solPrice: price,
80
+ fetchedAt: /* @__PURE__ */ new Date(),
81
+ source: provider.name
82
+ };
31
83
  return cachedPrice;
84
+ } catch {
85
+ continue;
32
86
  }
33
- return {
34
- solPrice: 150,
35
- // Fallback price
36
- fetchedAt: /* @__PURE__ */ new Date(),
37
- source: "fallback"
38
- };
39
87
  }
88
+ if (cachedPrice) {
89
+ return cachedPrice;
90
+ }
91
+ return {
92
+ solPrice: 150,
93
+ // Reasonable fallback
94
+ fetchedAt: /* @__PURE__ */ new Date(),
95
+ source: "fallback"
96
+ };
40
97
  }
41
98
  async function lamportsToUsd(lamports) {
42
99
  const { solPrice } = await getSolPrice();
@@ -65,8 +122,12 @@ function formatPriceSync(lamports, solPrice) {
65
122
  }
66
123
  function clearPriceCache() {
67
124
  cachedPrice = null;
125
+ lastProviderIndex = -1;
126
+ }
127
+ function getProviders() {
128
+ return PROVIDERS.map((p) => ({ name: p.name, url: p.url }));
68
129
  }
69
130
 
70
- export { clearPriceCache, formatPriceDisplay, formatPriceSync, getSolPrice, lamportsToUsd, usdToLamports };
131
+ export { clearPriceCache, configurePricing, formatPriceDisplay, formatPriceSync, getProviders, getSolPrice, lamportsToUsd, usdToLamports };
71
132
  //# sourceMappingURL=index.js.map
72
133
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/pricing/index.ts"],"names":[],"mappings":";AAkBA,IAAI,WAAA,GAAgC,IAAA;AACpC,IAAM,YAAA,GAAe,GAAA;AAYrB,eAAsB,WAAA,GAAkC;AAEpD,EAAA,IAAI,WAAA,IAAe,KAAK,GAAA,EAAI,GAAI,YAAY,SAAA,CAAU,OAAA,KAAY,YAAA,EAAc;AAC5E,IAAA,OAAO,WAAA;AAAA,EACX;AAEA,EAAA,IAAI;AAEA,IAAA,MAAM,WAAW,MAAM,KAAA;AAAA,MACnB,4EAAA;AAAA,MACA;AAAA,QACI,OAAA,EAAS,EAAE,QAAA,EAAU,kBAAA,EAAmB;AAAA,QACxC,MAAA,EAAQ,WAAA,CAAY,OAAA,CAAQ,GAAI;AAAA;AACpC,KACJ;AAEA,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AACd,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,oBAAA,EAAuB,QAAA,CAAS,MAAM,CAAA,CAAE,CAAA;AAAA,IAC5D;AAEA,IAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AAEjC,IAAA,IAAI,CAAC,IAAA,CAAK,MAAA,EAAQ,GAAA,EAAK;AACnB,MAAA,MAAM,IAAI,MAAM,wBAAwB,CAAA;AAAA,IAC5C;AAEA,IAAA,WAAA,GAAc;AAAA,MACV,QAAA,EAAU,KAAK,MAAA,CAAO,GAAA;AAAA,MACtB,SAAA,sBAAe,IAAA,EAAK;AAAA,MACpB,MAAA,EAAQ;AAAA,KACZ;AAEA,IAAA,OAAO,WAAA;AAAA,EACX,SAAS,KAAA,EAAO;AAEZ,IAAA,IAAI,WAAA,EAAa;AACb,MAAA,OAAO,WAAA;AAAA,IACX;AAGA,IAAA,OAAO;AAAA,MACH,QAAA,EAAU,GAAA;AAAA;AAAA,MACV,SAAA,sBAAe,IAAA,EAAK;AAAA,MACpB,MAAA,EAAQ;AAAA,KACZ;AAAA,EACJ;AACJ;AAWA,eAAsB,cAAc,QAAA,EAAmC;AACnE,EAAA,MAAM,EAAE,QAAA,EAAS,GAAI,MAAM,WAAA,EAAY;AACvC,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,QAAQ,CAAA,GAAI,GAAA;AAC/B,EAAA,OAAO,GAAA,GAAM,QAAA;AACjB;AAWA,eAAsB,cAAc,GAAA,EAA8B;AAC9D,EAAA,MAAM,EAAE,QAAA,EAAS,GAAI,MAAM,WAAA,EAAY;AACvC,EAAA,MAAM,MAAM,GAAA,GAAM,QAAA;AAClB,EAAA,OAAO,MAAA,CAAO,IAAA,CAAK,KAAA,CAAM,GAAA,GAAM,GAAa,CAAC,CAAA;AACjD;AAWA,eAAsB,mBAAmB,QAAA,EAAmC;AACxE,EAAA,MAAM,EAAE,QAAA,EAAS,GAAI,MAAM,WAAA,EAAY;AACvC,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,QAAQ,CAAA,GAAI,GAAA;AAC/B,EAAA,MAAM,MAAM,GAAA,GAAM,QAAA;AAElB,EAAA,OAAO,CAAA,EAAG,IAAI,OAAA,CAAQ,CAAC,CAAC,CAAA,QAAA,EAAW,GAAA,CAAI,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAA,CAAA;AACrD;AAMO,SAAS,eAAA,CAAgB,UAAkB,QAAA,EAIhD;AACE,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,QAAQ,CAAA,GAAI,GAAA;AAC/B,EAAA,MAAM,MAAM,GAAA,GAAM,QAAA;AAElB,EAAA,OAAO;AAAA,IACH,GAAA;AAAA,IACA,GAAA;AAAA,IACA,SAAA,EAAW,CAAA,EAAG,GAAA,CAAI,OAAA,CAAQ,CAAC,CAAC,CAAA,QAAA,EAAW,GAAA,CAAI,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAA;AAAA,GACzD;AACJ;AAKO,SAAS,eAAA,GAAwB;AACpC,EAAA,WAAA,GAAc,IAAA;AAClB","file":"index.js","sourcesContent":["// Price Conversion Helpers\n// Fetch SOL price and convert between USD/SOL\n\n/**\n * Price data from API\n */\nexport interface PriceData {\n /** SOL price in USD */\n solPrice: number;\n /** Timestamp of price fetch */\n fetchedAt: Date;\n /** Source of price data */\n source: string;\n}\n\n/**\n * Cached price data\n */\nlet cachedPrice: PriceData | null = null;\nconst CACHE_TTL_MS = 60000; // 1 minute cache\n\n/**\n * Fetch current SOL price from CoinGecko\n * Includes caching to avoid rate limits\n * \n * @example\n * ```typescript\n * const { solPrice } = await getSolPrice();\n * console.log(`SOL is $${solPrice}`);\n * ```\n */\nexport async function getSolPrice(): Promise<PriceData> {\n // Return cached price if still valid\n if (cachedPrice && Date.now() - cachedPrice.fetchedAt.getTime() < CACHE_TTL_MS) {\n return cachedPrice;\n }\n\n try {\n // CoinGecko free API (no key required, 10-30 calls/min)\n const response = await fetch(\n 'https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd',\n {\n headers: { 'Accept': 'application/json' },\n signal: AbortSignal.timeout(5000),\n }\n );\n\n if (!response.ok) {\n throw new Error(`Price fetch failed: ${response.status}`);\n }\n\n const data = await response.json() as { solana?: { usd?: number } };\n\n if (!data.solana?.usd) {\n throw new Error('Invalid price response');\n }\n\n cachedPrice = {\n solPrice: data.solana.usd,\n fetchedAt: new Date(),\n source: 'coingecko',\n };\n\n return cachedPrice;\n } catch (error) {\n // If fetch fails and we have stale cache, use it\n if (cachedPrice) {\n return cachedPrice;\n }\n\n // Fallback to a reasonable estimate (update periodically)\n return {\n solPrice: 150, // Fallback price\n fetchedAt: new Date(),\n source: 'fallback',\n };\n }\n}\n\n/**\n * Convert lamports to USD\n * \n * @example\n * ```typescript\n * const usd = await lamportsToUsd(10000000n); // 0.01 SOL\n * console.log(`$${usd.toFixed(2)}`);\n * ```\n */\nexport async function lamportsToUsd(lamports: bigint): Promise<number> {\n const { solPrice } = await getSolPrice();\n const sol = Number(lamports) / 1_000_000_000; // LAMPORTS_PER_SOL\n return sol * solPrice;\n}\n\n/**\n * Convert USD to lamports\n * \n * @example\n * ```typescript\n * const lamports = await usdToLamports(1.50); // $1.50\n * console.log(`${lamports} lamports`);\n * ```\n */\nexport async function usdToLamports(usd: number): Promise<bigint> {\n const { solPrice } = await getSolPrice();\n const sol = usd / solPrice;\n return BigInt(Math.floor(sol * 1_000_000_000));\n}\n\n/**\n * Format a price for display with both SOL and USD\n * \n * @example\n * ```typescript\n * const display = await formatPriceDisplay(10000000n);\n * // Returns: \"0.01 SOL (~$1.50)\"\n * ```\n */\nexport async function formatPriceDisplay(lamports: bigint): Promise<string> {\n const { solPrice } = await getSolPrice();\n const sol = Number(lamports) / 1_000_000_000;\n const usd = sol * solPrice;\n\n return `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`;\n}\n\n/**\n * Synchronous price formatting (requires pre-fetched price)\n * Use this when you've already fetched the price\n */\nexport function formatPriceSync(lamports: bigint, solPrice: number): {\n sol: number;\n usd: number;\n formatted: string;\n} {\n const sol = Number(lamports) / 1_000_000_000;\n const usd = sol * solPrice;\n\n return {\n sol,\n usd,\n formatted: `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`,\n };\n}\n\n/**\n * Clear the price cache (for testing or manual refresh)\n */\nexport function clearPriceCache(): void {\n cachedPrice = null;\n}\n"]}
1
+ {"version":3,"sources":["../../src/pricing/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// Price Conversion Helpers\n// Multi-provider SOL price fetching with fallback rotation\n\n/**\n * Price data from API\n */\nexport interface PriceData {\n /** SOL price in USD */\n solPrice: number;\n /** Timestamp of price fetch */\n fetchedAt: Date;\n /** Source of price data */\n source: string;\n}\n\n/**\n * Custom price provider function type\n */\nexport type CustomPriceProvider = () => Promise<number>;\n\n/**\n * Price provider configuration\n */\nexport interface PriceConfig {\n /** Custom price provider (if set, skips built-in providers) */\n customProvider?: CustomPriceProvider;\n /** Cache TTL in milliseconds (default: 60000) */\n cacheTTL?: number;\n /** Request timeout in milliseconds (default: 5000) */\n timeout?: number;\n}\n\n// Cached price data\nlet cachedPrice: PriceData | null = null;\nlet config: PriceConfig = {};\nlet lastProviderIndex = -1;\n\n/**\n * Configure price fetching\n * \n * @example\n * ```typescript\n * // Use custom API\n * configurePricing({\n * customProvider: async () => {\n * const res = await fetch('https://my-api.com/sol-price');\n * return (await res.json()).price;\n * },\n * });\n * \n * // Or just adjust cache TTL\n * configurePricing({ cacheTTL: 30000 }); // 30 seconds\n * ```\n */\nexport function configurePricing(newConfig: PriceConfig): void {\n config = { ...config, ...newConfig };\n cachedPrice = null; // Clear cache on config change\n}\n\n/**\n * Built-in price providers with reliability rotation\n */\nconst PROVIDERS = [\n {\n name: 'coincap',\n url: 'https://api.coincap.io/v2/assets/solana',\n parse: (data: { data?: { priceUsd?: string } }) => parseFloat(data.data?.priceUsd || '0'),\n },\n {\n name: 'binance',\n url: 'https://api.binance.com/api/v3/ticker/price?symbol=SOLUSDT',\n parse: (data: { price?: string }) => parseFloat(data.price || '0'),\n },\n {\n name: 'coingecko',\n url: 'https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd',\n parse: (data: { solana?: { usd?: number } }) => data.solana?.usd || 0,\n },\n {\n name: 'kraken',\n url: 'https://api.kraken.com/0/public/Ticker?pair=SOLUSD',\n parse: (data: { result?: { SOLUSD?: { c?: string[] } } }) =>\n parseFloat(data.result?.SOLUSD?.c?.[0] || '0'),\n },\n];\n\n/**\n * Fetch price from a single provider\n */\nasync function fetchFromProvider(\n provider: typeof PROVIDERS[0],\n timeout: number\n): Promise<number> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(provider.url, {\n headers: { 'Accept': 'application/json' },\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n\n const data = await response.json() as Record<string, unknown>;\n const price = provider.parse(data as Parameters<typeof provider.parse>[0]);\n\n if (!price || price <= 0) {\n throw new Error('Invalid price');\n }\n\n return price;\n } finally {\n clearTimeout(timeoutId);\n }\n}\n\n/**\n * Get SOL price with multi-provider fallback\n * \n * Provider rotation order:\n * 1. CoinCap (primary)\n * 2. Binance (backup #1)\n * 3. CoinGecko (backup #2)\n * 4. Kraken (backup #3)\n * \n * @example\n * ```typescript\n * const { solPrice, source } = await getSolPrice();\n * console.log(`SOL is $${solPrice} (from ${source})`);\n * ```\n */\nexport async function getSolPrice(): Promise<PriceData> {\n const cacheTTL = config.cacheTTL ?? 60000;\n const timeout = config.timeout ?? 5000;\n\n // Return cached price if valid\n if (cachedPrice && Date.now() - cachedPrice.fetchedAt.getTime() < cacheTTL) {\n return cachedPrice;\n }\n\n // Try custom provider first if configured\n if (config.customProvider) {\n try {\n const price = await config.customProvider();\n if (price > 0) {\n cachedPrice = {\n solPrice: price,\n fetchedAt: new Date(),\n source: 'custom',\n };\n return cachedPrice;\n }\n } catch {\n // Fall through to built-in providers\n }\n }\n\n // Try providers in rotation, starting after the last successful one\n for (let i = 0; i < PROVIDERS.length; i++) {\n const idx = (lastProviderIndex + 1 + i) % PROVIDERS.length;\n const provider = PROVIDERS[idx];\n\n try {\n const price = await fetchFromProvider(provider, timeout);\n lastProviderIndex = idx;\n\n cachedPrice = {\n solPrice: price,\n fetchedAt: new Date(),\n source: provider.name,\n };\n return cachedPrice;\n } catch {\n // Try next provider\n continue;\n }\n }\n\n // All providers failed, use stale cache or fallback\n if (cachedPrice) {\n return cachedPrice;\n }\n\n // Last resort fallback\n return {\n solPrice: 150, // Reasonable fallback\n fetchedAt: new Date(),\n source: 'fallback',\n };\n}\n\n/**\n * Convert lamports to USD\n * \n * @example\n * ```typescript\n * const usd = await lamportsToUsd(10_000_000n); // 0.01 SOL\n * console.log(`$${usd.toFixed(2)}`);\n * ```\n */\nexport async function lamportsToUsd(lamports: bigint): Promise<number> {\n const { solPrice } = await getSolPrice();\n const sol = Number(lamports) / 1_000_000_000;\n return sol * solPrice;\n}\n\n/**\n * Convert USD to lamports\n * \n * @example\n * ```typescript\n * const lamports = await usdToLamports(1.50); // $1.50\n * console.log(`${lamports} lamports`);\n * ```\n */\nexport async function usdToLamports(usd: number): Promise<bigint> {\n const { solPrice } = await getSolPrice();\n const sol = usd / solPrice;\n return BigInt(Math.floor(sol * 1_000_000_000));\n}\n\n/**\n * Format a price for display with both SOL and USD\n * \n * @example\n * ```typescript\n * const display = await formatPriceDisplay(10_000_000n);\n * // Returns: \"0.0100 SOL (~$1.50)\"\n * ```\n */\nexport async function formatPriceDisplay(lamports: bigint): Promise<string> {\n const { solPrice } = await getSolPrice();\n const sol = Number(lamports) / 1_000_000_000;\n const usd = sol * solPrice;\n\n return `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`;\n}\n\n/**\n * Synchronous price formatting (requires pre-fetched price)\n */\nexport function formatPriceSync(lamports: bigint, solPrice: number): {\n sol: number;\n usd: number;\n formatted: string;\n} {\n const sol = Number(lamports) / 1_000_000_000;\n const usd = sol * solPrice;\n\n return {\n sol,\n usd,\n formatted: `${sol.toFixed(4)} SOL (~$${usd.toFixed(2)})`,\n };\n}\n\n/**\n * Clear the price cache (for testing or manual refresh)\n */\nexport function clearPriceCache(): void {\n cachedPrice = null;\n lastProviderIndex = -1;\n}\n\n/**\n * Get list of available built-in providers\n */\nexport function getProviders(): { name: string; url: string }[] {\n return PROVIDERS.map(p => ({ name: p.name, url: p.url }));\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alleyboss/micropay-solana-x402-paywall",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "Production-ready Solana micropayments library implementing x402 protocol with SPL token support",
5
5
  "author": "AlleyBoss",
6
6
  "license": "MIT",