From 082f87d22e8d97d9a4ffb00046e4d947086cac4d Mon Sep 17 00:00:00 2001 From: Eugene Chybisov Date: Thu, 3 Aug 2023 16:22:08 +0300 Subject: [PATCH 01/24] feat: ethers -> viem migration --- package.json | 23 +- packages/wallet-management/package.json | 4 +- packages/widget-embedded/package.json | 18 +- .../NFTOpenSea/NFTOpenSeaSecondary.tsx | 2 +- packages/widget-playground/package.json | 14 +- .../src/components/ViemTest.tsx | 129 ++ packages/widget-playground/src/index.tsx | 2 + packages/widget/package.json | 19 +- .../AmountInput/AmountInputEndAdornment.tsx | 25 +- .../AmountInput/FormPriceHelperText.tsx | 2 +- .../src/components/GasMessage/GasMessage.tsx | 6 +- .../Insurance/InsuranceCollapsed.tsx | 2 +- packages/widget/src/components/NFT/types.ts | 2 +- .../src/components/RouteCard/RouteCard.tsx | 6 +- .../RouteCard/RouteCardEssentials.tsx | 11 +- .../widget/src/components/RouteCard/utils.ts | 29 +- packages/widget/src/components/Step/Step.tsx | 5 +- .../widget/src/components/Step/StepList.tsx | 11 +- .../components/StepActions/StepActions.tsx | 26 +- .../widget/src/components/Token/Token.tsx | 17 +- .../components/TokenList/TokenListItem.tsx | 80 +- .../widget/src/hooks/useFundsSufficiency.ts | 21 +- packages/widget/src/hooks/useGasRefuel.ts | 9 +- .../widget/src/hooks/useGasSufficiency.ts | 76 +- .../widget/src/hooks/useProcessMessage.ts | 73 +- .../widget/src/hooks/useRouteExecution.ts | 49 +- packages/widget/src/hooks/useRoutes.ts | 13 +- packages/widget/src/hooks/useTokenBalances.ts | 18 +- packages/widget/src/i18n/en.json | 9 +- .../TransactionDetailsPage.tsx | 2 +- .../TransactionHistoryItem.tsx | 4 +- .../ExchangeRateBottomSheet.tsx | 30 +- .../StartTransactionButton.tsx | 5 +- .../TransactionPage/StatusBottomSheet.tsx | 13 +- .../pages/TransactionPage/TransactionPage.tsx | 2 +- .../src/providers/I18nProvider/types.ts | 8 +- .../WalletProvider/WalletProvider.tsx | 2 +- .../src/providers/WalletProvider/types.ts | 2 + .../src/stores/chains/useChainOrderStore.ts | 5 +- .../src/stores/header/useHeaderStore.tsx | 7 +- .../routes/createRouteExecutionStore.ts | 5 +- packages/widget/src/stores/routes/utils.ts | 14 +- .../widget/src/stores/settings/useSettings.ts | 11 +- .../src/stores/settings/useSettingsStore.ts | 5 +- .../settings/useSplitSubvariantStore.tsx | 3 +- packages/widget/src/utils/format.ts | 20 +- yarn.lock | 1256 +++++++---------- 47 files changed, 1029 insertions(+), 1066 deletions(-) create mode 100644 packages/widget-playground/src/components/ViemTest.tsx diff --git a/package.json b/package.json index 634345d3a..f2d7394c5 100644 --- a/package.json +++ b/package.json @@ -28,30 +28,33 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^14.0.0", "@testing-library/user-event": "^14.4.3", - "@types/big.js": "^6.1.6", "@types/eslint": "^8.44.1", "@types/events": "^3.0.0", - "@types/node": "^20.4.5", - "@types/react": "^18.2.16", + "@types/node": "^20.4.6", + "@types/react": "^18.2.18", "@types/react-dom": "^18.2.7", "@types/uuid": "^9.0.2", - "@typescript-eslint/eslint-plugin": "^6.2.0", - "@typescript-eslint/parser": "^6.2.0", + "@typescript-eslint/eslint-plugin": "^6.2.1", + "@typescript-eslint/parser": "^6.2.1", "cpy-cli": "^5.0.0", - "eslint": "^8.45.0", - "eslint-config-prettier": "^8.8.0", + "eslint": "^8.46.0", + "eslint-config-prettier": "^8.10.0", "eslint-config-react-app": "^7.0.1", "eslint-plugin-flowtype": "^8.0.3", - "eslint-plugin-import": "^2.27.5", + "eslint-plugin-import": "^2.28.0", "eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-prettier": "^5.0.0", - "eslint-plugin-react": "^7.33.0", + "eslint-plugin-react": "^7.33.1", "eslint-plugin-react-hooks": "^4.6.0", "lerna": "^7.1.4", - "prettier": "^3.0.0", + "prettier": "^3.0.1", "react": "^18.2.0", "react-dom": "^18.2.0", "standard-version": "^9.5.0", "typescript": "^5.1.6" + }, + "resolutions": { + "@lifi/sdk": "portal:/Users/Eugene/Projects/sdk", + "@lifi/types": "portal:/Users/Eugene/Projects/types" } } diff --git a/packages/wallet-management/package.json b/packages/wallet-management/package.json index c433ed0c9..233c7a4e8 100644 --- a/packages/wallet-management/package.json +++ b/packages/wallet-management/package.json @@ -52,10 +52,10 @@ "@ethersproject/abstract-signer": "^5.7.0", "@ethersproject/experimental": "^5.7.0", "@ethersproject/providers": "^5.7.2", - "@lifi/sdk": "^2.2.1", + "@lifi/sdk": "^2.2.2", "@safe-global/safe-apps-provider": "^0.17.1", "@safe-global/safe-apps-sdk": "^8.0.0", - "@walletconnect/ethereum-provider": "^2.9.1", + "@walletconnect/ethereum-provider": "^2.9.2", "@walletconnect/modal": "^2.6.1", "events": "^3.3.0", "react": "^18.2.0" diff --git a/packages/widget-embedded/package.json b/packages/widget-embedded/package.json index f60124d5f..bed0e864d 100644 --- a/packages/widget-embedded/package.json +++ b/packages/widget-embedded/package.json @@ -24,14 +24,14 @@ "author": "Eugene Chybisov ", "dependencies": { "@ethersproject/abstract-signer": "^5.7.0", - "@lifi/sdk": "^2.2.1", + "@lifi/sdk": "^2.2.2", "@lifi/wallet-management": "^2.2.3", "@lifi/widget": "^2.2.5", - "@mui/icons-material": "^5.14.1", - "@mui/lab": "^5.0.0-alpha.137", - "@mui/material": "^5.14.2", - "@opensea/seaport-js": "2.0.5", - "@tanstack/react-query": "^4.32.0", + "@mui/icons-material": "^5.14.3", + "@mui/lab": "^5.0.0-alpha.138", + "@mui/material": "^5.14.3", + "@opensea/seaport-js": "2.0.6", + "@tanstack/react-query": "^4.32.1", "bignumber.js": "^9.1.1", "ethers": "^5.7.2", "events": "^3.3.0", @@ -41,13 +41,13 @@ }, "devDependencies": { "@esbuild-plugins/node-globals-polyfill": "^0.2.3", - "@vitejs/plugin-react": "^4.0.3", + "@vitejs/plugin-react": "^4.0.4", "buffer": "^6.0.3", "esbuild": "^0.18.17", - "rollup": "^3.26.3", + "rollup": "^3.27.0", "rollup-plugin-polyfill-node": "^0.12.0", "typescript": "^5.1.6", - "vite": "^4.4.7", + "vite": "^4.4.8", "web-vitals": "^3.4.0" }, "eslintConfig": { diff --git a/packages/widget-embedded/src/components/NFTOpenSea/NFTOpenSeaSecondary.tsx b/packages/widget-embedded/src/components/NFTOpenSea/NFTOpenSeaSecondary.tsx index 34424bbfb..1e495354f 100644 --- a/packages/widget-embedded/src/components/NFTOpenSea/NFTOpenSeaSecondary.tsx +++ b/packages/widget-embedded/src/components/NFTOpenSea/NFTOpenSeaSecondary.tsx @@ -26,7 +26,7 @@ export const NFTOpenSeaSecondary: React.FC = ({ }; const token: TokenAmount = { symbol: order?.takerAssetBundle.assets[0]?.assetContract?.tokenSymbol!, - amount: order?.currentPrice!, + amount: BigInt(order?.currentPrice ?? 0), decimals: order?.takerAssetBundle.assets[0].decimals!, address: order?.takerAssetBundle.assets[0].tokenAddress!, chainId: OpenSeaChainId[network as NFTNetwork] as number as ChainId, diff --git a/packages/widget-playground/package.json b/packages/widget-playground/package.json index 1d9974a87..cbf456eda 100644 --- a/packages/widget-playground/package.json +++ b/packages/widget-playground/package.json @@ -23,26 +23,26 @@ }, "author": "Eugene Chybisov ", "dependencies": { - "@lifi/sdk": "^2.2.1", + "@lifi/sdk": "^2.2.2", "@lifi/wallet-management": "^2.2.3", "@lifi/widget": "^2.2.5", - "@mui/icons-material": "^5.14.1", - "@mui/lab": "^5.0.0-alpha.137", - "@mui/material": "^5.14.2", + "@mui/icons-material": "^5.14.3", + "@mui/lab": "^5.0.0-alpha.138", + "@mui/material": "^5.14.3", "ethers": "^5.7.2", "events": "^3.3.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.14.2", - "viem": "^1.4.1", + "viem": "^1.5.2", "wagmi": "^1.3.9" }, "devDependencies": { "@esbuild-plugins/node-globals-polyfill": "^0.2.3", - "@vitejs/plugin-react": "^4.0.3", + "@vitejs/plugin-react": "^4.0.4", "rollup-plugin-polyfill-node": "^0.12.0", "typescript": "^5.1.6", - "vite": "^4.4.7", + "vite": "^4.4.8", "web-vitals": "^3.4.0" }, "eslintConfig": { diff --git a/packages/widget-playground/src/components/ViemTest.tsx b/packages/widget-playground/src/components/ViemTest.tsx new file mode 100644 index 000000000..8ab2da135 --- /dev/null +++ b/packages/widget-playground/src/components/ViemTest.tsx @@ -0,0 +1,129 @@ +import { Box } from '@mui/material'; +import { getWalletClient } from '@wagmi/core'; +import { useEffect } from 'react'; +import type { Address, WalletClient } from 'viem'; +import { createWalletClient, http, publicActions } from 'viem'; +import { mnemonicToAccount } from 'viem/accounts'; +import { + WagmiConfig, + configureChains, + createConfig, + useAccount, + useConnect, + useDisconnect, + useWalletClient, +} from 'wagmi'; +import * as wagmiChains from 'wagmi/chains'; +import { InjectedConnector } from 'wagmi/connectors/injected'; +import { publicProvider } from 'wagmi/providers/public'; + +const { chains, publicClient, webSocketPublicClient } = configureChains( + Object.values(wagmiChains), + [publicProvider()], +); + +const config = createConfig({ + autoConnect: true, + publicClient, + webSocketPublicClient, + connectors: [new InjectedConnector()], +}); + +export const ViemTest = () => { + return ( + + + + ); +}; + +export const ViemTest1 = () => { + const { connector: activeConnector, isConnected, address } = useAccount(); + const { connect, connectors, error, isLoading, pendingConnector } = + useConnect(); + const { disconnect } = useDisconnect(); + const { data: wc, isError } = useWalletClient(); + + useEffect(() => { + if (!wc) { + return; + } + (async () => { + const walletClient = await getWalletClient({ + chainId: await wc.getChainId(), + }); + console.log( + await walletClient?.getChainId(), + walletClient?.chain?.id, + walletClient?.account.type, + walletClient?.account.address, + ); + })(); + }, [wc]); + + const handleSwitchChain = async () => { + if (!wc) { + return; + } + const account = mnemonicToAccount( + 'mirror either social pioneer detail essay tribe upset increase hire office draw' as Address, + ); + const walletClient: WalletClient = createWalletClient({ + account, + chain: wagmiChains.mainnet, + transport: http( + 'https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161', + ), + }); + // const walletClient = await getWalletClient({ + // chainId: await wc.getChainId(), + // }); + const client = walletClient?.extend(publicActions); + const balance1 = await client?.getBalance({ + address: client?.account?.address!, + }); + await client?.switchChain({ id: 137 }); + const balance2 = await client?.getBalance({ + address: client?.account?.address!, + }); + console.log( + 'switched', + await client?.getChainId(), + client?.chain?.id, + client?.account?.type, + client?.account?.address, + balance1, + balance2, + ); + }; + + return ( + + + {isConnected && ( +
+ Connected to {activeConnector?.name} {address} +
+ )} + + {connectors.map((connector) => ( + + ))} + + {error &&
{error.message}
} + + + +
+
+ ); +}; diff --git a/packages/widget-playground/src/index.tsx b/packages/widget-playground/src/index.tsx index 5b28123fa..14d0c4172 100644 --- a/packages/widget-playground/src/index.tsx +++ b/packages/widget-playground/src/index.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { createRoot } from 'react-dom/client'; import { BrowserRouter, Route, Routes } from 'react-router-dom'; import { App } from './App'; +import { ViemTest } from './components/ViemTest'; import { WalletProvider } from './providers/WalletProvider'; import { reportWebVitals } from './reportWebVitals'; @@ -17,6 +18,7 @@ root.render( + } /> } /> diff --git a/packages/widget/package.json b/packages/widget/package.json index b547bcaf3..91de37442 100644 --- a/packages/widget/package.json +++ b/packages/widget/package.json @@ -53,27 +53,28 @@ "@ethersproject/address": "^5.7.0", "@ethersproject/experimental": "^5.7.0", "@ethersproject/providers": "^5.7.2", - "@lifi/sdk": "^2.2.1", + "@lifi/sdk": "^2.2.2", "@lifi/wallet-management": "^2.2.3", - "@mui/icons-material": "^5.14.1", - "@mui/lab": "^5.0.0-alpha.137", - "@mui/material": "^5.14.2", - "@tanstack/react-query": "^4.32.0", + "@mui/icons-material": "^5.14.3", + "@mui/lab": "^5.0.0-alpha.138", + "@mui/material": "^5.14.3", + "@tanstack/react-query": "^4.32.1", "@tanstack/react-virtual": "^3.0.0-beta.54", - "big.js": "^6.2.1", - "i18next": "^23.3.0", + "i18next": "^23.4.1", "i18next-browser-languagedetector": "^7.1.0", "microdiff": "^1.3.2", "mitt": "^3.0.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.45.2", - "react-i18next": "^13.0.2", + "react-i18next": "^13.0.3", "react-intersection-observer": "^9.5.2", "react-router-dom": "^6.14.2", "react-timer-hook": "^3.0.7", "uuid": "^9.0.0", - "zustand": "^4.3.9" + "viem": "^1.5.2", + "wagmi": "^1.3.9", + "zustand": "^4.4.0" }, "devDependencies": { "cpy-cli": "^5.0.0", diff --git a/packages/widget/src/components/AmountInput/AmountInputEndAdornment.tsx b/packages/widget/src/components/AmountInput/AmountInputEndAdornment.tsx index bcb08182e..d14de44fe 100644 --- a/packages/widget/src/components/AmountInput/AmountInputEndAdornment.tsx +++ b/packages/widget/src/components/AmountInput/AmountInputEndAdornment.tsx @@ -1,7 +1,7 @@ import { InputAdornment, Skeleton } from '@mui/material'; -import Big from 'big.js'; import { useFormContext, useWatch } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; +import { formatUnits } from 'viem'; import { useChains, useGasRecommendation, @@ -9,7 +9,6 @@ import { } from '../../hooks'; import type { FormTypeProps } from '../../providers'; import { FormKeyHelper } from '../../providers'; -import { formatTokenAmount } from '../../utils'; import { Button } from './AmountInputAdornment.style'; export const AmountInputEndAdornment = ({ formType }: FormTypeProps) => { @@ -34,20 +33,20 @@ export const AmountInputEndAdornment = ({ formType }: FormTypeProps) => { data?.available && data?.recommended ) { - const tokenAmount = Big(token?.amount ?? 0); - const recommendedAmount = Big(data.recommended.amount) - .div(10 ** data.recommended.token.decimals) - .div(2); - if (tokenAmount.gt(recommendedAmount)) { - maxAmount = formatTokenAmount( - tokenAmount.minus(recommendedAmount).toString(), - ); + const tokenAmount = token?.amount ?? 0n; + const recommendedAmount = BigInt(data.recommended.amount) / 2n; + if (tokenAmount > recommendedAmount) { + maxAmount = tokenAmount - recommendedAmount; } } - setValue(FormKeyHelper.getAmountKey(formType), maxAmount || '', { - shouldTouch: true, - }); + setValue( + FormKeyHelper.getAmountKey(formType), + maxAmount && token ? formatUnits(maxAmount, token.decimals) : '', + { + shouldTouch: true, + }, + ); }; return ( diff --git a/packages/widget/src/components/AmountInput/FormPriceHelperText.tsx b/packages/widget/src/components/AmountInput/FormPriceHelperText.tsx index fb0cb3501..fe9bde21c 100644 --- a/packages/widget/src/components/AmountInput/FormPriceHelperText.tsx +++ b/packages/widget/src/components/AmountInput/FormPriceHelperText.tsx @@ -78,7 +78,7 @@ export const FormPriceHelperTextBase: React.FC< pl={0.25} > {`/ ${t(`format.number`, { - value: formatTokenAmount(token?.amount), + value: formatTokenAmount(token.amount, token.decimals), })}`} ) : null} diff --git a/packages/widget/src/components/GasMessage/GasMessage.tsx b/packages/widget/src/components/GasMessage/GasMessage.tsx index 784d41a41..4f3f4aaec 100644 --- a/packages/widget/src/components/GasMessage/GasMessage.tsx +++ b/packages/widget/src/components/GasMessage/GasMessage.tsx @@ -14,9 +14,11 @@ export const GasMessage: React.FC = ({ route, ...props }) => { const { insufficientGas } = useGasSufficiency(route); const { insufficientFunds } = useFundsSufficiency(route); const { sdkConfig } = useWidgetConfig(); - const isMultisigSigner = sdkConfig?.multisigConfig?.isMultisigSigner; + const isMultisigWalletClient = + sdkConfig?.multisigConfig?.isMultisigWalletClient; - const validInsufficientGas = insufficientGas?.length && !isMultisigSigner; + const validInsufficientGas = + insufficientGas?.length && !isMultisigWalletClient; return ( = ({ {...props} status={status} insuredAmount={formatTokenAmount( - insuredRoute.toAmountMin, + BigInt(insuredRoute.toAmountMin), insuredRoute.toToken.decimals, )} insuredTokenSymbol={insuredRoute.toToken.symbol} diff --git a/packages/widget/src/components/NFT/types.ts b/packages/widget/src/components/NFT/types.ts index ec2d29ec2..1ddac0227 100644 --- a/packages/widget/src/components/NFT/types.ts +++ b/packages/widget/src/components/NFT/types.ts @@ -7,7 +7,7 @@ export interface NFTBaseProps { assetName?: string; isLoading?: boolean; owner?: NFTOwner; - token?: TokenAmount; + token: TokenAmount; } export interface NFTProps extends NFTBaseProps { diff --git a/packages/widget/src/components/RouteCard/RouteCard.tsx b/packages/widget/src/components/RouteCard/RouteCard.tsx index f83bc1008..624c32e20 100644 --- a/packages/widget/src/components/RouteCard/RouteCard.tsx +++ b/packages/widget/src/components/RouteCard/RouteCard.tsx @@ -32,8 +32,8 @@ export const RouteCard: React.FC< const token: TokenAmount = subvariant === 'nft' - ? { ...route.fromToken, amount: route.fromAmount } - : { ...route.toToken, amount: route.toAmount }; + ? { ...route.fromToken, amount: BigInt(route.fromAmount) } + : { ...route.toToken, amount: BigInt(route.toAmount) }; const RecommendedTagTooltip = route.tags?.[0] === 'RECOMMENDED' ? RecommendedTooltip : Fragment; @@ -45,7 +45,7 @@ export const RouteCard: React.FC< {insurable ? ( = ({ fontWeight="500" key={`${gas.token.address}${index}`} > - {gas.amount?.toFixed(9)} {gas.token.symbol} ( + {parseFloat( + formatUnits(gas.amount, gas.token.decimals), + )?.toFixed(9)}{' '} + {gas.token.symbol} ( {t(`format.currency`, { value: gas.amountUSD })}) ))} @@ -67,7 +71,10 @@ export const RouteCardEssentials: React.FC = ({ fontWeight="500" key={`${fee.token.address}${index}`} > - {fee.amount?.toFixed(9)} {fee.token.symbol} ( + {parseFloat( + formatUnits(fee.amount, fee.token.decimals), + )?.toFixed(9)}{' '} + {fee.token.symbol} ( {t(`format.currency`, { value: fee.amountUSD })}) ))} diff --git a/packages/widget/src/components/RouteCard/utils.ts b/packages/widget/src/components/RouteCard/utils.ts index a4dc74ca4..06a3601f8 100644 --- a/packages/widget/src/components/RouteCard/utils.ts +++ b/packages/widget/src/components/RouteCard/utils.ts @@ -1,5 +1,4 @@ import type { Route, Token } from '@lifi/sdk'; -import Big from 'big.js'; export const getGasCostsBreakdown = (route: Route) => { return Object.values( @@ -7,19 +6,17 @@ export const getGasCostsBreakdown = (route: Route) => { (groupedGasCosts, step) => { if (step.estimate.gasCosts?.length) { const { token } = step.estimate.gasCosts[0]; - const gasCostAmount = step.estimate.gasCosts - .reduce( - (amount, gasCost) => amount.plus(Big(gasCost.amount || 0)), - Big(0), - ) - .div(10 ** token.decimals); + const gasCostAmount = step.estimate.gasCosts.reduce( + (amount, gasCost) => amount + BigInt(gasCost.amount || 0), + 0n, + ); const gasCostAmountUSD = step.estimate.gasCosts.reduce( (amount, gasCost) => amount + parseFloat(gasCost.amountUSD || '0'), 0, ); const groupedGasCost = groupedGasCosts[token.chainId]; const amount = groupedGasCost - ? groupedGasCost.amount.plus(gasCostAmount) + ? groupedGasCost.amount + gasCostAmount : gasCostAmount; const amountUSD = groupedGasCost ? groupedGasCost.amountUSD + gasCostAmountUSD @@ -36,7 +33,7 @@ export const getGasCostsBreakdown = (route: Route) => { {} as Record< number, { - amount: Big; + amount: bigint; amountUSD: number; token: Token; } @@ -57,19 +54,17 @@ export const getFeeCostsBreakdown = (route: Route, included?: boolean) => { } if (feeCosts?.length) { const { token } = feeCosts[0]; - const feeCostAmount = feeCosts - .reduce( - (amount, feeCost) => amount.plus(Big(feeCost.amount || 0)), - Big(0), - ) - .div(10 ** token.decimals); + const feeCostAmount = feeCosts.reduce( + (amount, feeCost) => amount + BigInt(feeCost.amount || 0), + 0n, + ); const feeCostAmountUSD = feeCosts.reduce( (amount, feeCost) => amount + parseFloat(feeCost.amountUSD || '0'), 0, ); const groupedFeeCost = groupedFeeCosts[token.chainId]; const amount = groupedFeeCost - ? groupedFeeCost.amount.plus(feeCostAmount) + ? groupedFeeCost.amount + feeCostAmount : feeCostAmount; const amountUSD = groupedFeeCost ? groupedFeeCost.amountUSD + feeCostAmountUSD @@ -86,7 +81,7 @@ export const getFeeCostsBreakdown = (route: Route, included?: boolean) => { {} as Record< number, { - amount: Big; + amount: bigint; amountUSD: number; token: Token; } diff --git a/packages/widget/src/components/Step/Step.tsx b/packages/widget/src/components/Step/Step.tsx index ba093ac30..49b554bdf 100644 --- a/packages/widget/src/components/Step/Step.tsx +++ b/packages/widget/src/components/Step/Step.tsx @@ -58,9 +58,8 @@ export const Step: React.FC<{ const formattedToAddress = shortenAddress(toAddress); const toAddressLink = toAddress - ? `${ - getChainById(step.action.toChainId)?.metamask.blockExplorerUrls[0] - }address/${toAddress}` + ? `${getChainById(step.action.toChainId)?.metamask + .blockExplorerUrls[0]}address/${toAddress}` : undefined; return ( diff --git a/packages/widget/src/components/Step/StepList.tsx b/packages/widget/src/components/Step/StepList.tsx index 9ea8da6bd..0794c253d 100644 --- a/packages/widget/src/components/Step/StepList.tsx +++ b/packages/widget/src/components/Step/StepList.tsx @@ -9,16 +9,17 @@ export const getStepList = (route?: Route, subvariant?: WidgetSubvariant) => const lastIndex = steps.length - 1; const fromToken: TokenAmount | undefined = index === 0 - ? { ...step.action.fromToken, amount: step.action.fromAmount } + ? { ...step.action.fromToken, amount: BigInt(step.action.fromAmount) } : undefined; const toToken: TokenAmount | undefined = index === lastIndex ? { ...(step.execution?.toToken ?? step.action?.toToken), - amount: - step.execution?.toAmount ?? subvariant === 'nft' - ? route.toAmount - : step.estimate.toAmount, + amount: step.execution?.toAmount + ? BigInt(step.execution.toAmount) + : subvariant === 'nft' + ? BigInt(route.toAmount) + : BigInt(step.estimate.toAmount), } : undefined; const toAddress = diff --git a/packages/widget/src/components/StepActions/StepActions.tsx b/packages/widget/src/components/StepActions/StepActions.tsx index 8ef1b4477..330f47ed4 100644 --- a/packages/widget/src/components/StepActions/StepActions.tsx +++ b/packages/widget/src/components/StepActions/StepActions.tsx @@ -11,10 +11,10 @@ import { Stepper, Typography, } from '@mui/material'; -import Big from 'big.js'; import type { MouseEventHandler } from 'react'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { formatUnits } from 'viem'; import { useChains } from '../../hooks'; import { LiFiToolLogo } from '../../icons'; import { useWidgetConfig } from '../../providers'; @@ -176,19 +176,19 @@ export const StepDetailsContent: React.FC<{ let fromAmount: string | undefined; if (sameTokenProtocolStep) { - const estimatedFromAmount = Big(step.estimate.fromAmount) - .div(10 ** step.action.fromToken.decimals) - .minus( - Big(step.estimate.toAmount).div(10 ** step.action.toToken.decimals), - ); - fromAmount = estimatedFromAmount.gt(0) - ? estimatedFromAmount.toString() - : Big(step.estimate.fromAmount) - .div(10 ** step.action.fromToken.decimals) - .toString(); + const estimatedFromAmount = + BigInt(step.estimate.fromAmount) - BigInt(step.estimate.toAmount); + + fromAmount = + estimatedFromAmount > 0n + ? formatUnits(estimatedFromAmount, step.action.fromToken.decimals) + : formatUnits( + BigInt(step.estimate.fromAmount), + step.action.fromToken.decimals, + ); } else { fromAmount = formatTokenAmount( - step.estimate.fromAmount, + BigInt(step.estimate.fromAmount), step.action.fromToken.decimals, ); } @@ -213,7 +213,7 @@ export const StepDetailsContent: React.FC<{ {t('format.number', { value: formatTokenAmount( - step.execution?.toAmount ?? step.estimate.toAmount, + BigInt(step.execution?.toAmount ?? step.estimate.toAmount), step.execution?.toToken?.decimals ?? step.action.toToken.decimals, ), })}{' '} diff --git a/packages/widget/src/components/Token/Token.tsx b/packages/widget/src/components/Token/Token.tsx index 2250807a0..5bd2220ba 100644 --- a/packages/widget/src/components/Token/Token.tsx +++ b/packages/widget/src/components/Token/Token.tsx @@ -11,7 +11,7 @@ import { TokenAvatar } from '../TokenAvatar'; import { TextSecondary, TextSecondaryContainer } from './Token.style'; interface TokenProps { - token?: TokenAmount; + token: TokenAmount; connected?: boolean; step?: LifiStep; disableDescription?: boolean; @@ -19,7 +19,7 @@ interface TokenProps { } export const Token: React.FC = ({ token, ...other }) => { - if (!token?.priceUSD || !token.logoURI) { + if (!token.priceUSD || !token.logoURI) { return ; } return ; @@ -31,8 +31,8 @@ export const TokenFallback: React.FC = ({ ...other }) => { const { token: chainToken, isLoading: isLoadingToken } = useToken( - token?.chainId, - token?.address, + token.chainId, + token.address, ); return ( @@ -54,13 +54,10 @@ export const TokenBase: React.FC = ({ }) => { const { t } = useTranslation(); const { chain } = useChain(token?.chainId); - const formattedTokenAmount = formatTokenAmount( - token?.amount, - token?.decimals, - ); + const formattedTokenAmount = formatTokenAmount(token.amount, token.decimals); const formattedTokenPrice = formatTokenPrice( formattedTokenAmount, - token?.priceUSD, + token.priceUSD, ); return ( @@ -117,7 +114,7 @@ export const TokenBase: React.FC = ({ ) : ( {t(`main.tokenOnChain`, { - tokenSymbol: token?.symbol, + tokenSymbol: token.symbol, chainName: chain?.name, })} diff --git a/packages/widget/src/components/TokenList/TokenListItem.tsx b/packages/widget/src/components/TokenList/TokenListItem.tsx index fc9cbfacb..622ac0f6d 100644 --- a/packages/widget/src/components/TokenList/TokenListItem.tsx +++ b/packages/widget/src/components/TokenList/TokenListItem.tsx @@ -9,8 +9,9 @@ import { Slide, Typography, } from '@mui/material'; -import { memo, useRef, useState } from 'react'; +import { useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { formatUnits } from 'viem'; import { formatTokenAmount, formatTokenPrice, @@ -19,40 +20,38 @@ import { import { IconButton, ListItem, ListItemButton } from './TokenList.style'; import type { TokenListItemButtonProps, TokenListItemProps } from './types'; -export const TokenListItem: React.FC = memo( - ({ - onClick, - size, - start, - token, - chain, - showBalance, - isBalanceLoading, - startAdornment, - endAdornment, - }) => { - const handleClick = () => onClick?.(token.address); - return ( - - {startAdornment} - - {endAdornment} - - ); - }, -); +export const TokenListItem: React.FC = ({ + onClick, + size, + start, + token, + chain, + showBalance, + isBalanceLoading, + startAdornment, + endAdornment, +}) => { + const handleClick = () => onClick?.(token.address); + return ( + + {startAdornment} + + {endAdornment} + + ); +}; export const TokenListItemButton: React.FC = ({ onClick, @@ -62,7 +61,12 @@ export const TokenListItemButton: React.FC = ({ isBalanceLoading, }) => { const { t } = useTranslation(); - const tokenPrice = formatTokenPrice(token.amount, token.priceUSD); + const tokenPrice = token.amount + ? formatTokenPrice( + formatUnits(token.amount, token.decimals), + token.priceUSD, + ) + : undefined; const container = useRef(null); const timeoutId = useRef>(); const [showAddress, setShowAddress] = useState(false); @@ -142,10 +146,10 @@ export const TokenListItemButton: React.FC = ({ ) : ( - {Number(token.amount) ? ( + {token.amount ? ( {t('format.number', { - value: formatTokenAmount(token.amount), + value: formatTokenAmount(token.amount, token.decimals), })} ) : null} diff --git a/packages/widget/src/hooks/useFundsSufficiency.ts b/packages/widget/src/hooks/useFundsSufficiency.ts index fc26671d3..cafb51b8b 100644 --- a/packages/widget/src/hooks/useFundsSufficiency.ts +++ b/packages/widget/src/hooks/useFundsSufficiency.ts @@ -1,7 +1,7 @@ import type { Route } from '@lifi/sdk'; import { useQuery } from '@tanstack/react-query'; -import Big from 'big.js'; import { useWatch } from 'react-hook-form'; +import { parseUnits } from 'viem'; import { FormKey, useWallet } from '../providers'; import { isRouteDone } from '../stores'; import { useGetTokenBalancesWithRetry } from './useGetTokenBalancesWithRetry'; @@ -38,10 +38,11 @@ export const useFundsSufficiency = (route?: Route) => { if (!account.address || !token) { return; } - let currentTokenBalance = Big(token?.amount || 0); + const parsedFromAmount = parseUnits(fromAmount, token.decimals); + let currentTokenBalance = token.amount ?? 0n; if (!route || isRouteDone(route)) { - const insufficientFunds = currentTokenBalance.lt(Big(fromAmount || 0)); + const insufficientFunds = currentTokenBalance < parsedFromAmount; return insufficientFunds; } @@ -52,11 +53,10 @@ export const useFundsSufficiency = (route?: Route) => { if ( token.chainId === currentAction.fromToken.chainId && token.address === currentAction.fromToken.address && - currentTokenBalance.gt(0) + currentTokenBalance > 0 ) { - const insufficientFunds = Big(route.fromAmount) - .div(10 ** route.fromToken.decimals) - .gt(currentTokenBalance); + const insufficientFunds = + BigInt(route.fromAmount) > currentTokenBalance; return insufficientFunds; } @@ -64,10 +64,9 @@ export const useFundsSufficiency = (route?: Route) => { currentAction.fromToken, ]); - currentTokenBalance = Big(tokenBalances?.[0]?.amount || 0); - const insufficientFunds = Big(currentAction.fromAmount) - .div(10 ** currentAction.fromToken.decimals) - .gt(currentTokenBalance); + currentTokenBalance = tokenBalances?.[0]?.amount ?? 0n; + const insufficientFunds = + BigInt(currentAction.fromAmount) > currentTokenBalance; return insufficientFunds; }, { diff --git a/packages/widget/src/hooks/useGasRefuel.ts b/packages/widget/src/hooks/useGasRefuel.ts index 560b50632..f8deae3d5 100644 --- a/packages/widget/src/hooks/useGasRefuel.ts +++ b/packages/widget/src/hooks/useGasRefuel.ts @@ -1,4 +1,3 @@ -import Big from 'big.js'; import { useMemo } from 'react'; import { useWatch } from 'react-hook-form'; import { useChains } from '.'; @@ -41,14 +40,12 @@ export const useGasRefuel = () => { ) { return false; } - const tokenBalance = Big(nativeToken.amount ?? 0); + const tokenBalance = nativeToken.amount ?? 0n; // check if the user balance < 50% of the recommended amount - const recommendedAmount = Big(gasRecommendation.recommended.amount) - .div(10 ** gasRecommendation.recommended.token.decimals) - .div(2); + const recommendedAmount = BigInt(gasRecommendation.recommended.amount) / 2n; - const insufficientGas = tokenBalance.lt(recommendedAmount); + const insufficientGas = tokenBalance < recommendedAmount; return insufficientGas; }, [fromChainId, gasRecommendation, nativeToken, toChainId]); diff --git a/packages/widget/src/hooks/useGasSufficiency.ts b/packages/widget/src/hooks/useGasSufficiency.ts index 96ff5a48a..b9038a2dd 100644 --- a/packages/widget/src/hooks/useGasSufficiency.ts +++ b/packages/widget/src/hooks/useGasSufficiency.ts @@ -1,14 +1,13 @@ import type { EVMChain, Route, Token } from '@lifi/sdk'; import { useQuery } from '@tanstack/react-query'; -import Big from 'big.js'; import { useChains, useGasRefuel, useGetTokenBalancesWithRetry } from '.'; import { useWallet } from '../providers'; import { useSettings } from '../stores'; export interface GasSufficiency { - gasAmount: Big; - tokenAmount?: Big; - insufficientAmount?: Big; + gasAmount: bigint; + tokenAmount?: bigint; + insufficientAmount?: bigint; insufficient?: boolean; token: Token; chain?: EVMChain; @@ -41,37 +40,35 @@ export const useGasSufficiency = (route?: Route) => { const gasCosts = route.steps .filter((step) => !step.execution || step.execution.status !== 'DONE') - .reduce((groupedGasCosts, step) => { - if (step.estimate.gasCosts) { - const { token } = step.estimate.gasCosts[0]; - const gasCostAmount = step.estimate.gasCosts - .reduce( - (amount, gasCost) => amount.plus(Big(gasCost.amount || 0)), - Big(0), - ) - .div(10 ** token.decimals); - const groupedGasCost = groupedGasCosts[token.chainId]; - const gasAmount = groupedGasCost - ? groupedGasCost.gasAmount.plus(gasCostAmount) - : gasCostAmount; - groupedGasCosts[token.chainId] = { - gasAmount, - tokenAmount: gasAmount, - token, - }; + .reduce( + (groupedGasCosts, step) => { + if (step.estimate.gasCosts) { + const { token } = step.estimate.gasCosts[0]; + const gasCostAmount = step.estimate.gasCosts.reduce( + (amount, gasCost) => amount + BigInt(gasCost.amount), + 0n, + ); + const groupedGasCost = groupedGasCosts[token.chainId]; + const gasAmount = groupedGasCost + ? groupedGasCost.gasAmount + gasCostAmount + : gasCostAmount; + groupedGasCosts[token.chainId] = { + gasAmount, + tokenAmount: gasAmount, + token, + }; + return groupedGasCosts; + } return groupedGasCosts; - } - return groupedGasCosts; - }, {} as Record); + }, + {} as Record, + ); if ( route.fromToken.address === gasCosts[route.fromChainId]?.token.address ) { - gasCosts[route.fromChainId].tokenAmount = gasCosts[ - route.fromChainId - ]?.gasAmount.plus( - Big(route.fromAmount).div(10 ** route.fromToken.decimals), - ); + gasCosts[route.fromChainId].tokenAmount = + gasCosts[route.fromChainId]?.gasAmount + BigInt(route.fromAmount); } const tokenBalances = await getTokenBalancesWithRetry( @@ -85,28 +82,27 @@ export const useGasSufficiency = (route?: Route) => { [route.fromChainId, route.toChainId].forEach((chainId) => { if (gasCosts[chainId]) { - const gasTokenBalance = Big( + const gasTokenBalance = tokenBalances?.find( (t) => t.chainId === gasCosts[chainId].token.chainId && t.address === gasCosts[chainId].token.address, - )?.amount ?? 0, - ); - + )?.amount ?? 0n; const insufficient = - gasTokenBalance.lte(0) || - gasTokenBalance.lt(gasCosts[chainId].gasAmount ?? Big(0)) || - gasTokenBalance.lt(gasCosts[chainId].tokenAmount ?? Big(0)); + gasTokenBalance <= 0n || + gasTokenBalance < gasCosts[chainId].gasAmount || + gasTokenBalance < (gasCosts[chainId].tokenAmount ?? 0n); const insufficientAmount = insufficient - ? gasCosts[chainId].tokenAmount?.minus(gasTokenBalance) ?? - gasCosts[chainId].gasAmount.minus(gasTokenBalance) + ? gasCosts[chainId].tokenAmount + ? gasCosts[chainId].tokenAmount! - gasTokenBalance + : gasCosts[chainId].gasAmount - gasTokenBalance : undefined; gasCosts[chainId] = { ...gasCosts[chainId], insufficient, - insufficientAmount: insufficientAmount?.round(5, Big.roundUp), + insufficientAmount, chain: insufficient ? getChainById(chainId) : undefined, }; } diff --git a/packages/widget/src/hooks/useProcessMessage.ts b/packages/widget/src/hooks/useProcessMessage.ts index 5ceb068c9..fe1b6efaf 100644 --- a/packages/widget/src/hooks/useProcessMessage.ts +++ b/packages/widget/src/hooks/useProcessMessage.ts @@ -7,7 +7,7 @@ import type { StatusMessage, Substatus, } from '@lifi/sdk'; -import { LifiErrorCode } from '@lifi/sdk'; +import { LiFiErrorCode } from '@lifi/sdk'; import type { TFunction } from 'i18next'; import { useTranslation } from 'react-i18next'; import { useWidgetConfig } from '../providers'; @@ -107,80 +107,87 @@ export function getProcessMessage( message?: string; } { if (process.error && process.status === 'FAILED') { - const getTransactionNotSentMessage = () => - t(`error.message.transactionNotSent`, { - amount: formatTokenAmount( - step.action.fromAmount, - step.action.fromToken.decimals, - ), - tokenSymbol: step.action.fromToken.symbol, - chainName: getChainById(step.action.fromChainId)?.name ?? '', - }); + const getDefaultErrorMessage = (key?: string) => + `${t((key as any) ?? 'error.message.transactionNotSent')} ${t( + 'error.message.remainInYourWallet', + { + amount: formatTokenAmount( + BigInt(step.action.fromAmount), + step.action.fromToken.decimals, + ), + tokenSymbol: step.action.fromToken.symbol, + chainName: getChainById(step.action.fromChainId)?.name ?? '', + }, + )}`; let title: string = ''; let message: string = ''; switch (process.error.code) { - case LifiErrorCode.AllowanceRequired: + case LiFiErrorCode.AllowanceRequired: title = t(`error.title.allowanceRequired`); message = t(`error.message.allowanceRequired`, { tokenSymbol: step.action.fromToken.symbol, }); break; - case LifiErrorCode.BalanceError: + case LiFiErrorCode.BalanceError: title = t(`error.title.balanceIsTooLow`); - message = getTransactionNotSentMessage(); + message = getDefaultErrorMessage(); break; - case LifiErrorCode.ChainSwitchError: + case LiFiErrorCode.ChainSwitchError: title = t(`error.title.chainSwitch`); - message = getTransactionNotSentMessage(); + message = getDefaultErrorMessage(); break; - case LifiErrorCode.GasLimitError: + case LiFiErrorCode.GasLimitError: title = t(`error.title.gasLimitIsTooLow`); - message = getTransactionNotSentMessage(); + message = getDefaultErrorMessage(); break; - case LifiErrorCode.InsufficientFunds: + case LiFiErrorCode.InsufficientFunds: title = t(`error.title.insufficientFunds`); message = `${t( `error.message.insufficientFunds`, - )} ${getTransactionNotSentMessage()}`; + )} ${getDefaultErrorMessage()}`; break; - case LifiErrorCode.SlippageError: + case LiFiErrorCode.SlippageError: title = t(`error.title.slippageNotMet`); message = t(`error.message.slippageThreshold`); break; - case LifiErrorCode.TransactionFailed: + case LiFiErrorCode.TransactionFailed: title = t(`error.title.transactionFailed`); message = t(`error.message.transactionFailed`); break; - case LifiErrorCode.TransactionUnderpriced: + case LiFiErrorCode.TransactionUnderpriced: title = t(`error.title.transactionUnderpriced`); - message = getTransactionNotSentMessage(); + message = getDefaultErrorMessage(); break; - case LifiErrorCode.TransactionUnprepared: + case LiFiErrorCode.TransactionUnprepared: title = t(`error.title.transactionUnprepared`); - message = getTransactionNotSentMessage(); + message = getDefaultErrorMessage(); break; - case LifiErrorCode.TransactionCanceled: + case LiFiErrorCode.TransactionCanceled: title = t(`error.title.transactionCanceled`); - message = getTransactionNotSentMessage(); + message = getDefaultErrorMessage('error.message.transactionCanceled'); + break; + case LiFiErrorCode.ExchangeRateUpdateCanceled: + title = t(`error.title.exchangeRateUpdateCanceled`); + message = getDefaultErrorMessage(); break; - case LifiErrorCode.TransactionRejected: - title = t(`error.title.transactionRejected`); - message = t(`error.message.transactionRejected`, { + case LiFiErrorCode.SignatureRejected: + title = t(`error.title.signatureRejected`); + message = t(`error.message.signatureRejected`, { amount: formatTokenAmount( - step.action.fromAmount, + BigInt(step.action.fromAmount), step.action.fromToken.decimals, ), tokenSymbol: step.action.fromToken.symbol, chainName: getChainById(step.action.fromChainId)?.name ?? '', }); break; - case LifiErrorCode.ProviderUnavailable: + case LiFiErrorCode.ProviderUnavailable: default: title = t(`error.title.unknown`); if (process.txLink) { message = t(`error.message.transactionFailed`); } else { - message = t(`error.message.unknown`); + message = process.error.message || t(`error.message.unknown`); } break; } diff --git a/packages/widget/src/hooks/useRouteExecution.ts b/packages/widget/src/hooks/useRouteExecution.ts index ffcac2569..f3f8ef825 100644 --- a/packages/widget/src/hooks/useRouteExecution.ts +++ b/packages/widget/src/hooks/useRouteExecution.ts @@ -1,6 +1,9 @@ import type { ExchangeRateUpdateParams, Route } from '@lifi/sdk'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useCallback, useEffect, useRef } from 'react'; +import type { WalletClient } from 'viem'; +import { createWalletClient, custom } from 'viem'; +import type { Address } from 'wagmi'; import { shallow } from 'zustand/shallow'; import { useLiFi, useWallet } from '../providers'; import { @@ -70,9 +73,11 @@ export const useRouteExecution = ({ console.log('Route updated.', clonedUpdatedRoute); }; - const switchChainHook = async (requiredChainId: number) => { + const switchChainHook = async ( + requiredChainId: number, + ): Promise => { if (!account.isActive || !account.signer) { - return account.signer; + return; } const currentChainId = await account.signer.getChainId(); if (currentChainId !== requiredChainId) { @@ -80,9 +85,17 @@ export const useRouteExecution = ({ if (!signer) { throw new Error('Chain was not switched.'); } - return signer; + const client = createWalletClient({ + account: (await signer.getAddress()) as Address, + transport: custom((signer.provider as any)?.provider as any), + }); + return client; } - return account.signer; + const client = createWalletClient({ + account: account.address as Address, + transport: custom(account.provider?.provider as any), + }); + return client; }; const acceptExchangeRateUpdateHook = async ( @@ -108,7 +121,11 @@ export const useRouteExecution = ({ throw Error('Execution route not found.'); } queryClient.removeQueries(['routes'], { exact: false }); - return lifi.executeRoute(account.signer, routeExecution.route, { + const client = createWalletClient({ + account: account.address as Address, + transport: custom(account.provider?.provider as any), + }); + return lifi.executeRoute(client, routeExecution.route, { updateRouteHook, switchChainHook, acceptExchangeRateUpdateHook, @@ -134,17 +151,17 @@ export const useRouteExecution = ({ if (!routeExecution?.route) { throw Error('Execution route not found.'); } - return lifi.resumeRoute( - account.signer, - resumedRoute ?? routeExecution.route, - { - updateRouteHook, - switchChainHook, - acceptExchangeRateUpdateHook, - infiniteApproval: false, - executeInBackground, - }, - ); + const client = createWalletClient({ + account: account.address as Address, + transport: custom(account.provider?.provider as any), + }); + return lifi.resumeRoute(client, resumedRoute ?? routeExecution.route, { + updateRouteHook, + switchChainHook, + acceptExchangeRateUpdateHook, + infiniteApproval: false, + executeInBackground, + }); }, { onMutate: () => { diff --git a/packages/widget/src/hooks/useRoutes.ts b/packages/widget/src/hooks/useRoutes.ts index 992362be2..e7c76b4ce 100644 --- a/packages/widget/src/hooks/useRoutes.ts +++ b/packages/widget/src/hooks/useRoutes.ts @@ -1,10 +1,10 @@ import { isAddress } from '@ethersproject/address'; import type { Route, RoutesResponse, Token } from '@lifi/sdk'; -import { LifiErrorCode } from '@lifi/sdk'; +import { LiFiErrorCode } from '@lifi/sdk'; import { useQuery, useQueryClient } from '@tanstack/react-query'; -import Big from 'big.js'; import { useWatch } from 'react-hook-form'; import { v4 as uuidv4 } from 'uuid'; +import { parseUnits } from 'viem'; import { useDebouncedWatch, useGasRefuel, useToken } from '.'; import { FormKey, useLiFi, useWallet, useWidgetConfig } from '../providers'; import { useSettings } from '../stores'; @@ -145,9 +145,10 @@ export const useRoutes = ({ insurableRoute }: RoutesProps = {}) => { } catch { toWalletAddress = isAddress(toAddress) ? toAddress : fromAddress; } - const fromAmount = Big(fromTokenAmount || 0) - .mul(10 ** (fromToken?.decimals ?? 0)) - .toFixed(0); + const fromAmount = parseUnits( + fromTokenAmount || 0, + fromToken!.decimals, + ).toString(); const formattedSlippage = parseFloat(slippage) / 100; const allowedBridges: string[] = insurableRoute @@ -266,7 +267,7 @@ export const useRoutes = ({ insurableRoute }: RoutesProps = {}) => { ); }, retry(failureCount, error: any) { - if (error?.code === LifiErrorCode.NotFound) { + if (error?.code === LiFiErrorCode.NotFound) { return false; } return true; diff --git a/packages/widget/src/hooks/useTokenBalances.ts b/packages/widget/src/hooks/useTokenBalances.ts index 4005c82b2..f0ddfa6bb 100644 --- a/packages/widget/src/hooks/useTokenBalances.ts +++ b/packages/widget/src/hooks/useTokenBalances.ts @@ -1,4 +1,5 @@ import { useQuery } from '@tanstack/react-query'; +import { formatUnits } from 'viem'; import { useLiFi, useWallet } from '../providers'; import type { TokenAmount } from '../types'; import { useFeaturedTokens } from './useFeaturedTokens'; @@ -34,9 +35,10 @@ export const useTokenBalances = (selectedChainId?: number) => { ); const sortFn = (a: TokenAmount, b: TokenAmount) => - parseFloat(b.amount ?? '0') * parseFloat(b.priceUSD ?? '0') - - parseFloat(a.amount ?? '0') * parseFloat(a.priceUSD ?? '0'); - + parseFloat(formatUnits(b.amount ?? 0n, b.decimals)) * + parseFloat(b.priceUSD ?? '0') - + parseFloat(formatUnits(a.amount ?? 0n, a.decimals)) * + parseFloat(a.priceUSD ?? '0'); const formattedTokens = ( tokenBalances.length === 0 ? tokens : tokenBalances ) as TokenAmount[]; @@ -45,23 +47,21 @@ export const useTokenBalances = (selectedChainId?: number) => { ...formattedTokens .filter( (token) => - token.amount !== '0' && featuredTokenAddresses.has(token.address), + token.amount && featuredTokenAddresses.has(token.address), ) .sort(sortFn), ...formattedTokens.filter( - (token) => - token.amount === '0' && featuredTokenAddresses.has(token.address), + (token) => !token.amount && featuredTokenAddresses.has(token.address), ), ...formattedTokens .filter( (token) => - token.amount !== '0' && - !featuredTokenAddresses.has(token.address), + token.amount && !featuredTokenAddresses.has(token.address), ) .sort(sortFn), ...formattedTokens.filter( (token) => - token.amount === '0' && !featuredTokenAddresses.has(token.address), + !token.amount && !featuredTokenAddresses.has(token.address), ), ]; return result; diff --git a/packages/widget/src/i18n/en.json b/packages/widget/src/i18n/en.json index 112a75cac..9b58703f1 100644 --- a/packages/widget/src/i18n/en.json +++ b/packages/widget/src/i18n/en.json @@ -119,10 +119,12 @@ "message": { "allowanceRequired": "Transfer amount for {{tokenSymbol}} exceeds your current allowance. Please increase your allowance and try again.", "insufficientFunds": "You don't have enough gas to cover the cost of the transaction.", + "signatureRejected": "Your signature is required to complete the transaction. {{amount, number(maximumFractionDigits: 9)}} {{tokenSymbol}} on {{chainName}} remain in your wallet.", "slippageThreshold": "The slippage is larger than the defined threshold. Please request a new quote.", "transactionFailed": "Please check the block explorer for more information.", - "transactionNotSent": "Transaction was not sent. {{amount, number(maximumFractionDigits: 9)}} {{tokenSymbol}} on {{chainName}} remain in your wallet.", - "transactionRejected": "Your signature is required to complete the transaction. {{amount, number(maximumFractionDigits: 9)}} {{tokenSymbol}} on {{chainName}} remain in your wallet.", + "transactionNotSent": "Transaction was not sent.", + "transactionCanceled": "Transaction was canceled.", + "remainInYourWallet": "{{amount, number(maximumFractionDigits: 9)}} {{tokenSymbol}} on {{chainName}} remain in your wallet.", "unknown": "Please try again or contact support." }, "title": { @@ -131,10 +133,11 @@ "chainSwitch": "Chain switch required", "gasLimitIsTooLow": "The gas limit is too low", "insufficientFunds": "Insufficient funds", + "signatureRejected": "Signature required", "slippageNotMet": "Slippage conditions not met", "transactionCanceled": "Transaction canceled", + "exchangeRateUpdateCanceled": "Exchange rate update canceled", "transactionFailed": "Transaction failed", - "transactionRejected": "Signature required", "transactionUnderpriced": "Transaction is underpriced", "transactionUnprepared": "Unable to prepare transaction", "unknown": "Something went wrong", diff --git a/packages/widget/src/pages/TransactionDetailsPage/TransactionDetailsPage.tsx b/packages/widget/src/pages/TransactionDetailsPage/TransactionDetailsPage.tsx index 84d47309e..df3cc5e66 100644 --- a/packages/widget/src/pages/TransactionDetailsPage/TransactionDetailsPage.tsx +++ b/packages/widget/src/pages/TransactionDetailsPage/TransactionDetailsPage.tsx @@ -111,7 +111,7 @@ export const TransactionDetailsPage: React.FC = () => { status={routeExecution.status} feeAmountUsd={routeExecution.route.insurance.feeAmountUsd} insuredAmount={formatTokenAmount( - routeExecution.route.toAmountMin, + BigInt(routeExecution.route.toAmountMin), routeExecution.route.toToken.decimals, )} insuredTokenSymbol={routeExecution.route.toToken.symbol} diff --git a/packages/widget/src/pages/TransactionHistoryPage/TransactionHistoryItem.tsx b/packages/widget/src/pages/TransactionHistoryPage/TransactionHistoryItem.tsx index fcbb77629..776208854 100644 --- a/packages/widget/src/pages/TransactionHistoryPage/TransactionHistoryItem.tsx +++ b/packages/widget/src/pages/TransactionHistoryPage/TransactionHistoryItem.tsx @@ -22,10 +22,10 @@ export const TransactionHistoryItem: React.FC<{ const startedAt = new Date( route.steps[0].execution?.process[0].startedAt ?? 0, ); - const fromToken = { ...route.fromToken, amount: route.fromAmount }; + const fromToken = { ...route.fromToken, amount: BigInt(route.fromAmount) }; const toToken = { ...(route.steps.at(-1)?.execution?.toToken ?? route.toToken), - amount: route.steps.at(-1)?.execution?.toAmount ?? route.toAmount, + amount: BigInt(route.steps.at(-1)?.execution?.toAmount ?? route.toAmount), }; return ( diff --git a/packages/widget/src/pages/TransactionPage/ExchangeRateBottomSheet.tsx b/packages/widget/src/pages/TransactionPage/ExchangeRateBottomSheet.tsx index 171c486fb..343e1de0d 100644 --- a/packages/widget/src/pages/TransactionPage/ExchangeRateBottomSheet.tsx +++ b/packages/widget/src/pages/TransactionPage/ExchangeRateBottomSheet.tsx @@ -1,7 +1,6 @@ import type { ExchangeRateUpdateParams } from '@lifi/sdk'; import WarningRoundedIcon from '@mui/icons-material/WarningRounded'; import { Box, Button, Typography } from '@mui/material'; -import Big from 'big.js'; import type { MutableRefObject } from 'react'; import { forwardRef, @@ -97,7 +96,18 @@ const ExchangeRateBottomSheetContent: React.FC< const ref = useRef(); useSetContentHeight(ref); - const oldAmount = Big(data?.oldToAmount || 1); + if (!data) { + return; + } + + const oldAmount = BigInt(data.oldToAmount || 1); + const rateChange = ( + (Number((BigInt(data.newToAmount || 0) * 1_000_000n) / oldAmount) / + 1_000_000) * + 100 - + 100 + ).toFixed(2); + return ( @@ -114,8 +124,8 @@ const ExchangeRateBottomSheetContent: React.FC< {t('format.number', { value: formatTokenAmount( - data?.oldToAmount, - data?.toToken.decimals, + BigInt(data.oldToAmount), + data.toToken.decimals, 5, ), })}{' '} @@ -127,7 +137,7 @@ const ExchangeRateBottomSheetContent: React.FC< {t('format.number', { value: formatTokenAmount( - data?.newToAmount, + BigInt(data?.newToAmount), data?.toToken.decimals, 5, ), @@ -137,15 +147,7 @@ const ExchangeRateBottomSheetContent: React.FC< {t('main.rateChange')} - - {Big(data?.newToAmount || 0) - .div(oldAmount.eq(0) ? Big(1) : oldAmount) - .minus(1) - .mul(100) - .round(2, Big.roundUp) - .toString()} - % - + {rateChange}%