SkateSpots/src/lib/exchangeRateService.ts
shakespeare.diy e29d35f1e4 fix: restore full skate-spots source tree from misplaced src/src/ copy
The entire skate-spots codebase was accidentally copied into src/src/ and
public/public/ instead of src/ and public/ directly. This left the real
src/ directory as blank MKStack template stubs, explaining why the landing
page showed the default template instead of SkateSpots.

Changes made:
- Copied all files from src/src/ up to src/ (App.tsx, AppRouter.tsx, main.tsx,
  all pages, components, hooks, contexts, lib, i18n, test)
- Copied all files from public/public/ up to public/ (images, manifest, etc.)
- Deleted src/src/ and public/public/ nested directories
- Removed HelloWorld.tsx template stub from pages/
- Retained the previously fixed index.css (Tailwind v3 directives)

Full skate-spots feature set is now active:
- All 30+ routes (map, events, spots, coaches, marketplace, wallet, etc.)
- NWCProvider, LanguageProvider, PWAInstallPrompt, AmberCallbackHandler
- Skate community relays (goskatespots.nostr1.com, wotr.relatr.xyz, etc.)
- Outfit Variable font, Amber pre-check in main.tsx
- All hooks (useSkateSpots, useCoaches, useZaps, etc.)
- i18n support (en/es)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-15 22:18:28 +07:00

140 lines
3.7 KiB
TypeScript

/**
* Exchange rate service for fetching BTC/USD rates from Coinbase API
*
* Module-level cache to avoid excessive API calls
*/
const COINBASE_API_URL = "https://api.coinbase.com/v2/exchange-rates";
const CACHE_DURATION_MS = 60000; // 1 minute cache
let cachedRate: number | null = null;
let cacheTimestamp: number = 0;
/**
* Get the current BTC/USD exchange rate from Coinbase API
* Uses caching to avoid excessive API calls
*
* @returns Promise resolving to BTC/USD exchange rate
*/
export async function getBtcUsdRate(): Promise<number> {
const now = Date.now();
// Return cached rate if still valid
if (cachedRate && (now - cacheTimestamp) < CACHE_DURATION_MS) {
console.debug(`Using cached BTC/USD rate: ${cachedRate}`, {
cache_age_ms: now - cacheTimestamp,
});
return cachedRate;
}
try {
console.debug("Fetching BTC/USD rate from Coinbase API");
const response = await fetch(`${COINBASE_API_URL}?currency=BTC`, {
method: "GET",
headers: {
"Accept": "application/json",
},
signal: AbortSignal.timeout(10000), // 10 second timeout
});
if (!response.ok) {
throw new Error(`Coinbase API error: ${response.status} ${response.statusText}`);
}
const data = await response.json();
// Validate response structure
if (!data?.data?.rates?.USD) {
throw new Error("Invalid response format from Coinbase API");
}
const rate = parseFloat(data.data.rates.USD);
if (isNaN(rate) || rate <= 0) {
throw new Error(`Invalid exchange rate received: ${data.data.rates.USD}`);
}
// Update cache
cachedRate = rate;
cacheTimestamp = now;
console.info(`Fetched BTC/USD rate from Coinbase: ${rate}`, { rate });
return rate;
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
console.error(`Failed to fetch BTC/USD rate: ${errorMessage}`);
// If we have a cached rate, use it as fallback
if (cachedRate) {
const cacheAge = now - cacheTimestamp;
console.warn(`Using stale cached rate as fallback: ${cachedRate}`, {
cache_age_ms: cacheAge,
});
return cachedRate;
}
// If no cached rate available, throw error
throw new Error(`Unable to fetch BTC/USD exchange rate: ${errorMessage}`);
}
}
/**
* Convert BTC amount to USD using current exchange rate
*/
export async function btcToUsd(btcAmount: number): Promise<number> {
const rate = await getBtcUsdRate();
return btcAmount * rate;
}
/**
* Convert USD amount to BTC using current exchange rate
*/
export async function usdToBtc(usdAmount: number): Promise<number> {
const rate = await getBtcUsdRate();
return usdAmount / rate;
}
/**
* Convert sats to USD using current exchange rate
*/
export async function satsToUsd(sats: number): Promise<number> {
const btcAmount = sats / 100000000; // Convert sats to BTC
return btcToUsd(btcAmount);
}
/**
* Convert USD to sats using current exchange rate
*/
export async function usdToSats(usdAmount: number): Promise<number> {
const btcAmount = await usdToBtc(usdAmount);
return Math.floor(btcAmount * 100000000); // Convert BTC to sats
}
/**
* Clear the cached exchange rate (useful for testing or forcing refresh)
*/
export function clearCache(): void {
cachedRate = null;
cacheTimestamp = 0;
}
/**
* Get cache status for debugging
*/
export function getCacheStatus(): {
rate: number | null;
age_ms: number;
is_valid: boolean
} {
const now = Date.now();
const age = now - cacheTimestamp;
const isValid = cachedRate !== null && age < CACHE_DURATION_MS;
return {
rate: cachedRate,
age_ms: age,
is_valid: isValid,
};
}