@@ -12,6 +12,8 @@ import {
1212 asGetRatesParams ,
1313 asIncomingGetRatesParams ,
1414 type CrossChainMapping ,
15+ type CryptoRate ,
16+ type FiatRate ,
1517 type GetRatesParams ,
1618 type IncomingGetRatesParams
1719} from './types'
@@ -32,9 +34,6 @@ const fixIncomingGetRatesParams = (
3234 if ( params . fiat . length > 256 ) {
3335 throw new Error ( 'fiat array must be less than 256' )
3436 }
35- if ( params . targetFiat !== 'USD' ) {
36- throw new Error ( 'targetFiat must be USD' )
37- }
3837
3938 params . crypto . forEach ( crypto => {
4039 // Sanity check that the tokenId doesn't include _
@@ -114,6 +113,76 @@ automatedCrossChainSyncDoc.onChange(automatedMappings => {
114113 }
115114} )
116115
116+ const toDatedFiatKey = ( asset : FiatRate ) : string => {
117+ return `${ asset . isoDate . toISOString ( ) } _${ asset . fiatCode } `
118+ }
119+ const toDatedCryptoKey = ( asset : CryptoRate ) : string => {
120+ return `${ asset . isoDate . toISOString ( ) } _${ toCryptoKey ( asset . asset ) } `
121+ }
122+ /**
123+ * Break up a non-USD request into two queries. The first finds all the
124+ * USD rates and the second finds all of the fiat/USD rates on across all
125+ * the dates requested.
126+ */
127+ const getNonUsdRates = async (
128+ initialParams : GetRatesParams ,
129+ rightNow : Date
130+ ) : Promise < GetRatesParams > => {
131+ // Get requested rates in USD
132+ const usdParams = {
133+ ...initialParams ,
134+ targetFiat : 'USD'
135+ }
136+ const usdResult = await getRates ( usdParams , rightNow )
137+
138+ // Loop over the USD rates and store them in a map. At the same time,
139+ // save the date strings we'll need to query the original requested fiat for
140+ const usdCryptoRatesMap = new Map < string , number | undefined > ( )
141+ const fiatUsdDateExchangeRateMap = new Map < string , number | undefined > ( )
142+ for ( const crypto of usdResult . crypto ) {
143+ usdCryptoRatesMap . set ( toDatedCryptoKey ( crypto ) , crypto . rate )
144+ fiatUsdDateExchangeRateMap . set ( crypto . isoDate . toISOString ( ) , undefined )
145+ }
146+ const usdFiatRatesMap = new Map < string , number | undefined > ( )
147+ for ( const fiat of usdResult . fiat ) {
148+ usdFiatRatesMap . set ( toDatedFiatKey ( fiat ) , fiat . rate )
149+ fiatUsdDateExchangeRateMap . set ( fiat . isoDate . toISOString ( ) , undefined )
150+ }
151+
152+ // Get fiat/USD rates
153+ const fiatParams = {
154+ targetFiat : 'USD' ,
155+ crypto : [ ] ,
156+ fiat : [ ...fiatUsdDateExchangeRateMap . keys ( ) ] . map ( date => ( {
157+ isoDate : new Date ( date ) ,
158+ fiatCode : initialParams . targetFiat ,
159+ rate : undefined
160+ } ) )
161+ }
162+ const fiatResult = await getRates ( fiatParams , rightNow )
163+ for ( const fiat of fiatResult . fiat ) {
164+ fiatUsdDateExchangeRateMap . set ( fiat . isoDate . toISOString ( ) , fiat . rate )
165+ }
166+
167+ // Loop over the initial request and bridge the rates
168+ for ( const crypto of initialParams . crypto ) {
169+ const usdCryptoRate = usdCryptoRatesMap . get ( toDatedCryptoKey ( crypto ) )
170+ const fiatRate = fiatUsdDateExchangeRateMap . get (
171+ crypto . isoDate . toISOString ( )
172+ )
173+ if ( usdCryptoRate == null || fiatRate == null ) continue
174+ crypto . rate = usdCryptoRate / fiatRate
175+ }
176+ for ( const fiat of initialParams . fiat ) {
177+ const usdFiatRate = usdFiatRatesMap . get ( toDatedFiatKey ( fiat ) )
178+ const fiatRate = fiatUsdDateExchangeRateMap . get ( fiat . isoDate . toISOString ( ) )
179+ if ( usdFiatRate == null || fiatRate == null ) continue
180+ fiat . rate = usdFiatRate / fiatRate
181+ }
182+
183+ return initialParams
184+ }
185+
117186export const ratesV3 = async (
118187 request : ExpressRequest
119188) : Promise < HttpResponse > => {
@@ -124,7 +193,11 @@ export const ratesV3 = async (
124193 // Map all incoming crypto assets to their canonical versions
125194 const { mappedParams, originalToCanonicalKey } =
126195 applyCrossChainMappings ( params )
127- const result = await getRates ( mappedParams , rightNow )
196+
197+ const result =
198+ mappedParams . targetFiat === 'USD'
199+ ? await getRates ( mappedParams , rightNow )
200+ : await getNonUsdRates ( mappedParams , rightNow )
128201
129202 // Build a quick lookup from canonical key + isoDate -> rate
130203 const canonicalLookup = new Map < string , number > ( )
0 commit comments