@alleyboss/micropay-solana-x402-paywall 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +110 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +109 -48
- package/dist/index.js.map +1 -1
- package/dist/pricing/index.cjs +90 -27
- package/dist/pricing/index.cjs.map +1 -1
- package/dist/pricing/index.d.cts +53 -9
- package/dist/pricing/index.d.ts +53 -9
- package/dist/pricing/index.js +89 -28
- package/dist/pricing/index.js.map +1 -1
- package/package.json +1 -1
package/dist/pricing/index.cjs
CHANGED
|
@@ -2,43 +2,100 @@
|
|
|
2
2
|
|
|
3
3
|
// src/pricing/index.ts
|
|
4
4
|
var cachedPrice = null;
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
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(`
|
|
42
|
+
throw new Error(`HTTP ${response.status}`);
|
|
20
43
|
}
|
|
21
44
|
const data = await response.json();
|
|
22
|
-
|
|
23
|
-
|
|
45
|
+
const price = provider.parse(data);
|
|
46
|
+
if (!price || price <= 0) {
|
|
47
|
+
throw new Error("Invalid price");
|
|
24
48
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
}
|
|
32
|
-
|
|
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"]}
|
package/dist/pricing/index.d.cts
CHANGED
|
@@ -10,13 +10,51 @@ interface PriceData {
|
|
|
10
10
|
source: string;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
|
|
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
|
-
*
|
|
19
|
-
*
|
|
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(
|
|
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(
|
|
49
|
-
* // Returns: "0.
|
|
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 };
|
package/dist/pricing/index.d.ts
CHANGED
|
@@ -10,13 +10,51 @@ interface PriceData {
|
|
|
10
10
|
source: string;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
|
|
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
|
-
*
|
|
19
|
-
*
|
|
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(
|
|
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(
|
|
49
|
-
* // Returns: "0.
|
|
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 };
|
package/dist/pricing/index.js
CHANGED
|
@@ -1,42 +1,99 @@
|
|
|
1
1
|
// src/pricing/index.ts
|
|
2
2
|
var cachedPrice = null;
|
|
3
|
-
var
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
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(`
|
|
40
|
+
throw new Error(`HTTP ${response.status}`);
|
|
18
41
|
}
|
|
19
42
|
const data = await response.json();
|
|
20
|
-
|
|
21
|
-
|
|
43
|
+
const price = provider.parse(data);
|
|
44
|
+
if (!price || price <= 0) {
|
|
45
|
+
throw new Error("Invalid price");
|
|
22
46
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
}
|
|
30
|
-
|
|
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