Skip to content

Commit a3bee0a

Browse files
committed
Add @thesis-co/cent, refactor currency conversion
Removes "convertUSDAmountToCurrency" in favor of converting asset price points into the user currency before formatting. cent is mostly providing currency metadata for now.
1 parent 41e7869 commit a3bee0a

File tree

33 files changed

+330
-185
lines changed

33 files changed

+330
-185
lines changed

background/assets.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { keyBy } from "lodash"
22
import { TokenList } from "@uniswap/token-lists"
3+
import { FixedPoint } from "@thesis-co/cent/dist/types"
34
import { UNIXTime, HexString } from "./types"
45
import {
56
NetworkSpecific,
@@ -100,16 +101,15 @@ export type FungibleAsset = Asset & {
100101
* to any `FungibleAsset` and vice versa.
101102
*/
102103

103-
export type FiatCurrency = FungibleAsset & {
104-
sign: string
105-
}
104+
export type FiatCurrency = FungibleAsset
106105

107106
// Used for displaying balances and values in the user selected currency
108-
export type DisplayCurrency = FiatCurrency & {
107+
export type DisplayCurrency = {
108+
code: string
109109
/**
110110
* Conversion rate for 1 usd
111111
*/
112-
rate: bigint
112+
rate: FixedPoint
113113
}
114114

115115
/**

background/constants/currencies.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,8 @@
1-
import { CoinGeckoAsset, DisplayCurrency } from "../assets"
1+
import { CoinGeckoAsset, FiatCurrency } from "../assets"
22
import { NetworkBaseAsset } from "../networks"
33
import { BASE_ASSETS_BY_CUSTOM_NAME } from "./base-assets"
44
import { coinTypesByAssetSymbol } from "./coin-types"
55

6-
export const USD: DisplayCurrency = {
7-
name: "United States Dollar",
8-
symbol: "USD",
9-
decimals: 10,
10-
rate: 1_000_000_0000n, // 1 USD = 1 USD
11-
sign: "$",
12-
}
13-
146
/**
157
* The default currency used for pricing assets in fiat money.
168
*
@@ -21,7 +13,11 @@ export const USD: DisplayCurrency = {
2113
* - A price oracle using the asset USDC pair.
2214
* - CoinGecko query using USD (currently disabled due to new limits).
2315
*/
24-
export const DEFAULT_DISPLAY_CURRENCY = USD
16+
export const USD: FiatCurrency = {
17+
name: "United States Dollar",
18+
symbol: "USD",
19+
decimals: 10,
20+
}
2521

2622
export const ETH_DATA = {
2723
coinType: coinTypesByAssetSymbol.ETH,

background/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"@tallyho/hd-keyring": "0.5.0",
4343
"@tallyho/provider-bridge-shared": "0.0.1",
4444
"@tallyho/window-provider": "0.0.1",
45+
"@thesis-co/cent": "0.0.4",
4546
"@testing-library/dom": "^9.3.1",
4647
"@types/w3c-web-usb": "^1.0.5",
4748
"@uniswap/token-lists": "^1.0.0-beta.30",

background/redux-slices/prices.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
PricePoint,
88
SmartContractFungibleAsset,
99
} from "../assets"
10-
import { DEFAULT_DISPLAY_CURRENCY } from "../constants"
10+
import { USD } from "../constants"
1111
import { convertFixedPoint } from "../lib/fixed-point"
1212
import {
1313
FullAssetID,
@@ -40,7 +40,7 @@ const pricesSlice = createSlice({
4040
pricePoints.forEach((pricePoint) => {
4141
const fiatCurrency = pricePoint.pair.find(
4242
// FIXME: What if we have price points not in USD?
43-
(asset) => asset.symbol === DEFAULT_DISPLAY_CURRENCY.symbol,
43+
(asset) => asset.symbol === USD.symbol,
4444
)
4545

4646
const [pricedAsset] = pricePoint.pair.filter(

background/redux-slices/selectors/accountsSelectors.ts

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
} from "../accounts"
99
import { AssetsState } from "../assets"
1010
import {
11-
convertUSDAmountToCurrency,
11+
convertUSDPricePointToCurrency,
1212
enrichAssetAmountWithDecimalValues,
1313
enrichAssetAmountWithMainCurrencyValues,
1414
formatCurrencyAmount,
@@ -273,7 +273,7 @@ export const selectAccountAndTimestampedActivities = createSelector(
273273
assets: combinedAssetAmounts,
274274
totalMainCurrencyValue: isDefined(totalMainCurrencyAmount)
275275
? formatCurrencyAmount(
276-
displayCurrency.symbol,
276+
displayCurrency.code,
277277
totalMainCurrencyAmount,
278278
desiredDecimals.default,
279279
)
@@ -326,7 +326,7 @@ export const selectCurrentAccountBalances = createSelector(
326326
unverifiedAssetAmounts,
327327
totalMainCurrencyValue: isDefined(totalMainCurrencyAmount)
328328
? formatCurrencyAmount(
329-
displayCurrency.symbol,
329+
displayCurrency.code,
330330
totalMainCurrencyAmount,
331331
desiredDecimals.default,
332332
)
@@ -411,28 +411,18 @@ const getTotalBalance = (
411411
return 0
412412
}
413413

414-
const usdAmount = convertAssetAmountViaPricePoint(
415-
assetAmount,
414+
const pricePoint = convertUSDPricePointToCurrency(
416415
assetPricePoint,
417-
)
418-
419-
if (!usdAmount) {
420-
return 0
421-
}
422-
423-
const convertedAmount = convertUSDAmountToCurrency(
424-
usdAmount,
425416
displayCurrency,
426417
)
427418

428-
if (typeof convertedAmount === "undefined") {
419+
const amount = convertAssetAmountViaPricePoint(assetAmount, pricePoint)
420+
421+
if (typeof amount === "undefined") {
429422
return 0
430423
}
431424

432-
return assetAmountToDesiredDecimals(
433-
convertedAmount,
434-
desiredDecimals.default,
435-
)
425+
return assetAmountToDesiredDecimals(amount, desiredDecimals.default)
436426
})
437427
.reduce((total, assetBalance) => total + assetBalance, 0)
438428

@@ -487,7 +477,7 @@ function getNetworkAccountTotalsByCategory(
487477
name: name ?? accountData.defaultName,
488478
avatarURL: avatarURL ?? accountData.defaultAvatar,
489479
localizedTotalMainCurrencyAmount: formatCurrencyAmount(
490-
displayCurrency.symbol,
480+
displayCurrency.code,
491481
getTotalBalance(accountData.balances, prices, displayCurrency),
492482
desiredDecimals.default,
493483
),
@@ -655,7 +645,7 @@ export const getTotalBalanceForOverview = createSelector(
655645
selectDisplayCurrency,
656646
(accountsTotal, displayCurrency) =>
657647
formatCurrencyAmount(
658-
displayCurrency.symbol,
648+
displayCurrency.code,
659649
Object.values(accountsTotal).reduce(
660650
(total, { totals }) =>
661651
Object.values(totals).reduce((sum, balance) => sum + balance) + total,

background/redux-slices/selectors/uiSelectors.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createSelector } from "@reduxjs/toolkit"
2+
import { currencies } from "@thesis-co/cent"
23
import type { RootState } from ".."
34

45
export const selectCurrentNetwork = createSelector(
@@ -49,5 +50,5 @@ export const selectDisplayCurrency = createSelector(
4950

5051
export const selectDisplayCurrencySign = createSelector(
5152
selectDisplayCurrency,
52-
(currency) => currency.sign,
53+
(currency) => currencies[currency.code].symbol,
5354
)

background/redux-slices/tests/assets-utils.unit.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { MATIC, OPTIMISTIC_ETH, AVAX, ETH, BNB, USD } from "../../constants"
1+
import { MATIC, OPTIMISTIC_ETH, AVAX, ETH, BNB } from "../../constants"
22
import {
33
enrichAssetAmountWithMainCurrencyValues,
44
formatCurrencyAmount,
@@ -56,7 +56,7 @@ describe(enrichAssetAmountWithMainCurrencyValues, () => {
5656
assetAmount,
5757
pricePoint,
5858
2,
59-
USD,
59+
{ code: "USD", rate: { amount: 1000000n, decimals: 6n } },
6060
)
6161

6262
expect(result).toMatchObject({

background/redux-slices/ui.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
import { createSlice, createSelector } from "@reduxjs/toolkit"
22
import Emittery from "emittery"
33
import { AddressOnNetwork } from "../accounts"
4-
import {
5-
DEFAULT_DISPLAY_CURRENCY,
6-
ETHEREUM,
7-
TEST_NETWORK_BY_CHAIN_ID,
8-
} from "../constants"
4+
import { ETHEREUM, TEST_NETWORK_BY_CHAIN_ID } from "../constants"
95
import { AnalyticsEvent, OneTimeAnalyticsEvent } from "../lib/posthog"
106
import { EVMNetwork } from "../networks"
117
import { AnalyticsPreferences, DismissableItem } from "../services/preferences"
@@ -99,7 +95,10 @@ export const initialState: UIState = {
9995
address: "",
10096
network: ETHEREUM,
10197
},
102-
displayCurrency: DEFAULT_DISPLAY_CURRENCY,
98+
displayCurrency: {
99+
code: "USD",
100+
rate: { amount: 1_000_000_000_0n, decimals: 10n },
101+
},
103102
initializationLoadingTimeExpired: false,
104103
settings: defaultSettings,
105104
snackbarMessage: "",

background/redux-slices/utils/asset-utils.ts

Lines changed: 76 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
1+
import { ExchangeRate, FixedPoint, Money, currencies } from "@thesis-co/cent"
12
import {
3+
AnyAsset,
24
AnyAssetAmount,
5+
AssetMetadata,
6+
CoinGeckoAsset,
7+
DisplayCurrency,
8+
FiatCurrency,
9+
FungibleAsset,
10+
PricePoint,
11+
SmartContractFungibleAsset,
12+
UnitPricePoint,
313
assetAmountToDesiredDecimals,
414
convertAssetAmountViaPricePoint,
5-
unitPricePointForPricePoint,
615
isFungibleAssetAmount,
7-
PricePoint,
8-
FungibleAsset,
9-
UnitPricePoint,
10-
AnyAsset,
11-
CoinGeckoAsset,
1216
isSmartContractFungibleAsset,
13-
SmartContractFungibleAsset,
14-
AssetMetadata,
15-
DisplayCurrency,
17+
unitPricePointForPricePoint,
1618
} from "../../assets"
1719
import {
1820
BUILT_IN_NETWORK_BASE_ASSETS,
1921
OPTIMISM,
2022
POLYGON,
21-
USD,
2223
} from "../../constants"
2324
import { fromFixedPointNumber } from "../../lib/fixed-point"
2425
import { sameEVMAddress } from "../../lib/utils"
@@ -203,7 +204,7 @@ export function formatCurrencyAmount(
203204
export function convertUSDPricePointToCurrency(
204205
pricePoint: PricePoint,
205206
currency: DisplayCurrency,
206-
): PricePoint {
207+
) {
207208
const { pair, amounts, time } = pricePoint
208209
const idx = pricePoint.pair.findIndex((asset) => asset.symbol === "USD")
209210

@@ -213,23 +214,35 @@ export function convertUSDPricePointToCurrency(
213214
time,
214215
}
215216

216-
newPricePoint.pair[idx] = currency
217-
newPricePoint.amounts[idx] *= currency.rate
217+
const { name, code } = currencies[currency.code]
218218

219-
return newPricePoint
220-
}
221-
222-
export function convertUSDAmountToCurrency(
223-
assetAmount: AnyAssetAmount,
224-
currency: DisplayCurrency,
225-
) {
226-
const pricePoint: PricePoint = {
227-
pair: [USD, currency],
228-
amounts: [1n * 10n ** BigInt(USD.decimals), currency.rate],
229-
time: Date.now(),
219+
const asset: FiatCurrency = {
220+
name,
221+
symbol: code,
222+
decimals: 10,
230223
}
231224

232-
return convertAssetAmountViaPricePoint(assetAmount, pricePoint)
225+
const rate = new ExchangeRate({
226+
// Keeping 10 decimals ensures we don't lose precision during conversion
227+
baseCurrency: { ...currencies[currency.code], decimals: 10n },
228+
quoteCurrency: currencies.USD,
229+
rate: FixedPoint(currency.rate),
230+
})
231+
232+
newPricePoint.pair[idx] = asset
233+
newPricePoint.amounts[idx] = rate
234+
.convert(
235+
Money({
236+
asset: currencies.USD,
237+
amount: FixedPoint({
238+
amount: newPricePoint.amounts[idx],
239+
decimals: 10n,
240+
}),
241+
}),
242+
)
243+
.concretize()[0].balance.amount.amount
244+
245+
return newPricePoint
233246
}
234247

235248
/**
@@ -269,47 +282,49 @@ export function enrichAssetAmountWithMainCurrencyValues<
269282
desiredDecimals: number,
270283
displayCurrency: DisplayCurrency,
271284
): T & AssetMainCurrencyAmount {
272-
// Converts to USD as price points are in USD
273-
const assetAmountInUSD = convertAssetAmountViaPricePoint(
274-
assetAmount,
275-
assetPricePoint,
276-
)
277-
const { unitPrice } = unitPricePointForPricePoint(assetPricePoint) ?? {
278-
unitPrice: undefined,
279-
}
280-
281-
if (typeof assetAmountInUSD !== "undefined") {
282-
const assetAmountInFiatCurrency = convertUSDAmountToCurrency(
283-
assetAmountInUSD,
285+
if (assetPricePoint) {
286+
// Create a temporary pricepoint in the target user currency
287+
const currencyPricePoint = convertUSDPricePointToCurrency(
288+
assetPricePoint,
284289
displayCurrency,
285-
)!
290+
)
286291

287-
const convertedDecimalValue = assetAmountToDesiredDecimals(
288-
assetAmountInFiatCurrency,
289-
desiredDecimals,
292+
const assetAmountInCurrency = convertAssetAmountViaPricePoint(
293+
assetAmount,
294+
currencyPricePoint,
290295
)
291-
const unitPriceDecimalValue =
292-
typeof unitPrice === "undefined"
293-
? undefined
294-
: assetAmountToDesiredDecimals(unitPrice, desiredDecimals)
295-
296-
return {
297-
...assetAmount,
298-
mainCurrencyAmount: convertedDecimalValue,
299-
localizedMainCurrencyAmount: formatCurrencyAmount(
300-
displayCurrency.symbol,
301-
convertedDecimalValue,
296+
const { unitPrice } = unitPricePointForPricePoint(currencyPricePoint) ?? {
297+
unitPrice: undefined,
298+
}
299+
300+
if (typeof assetAmountInCurrency !== "undefined") {
301+
const convertedDecimalValue = assetAmountToDesiredDecimals(
302+
assetAmountInCurrency,
302303
desiredDecimals,
303-
),
304-
unitPrice: unitPriceDecimalValue,
305-
localizedUnitPrice:
306-
typeof unitPriceDecimalValue === "undefined"
304+
)
305+
const unitPriceDecimalValue =
306+
typeof unitPrice === "undefined"
307307
? undefined
308-
: formatCurrencyAmount(
309-
assetAmountInUSD.asset.symbol,
310-
unitPriceDecimalValue,
311-
desiredDecimals,
312-
),
308+
: assetAmountToDesiredDecimals(unitPrice, desiredDecimals)
309+
310+
return {
311+
...assetAmount,
312+
mainCurrencyAmount: convertedDecimalValue,
313+
localizedMainCurrencyAmount: formatCurrencyAmount(
314+
displayCurrency.code,
315+
convertedDecimalValue,
316+
desiredDecimals,
317+
),
318+
unitPrice: unitPriceDecimalValue,
319+
localizedUnitPrice:
320+
typeof unitPriceDecimalValue === "undefined"
321+
? undefined
322+
: formatCurrencyAmount(
323+
assetAmountInCurrency.asset.symbol,
324+
unitPriceDecimalValue,
325+
desiredDecimals,
326+
),
327+
}
313328
}
314329
}
315330

0 commit comments

Comments
 (0)