Skip to content

Commit 8fd8413

Browse files
committed
Hack support for non-USD targetFiat
1 parent 81c87ec commit 8fd8413

File tree

1 file changed

+84
-4
lines changed

1 file changed

+84
-4
lines changed

src/v3/router.ts

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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 > FIAT_LIMIT) {
3335
throw new Error(`fiat array must be less than ${FIAT_LIMIT}`)
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,83 @@ 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 dateSet = new Set<string>()
142+
for (const crypto of usdResult.crypto) {
143+
usdCryptoRatesMap.set(toDatedCryptoKey(crypto), crypto.rate)
144+
dateSet.add(crypto.isoDate.toISOString())
145+
}
146+
const usdFiatRatesMap = new Map<string, number | undefined>()
147+
for (const fiat of usdResult.fiat) {
148+
usdFiatRatesMap.set(toDatedFiatKey(fiat), fiat.rate)
149+
dateSet.add(fiat.isoDate.toISOString())
150+
}
151+
152+
// Get fiat/USD rates
153+
// The number of unique dates across crypto and fiat could exceed 256 so we need to chunk them
154+
const dateArray = Array.from(dateSet)
155+
const chunkSize = FIAT_LIMIT
156+
const fiatUsdDateExchangeRateMap = new Map<string, number | undefined>()
157+
for (let i = 0; i < dateArray.length; i += chunkSize) {
158+
const chunk = dateArray.slice(i, i + chunkSize)
159+
const fiatParams = {
160+
targetFiat: 'USD',
161+
crypto: [],
162+
fiat: chunk.map(date => ({
163+
isoDate: new Date(date),
164+
fiatCode: initialParams.targetFiat,
165+
rate: undefined
166+
}))
167+
}
168+
const fiatResult = await getRates(fiatParams, rightNow)
169+
for (const fiat of fiatResult.fiat) {
170+
fiatUsdDateExchangeRateMap.set(fiat.isoDate.toISOString(), fiat.rate)
171+
}
172+
}
173+
174+
// Loop over the initial request and bridge the rates
175+
for (const crypto of initialParams.crypto) {
176+
const usdCryptoRate = usdCryptoRatesMap.get(toDatedCryptoKey(crypto))
177+
const fiatRate = fiatUsdDateExchangeRateMap.get(
178+
crypto.isoDate.toISOString()
179+
)
180+
if (usdCryptoRate == null || fiatRate == null) continue
181+
crypto.rate = usdCryptoRate / fiatRate
182+
}
183+
for (const fiat of initialParams.fiat) {
184+
const usdFiatRate = usdFiatRatesMap.get(toDatedFiatKey(fiat))
185+
const fiatRate = fiatUsdDateExchangeRateMap.get(fiat.isoDate.toISOString())
186+
if (usdFiatRate == null || fiatRate == null) continue
187+
fiat.rate = usdFiatRate / fiatRate
188+
}
189+
190+
return initialParams
191+
}
192+
117193
export const ratesV3 = async (
118194
request: ExpressRequest
119195
): Promise<HttpResponse> => {
@@ -124,7 +200,11 @@ export const ratesV3 = async (
124200
// Map all incoming crypto assets to their canonical versions
125201
const { mappedParams, originalToCanonicalKey } =
126202
applyCrossChainMappings(params)
127-
const result = await getRates(mappedParams, rightNow)
203+
204+
const result =
205+
mappedParams.targetFiat === 'USD'
206+
? await getRates(mappedParams, rightNow)
207+
: await getNonUsdRates(mappedParams, rightNow)
128208

129209
// Build a quick lookup from canonical key + isoDate -> rate
130210
const canonicalLookup = new Map<string, number>()

0 commit comments

Comments
 (0)