@@ -359,3 +359,117 @@ export const closestRateForTimestamp = (
359
359
}
360
360
return bestRate
361
361
}
362
+
363
+ /**
364
+ * Fetches exchange rates for a specific fiat currency on demand.
365
+ * This is used when the user wants to view rates in a currency other than their default.
366
+ */
367
+ export function fetchExchangeRatesForFiat (
368
+ fiatCurrencyCode : string
369
+ ) : ThunkAction < Promise < void > > {
370
+ return async ( dispatch , getState ) => {
371
+ const state = getState ( )
372
+ const { account } = state . core
373
+ const { currencyWallets } = account
374
+ const now = Date . now ( )
375
+ const yesterday = getYesterdayDateRoundDownHour ( now ) . toISOString ( )
376
+
377
+ // Build asset pairs for the requested fiat currency
378
+ const assetPairs : AssetPair [ ] = [ ]
379
+ const pairExpiration = now + ONE_MONTH
380
+ const rateExpiration = now + ONE_DAY
381
+
382
+ // If the fiat isn't dollars, get its price relative to USD
383
+ if ( fiatCurrencyCode !== 'iso:USD' ) {
384
+ assetPairs . push ( {
385
+ currency_pair : `iso:USD_${ fiatCurrencyCode } ` ,
386
+ date : undefined ,
387
+ expiration : pairExpiration
388
+ } )
389
+ }
390
+
391
+ // Get rates for all wallet assets
392
+ for ( const walletId of Object . keys ( currencyWallets ) ) {
393
+ const wallet = currencyWallets [ walletId ]
394
+ const { currencyCode } = wallet . currencyInfo
395
+
396
+ // Get the primary asset's prices for today
397
+ assetPairs . push ( {
398
+ currency_pair : `${ currencyCode } _${ fiatCurrencyCode } ` ,
399
+ date : undefined ,
400
+ expiration : pairExpiration
401
+ } )
402
+
403
+ // Do the same for any tokens
404
+ for ( const tokenId of wallet . enabledTokenIds ) {
405
+ const token = wallet . currencyConfig . allTokens [ tokenId ]
406
+ if ( token == null ) continue
407
+ if ( token . currencyCode === currencyCode ) continue
408
+ assetPairs . push ( {
409
+ currency_pair : `${ token . currencyCode } _${ fiatCurrencyCode } ` ,
410
+ date : undefined ,
411
+ expiration : pairExpiration
412
+ } )
413
+ }
414
+ }
415
+
416
+ // Fetch rates from server in batches
417
+ const newRates : ExchangeRateCache = { }
418
+ for ( let i = 0 ; i < assetPairs . length ; i += RATES_SERVER_MAX_QUERY_SIZE ) {
419
+ const query = assetPairs . slice ( i , i + RATES_SERVER_MAX_QUERY_SIZE )
420
+
421
+ const options = {
422
+ method : 'POST' ,
423
+ headers : { 'Content-Type' : 'application/json' } ,
424
+ body : JSON . stringify ( { data : query } )
425
+ }
426
+
427
+ try {
428
+ const response = await fetchRates ( 'v2/exchangeRates' , options )
429
+ if ( response . ok ) {
430
+ const json = await response . json ( )
431
+ const cleanedRates = asRatesResponse ( json )
432
+ for ( const rate of cleanedRates . data ) {
433
+ const { currency_pair : currencyPair , exchangeRate, date } = rate
434
+ const isHistorical = now - new Date ( date ) . valueOf ( ) > HOUR_MS
435
+ const key = isHistorical ? `${ currencyPair } _${ date } ` : currencyPair
436
+
437
+ if ( exchangeRate != null ) {
438
+ newRates [ key ] = {
439
+ expiration : rateExpiration ,
440
+ rate : parseFloat ( exchangeRate )
441
+ }
442
+ }
443
+ }
444
+ }
445
+ } catch ( error : unknown ) {
446
+ console . log (
447
+ `fetchExchangeRatesForFiat error querying rates server ${ String (
448
+ error
449
+ ) } `
450
+ )
451
+ }
452
+ }
453
+
454
+ // Merge with existing rates
455
+ const mergedRates = { ...exchangeRateCache ?. rates , ...newRates }
456
+ const { exchangeRates, exchangeRatesMap } = buildGuiRates (
457
+ mergedRates ,
458
+ yesterday
459
+ )
460
+
461
+ // Update Redux state
462
+ dispatch ( {
463
+ type : 'EXCHANGE_RATES/UPDATE_EXCHANGE_RATES' ,
464
+ data : {
465
+ exchangeRates,
466
+ exchangeRatesMap
467
+ }
468
+ } )
469
+
470
+ // Update the in-memory cache if it exists
471
+ if ( exchangeRateCache != null ) {
472
+ exchangeRateCache . rates = mergedRates
473
+ }
474
+ }
475
+ }
0 commit comments