@ensofinance/checkout-widget 0.0.6 → 0.0.7

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ensofinance/checkout-widget",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "type": "module",
5
5
  "homepage": "https://www.enso.build/",
6
6
  "repository": {
@@ -23,7 +23,7 @@
23
23
  "dev": "vite build --watch --mode=dev --minify=false --sourcemap=false --emptyOutDir=false",
24
24
  "build": "vite build",
25
25
  "preview": "vite preview",
26
- "build:pump:publish": "npm run build && npm run bump && npm run publish"
26
+ "build:pump:publish": "npm run bump && npm run build && npm run publish"
27
27
  },
28
28
  "dependencies": {
29
29
  "@chakra-ui/react": "^3.19.1",
@@ -23,11 +23,24 @@ import {
23
23
  } from "../ui/styled";
24
24
  import { CheckoutContext } from "../Checkout";
25
25
  import Modal from "../modal";
26
- import { createLink, IntegrationAccessToken } from "@meshconnect/web-link-sdk";
26
+ import {
27
+ AccessTokenPayload,
28
+ createLink,
29
+ IntegrationAccessToken,
30
+ } from "@meshconnect/web-link-sdk";
27
31
  import { useAppStore } from "@/store";
28
32
  import { AssetCard } from "../cards";
29
- import { formatNumber, formatUSD } from "@/util";
30
- import { useTokenFromListBySymbols, getChainName } from "@/util/common";
33
+ import {
34
+ denormalizeValue,
35
+ formatNumber,
36
+ formatUSD,
37
+ normalizeValue,
38
+ } from "@/util";
39
+ import {
40
+ useTokenFromListBySymbols,
41
+ getChainName,
42
+ precisionizeNumber,
43
+ } from "@/util/common";
31
44
  import {
32
45
  EXCHANGE_MAX_LIMIT_GAP_USD,
33
46
  EXCHANGE_MIN_LIMIT,
@@ -150,10 +163,30 @@ const MESH_NETWORK_IDS: { [chainId: number]: string } = {
150
163
  // 11155111: "03b2d786-7092-4a6a-9737-d6013e21819b", // Sepolia (testnet)
151
164
  };
152
165
 
166
+ const MESH_NETWORKS = Object.keys(MESH_NETWORK_IDS).map(Number);
167
+
153
168
  const getNetworkId = (chainId: number): string => {
154
169
  return MESH_NETWORK_IDS[chainId] || MESH_NETWORK_IDS[8453]; // Default to Base
155
170
  };
156
171
 
172
+ const useHandleMeshAccessPayload = () => {
173
+ const { setMeshAccessToken } = useAppStore();
174
+ const deviceKey = useDeviceKey();
175
+
176
+ return useCallback((accessTokenPayload: AccessTokenPayload) => {
177
+ setMeshAccessToken(accessTokenPayload); // Persist access token and session id for future reloads
178
+
179
+ sessionStorage.setItem(
180
+ deviceKey,
181
+ JSON.stringify({
182
+ accessToken: accessTokenPayload.accessToken,
183
+ sessionId: accessTokenPayload.content.sessionId,
184
+ timestamp: Date.now(),
185
+ }),
186
+ );
187
+ }, []);
188
+ };
189
+
157
190
  const CheckSessionKeyStep = ({
158
191
  setStep,
159
192
  }: {
@@ -161,12 +194,16 @@ const CheckSessionKeyStep = ({
161
194
  }) => {
162
195
  const { chainIdOut, setMeshAccessToken, setSessionId } = useAppStore();
163
196
  const { address } = useAccount();
164
- const storageKey = useStorageKey();
197
+ const deviceKey = useDeviceKey();
165
198
  const [showConfirmation, setShowConfirmation] = useState(false);
166
199
 
200
+ const invalidChainId = chainIdOut && !MESH_NETWORKS.includes(chainIdOut);
201
+ const handleMeshAccessPayload = useHandleMeshAccessPayload();
202
+
167
203
  useEffect(() => {
204
+ if (invalidChainId) return;
168
205
  // If connection is persisted, skip fetching a new link token
169
- const saved = localStorage.getItem(storageKey);
206
+ const saved = sessionStorage.getItem(deviceKey);
170
207
  // On load: check for persisted connection and hydrate state
171
208
  if (saved) {
172
209
  try {
@@ -185,7 +222,7 @@ const CheckSessionKeyStep = ({
185
222
 
186
223
  // Show confirmation instead of auto-connecting
187
224
  setShowConfirmation(true);
188
- }, [chainIdOut, address, storageKey]);
225
+ }, [deviceKey, invalidChainId]);
189
226
 
190
227
  const handleConfirmAuth = () => {
191
228
  fetch(`${MESH_API_URL}/linktoken`, {
@@ -194,7 +231,7 @@ const CheckSessionKeyStep = ({
194
231
  "Content-Type": "application/json",
195
232
  },
196
233
  body: JSON.stringify({
197
- userId: address,
234
+ userId: deviceKey,
198
235
  integrationId: BINANCE_INTEGRATION_ID,
199
236
  address,
200
237
  }),
@@ -207,31 +244,15 @@ const CheckSessionKeyStep = ({
207
244
  clientId: address,
208
245
  onIntegrationConnected: (payload) => {
209
246
  console.log("onIntegrationConnected", payload);
210
- setMeshAccessToken(payload.accessToken);
211
247
  setStep(WithdrawalStep.GetHoldings);
212
- // Persist access token and session id for future reloads
213
- try {
214
- localStorage.setItem(
215
- storageKey,
216
- JSON.stringify({
217
- accessToken: payload.accessToken,
218
- sessionId: response.content.sessionId,
219
- timestamp: Date.now(),
220
- }),
221
- );
222
- meshLink.closeLink();
223
- } catch (e) {
224
- console.error(
225
- "Failed to persist Mesh connection",
226
- e,
227
- );
228
- }
248
+ handleMeshAccessPayload(payload.accessToken); // Persist access token and session id for future reloads
249
+ meshLink.closeLink();
229
250
  },
230
251
  onExit: (error) => {
231
- debugger;
252
+ console.log("onExit", error);
232
253
  },
233
254
  onTransferFinished: (transferData) => {
234
- debugger;
255
+ console.log("onTransferFinished", transferData);
235
256
  },
236
257
  onEvent: (ev) => {
237
258
  console.log(ev);
@@ -243,6 +264,28 @@ const CheckSessionKeyStep = ({
243
264
  .catch((err) => console.error(err));
244
265
  };
245
266
 
267
+ if (invalidChainId) {
268
+ return (
269
+ <BodyWrapper>
270
+ <Box textAlign="center" py={8}>
271
+ <Text
272
+ fontSize="lg"
273
+ fontWeight="bold"
274
+ color="red.500"
275
+ mb={4}
276
+ >
277
+ Unsupported Network
278
+ </Text>
279
+ <Text color="fg.muted" mb={6}>
280
+ The selected network (Chain ID: {chainIdOut}) is not
281
+ supported for exchange transactions. Please switch to a
282
+ supported network such as: {MESH_NETWORKS.join(", ")}
283
+ </Text>
284
+ </Box>
285
+ </BodyWrapper>
286
+ );
287
+ }
288
+
246
289
  if (showConfirmation) {
247
290
  return <ConfirmExchangeStep onConfirm={handleConfirmAuth} />;
248
291
  }
@@ -251,7 +294,7 @@ const CheckSessionKeyStep = ({
251
294
  };
252
295
 
253
296
  // Generate a unique device ID to use as user id for Mesh
254
- const useStorageKey = () => {
297
+ const useDeviceKey = () => {
255
298
  return useMemo(() => {
256
299
  const deviceIdKey = "meshDeviceId";
257
300
  let deviceId = localStorage.getItem(deviceIdKey);
@@ -272,10 +315,10 @@ const ChooseAssetStep = ({
272
315
  setStep: (step: WithdrawalStep) => void;
273
316
  onTokenSelect: (token: MatchedToken) => void;
274
317
  }) => {
275
- const [holdings, setHoldings] = useState<CryptocurrencyPosition[]>([]);
276
- const [supportedTokens, setSupportedTokens] = useState<SupportedToken[]>(
277
- [],
278
- );
318
+ // const [holdings, setHoldings] = useState<CryptocurrencyPosition[]>([]);
319
+ // const [supportedTokens, setSupportedTokens] = useState<SupportedToken[]>(
320
+ // [],
321
+ // );
279
322
  const [matchedTokens, setMatchedTokens] = useState<MatchedToken[]>([]);
280
323
  const [selectedTokenSymbol, setSelectedTokenSymbol] = useState<
281
324
  string | null
@@ -293,7 +336,7 @@ const ChooseAssetStep = ({
293
336
  chainIdIn,
294
337
  } = useAppStore();
295
338
 
296
- const storageKey = useStorageKey();
339
+ const deviceKey = useDeviceKey();
297
340
  useEffect(() => {
298
341
  const fetchData = async () => {
299
342
  try {
@@ -319,17 +362,15 @@ const ChooseAssetStep = ({
319
362
  console.log("Holdings data:", holdingsData);
320
363
 
321
364
  if (
322
- holdingsData.status === "ok" &&
323
- holdingsData.content?.cryptocurrencyPositions
365
+ holdingsData.status !== "ok" ||
366
+ !holdingsData.content?.cryptocurrencyPositions
324
367
  ) {
325
- setHoldings(holdingsData.content.cryptocurrencyPositions);
326
- } else {
327
368
  if (holdingsData.errorType === "invalidIntegrationToken") {
328
369
  console.log("Invalid integration token");
329
370
  setStep(WithdrawalStep.CheckSessionKey);
330
371
  setMeshAccessToken(null);
331
372
  setSessionId(null);
332
- localStorage.removeItem(storageKey);
373
+ sessionStorage.removeItem(deviceKey);
333
374
  }
334
375
  throw new Error(
335
376
  holdingsData.message || "Failed to fetch holdings",
@@ -347,7 +388,7 @@ const ChooseAssetStep = ({
347
388
  tokensData.status === "success" &&
348
389
  tokensData.content?.tokens
349
390
  ) {
350
- setSupportedTokens(tokensData.content.tokens);
391
+ // setSupportedTokens(tokensData.content.tokens);
351
392
 
352
393
  // Match holdings with supported tokens
353
394
  const matched = tokensData.content.tokens
@@ -481,26 +522,30 @@ const ChooseAmountStep = ({
481
522
  const [usdValue, setUsdValue] = useState<string>("");
482
523
  const { setAmountIn } = useAppStore();
483
524
  const { tokenInData, tokenIn } = useAppDetails();
525
+ const isStable = selectedToken?.symbol.toLowerCase().includes("USD");
526
+ const roundingPrecision = isStable ? 2 : 6;
527
+
528
+ const maxUsdAmount = selectedToken
529
+ ? (selectedToken.marketValue - EXCHANGE_MAX_LIMIT_GAP_USD).toFixed(2)
530
+ : 0;
484
531
 
485
532
  // Handle percentage selection with limits
486
533
  const handlePercentageSelect = useCallback(
487
534
  (percent: number) => {
488
535
  if (!selectedToken) return;
489
536
 
490
- const minAmountForToken =
537
+ const minValueForToken =
491
538
  EXCHANGE_MIN_LIMIT[
492
539
  selectedToken.symbol as keyof typeof EXCHANGE_MIN_LIMIT
493
540
  ] || 0;
494
- const maxUsdAmount =
495
- selectedToken.marketValue - EXCHANGE_MAX_LIMIT_GAP_USD;
496
541
 
497
542
  // Calculate target USD amount based on percentage
498
543
  const targetUsdAmount = (selectedToken.marketValue * percent) / 100;
499
544
 
500
545
  // Apply limits to the target USD amount
501
546
  const limitedUsdAmount = Math.max(
502
- minAmountForToken,
503
- Math.min(targetUsdAmount, maxUsdAmount),
547
+ minValueForToken,
548
+ Math.min(targetUsdAmount, +maxUsdAmount),
504
549
  );
505
550
 
506
551
  // Convert back to token amount
@@ -511,7 +556,9 @@ const ChooseAmountStep = ({
511
556
  selectedToken.balance,
512
557
  );
513
558
 
514
- setAmount(limitedTokenAmount.toString());
559
+ setAmount(
560
+ precisionizeNumber(limitedTokenAmount, roundingPrecision),
561
+ );
515
562
  setUsdValue(limitedUsdAmount.toFixed(2));
516
563
  },
517
564
  [selectedToken],
@@ -525,10 +572,11 @@ const ChooseAmountStep = ({
525
572
  }, [selectedToken, handlePercentageSelect]);
526
573
 
527
574
  useEffect(() => {
528
- console.log("tokenIn", tokenIn);
529
575
  if (tokenInData?.decimals)
530
576
  setAmountIn(
531
- (Number(amount) * 10 ** tokenInData?.decimals).toFixed(),
577
+ Number(
578
+ denormalizeValue(amount || "0", tokenInData?.decimals),
579
+ ).toFixed(),
532
580
  );
533
581
  }, [amount, tokenInData?.decimals]);
534
582
 
@@ -537,15 +585,15 @@ const ChooseAmountStep = ({
537
585
  if (!selectedToken) return;
538
586
 
539
587
  if (inputMode === "usd") {
540
- const cleanUsd = value.replace("$", "");
588
+ const cleanUsd = value.replace("$", "") || "";
541
589
  setUsdValue(cleanUsd);
542
590
  // Calculate token amount from USD value
543
591
  const tokenPrice =
544
592
  selectedToken.marketValue / selectedToken.balance;
545
- const tokenAmount = parseFloat(cleanUsd) / tokenPrice;
546
- setAmount(tokenAmount.toString());
593
+ const tokenAmount = parseFloat(cleanUsd || "0") / tokenPrice;
594
+ setAmount(precisionizeNumber(tokenAmount, roundingPrecision));
547
595
  } else {
548
- setAmount(value);
596
+ setAmount(precisionizeNumber(value, roundingPrecision));
549
597
  // Calculate USD value from token amount
550
598
  const tokenPrice =
551
599
  selectedToken.marketValue / selectedToken.balance;
@@ -585,20 +633,19 @@ const ChooseAmountStep = ({
585
633
 
586
634
  // Limits validation logic
587
635
  const currentUsdValue = parseFloat(usdValue);
588
- const minAmountForToken = selectedToken
636
+ const minValueForToken = selectedToken
589
637
  ? EXCHANGE_MIN_LIMIT[
590
638
  selectedToken.symbol as keyof typeof EXCHANGE_MIN_LIMIT
591
639
  ]
592
640
  : 0;
593
- const maxUsdAmount = selectedToken ? selectedToken.marketValue - 5 : 0;
594
641
 
595
642
  const isBelowMinAmount =
596
643
  selectedToken &&
597
644
  currentUsdValue > 0 &&
598
- minAmountForToken &&
599
- currentUsdValue < minAmountForToken;
645
+ minValueForToken &&
646
+ +amount < minValueForToken;
600
647
  const isAboveMaxAmount =
601
- selectedToken && currentUsdValue > 0 && currentUsdValue > maxUsdAmount;
648
+ selectedToken && currentUsdValue > 0 && currentUsdValue > +maxUsdAmount;
602
649
 
603
650
  const isAmountInvalid =
604
651
  isBelowMinAmount || isAboveMaxAmount || notEnoughBalance;
@@ -696,32 +743,39 @@ const ChooseAmountStep = ({
696
743
  </Box>
697
744
  </Box>
698
745
 
699
- <Tooltip
700
- disabled={!isAmountInvalid && !!amount}
701
- content={
702
- !amount
746
+ {
747
+ <Box
748
+ textAlign="center"
749
+ color="fg.subtle"
750
+ fontSize="xs"
751
+ h={3}
752
+ m={-1}
753
+ visibility={
754
+ isAmountInvalid || !amount ? "visible" : "hidden"
755
+ }
756
+ >
757
+ {!amount
703
758
  ? "Please enter an amount"
704
759
  : isBelowMinAmount
705
- ? `Minimum amount is $${minAmountForToken} USD`
760
+ ? `Minimum amount is ${formatNumber(minValueForToken)} ${selectedToken.symbol}`
706
761
  : isAboveMaxAmount
707
- ? `Maximum amount is $${maxUsdAmount.toFixed(2)} USD (balance - $10)`
762
+ ? `Maximum amount is ${formatUSD(maxUsdAmount)} (balance - $${EXCHANGE_MAX_LIMIT_GAP_USD})`
708
763
  : notEnoughBalance
709
764
  ? "Amount exceeds available balance"
710
- : ""
765
+ : ""}
766
+ </Box>
767
+ }
768
+
769
+ <Button
770
+ onClick={() =>
771
+ isAmountInvalid || !amount
772
+ ? undefined
773
+ : setStep(WithdrawalStep.GetUserOpSignature)
711
774
  }
712
- showArrow
775
+ disabled={isAmountInvalid || !amount}
713
776
  >
714
- <Button
715
- onClick={() =>
716
- isAmountInvalid || !amount
717
- ? undefined
718
- : setStep(WithdrawalStep.GetUserOpSignature)
719
- }
720
- disabled={isAmountInvalid || !amount}
721
- >
722
- Continue
723
- </Button>
724
- </Tooltip>
777
+ Continue
778
+ </Button>
725
779
  </BodyWrapper>
726
780
  );
727
781
  };
@@ -848,6 +902,9 @@ const InitiateWithdrawalStep = ({
848
902
  const { tokenInData } = useAppDetails();
849
903
  const [isLoading, setIsLoading] = useState(true);
850
904
 
905
+ const handleMeshAccessPayload = useHandleMeshAccessPayload();
906
+ const deviceKey = useDeviceKey();
907
+
851
908
  useEffect(() => {
852
909
  if (!selectedToken || !userOp || !meshAccessToken) {
853
910
  console.error("Missing required data for withdrawal initiation");
@@ -856,9 +913,11 @@ const InitiateWithdrawalStep = ({
856
913
 
857
914
  // Convert amountIn from wei to token amount
858
915
  const transferAmount = tokenInData?.decimals
859
- ? Number(amountIn) / 10 ** tokenInData.decimals
916
+ ? normalizeValue(amountIn, tokenInData.decimals)
860
917
  : 0;
861
918
 
919
+ console.log("transferAmount", transferAmount);
920
+
862
921
  const fetchLinkTokenAndOpen = async () => {
863
922
  try {
864
923
  const toAddresses = [
@@ -871,7 +930,7 @@ const InitiateWithdrawalStep = ({
871
930
  ];
872
931
  const meshData = {
873
932
  restrictMultipleAccounts: true,
874
- userId: address,
933
+ userId: deviceKey,
875
934
  integrationId: BINANCE_INTEGRATION_ID,
876
935
  transferOptions: {
877
936
  toAddresses,
@@ -887,7 +946,7 @@ const InitiateWithdrawalStep = ({
887
946
  },
888
947
  body: JSON.stringify({
889
948
  restrictMultipleAccounts: true,
890
- userId: address,
949
+ userId: deviceKey,
891
950
  integrationId: BINANCE_INTEGRATION_ID,
892
951
  transferOptions: {
893
952
  toAddresses,
@@ -912,8 +971,9 @@ const InitiateWithdrawalStep = ({
912
971
  const link = createLink({
913
972
  clientId: address,
914
973
  accessTokens,
915
- onIntegrationConnected: (e) => {
916
- console.log("Integration connected", e);
974
+ onIntegrationConnected: (payload) => {
975
+ handleMeshAccessPayload(payload.accessToken); // Persist access token and session id for future reloads
976
+ console.log("Integration connected", payload);
917
977
  },
918
978
  onTransferFinished: (transferData) => {
919
979
  console.log("Transfer finished:", transferData);
@@ -1275,10 +1335,10 @@ const ExchangeFlow = () => {
1275
1335
  const { chainIdOut } = useAppStore();
1276
1336
  const setIsCheckout = useAppStore((state) => state.setIsCheckout);
1277
1337
  const setChainIdIn = useAppStore((state) => state.setChainIdIn);
1278
- const walletChainId = useChainId();
1279
- const { switchChain } = useSwitchChain();
1280
-
1281
- const wrongChain = walletChainId !== chainIdOut;
1338
+ // const walletChainId = useChainId();
1339
+ // const { switchChain } = useSwitchChain();
1340
+ //
1341
+ // const wrongChain = walletChainId !== chainIdOut;
1282
1342
 
1283
1343
  const handleTokenSelect = (token: MatchedToken) => {
1284
1344
  setSelectedToken(token);
@@ -1394,34 +1454,33 @@ const ExchangeFlow = () => {
1394
1454
  </HeaderWrapper>
1395
1455
  </Modal.Header>
1396
1456
  <Modal.Body>
1397
- {wrongChain ? (
1398
- <BodyWrapper>
1399
- <Box
1400
- display="flex"
1401
- flexDirection="column"
1402
- alignItems="center"
1403
- gap="16px"
1404
- textAlign="center"
1405
- >
1406
- <Text fontSize="16px" fontWeight="600">
1407
- Wrong Network
1408
- </Text>
1409
- <Text fontSize="14px" color="fg.muted">
1410
- Please switch to {getChainName(chainIdOut)} to
1411
- continue with your Binance withdrawal.
1412
- </Text>
1413
- <Button
1414
- onClick={() => {
1415
- switchChain({ chainId: chainIdOut });
1416
- }}
1417
- >
1418
- Switch to {getChainName(chainIdOut)}
1419
- </Button>
1420
- </Box>
1421
- </BodyWrapper>
1422
- ) : (
1423
- currentStepComponent
1424
- )}
1457
+ {/*{wrongChain ? (*/}
1458
+ {/* <BodyWrapper>*/}
1459
+ {/* <Box*/}
1460
+ {/* display="flex"*/}
1461
+ {/* flexDirection="column"*/}
1462
+ {/* alignItems="center"*/}
1463
+ {/* gap="16px"*/}
1464
+ {/* textAlign="center"*/}
1465
+ {/* >*/}
1466
+ {/* <Text fontSize="16px" fontWeight="600">*/}
1467
+ {/* Wrong Network*/}
1468
+ {/* </Text>*/}
1469
+ {/* <Text fontSize="14px" color="fg.muted">*/}
1470
+ {/* Please switch to {getChainName(chainIdOut)} to*/}
1471
+ {/* continue with your Binance withdrawal.*/}
1472
+ {/* </Text>*/}
1473
+ {/* <Button*/}
1474
+ {/* onClick={() => {*/}
1475
+ {/* switchChain({ chainId: chainIdOut });*/}
1476
+ {/* }}*/}
1477
+ {/* >*/}
1478
+ {/* Switch to {getChainName(chainIdOut)}*/}
1479
+ {/* </Button>*/}
1480
+ {/* </Box>*/}
1481
+ {/* </BodyWrapper>*/}
1482
+ {/*) : */}
1483
+ {currentStepComponent}
1425
1484
  </Modal.Body>
1426
1485
  </>
1427
1486
  );
@@ -2,7 +2,7 @@ import { Box, Icon, Skeleton } from "@chakra-ui/react";
2
2
  import { X } from "lucide-react";
3
3
  import { useContext, useMemo } from "react";
4
4
  import { useAccount } from "wagmi";
5
- import { IconButton, Tooltip } from "../ui";
5
+ import { IconButton } from "../ui";
6
6
  import {
7
7
  HeaderWrapperSpaceBetween as InitialStepHeaderWrapper,
8
8
  HeaderTitle,
@@ -17,10 +17,6 @@ import { CheckoutContext } from "../Checkout";
17
17
  import { WalletCard, OptionCard } from "../cards";
18
18
  import { WalletStatus } from "../cards/WalletCard";
19
19
  import { useWalletIcon } from "@/util/enso-hooks";
20
- import RabbyIcon from "../../assets/rabby.png";
21
- import MetamaskIcon from "../../assets/metamask.png";
22
- import MastercardIcon from "../../assets/mastercard.png";
23
- import VisaIcon from "../../assets/visa.png";
24
20
  import BinanceIcon from "../../assets/BinanceBadge.svg";
25
21
 
26
22
  const InitialStep = () => {
@@ -28,7 +24,7 @@ const InitialStep = () => {
28
24
  useContext(CheckoutContext);
29
25
 
30
26
  const { total, isLoading } = useWalletBalance();
31
- const { address, isConnected } = useAccount();
27
+ const { address } = useAccount();
32
28
 
33
29
  const { walletIcon, walletDisplayName } = useWalletIcon();
34
30
 
@@ -284,9 +284,9 @@ const CardTitle: React.FC<{ children: React.ReactNode }> = ({ children }) => (
284
284
  const CardDescription: React.FC<{ children: React.ReactNode }> = ({
285
285
  children,
286
286
  }) => (
287
- <Text fontSize="xs" color="fg.muted">
287
+ <Box fontSize="xs" color="fg.muted">
288
288
  {children}
289
- </Text>
289
+ </Box>
290
290
  );
291
291
 
292
292
  export const Card: React.FC<CardProps> & {
@@ -40,7 +40,7 @@ export const useWalletBalance = () => {
40
40
  ?.map((balanace) => ({
41
41
  ...balanace,
42
42
  total:
43
- (+balanace.amount / 10 ** balanace.decimals) *
43
+ +normalizeValue(balanace.amount, balanace.decimals) *
44
44
  +balanace.price,
45
45
  }))
46
46
  .filter((balance) => balance.total > 1 && balance.total < 10000)
@@ -322,3 +322,6 @@ export const getChainEtherscanUrl = ({
322
322
 
323
323
  if (hash) return `${chainPrefix}${type}/${hash}`;
324
324
  };
325
+
326
+ export const precisionizeNumber = (value: number | string, precision: number) =>
327
+ Number(parseFloat(value.toString()).toFixed(precision)).toString();