@mezo-org/passport 0.4.0-dev.81 → 0.4.0-dev.83

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/dist/src/api/auth.d.ts +1 -1
  2. package/dist/src/api/auth.d.ts.map +1 -1
  3. package/dist/src/components/Dropdown/Content.d.ts +0 -15
  4. package/dist/src/components/Dropdown/Content.d.ts.map +1 -1
  5. package/dist/src/components/Dropdown/Content.js +3 -3
  6. package/dist/src/components/Dropdown/Content.js.map +1 -1
  7. package/dist/src/components/Dropdown/Dropdown.d.ts +0 -4
  8. package/dist/src/components/Dropdown/Dropdown.d.ts.map +1 -1
  9. package/dist/src/components/Dropdown/Dropdown.js +10 -33
  10. package/dist/src/components/Dropdown/Dropdown.js.map +1 -1
  11. package/dist/src/components/Dropdown/ListingItem.d.ts +8 -7
  12. package/dist/src/components/Dropdown/ListingItem.d.ts.map +1 -1
  13. package/dist/src/components/Dropdown/ListingItem.js +36 -28
  14. package/dist/src/components/Dropdown/ListingItem.js.map +1 -1
  15. package/dist/src/components/Dropdown/NestedViewLayout.d.ts +2 -1
  16. package/dist/src/components/Dropdown/NestedViewLayout.d.ts.map +1 -1
  17. package/dist/src/components/Dropdown/NestedViewLayout.js +13 -15
  18. package/dist/src/components/Dropdown/NestedViewLayout.js.map +1 -1
  19. package/dist/src/components/Dropdown/Receive/Receive.d.ts +1 -4
  20. package/dist/src/components/Dropdown/Receive/Receive.d.ts.map +1 -1
  21. package/dist/src/components/Dropdown/Receive/Receive.js +36 -19
  22. package/dist/src/components/Dropdown/Receive/Receive.js.map +1 -1
  23. package/dist/src/components/Dropdown/Root/AccountAddressActions.d.ts +2 -5
  24. package/dist/src/components/Dropdown/Root/AccountAddressActions.d.ts.map +1 -1
  25. package/dist/src/components/Dropdown/Root/AccountAddressActions.js +28 -12
  26. package/dist/src/components/Dropdown/Root/AccountAddressActions.js.map +1 -1
  27. package/dist/src/components/Dropdown/Root/AccountBalance.d.ts +2 -4
  28. package/dist/src/components/Dropdown/Root/AccountBalance.d.ts.map +1 -1
  29. package/dist/src/components/Dropdown/Root/AccountBalance.js +24 -8
  30. package/dist/src/components/Dropdown/Root/AccountBalance.js.map +1 -1
  31. package/dist/src/components/Dropdown/Root/AccountBtcListing.d.ts +6 -0
  32. package/dist/src/components/Dropdown/Root/AccountBtcListing.d.ts.map +1 -0
  33. package/dist/src/components/Dropdown/Root/AccountBtcListing.js +27 -0
  34. package/dist/src/components/Dropdown/Root/AccountBtcListing.js.map +1 -0
  35. package/dist/src/components/Dropdown/Root/AccountError.d.ts +8 -0
  36. package/dist/src/components/Dropdown/Root/AccountError.d.ts.map +1 -0
  37. package/dist/src/components/Dropdown/Root/AccountError.js +17 -0
  38. package/dist/src/components/Dropdown/Root/AccountError.js.map +1 -0
  39. package/dist/src/components/Dropdown/Root/AccountMusdListing.d.ts +4 -0
  40. package/dist/src/components/Dropdown/Root/AccountMusdListing.d.ts.map +1 -0
  41. package/dist/src/components/Dropdown/Root/AccountMusdListing.js +21 -0
  42. package/dist/src/components/Dropdown/Root/AccountMusdListing.js.map +1 -0
  43. package/dist/src/components/Dropdown/Root/AccountOtherAssets.d.ts +2 -3
  44. package/dist/src/components/Dropdown/Root/AccountOtherAssets.d.ts.map +1 -1
  45. package/dist/src/components/Dropdown/Root/AccountOtherAssets.js +34 -39
  46. package/dist/src/components/Dropdown/Root/AccountOtherAssets.js.map +1 -1
  47. package/dist/src/components/Dropdown/Root/Root.d.ts +0 -15
  48. package/dist/src/components/Dropdown/Root/Root.d.ts.map +1 -1
  49. package/dist/src/components/Dropdown/Root/Root.js +22 -34
  50. package/dist/src/components/Dropdown/Root/Root.js.map +1 -1
  51. package/dist/src/components/Dropdown/Root/WalletAddress.d.ts +2 -6
  52. package/dist/src/components/Dropdown/Root/WalletAddress.d.ts.map +1 -1
  53. package/dist/src/components/Dropdown/Root/WalletAddress.js +43 -34
  54. package/dist/src/components/Dropdown/Root/WalletAddress.js.map +1 -1
  55. package/dist/src/components/Dropdown/Root/WelcomeBlock.d.ts +2 -4
  56. package/dist/src/components/Dropdown/Root/WelcomeBlock.d.ts.map +1 -1
  57. package/dist/src/components/Dropdown/Root/WelcomeBlock.js +60 -16
  58. package/dist/src/components/Dropdown/Root/WelcomeBlock.js.map +1 -1
  59. package/dist/src/components/Dropdown/SlotNumber.d.ts +19 -0
  60. package/dist/src/components/Dropdown/SlotNumber.d.ts.map +1 -0
  61. package/dist/src/components/Dropdown/SlotNumber.js +67 -0
  62. package/dist/src/components/Dropdown/SlotNumber.js.map +1 -0
  63. package/dist/src/config.d.ts +1 -11
  64. package/dist/src/config.d.ts.map +1 -1
  65. package/dist/src/config.js +3 -40
  66. package/dist/src/config.js.map +1 -1
  67. package/dist/src/constants.d.ts +0 -2
  68. package/dist/src/constants.d.ts.map +1 -1
  69. package/dist/src/constants.js +0 -2
  70. package/dist/src/constants.js.map +1 -1
  71. package/dist/src/hooks/useAssetsConversionRates.d.ts +8 -13
  72. package/dist/src/hooks/useAssetsConversionRates.d.ts.map +1 -1
  73. package/dist/src/hooks/useAssetsConversionRates.js +44 -67
  74. package/dist/src/hooks/useAssetsConversionRates.js.map +1 -1
  75. package/dist/src/hooks/useAuthenticateWithWallet.d.ts.map +1 -1
  76. package/dist/src/hooks/useAuthenticateWithWallet.js +1 -1
  77. package/dist/src/hooks/useAuthenticateWithWallet.js.map +1 -1
  78. package/dist/src/hooks/useBorrowData.d.ts +31 -5
  79. package/dist/src/hooks/useBorrowData.d.ts.map +1 -1
  80. package/dist/src/hooks/useBorrowData.js +53 -11
  81. package/dist/src/hooks/useBorrowData.js.map +1 -1
  82. package/dist/src/hooks/useCreateAccount.d.ts.map +1 -1
  83. package/dist/src/hooks/useCreateAccount.js +3 -3
  84. package/dist/src/hooks/useCreateAccount.js.map +1 -1
  85. package/dist/src/hooks/useGetCurrentAccount.d.ts +2 -2
  86. package/dist/src/hooks/useGetCurrentAccount.d.ts.map +1 -1
  87. package/dist/src/hooks/useGetCurrentAccount.js +4 -6
  88. package/dist/src/hooks/useGetCurrentAccount.js.map +1 -1
  89. package/dist/src/hooks/useLinkAccount.d.ts.map +1 -1
  90. package/dist/src/hooks/useLinkAccount.js +3 -3
  91. package/dist/src/hooks/useLinkAccount.js.map +1 -1
  92. package/dist/src/hooks/useTokensBalances.d.ts +36 -35
  93. package/dist/src/hooks/useTokensBalances.d.ts.map +1 -1
  94. package/dist/src/hooks/useTokensBalances.js +93 -52
  95. package/dist/src/hooks/useTokensBalances.js.map +1 -1
  96. package/dist/src/hooks/useWalletAccount.d.ts +8 -10
  97. package/dist/src/hooks/useWalletAccount.d.ts.map +1 -1
  98. package/dist/src/hooks/useWalletAccount.js +22 -19
  99. package/dist/src/hooks/useWalletAccount.js.map +1 -1
  100. package/dist/src/index.d.ts +1 -1
  101. package/dist/src/index.d.ts.map +1 -1
  102. package/dist/src/index.js +1 -1
  103. package/dist/src/index.js.map +1 -1
  104. package/dist/src/lib/contracts/index.d.ts +1 -1
  105. package/dist/src/lib/contracts/index.d.ts.map +1 -1
  106. package/dist/src/lib/contracts/index.js +4 -0
  107. package/dist/src/lib/contracts/index.js.map +1 -1
  108. package/dist/src/provider.d.ts +7 -1
  109. package/dist/src/provider.d.ts.map +1 -1
  110. package/dist/src/provider.js +4 -1
  111. package/dist/src/provider.js.map +1 -1
  112. package/dist/src/utils/assets.d.ts +145 -0
  113. package/dist/src/utils/assets.d.ts.map +1 -0
  114. package/dist/src/utils/assets.js +100 -0
  115. package/dist/src/utils/assets.js.map +1 -0
  116. package/dist/src/utils/assets.test.d.ts +2 -0
  117. package/dist/src/utils/assets.test.d.ts.map +1 -0
  118. package/dist/src/utils/assets.test.js +46 -0
  119. package/dist/src/utils/assets.test.js.map +1 -0
  120. package/dist/src/utils/currency.d.ts +6 -3
  121. package/dist/src/utils/currency.d.ts.map +1 -1
  122. package/dist/src/utils/currency.js +13 -10
  123. package/dist/src/utils/currency.js.map +1 -1
  124. package/dist/src/utils/currency.test.js +44 -2
  125. package/dist/src/utils/currency.test.js.map +1 -1
  126. package/dist/src/utils/numbers.d.ts +13 -53
  127. package/dist/src/utils/numbers.d.ts.map +1 -1
  128. package/dist/src/utils/numbers.js +16 -118
  129. package/dist/src/utils/numbers.js.map +1 -1
  130. package/dist/src/utils/numbers.test.js +24 -142
  131. package/dist/src/utils/numbers.test.js.map +1 -1
  132. package/package.json +2 -1
  133. package/src/api/auth.ts +1 -1
  134. package/src/components/Dropdown/Content.tsx +3 -48
  135. package/src/components/Dropdown/Dropdown.tsx +7 -55
  136. package/src/components/Dropdown/ListingItem.tsx +155 -59
  137. package/src/components/Dropdown/NestedViewLayout.tsx +32 -20
  138. package/src/components/Dropdown/Receive/Receive.tsx +69 -32
  139. package/src/components/Dropdown/Root/AccountAddressActions.tsx +64 -35
  140. package/src/components/Dropdown/Root/AccountBalance.tsx +54 -16
  141. package/src/components/Dropdown/Root/AccountBtcListing.tsx +52 -0
  142. package/src/components/Dropdown/Root/AccountError.tsx +34 -0
  143. package/src/components/Dropdown/Root/AccountMusdListing.tsx +45 -0
  144. package/src/components/Dropdown/Root/AccountOtherAssets.tsx +63 -46
  145. package/src/components/Dropdown/Root/Root.tsx +28 -98
  146. package/src/components/Dropdown/Root/WalletAddress.tsx +95 -89
  147. package/src/components/Dropdown/Root/WelcomeBlock.tsx +109 -29
  148. package/src/components/Dropdown/SlotNumber.tsx +131 -0
  149. package/src/config.ts +3 -59
  150. package/src/constants.ts +0 -6
  151. package/src/hooks/useAssetsConversionRates.ts +49 -67
  152. package/src/hooks/useAuthenticateWithWallet.ts +7 -5
  153. package/src/hooks/useBorrowData.ts +71 -12
  154. package/src/hooks/useCreateAccount.ts +5 -4
  155. package/src/hooks/useGetCurrentAccount.ts +5 -7
  156. package/src/hooks/useLinkAccount.ts +5 -4
  157. package/src/hooks/useTokensBalances.ts +152 -74
  158. package/src/hooks/useWalletAccount.ts +27 -36
  159. package/src/index.ts +0 -1
  160. package/src/lib/contracts/index.ts +8 -1
  161. package/src/provider.ts +11 -3
  162. package/src/utils/assets.test.ts +57 -0
  163. package/src/utils/assets.ts +103 -0
  164. package/src/utils/currency.test.ts +76 -2
  165. package/src/utils/currency.ts +20 -15
  166. package/src/utils/numbers.test.ts +29 -180
  167. package/src/utils/numbers.ts +22 -171
  168. package/dist/src/components/Dropdown/Root/AccountAssetItem.d.ts +0 -11
  169. package/dist/src/components/Dropdown/Root/AccountAssetItem.d.ts.map +0 -1
  170. package/dist/src/components/Dropdown/Root/AccountAssetItem.js +0 -9
  171. package/dist/src/components/Dropdown/Root/AccountAssetItem.js.map +0 -1
  172. package/dist/src/hooks/useDropdownData.d.ts +0 -47
  173. package/dist/src/hooks/useDropdownData.d.ts.map +0 -1
  174. package/dist/src/hooks/useDropdownData.js +0 -97
  175. package/dist/src/hooks/useDropdownData.js.map +0 -1
  176. package/dist/src/utils/cryptoAssets.d.ts +0 -44
  177. package/dist/src/utils/cryptoAssets.d.ts.map +0 -1
  178. package/dist/src/utils/cryptoAssets.js +0 -129
  179. package/dist/src/utils/cryptoAssets.js.map +0 -1
  180. package/dist/src/utils/cryptoAssets.test.d.ts +0 -2
  181. package/dist/src/utils/cryptoAssets.test.d.ts.map +0 -1
  182. package/dist/src/utils/cryptoAssets.test.js +0 -67
  183. package/dist/src/utils/cryptoAssets.test.js.map +0 -1
  184. package/src/components/Dropdown/Root/AccountAssetItem.tsx +0 -26
  185. package/src/hooks/useDropdownData.ts +0 -152
  186. package/src/utils/cryptoAssets.test.ts +0 -79
  187. package/src/utils/cryptoAssets.ts +0 -171
package/src/provider.ts CHANGED
@@ -1,10 +1,13 @@
1
1
  import { createContext, createElement } from "react"
2
2
  import { AuthApiClient, PortalApiClient } from "./api"
3
+ import { ONE_SECOND_MS } from "./utils/time"
3
4
 
4
5
  export interface PassportContextValue {
5
6
  authApiClient: AuthApiClient
6
7
  portalApiClient: PortalApiClient
7
8
  environment?: Environment
9
+ accountDataRefetchInterval: number
10
+ nativeBalanceRefetchInterval: number
8
11
  }
9
12
 
10
13
  export const PassportContext = createContext<PassportContextValue | undefined>(
@@ -15,12 +18,13 @@ type Environment = "mainnet" | "testnet"
15
18
 
16
19
  type PassportProviderProps = {
17
20
  children: React.ReactNode
18
- // eslint-disable-next-line react/require-default-props
19
21
  environment?: Environment
20
- // eslint-disable-next-line react/require-default-props
21
22
  authApiUrl?: string
22
- // eslint-disable-next-line react/require-default-props
23
23
  portalApiUrl?: string
24
+ /** Time in milliseconds after which account data (like mats or mezo id) in dropdown should be re-fetched. Default is 90000 (90 secs) */
25
+ accountDataRefetchInterval?: number
26
+ /** Time in milliseconds after which native balance in dropdown should be re-fetched. Default is 90000 (90 secs) */
27
+ nativeBalanceRefetchInterval?: number
24
28
  }
25
29
 
26
30
  export function PassportProvider({
@@ -28,6 +32,8 @@ export function PassportProvider({
28
32
  authApiUrl,
29
33
  portalApiUrl,
30
34
  children,
35
+ accountDataRefetchInterval = 90 * ONE_SECOND_MS,
36
+ nativeBalanceRefetchInterval = 90 * ONE_SECOND_MS,
31
37
  }: PassportProviderProps) {
32
38
  if (environment && !["mainnet", "testnet"].includes(environment)) {
33
39
  throw new Error("Wrong environment passed to PassportProvider.")
@@ -43,6 +49,8 @@ export function PassportProvider({
43
49
  portalApiUrl,
44
50
  ),
45
51
  environment,
52
+ accountDataRefetchInterval,
53
+ nativeBalanceRefetchInterval,
46
54
  },
47
55
  },
48
56
  children,
@@ -0,0 +1,57 @@
1
+ import {
2
+ getAsset,
3
+ isBitcoinLikeCryptoAsset,
4
+ isUsdLikeCryptoAsset,
5
+ isTTokenCryptoAsset,
6
+ } from "./assets"
7
+
8
+ describe("getAsset", () => {
9
+ it("should return correct asset data", () => {
10
+ const asset = getAsset("BTC")
11
+ expect(asset).toEqual({
12
+ name: "Bitcoin",
13
+ symbol: "BTC",
14
+ decimals: 18,
15
+ })
16
+ })
17
+ })
18
+
19
+ describe("isBitcoinLikeCryptoAsset", () => {
20
+ it('should return true for keys containing "btc"', () => {
21
+ expect(isBitcoinLikeCryptoAsset("BTC")).toBe(true)
22
+ expect(isBitcoinLikeCryptoAsset("mFBTC")).toBe(true)
23
+ expect(isBitcoinLikeCryptoAsset("mcbBTC")).toBe(true)
24
+ expect(isBitcoinLikeCryptoAsset("mxSolvBTC")).toBe(true)
25
+ })
26
+
27
+ it("should return false for non-Bitcoin assets", () => {
28
+ expect(isBitcoinLikeCryptoAsset("ETH")).toBe(false)
29
+ expect(isBitcoinLikeCryptoAsset("mUSDC")).toBe(false)
30
+ })
31
+ })
32
+
33
+ describe("isUsdLikeCryptoAsset", () => {
34
+ it('should return true for keys containing "usd" or "dai"', () => {
35
+ expect(isUsdLikeCryptoAsset("mUSDC")).toBe(true)
36
+ expect(isUsdLikeCryptoAsset("mUSDT")).toBe(true)
37
+ expect(isUsdLikeCryptoAsset("mUSDe")).toBe(true)
38
+ expect(isUsdLikeCryptoAsset("mDAI")).toBe(true)
39
+ })
40
+
41
+ it("should return false for other tokens", () => {
42
+ expect(isUsdLikeCryptoAsset("BTC")).toBe(false)
43
+ expect(isUsdLikeCryptoAsset("mT")).toBe(false)
44
+ })
45
+ })
46
+
47
+ describe("isTTokenCryptoAsset", () => {
48
+ it('should return true for keys like "mT" or "t"', () => {
49
+ expect(isTTokenCryptoAsset("mT")).toBe(true)
50
+ expect(isTTokenCryptoAsset("T")).toBe(true)
51
+ })
52
+
53
+ it("should return false for other tokens", () => {
54
+ expect(isTTokenCryptoAsset("BTC")).toBe(false)
55
+ expect(isTTokenCryptoAsset("mUSDT")).toBe(false)
56
+ })
57
+ })
@@ -0,0 +1,103 @@
1
+ const ASSETS = {
2
+ BTC: {
3
+ name: "Bitcoin",
4
+ symbol: "BTC",
5
+ decimals: 18,
6
+ },
7
+ ETH: {
8
+ name: "Ethereum",
9
+ symbol: "ETH",
10
+ decimals: 18,
11
+ },
12
+ MUSD: {
13
+ name: "MUSD",
14
+ symbol: "MUSD",
15
+ decimals: 18,
16
+ },
17
+ mDAI: {
18
+ name: "Mezo Dai Stablecoin",
19
+ symbol: "mDAI",
20
+ decimals: 18,
21
+ },
22
+ mFBTC: {
23
+ name: "Mezo Fire Bitcoin",
24
+ symbol: "mFBTC",
25
+ decimals: 8,
26
+ },
27
+ mcbBTC: {
28
+ name: "Mezo Coinbase Wrapped BTC",
29
+ symbol: "mcbBTC",
30
+ decimals: 8,
31
+ },
32
+ mSolvBTC: {
33
+ name: "Mezo SolvBTC",
34
+ symbol: "mSolvBTC",
35
+ decimals: 18,
36
+ },
37
+ mswBTC: {
38
+ name: "Mezo swBTC",
39
+ symbol: "mswBTC",
40
+ decimals: 8,
41
+ },
42
+ mT: {
43
+ name: "Mezo Threshold Network Token",
44
+ symbol: "mT",
45
+ decimals: 18,
46
+ },
47
+ mUSDC: {
48
+ name: "Mezo Circle USDC",
49
+ symbol: "mUSDC",
50
+ decimals: 6,
51
+ },
52
+ mUSDe: {
53
+ name: "Mezo Ethena USDe",
54
+ symbol: "mUSDe",
55
+ decimals: 18,
56
+ },
57
+ mUSDT: {
58
+ name: "Mezo Tether USDe",
59
+ symbol: "mUSDT",
60
+ decimals: 6,
61
+ },
62
+ mxSolvBTC: {
63
+ name: "Mezo xSolvBTC",
64
+ symbol: "mxSolvBTC",
65
+ decimals: 18,
66
+ },
67
+ }
68
+
69
+ /**
70
+ * Gets details of given crypto asset
71
+ * @param key The key of crypto asset
72
+ * @returns The crypto asset details
73
+ */
74
+ export function getAsset(key: keyof typeof ASSETS) {
75
+ return ASSETS[key]
76
+ }
77
+
78
+ /**
79
+ * Checks if given crypto asset is Bitcoin-like
80
+ * @param key The key of crypto asset
81
+ * @returns True if crypto asset is Bitcoin-like
82
+ */
83
+ export function isBitcoinLikeCryptoAsset(key: string) {
84
+ return /(btc)/i.test(key)
85
+ }
86
+
87
+ /**
88
+ * Checks if given crypto asset is USD-like
89
+ * @param key The key of crypto asset
90
+ * @returns True if crypto asset is USD-like
91
+ */
92
+ export function isUsdLikeCryptoAsset(key: string) {
93
+ return /(usd|dai)/i.test(key)
94
+ }
95
+
96
+ /**
97
+ * Checks if given crypto asset is T token
98
+ * @param key The key of crypto asset
99
+ * @returns True if crypto asset is T token
100
+ */
101
+ export function isTTokenCryptoAsset(key: string) {
102
+ return /^(mt|t)$/i.test(key)
103
+ }
@@ -1,4 +1,4 @@
1
- import { formatCurrency, formatUsd } from "./currency"
1
+ import { convertToUsd, formatCurrency, formatUsd } from "./currency"
2
2
 
3
3
  describe("formatCurrency", () => {
4
4
  it("formats a number as currency with default options", () => {
@@ -37,4 +37,78 @@ describe("formatUsd", () => {
37
37
  })
38
38
  })
39
39
 
40
- // TODO: Add tests for convertToUsd function
40
+ describe("convertToUsd", () => {
41
+ it("converts 1 BTC (8 decimals) with $30,000 rate (6 decimals)", () => {
42
+ const assetAmount = 1_00000000n // 1 BTC
43
+ const assetDecimals = 8
44
+ const conversionRate = 30_000_000000n // $30,000 in 6 decimals
45
+ const conversionRateDecimals = 6
46
+
47
+ const result = convertToUsd(
48
+ assetAmount,
49
+ assetDecimals,
50
+ conversionRate,
51
+ conversionRateDecimals,
52
+ )
53
+
54
+ expect(result.value).toBe(30_000_000000n)
55
+ expect(result.formatted).toBe("30000")
56
+ })
57
+
58
+ it("converts 0.5 BTC correctly", () => {
59
+ const assetAmount = 50_000000n // 0.5 BTC
60
+ const assetDecimals = 8
61
+ const conversionRate = 40_000_000000n // $40,000
62
+ const conversionRateDecimals = 6
63
+
64
+ const result = convertToUsd(
65
+ assetAmount,
66
+ assetDecimals,
67
+ conversionRate,
68
+ conversionRateDecimals,
69
+ )
70
+
71
+ expect(result.value).toBe(20_000_000000n)
72
+ expect(result.formatted).toBe("20000")
73
+ })
74
+
75
+ it("converts 1 ETH (18 decimals) with $2,000 rate (6 decimals)", () => {
76
+ const assetAmount = 1_000000000000000000n // 1 ETH
77
+ const assetDecimals = 18
78
+ const conversionRate = 2_000_000000n
79
+ const conversionRateDecimals = 6
80
+
81
+ const result = convertToUsd(
82
+ assetAmount,
83
+ assetDecimals,
84
+ conversionRate,
85
+ conversionRateDecimals,
86
+ )
87
+
88
+ expect(result.value).toBe(2_000_000000n)
89
+ expect(result.formatted).toBe("2000")
90
+ })
91
+
92
+ it("handles zero amount correctly", () => {
93
+ const result = convertToUsd(0n, 18, 999_999999n, 6)
94
+ expect(result.value).toBe(0n)
95
+ expect(result.formatted).toBe("0")
96
+ })
97
+
98
+ it("handles assets with fewer decimals than conversion rate", () => {
99
+ const assetAmount = 100n
100
+ const assetDecimals = 2
101
+ const conversionRate = 1_500_000000n // $1500
102
+ const conversionRateDecimals = 6
103
+
104
+ const result = convertToUsd(
105
+ assetAmount,
106
+ assetDecimals,
107
+ conversionRate,
108
+ conversionRateDecimals,
109
+ )
110
+
111
+ expect(result.value).toBe(1_500_000000n)
112
+ expect(result.formatted).toBe("1500")
113
+ })
114
+ })
@@ -1,10 +1,5 @@
1
- import { fromFixedPoint } from "./numbers"
2
-
3
- const DEFAULT_FORMAT_OPTIONS: Intl.NumberFormatOptions = {
4
- style: "currency",
5
- minimumFractionDigits: 2,
6
- maximumFractionDigits: 2,
7
- }
1
+ import { formatUnits } from "viem"
2
+ import { normalizePrecision } from "./numbers"
8
3
 
9
4
  /**
10
5
  * Formats a number as a currency
@@ -14,10 +9,12 @@ const DEFAULT_FORMAT_OPTIONS: Intl.NumberFormatOptions = {
14
9
  */
15
10
  export function formatCurrency(
16
11
  value: number,
17
- options: Omit<Intl.NumberFormatOptions, "style"> = {},
12
+ options: Omit<Intl.NumberFormatOptions, "style">,
18
13
  ): string {
19
14
  const formatter = new Intl.NumberFormat("en-US", {
20
- ...DEFAULT_FORMAT_OPTIONS,
15
+ style: "currency",
16
+ minimumFractionDigits: 2,
17
+ maximumFractionDigits: 2,
21
18
  ...options,
22
19
  })
23
20
 
@@ -29,8 +26,8 @@ export function formatCurrency(
29
26
  * @param value The value to format
30
27
  * @returns The formatted currency
31
28
  */
32
- export function formatUsd(value: number): string {
33
- return formatCurrency(value, { currency: "USD" })
29
+ export function formatUsd(value: number | string): string {
30
+ return formatCurrency(Number(value), { currency: "USD" })
34
31
  }
35
32
 
36
33
  /**
@@ -45,9 +42,17 @@ export function convertToUsd(
45
42
  assetDecimals: number,
46
43
  conversionRate: bigint,
47
44
  conversionRateDecimals: number,
48
- ): number {
49
- return fromFixedPoint(
50
- (assetAmount * conversionRate) / 10n ** BigInt(conversionRateDecimals),
51
- assetDecimals,
45
+ ) {
46
+ const value = normalizePrecision(
47
+ assetAmount * conversionRate,
48
+ assetDecimals + conversionRateDecimals,
49
+ conversionRateDecimals,
52
50
  )
51
+
52
+ const formatted = formatUnits(value, conversionRateDecimals)
53
+
54
+ return {
55
+ value,
56
+ formatted,
57
+ }
53
58
  }
@@ -1,10 +1,7 @@
1
1
  import {
2
2
  formatNumberToCompactString,
3
- fromFixedPoint,
4
- roundUpNumber,
5
- formatNumberToLocaleString,
6
- fromFixedPointToString,
7
3
  bigIntMax,
4
+ normalizePrecision,
8
5
  } from "./numbers"
9
6
 
10
7
  describe("formatNumberToCompactString", () => {
@@ -44,182 +41,6 @@ describe("formatNumberToCompactString", () => {
44
41
  })
45
42
  })
46
43
 
47
- describe("roundUpNumber", () => {
48
- it("rounds up to 2 decimals", () => {
49
- expect(roundUpNumber(1.234)).toBe(1.24)
50
- })
51
-
52
- it("rounds up to 3 decimals", () => {
53
- expect(roundUpNumber(9.87654, 3)).toBe(9.877)
54
- })
55
-
56
- it("rounds up exact decimal", () => {
57
- expect(roundUpNumber(2.5, 1)).toBe(2.5)
58
- })
59
- })
60
-
61
- describe("fromFixedPoint", () => {
62
- it("converts fixed point with 18 decimals to float", () => {
63
- const raw = 1234567890000000000n // 1.23456789 ETH in 18 decimals
64
- expect(fromFixedPoint(raw, 18)).toBe(1.2345)
65
- })
66
-
67
- it("converts with truncation to 2 decimals", () => {
68
- const raw = 987654321000000000n // ~0.987654321 ETH
69
- expect(fromFixedPoint(raw, 18, 2)).toBe(0.98)
70
- })
71
-
72
- it("handles large fixedPoint decimals", () => {
73
- const raw = 1_000_000_000_000_000_000_000_000n // 1M with 6 decimals
74
- expect(fromFixedPoint(raw, 6, 2)).toBe(1000000000000000000)
75
- })
76
- })
77
-
78
- describe("formatNumberToLocaleString", () => {
79
- it("formats zero with 0 decimals", () => {
80
- expect(formatNumberToLocaleString(0, 0)).toBe("0")
81
- })
82
-
83
- it("formats zero with >0 decimals", () => {
84
- expect(formatNumberToLocaleString(0, 2)).toBe("0.00")
85
- expect(formatNumberToLocaleString(0, 4)).toBe("0.0000")
86
- })
87
-
88
- it("formats small decimal number", () => {
89
- expect(formatNumberToLocaleString(0.123456, 2)).toBe("0.12")
90
- expect(formatNumberToLocaleString(0.123456, 4)).toBe("0.1235")
91
- })
92
-
93
- it("formats number just above 1", () => {
94
- expect(formatNumberToLocaleString(1.000001, 3)).toBe("1.000")
95
- expect(formatNumberToLocaleString(1.234567, 2)).toBe("1.23")
96
- })
97
-
98
- it("formats whole number", () => {
99
- expect(formatNumberToLocaleString(1234, 0)).toBe("1,234")
100
- expect(formatNumberToLocaleString(1000, 2)).toBe("1,000.00")
101
- })
102
-
103
- it("formats number with trailing decimal zeroes", () => {
104
- expect(formatNumberToLocaleString(10.1, 3)).toBe("10.100")
105
- expect(formatNumberToLocaleString(5.0, 2)).toBe("5.00")
106
- })
107
-
108
- it("formats string input", () => {
109
- expect(formatNumberToLocaleString("123.456", 2)).toBe("123.46")
110
- expect(formatNumberToLocaleString("0.00123", 4)).toBe("0.0012")
111
- })
112
-
113
- it("formats negative number", () => {
114
- expect(formatNumberToLocaleString(-123.456, 2)).toBe("-123.46")
115
- expect(formatNumberToLocaleString("-0.00999", 3)).toBe("-0.010")
116
- })
117
-
118
- it("formats large number with commas", () => {
119
- expect(formatNumberToLocaleString(12345678.9, 2)).toBe("12,345,678.90")
120
- })
121
-
122
- it("handles max decimals safely", () => {
123
- expect(formatNumberToLocaleString(1.123456789, 8)).toBe("1.12345679")
124
- expect(formatNumberToLocaleString("0.000000000000000001", 18)).toBe(
125
- "0.000000000000000001",
126
- )
127
- })
128
-
129
- it("defaults to 0 decimals if not passed", () => {
130
- expect(formatNumberToLocaleString(1234.567)).toBe("1,235") // rounds up by default
131
- })
132
- })
133
-
134
- describe("fromFixedPointToString", () => {
135
- const toFixedPoint = (num: number) => BigInt(Math.round(num * 1e18))
136
-
137
- const cases = [
138
- { input: 2.0, expected: "2.000" },
139
- { input: 2.009, expected: "2.009" },
140
- { input: 2.01, expected: "2.010" },
141
- { input: 2.015, expected: "2.016" },
142
- { input: 2.05, expected: "2.050" },
143
- { input: 2.09, expected: "2.090" },
144
- ]
145
-
146
- describe("withRoundUp: true", () => {
147
- cases.forEach(({ input, expected }) => {
148
- it(`formats ${input} → ${expected}`, () => {
149
- const fixed = toFixedPoint(input)
150
- expect(fromFixedPointToString(fixed, 18, 3, true)).toBe(expected)
151
- })
152
- })
153
- })
154
-
155
- describe("withRoundUp: false", () => {
156
- const noRoundCases = [
157
- { input: 2.0099, expected: "2.009" },
158
- { input: 2.0159, expected: "2.015" },
159
- { input: 2.0101, expected: "2.010" },
160
- { input: 2.0199, expected: "2.019" },
161
- ]
162
-
163
- noRoundCases.forEach(({ input, expected }) => {
164
- it(`formats ${input} → ${expected} (no rounding)`, () => {
165
- const fixed = toFixedPoint(input)
166
- expect(fromFixedPointToString(fixed, 18, 3, false)).toBe(expected)
167
- })
168
- })
169
- })
170
-
171
- describe("zero handling", () => {
172
- it("formats 0 → 0.000 (3 decimals)", () => {
173
- expect(fromFixedPointToString(0n, 18, 3)).toBe("0.000")
174
- })
175
-
176
- it("formats 0 → 0.0000 (4 decimals)", () => {
177
- expect(fromFixedPointToString(0n, 18, 4)).toBe("0.0000")
178
- })
179
- })
180
-
181
- describe("very small numbers below display threshold", () => {
182
- it("returns <0.0001 for small number (3 decimals)", () => {
183
- const small = BigInt(123) // ~1.23e-16
184
- expect(fromFixedPointToString(small, 18, 4)).toBe("<0.0001")
185
- })
186
-
187
- it("returns <0.01 for small number with only 2 decimals", () => {
188
- const tiny = BigInt(9999999999999) // <0.01
189
- expect(fromFixedPointToString(tiny, 18, 2)).toBe("<0.01")
190
- })
191
- })
192
-
193
- describe("input types", () => {
194
- it("accepts bigint", () => {
195
- const amount = 2100000000000000000n
196
- expect(fromFixedPointToString(amount, 18, 3)).toBe("2.100")
197
- })
198
-
199
- it("accepts string", () => {
200
- expect(fromFixedPointToString("2000000000000000000", 18, 3)).toBe("2.000")
201
- })
202
-
203
- it("accepts number", () => {
204
- expect(fromFixedPointToString(Number("1000000000000000000"), 18, 3)).toBe(
205
- "1.000",
206
- )
207
- })
208
- })
209
-
210
- describe("high values", () => {
211
- it("formats 1_000_000.123456789 correctly", () => {
212
- const big = toFixedPoint(1_000_000.123456789)
213
- expect(fromFixedPointToString(big, 18, 6)).toBe("1,000,000.123456")
214
- })
215
-
216
- it("formats 1e12 ETH", () => {
217
- const big = BigInt("1000000000000000000000000000000")
218
- expect(fromFixedPointToString(big, 18, 2)).toBe("1,000,000,000,000.00")
219
- })
220
- })
221
- })
222
-
223
44
  describe("bigIntMax", () => {
224
45
  it("returns the maximum of positive bigint values", () => {
225
46
  expect(bigIntMax(1n, 2n, 3n)).toBe(3n)
@@ -245,3 +66,31 @@ describe("bigIntMax", () => {
245
66
  expect(bigIntMax(5n, 5n, 5n)).toBe(5n)
246
67
  })
247
68
  })
69
+
70
+ describe("normalizePrecision", () => {
71
+ it("reduces precision correctly (scale down)", () => {
72
+ const result = normalizePrecision(1234500n, 6, 3)
73
+ expect(result).toBe(1234n)
74
+ })
75
+
76
+ it("increases precision correctly (scale up)", () => {
77
+ const result = normalizePrecision(1234n, 3, 6)
78
+ expect(result).toBe(1234000n)
79
+ })
80
+
81
+ it("returns the same value when precision is equal", () => {
82
+ const result = normalizePrecision(987654321n, 5, 5)
83
+ expect(result).toBe(987654321n)
84
+ })
85
+
86
+ it("handles zero value correctly", () => {
87
+ const result = normalizePrecision(0n, 4, 8)
88
+ expect(result).toBe(0n)
89
+ })
90
+
91
+ it("handles large numbers without precision loss", () => {
92
+ const value = 123456789012345678901234567890n
93
+ const result = normalizePrecision(value, 18, 20)
94
+ expect(result).toBe(value * 100n)
95
+ })
96
+ })