@lifi/widget 3.13.2 → 3.14.0
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/CHANGELOG.md +15 -0
- package/dist/esm/AppDrawer.style.d.ts +1 -1
- package/dist/esm/components/ActiveTransactions/ActiveTransactions.style.d.ts +1 -1
- package/dist/esm/components/AlertMessage/AlertMessage.d.ts +2 -2
- package/dist/esm/components/AlertMessage/AlertMessage.js +1 -1
- package/dist/esm/components/AlertMessage/AlertMessage.js.map +1 -1
- package/dist/esm/components/AmountInput/AmountInputAdornment.style.d.ts +1 -1
- package/dist/esm/components/AmountInput/PriceFormHelperText.js +7 -4
- package/dist/esm/components/AmountInput/PriceFormHelperText.js.map +1 -1
- package/dist/esm/components/BaseTransactionButton/BaseTransactionButton.js +2 -2
- package/dist/esm/components/BaseTransactionButton/BaseTransactionButton.js.map +1 -1
- package/dist/esm/components/ButtonTertiary.d.ts +1 -1
- package/dist/esm/components/ButtonTertiary.js +4 -5
- package/dist/esm/components/ButtonTertiary.js.map +1 -1
- package/dist/esm/components/Card/CardHeader.d.ts +1 -1
- package/dist/esm/components/Card/CardIconButton.d.ts +1 -1
- package/dist/esm/components/Card/CardLabel.js +0 -1
- package/dist/esm/components/Card/CardLabel.js.map +1 -1
- package/dist/esm/components/FeeBreakdownTooltip.js +3 -4
- package/dist/esm/components/FeeBreakdownTooltip.js.map +1 -1
- package/dist/esm/components/GasMessage/FundsSufficiencyMessage.js +1 -1
- package/dist/esm/components/GasMessage/FundsSufficiencyMessage.js.map +1 -1
- package/dist/esm/components/Header/Header.style.d.ts +1 -1
- package/dist/esm/components/Header/SettingsButton.style.d.ts +1 -1
- package/dist/esm/components/RouteCard/RouteCard.js +8 -3
- package/dist/esm/components/RouteCard/RouteCard.js.map +1 -1
- package/dist/esm/components/RouteCard/getMatchingLabels.d.ts +3 -0
- package/dist/esm/components/RouteCard/getMatchingLabels.js +34 -0
- package/dist/esm/components/RouteCard/getMatchingLabels.js.map +1 -0
- package/dist/esm/components/SelectTokenButton/SelectTokenButton.style.d.ts +1 -1
- package/dist/esm/components/SendToWallet/SendToWallet.style.d.ts +1 -1
- package/dist/esm/components/Skeleton/WidgetSkeleton.style.d.ts +2 -2
- package/dist/esm/components/Step/Step.js +17 -8
- package/dist/esm/components/Step/Step.js.map +1 -1
- package/dist/esm/components/StepActions/StepActions.js +8 -8
- package/dist/esm/components/StepActions/StepActions.js.map +1 -1
- package/dist/esm/components/ToAddressRequiredMessage.js +1 -1
- package/dist/esm/components/ToAddressRequiredMessage.js.map +1 -1
- package/dist/esm/components/Token/Token.js +3 -3
- package/dist/esm/components/Token/Token.js.map +1 -1
- package/dist/esm/components/TokenList/TokenList.style.d.ts +1 -1
- package/dist/esm/components/TokenList/TokenListItem.js +4 -6
- package/dist/esm/components/TokenList/TokenListItem.js.map +1 -1
- package/dist/esm/components/TokenRate/TokenRate.js +5 -7
- package/dist/esm/components/TokenRate/TokenRate.js.map +1 -1
- package/dist/esm/components/TransactionDetails.js +3 -7
- package/dist/esm/components/TransactionDetails.js.map +1 -1
- package/dist/esm/config/version.d.ts +1 -1
- package/dist/esm/config/version.js +1 -1
- package/dist/esm/i18n/en.json +4 -2
- package/dist/esm/pages/SendToWallet/BookmarkAddressSheet.js +1 -2
- package/dist/esm/pages/SendToWallet/BookmarkAddressSheet.js.map +1 -1
- package/dist/esm/pages/SendToWallet/ConfirmAddressSheet.d.ts +1 -0
- package/dist/esm/pages/SendToWallet/ConfirmAddressSheet.js +6 -4
- package/dist/esm/pages/SendToWallet/ConfirmAddressSheet.js.map +1 -1
- package/dist/esm/pages/SendToWallet/SendToWalletPage.js +5 -3
- package/dist/esm/pages/SendToWallet/SendToWalletPage.js.map +1 -1
- package/dist/esm/pages/SendToWallet/SendToWalletPage.style.d.ts +2 -2
- package/dist/esm/pages/TransactionPage/ExchangeRateBottomSheet.js +2 -2
- package/dist/esm/pages/TransactionPage/ExchangeRateBottomSheet.js.map +1 -1
- package/dist/esm/providers/I18nProvider/I18nProvider.js +3 -1
- package/dist/esm/providers/I18nProvider/I18nProvider.js.map +1 -1
- package/dist/esm/stores/routes/useExecutingRoutesIds.js +2 -2
- package/dist/esm/stores/routes/useExecutingRoutesIds.js.map +1 -1
- package/dist/esm/themes/createTheme.js +4 -5
- package/dist/esm/themes/createTheme.js.map +1 -1
- package/dist/esm/types/widget.d.ts +18 -1
- package/dist/esm/types/widget.js.map +1 -1
- package/dist/esm/utils/compactNumberFormatter.d.ts +10 -0
- package/dist/esm/utils/compactNumberFormatter.js +81 -0
- package/dist/esm/utils/compactNumberFormatter.js.map +1 -0
- package/dist/esm/utils/compactNumberFormatter.test.d.ts +1 -0
- package/dist/esm/utils/compactNumberFormatter.test.js +64 -0
- package/dist/esm/utils/compactNumberFormatter.test.js.map +1 -0
- package/dist/esm/utils/converters.js +2 -2
- package/dist/esm/utils/converters.js.map +1 -1
- package/dist/esm/utils/fees.js +2 -3
- package/dist/esm/utils/fees.js.map +1 -1
- package/dist/esm/utils/format.d.ts +1 -10
- package/dist/esm/utils/format.js +8 -47
- package/dist/esm/utils/format.js.map +1 -1
- package/dist/esm/utils/getPriceImpact.js +7 -6
- package/dist/esm/utils/getPriceImpact.js.map +1 -1
- package/dist/esm/utils/percentFormatter.js.map +1 -0
- package/package.json +13 -14
- package/src/components/AlertMessage/AlertMessage.tsx +3 -3
- package/src/components/AmountInput/PriceFormHelperText.tsx +8 -4
- package/src/components/BaseTransactionButton/BaseTransactionButton.tsx +3 -3
- package/src/components/ButtonTertiary.tsx +4 -5
- package/src/components/Card/CardLabel.tsx +0 -1
- package/src/components/FeeBreakdownTooltip.tsx +3 -4
- package/src/components/GasMessage/FundsSufficiencyMessage.tsx +1 -1
- package/src/components/RouteCard/RouteCard.tsx +12 -2
- package/src/components/RouteCard/getMatchingLabels.ts +53 -0
- package/src/components/Step/Step.tsx +23 -12
- package/src/components/StepActions/StepActions.tsx +16 -13
- package/src/components/ToAddressRequiredMessage.tsx +1 -1
- package/src/components/Token/Token.tsx +7 -2
- package/src/components/TokenList/TokenListItem.tsx +9 -9
- package/src/components/TokenRate/TokenRate.tsx +5 -11
- package/src/components/TransactionDetails.tsx +4 -8
- package/src/config/version.ts +1 -1
- package/src/i18n/en.json +4 -2
- package/src/pages/SendToWallet/BookmarkAddressSheet.tsx +2 -3
- package/src/pages/SendToWallet/ConfirmAddressSheet.tsx +28 -6
- package/src/pages/SendToWallet/SendToWalletPage.tsx +8 -2
- package/src/pages/TransactionPage/ExchangeRateBottomSheet.tsx +2 -2
- package/src/providers/I18nProvider/I18nProvider.tsx +3 -1
- package/src/stores/routes/useExecutingRoutesIds.ts +2 -2
- package/src/themes/createTheme.ts +6 -8
- package/src/themes/types.ts +0 -1
- package/src/types/widget.ts +22 -0
- package/src/utils/compactNumberFormatter.test.ts +67 -0
- package/src/utils/compactNumberFormatter.ts +91 -0
- package/src/utils/converters.ts +6 -4
- package/src/utils/fees.ts +6 -4
- package/src/utils/format.ts +14 -60
- package/src/utils/getPriceImpact.ts +15 -6
- package/dist/esm/providers/I18nProvider/percentFormatter.js.map +0 -1
- /package/dist/esm/{providers/I18nProvider → utils}/percentFormatter.d.ts +0 -0
- /package/dist/esm/{providers/I18nProvider → utils}/percentFormatter.js +0 -0
- /package/src/{providers/I18nProvider → utils}/percentFormatter.ts +0 -0
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
import type { MouseEventHandler } from 'react'
|
|
14
14
|
import { useRef, useState } from 'react'
|
|
15
15
|
import { useTranslation } from 'react-i18next'
|
|
16
|
-
import { formatUnits } from 'viem'
|
|
17
16
|
import { useExplorer } from '../../hooks/useExplorer.js'
|
|
18
17
|
import { formatTokenAmount, formatTokenPrice } from '../../utils/format.js'
|
|
19
18
|
import { shortenAddress } from '../../utils/wallet.js'
|
|
@@ -66,12 +65,6 @@ export const TokenListItemButton: React.FC<TokenListItemButtonProps> = ({
|
|
|
66
65
|
const { t } = useTranslation()
|
|
67
66
|
const { getAddressLink } = useExplorer()
|
|
68
67
|
|
|
69
|
-
const tokenPrice = token.amount
|
|
70
|
-
? formatTokenPrice(
|
|
71
|
-
formatUnits(token.amount, token.decimals),
|
|
72
|
-
token.priceUSD
|
|
73
|
-
)
|
|
74
|
-
: undefined
|
|
75
68
|
const container = useRef(null)
|
|
76
69
|
const timeoutId = useRef<ReturnType<typeof setTimeout>>(undefined)
|
|
77
70
|
const [showAddress, setShowAddress] = useState(false)
|
|
@@ -93,6 +86,12 @@ export const TokenListItemButton: React.FC<TokenListItemButtonProps> = ({
|
|
|
93
86
|
setShowAddress(false)
|
|
94
87
|
}
|
|
95
88
|
}
|
|
89
|
+
const tokenAmount = formatTokenAmount(token.amount, token.decimals)
|
|
90
|
+
const tokenPrice = formatTokenPrice(
|
|
91
|
+
token.amount,
|
|
92
|
+
token.priceUSD,
|
|
93
|
+
token.decimals
|
|
94
|
+
)
|
|
96
95
|
|
|
97
96
|
return (
|
|
98
97
|
<ListItemButton
|
|
@@ -186,9 +185,10 @@ export const TokenListItemButton: React.FC<TokenListItemButtonProps> = ({
|
|
|
186
185
|
sx={{
|
|
187
186
|
fontWeight: 600,
|
|
188
187
|
}}
|
|
188
|
+
title={tokenAmount}
|
|
189
189
|
>
|
|
190
|
-
{t('format.
|
|
191
|
-
value:
|
|
190
|
+
{t('format.tokenAmount', {
|
|
191
|
+
value: tokenAmount,
|
|
192
192
|
})}
|
|
193
193
|
</Typography>
|
|
194
194
|
) : null}
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import type { RouteExtended } from '@lifi/sdk'
|
|
2
2
|
import type { TypographyProps } from '@mui/material'
|
|
3
3
|
import type { MouseEventHandler } from 'react'
|
|
4
|
+
import { useTranslation } from 'react-i18next'
|
|
4
5
|
import { formatUnits } from 'viem'
|
|
5
6
|
import { create } from 'zustand'
|
|
6
|
-
import {
|
|
7
|
-
convertToSubscriptFormat,
|
|
8
|
-
precisionFormatter,
|
|
9
|
-
} from '../../utils/format.js'
|
|
10
7
|
import { TokenRateTypography } from './TokenRate.style.js'
|
|
11
8
|
|
|
12
9
|
interface TokenRateProps extends TypographyProps {
|
|
@@ -24,6 +21,7 @@ const useTokenRate = create<TokenRateState>((set) => ({
|
|
|
24
21
|
}))
|
|
25
22
|
|
|
26
23
|
export const TokenRate: React.FC<TokenRateProps> = ({ route }) => {
|
|
24
|
+
const { t } = useTranslation()
|
|
27
25
|
const { isForward, toggleIsForward } = useTokenRate()
|
|
28
26
|
|
|
29
27
|
const toggleRate: MouseEventHandler<HTMLSpanElement> = (e) => {
|
|
@@ -54,16 +52,12 @@ export const TokenRate: React.FC<TokenRateProps> = ({ route }) => {
|
|
|
54
52
|
Number.parseFloat(formatUnits(toToken.amount!, toToken.decimals))
|
|
55
53
|
|
|
56
54
|
const rateText = isForward
|
|
57
|
-
? `1 ${fromToken.symbol} ≈ ${
|
|
58
|
-
: `1 ${toToken.symbol} ≈ ${
|
|
59
|
-
|
|
60
|
-
const rateTitle = isForward
|
|
61
|
-
? `1 ${fromToken.symbol} ≈ ${precisionFormatter.format(fromToRate)} ${toToken.symbol}`
|
|
62
|
-
: `1 ${toToken.symbol} ≈ ${precisionFormatter.format(toFromRate)} ${fromToken.symbol}`
|
|
55
|
+
? `1 ${fromToken.symbol} ≈ ${t('format.tokenAmount', { value: fromToRate })} ${toToken.symbol}`
|
|
56
|
+
: `1 ${toToken.symbol} ≈ ${t('format.tokenAmount', { value: toFromRate })} ${fromToken.symbol}`
|
|
63
57
|
|
|
64
58
|
return (
|
|
65
59
|
// biome-ignore lint/a11y/useSemanticElements:
|
|
66
|
-
<TokenRateTypography onClick={toggleRate} role="button"
|
|
60
|
+
<TokenRateTypography onClick={toggleRate} role="button">
|
|
67
61
|
{rateText}
|
|
68
62
|
</TokenRateTypography>
|
|
69
63
|
)
|
|
@@ -8,11 +8,10 @@ import type { CardProps } from '@mui/material'
|
|
|
8
8
|
import { Box, Collapse, Tooltip, Typography } from '@mui/material'
|
|
9
9
|
import { useState } from 'react'
|
|
10
10
|
import { useTranslation } from 'react-i18next'
|
|
11
|
-
import { formatUnits } from 'viem'
|
|
12
11
|
import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
|
|
13
12
|
import { isRouteDone } from '../stores/routes/utils.js'
|
|
14
13
|
import { getAccumulatedFeeCostsBreakdown } from '../utils/fees.js'
|
|
15
|
-
import { formatTokenAmount } from '../utils/format.js'
|
|
14
|
+
import { formatTokenAmount, formatTokenPrice } from '../utils/format.js'
|
|
16
15
|
import { getPriceImpact } from '../utils/getPriceImpact.js'
|
|
17
16
|
import { Card } from './Card/Card.js'
|
|
18
17
|
import { CardIconButton } from './Card/CardIconButton.js'
|
|
@@ -56,14 +55,11 @@ export const TransactionDetails: React.FC<TransactionDetailsProps> = ({
|
|
|
56
55
|
BigInt(feeCollectionStep.estimate.fromAmount) -
|
|
57
56
|
BigInt(feeCollectionStep.estimate.toAmount)
|
|
58
57
|
|
|
59
|
-
|
|
58
|
+
feeAmountUSD = formatTokenPrice(
|
|
60
59
|
estimatedFromAmount,
|
|
60
|
+
feeCollectionStep.action.fromToken.priceUSD,
|
|
61
61
|
feeCollectionStep.action.fromToken.decimals
|
|
62
62
|
)
|
|
63
|
-
|
|
64
|
-
feeAmountUSD =
|
|
65
|
-
Number.parseFloat(feeAmount) *
|
|
66
|
-
Number.parseFloat(feeCollectionStep.action.fromToken.priceUSD)
|
|
67
63
|
}
|
|
68
64
|
|
|
69
65
|
const showIntegratorFeeCollectionDetails =
|
|
@@ -245,7 +241,7 @@ export const TransactionDetails: React.FC<TransactionDetailsProps> = ({
|
|
|
245
241
|
sx={{ cursor: 'help' }}
|
|
246
242
|
>
|
|
247
243
|
<Typography variant="body2">
|
|
248
|
-
{t('format.
|
|
244
|
+
{t('format.tokenAmount', {
|
|
249
245
|
value: formatTokenAmount(
|
|
250
246
|
BigInt(route.toAmountMin),
|
|
251
247
|
route.toToken.decimals
|
package/src/config/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export const name = '@lifi/widget'
|
|
2
|
-
export const version = '3.
|
|
2
|
+
export const version = '3.14.0'
|
package/src/i18n/en.json
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
"format": {
|
|
7
7
|
"currency": "{{value, currencyExt(currency: USD)}}",
|
|
8
8
|
"number": "{{value, number(maximumFractionDigits: 9)}}",
|
|
9
|
-
"percent": "{{value, percent(maximumFractionDigits: 2)}}"
|
|
9
|
+
"percent": "{{value, percent(maximumFractionDigits: 2)}}",
|
|
10
|
+
"tokenAmount": "{{value, numberExt}}"
|
|
10
11
|
},
|
|
11
12
|
"button": {
|
|
12
13
|
"auto": "Auto",
|
|
@@ -88,7 +89,8 @@
|
|
|
88
89
|
"emptyBridgesList": "We couldn't find any bridges that match your search",
|
|
89
90
|
"emptyExchangesList": "We couldn't find any exchanges that match your search",
|
|
90
91
|
"emptyTransactionHistory": "Transaction history is only stored locally and will be deleted if you clear your browser data.",
|
|
91
|
-
"
|
|
92
|
+
"smartContractAccount": "Always ensure your smart contract account is set up on the destination chain to avoid potential fund loss.",
|
|
93
|
+
"fundsToExchange": "Funds sent directly to exchanges might not be recoverable.",
|
|
92
94
|
"toAddressIsRequired": "Please provide the destination wallet address to which the funds will be transferred.",
|
|
93
95
|
"routeNotFound": "Reasons for that could be: low liquidity, amount selected is too low, gas costs are too high or there are no routes for the selected combination."
|
|
94
96
|
},
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Error as ErrorIcon, Info, TurnedIn } from '@mui/icons-material'
|
|
2
|
-
import { LoadingButton } from '@mui/lab'
|
|
3
2
|
import { Button, Typography } from '@mui/material'
|
|
4
3
|
import type { ChangeEvent, MutableRefObject } from 'react'
|
|
5
4
|
import { forwardRef, useState } from 'react'
|
|
@@ -217,7 +216,7 @@ export const BookmarkAddressSheet = forwardRef<
|
|
|
217
216
|
<Button variant="text" onClick={handleCancel} fullWidth>
|
|
218
217
|
{t('button.cancel')}
|
|
219
218
|
</Button>
|
|
220
|
-
<
|
|
219
|
+
<Button
|
|
221
220
|
variant="contained"
|
|
222
221
|
onClick={handleBookmark}
|
|
223
222
|
loading={isValidating}
|
|
@@ -226,7 +225,7 @@ export const BookmarkAddressSheet = forwardRef<
|
|
|
226
225
|
focusRipple
|
|
227
226
|
>
|
|
228
227
|
{t('button.bookmark')}
|
|
229
|
-
</
|
|
228
|
+
</Button>
|
|
230
229
|
</SendToWalletButtonRow>
|
|
231
230
|
</SendToWalletSheetContainer>
|
|
232
231
|
</BottomSheet>
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { Info, Wallet } from '@mui/icons-material'
|
|
2
|
-
import { Button, Typography } from '@mui/material'
|
|
1
|
+
import { Info, Wallet, Warning } from '@mui/icons-material'
|
|
2
|
+
import { Box, Button, Typography } from '@mui/material'
|
|
3
3
|
import type { MutableRefObject } from 'react'
|
|
4
4
|
import { forwardRef } from 'react'
|
|
5
5
|
import { useTranslation } from 'react-i18next'
|
|
6
6
|
import { AlertMessage } from '../../components/AlertMessage/AlertMessage.js'
|
|
7
7
|
import { BottomSheet } from '../../components/BottomSheet/BottomSheet.js'
|
|
8
8
|
import type { BottomSheetBase } from '../../components/BottomSheet/types.js'
|
|
9
|
+
import { useIsContractAddress } from '../../hooks/useIsContractAddress.js'
|
|
9
10
|
import { useNavigateBack } from '../../hooks/useNavigateBack.js'
|
|
10
11
|
import type { Bookmark } from '../../stores/bookmarks/types.js'
|
|
11
12
|
import { useFieldActions } from '../../stores/form/useFieldActions.js'
|
|
@@ -21,16 +22,22 @@ import {
|
|
|
21
22
|
interface ConfirmAddressSheetProps {
|
|
22
23
|
onConfirm: (wallet: Bookmark) => void
|
|
23
24
|
validatedBookmark?: Bookmark
|
|
25
|
+
chainId?: number
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
export const ConfirmAddressSheet = forwardRef<
|
|
27
29
|
BottomSheetBase,
|
|
28
30
|
ConfirmAddressSheetProps
|
|
29
|
-
>(({ validatedBookmark, onConfirm }, ref) => {
|
|
31
|
+
>(({ validatedBookmark, onConfirm, chainId }, ref) => {
|
|
30
32
|
const { t } = useTranslation()
|
|
31
33
|
const { navigateBack } = useNavigateBack()
|
|
32
34
|
const { setFieldValue } = useFieldActions()
|
|
33
35
|
const { setSendToWallet } = useSendToWalletActions()
|
|
36
|
+
const isContractAddress = useIsContractAddress(
|
|
37
|
+
validatedBookmark?.address,
|
|
38
|
+
chainId,
|
|
39
|
+
validatedBookmark?.chainType
|
|
40
|
+
)
|
|
34
41
|
|
|
35
42
|
const handleClose = () => {
|
|
36
43
|
;(ref as MutableRefObject<BottomSheetBase>).current?.close()
|
|
@@ -71,12 +78,27 @@ export const ConfirmAddressSheet = forwardRef<
|
|
|
71
78
|
</SheetAddressContainer>
|
|
72
79
|
<AlertMessage
|
|
73
80
|
title={
|
|
74
|
-
<
|
|
75
|
-
{
|
|
76
|
-
|
|
81
|
+
<Box sx={{ display: 'grid', gap: 1 }}>
|
|
82
|
+
<Typography variant="body2" fontWeight={500}>
|
|
83
|
+
{t('info.message.fundsToExchange')}
|
|
84
|
+
</Typography>
|
|
85
|
+
</Box>
|
|
77
86
|
}
|
|
78
87
|
icon={<Info />}
|
|
88
|
+
multiline
|
|
79
89
|
/>
|
|
90
|
+
{isContractAddress ? (
|
|
91
|
+
<AlertMessage
|
|
92
|
+
title={
|
|
93
|
+
<Typography variant="body2" fontWeight={500}>
|
|
94
|
+
{t('info.message.smartContractAccount')}
|
|
95
|
+
</Typography>
|
|
96
|
+
}
|
|
97
|
+
icon={<Warning />}
|
|
98
|
+
severity="warning"
|
|
99
|
+
multiline
|
|
100
|
+
/>
|
|
101
|
+
) : null}
|
|
80
102
|
<SendToWalletButtonRow>
|
|
81
103
|
<Button variant="text" onClick={handleClose} fullWidth>
|
|
82
104
|
{t('button.cancel')}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ChainType } from '@lifi/sdk'
|
|
1
2
|
import { useAccount } from '@lifi/wallet-management'
|
|
2
3
|
import {
|
|
3
4
|
Error as ErrorIcon,
|
|
@@ -58,8 +59,6 @@ export const SendToWalletPage = () => {
|
|
|
58
59
|
const [validatedWallet, setValidatedWallet] = useState<Bookmark>()
|
|
59
60
|
const [errorMessage, setErrorMessage] = useState('')
|
|
60
61
|
const { validateAddress, isValidating } = useAddressValidation()
|
|
61
|
-
const { accounts } = useAccount()
|
|
62
|
-
const connectedWallets = accounts.filter((account) => account.isConnected)
|
|
63
62
|
const { requiredToChainType } = useToAddressRequirements()
|
|
64
63
|
const [toChainId] = useFieldValues('toChain')
|
|
65
64
|
const { chain: toChain } = useChain(toChainId)
|
|
@@ -67,6 +66,12 @@ export const SendToWalletPage = () => {
|
|
|
67
66
|
const [isBookmarkButtonLoading, setIsBookmarkButtonLoading] = useState(false)
|
|
68
67
|
const { variant } = useWidgetConfig()
|
|
69
68
|
|
|
69
|
+
const { accounts } = useAccount()
|
|
70
|
+
const connectedWallets = accounts.filter((account) => account.isConnected)
|
|
71
|
+
const connectedEVMChainId = connectedWallets.find(
|
|
72
|
+
(account) => account.chainType === ChainType.EVM
|
|
73
|
+
)?.chainId
|
|
74
|
+
|
|
70
75
|
useHeader(t('header.sendToWallet'))
|
|
71
76
|
|
|
72
77
|
const handleInputChange = (e: ChangeEvent) => {
|
|
@@ -241,6 +246,7 @@ export const SendToWalletPage = () => {
|
|
|
241
246
|
ref={confirmAddressSheetRef}
|
|
242
247
|
validatedBookmark={validatedWallet}
|
|
243
248
|
onConfirm={handleOnConfirm}
|
|
249
|
+
chainId={connectedEVMChainId || toChainId}
|
|
244
250
|
/>
|
|
245
251
|
<BookmarkAddressSheet
|
|
246
252
|
ref={bookmarkAddressSheetRef}
|
|
@@ -146,7 +146,7 @@ const ExchangeRateBottomSheetContent: React.FC<
|
|
|
146
146
|
fontWeight: 600,
|
|
147
147
|
}}
|
|
148
148
|
>
|
|
149
|
-
{t('format.
|
|
149
|
+
{t('format.tokenAmount', {
|
|
150
150
|
value: formatTokenAmount(
|
|
151
151
|
BigInt(data.oldToAmount),
|
|
152
152
|
data.toToken.decimals
|
|
@@ -168,7 +168,7 @@ const ExchangeRateBottomSheetContent: React.FC<
|
|
|
168
168
|
fontWeight: 600,
|
|
169
169
|
}}
|
|
170
170
|
>
|
|
171
|
-
{t('format.
|
|
171
|
+
{t('format.tokenAmount', {
|
|
172
172
|
value: formatTokenAmount(
|
|
173
173
|
BigInt(data?.newToAmount),
|
|
174
174
|
data?.toToken.decimals
|
|
@@ -4,11 +4,12 @@ import { useMemo } from 'react'
|
|
|
4
4
|
import { I18nextProvider } from 'react-i18next'
|
|
5
5
|
import * as supportedLanguages from '../../i18n/index.js'
|
|
6
6
|
import { useSettings } from '../../stores/settings/useSettings.js'
|
|
7
|
+
import { compactNumberFormatter } from '../../utils/compactNumberFormatter.js'
|
|
7
8
|
import { deepMerge } from '../../utils/deepMerge.js'
|
|
8
9
|
import { isItemAllowed } from '../../utils/item.js'
|
|
10
|
+
import { percentFormatter } from '../../utils/percentFormatter.js'
|
|
9
11
|
import { useWidgetConfig } from '../WidgetProvider/WidgetProvider.js'
|
|
10
12
|
import { currencyExtendedFormatter } from './currencyExtendedFormatter.js'
|
|
11
|
-
import { percentFormatter } from './percentFormatter.js'
|
|
12
13
|
import type { LanguageKey, LanguageTranslationResources } from './types.js'
|
|
13
14
|
|
|
14
15
|
export const I18nProvider: React.FC<React.PropsWithChildren> = ({
|
|
@@ -63,6 +64,7 @@ export const I18nProvider: React.FC<React.PropsWithChildren> = ({
|
|
|
63
64
|
|
|
64
65
|
i18n.init()
|
|
65
66
|
|
|
67
|
+
i18n.services.formatter?.addCached('numberExt', compactNumberFormatter)
|
|
66
68
|
i18n.services.formatter?.addCached('currencyExt', currencyExtendedFormatter)
|
|
67
69
|
i18n.services.formatter?.addCached('percent', percentFormatter)
|
|
68
70
|
|
|
@@ -18,8 +18,8 @@ export const useExecutingRoutesIds = () => {
|
|
|
18
18
|
)
|
|
19
19
|
.sort(
|
|
20
20
|
(a, b) =>
|
|
21
|
-
(b?.route.steps[0].execution?.process[0]
|
|
22
|
-
(a?.route.steps[0].execution?.process[0]
|
|
21
|
+
(b?.route.steps[0].execution?.process[0]?.startedAt ?? 0) -
|
|
22
|
+
(a?.route.steps[0].execution?.process[0]?.startedAt ?? 0)
|
|
23
23
|
)
|
|
24
24
|
.map(({ route }) => route.id),
|
|
25
25
|
shallow
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { loadingButtonClasses } from '@mui/lab'
|
|
2
|
-
import type {} from '@mui/lab/themeAugmentation'
|
|
3
1
|
import type {
|
|
4
2
|
CSSObject,
|
|
5
3
|
PaletteMode,
|
|
@@ -8,6 +6,7 @@ import type {
|
|
|
8
6
|
} from '@mui/material'
|
|
9
7
|
import {
|
|
10
8
|
alpha,
|
|
9
|
+
buttonClasses,
|
|
11
10
|
createTheme as createMuiTheme,
|
|
12
11
|
css,
|
|
13
12
|
darken,
|
|
@@ -247,19 +246,18 @@ export const createTheme = (
|
|
|
247
246
|
cursor: 'not-allowed',
|
|
248
247
|
pointerEvents: 'auto',
|
|
249
248
|
},
|
|
250
|
-
[`&.${
|
|
249
|
+
[`&.${buttonClasses.loading}.Mui-disabled`]: {
|
|
251
250
|
backgroundColor: primaryMainColor,
|
|
252
251
|
color: contrastButtonColor,
|
|
253
252
|
cursor: 'auto',
|
|
254
253
|
pointerEvents: 'auto',
|
|
255
254
|
},
|
|
256
|
-
[`.${
|
|
255
|
+
[`.${buttonClasses.loadingIndicator}`]: {
|
|
257
256
|
color: contrastButtonColor,
|
|
258
257
|
},
|
|
259
|
-
[`&.${
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
},
|
|
258
|
+
[`&.${buttonClasses.root}.${buttonClasses.loading}`]: {
|
|
259
|
+
color: 'transparent',
|
|
260
|
+
},
|
|
263
261
|
...getStyleOverrides('MuiButton', 'root', widgetTheme, ownerState),
|
|
264
262
|
}),
|
|
265
263
|
text: ({ ownerState }) => ({
|
package/src/themes/types.ts
CHANGED
package/src/types/widget.ts
CHANGED
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
PaletteMode,
|
|
14
14
|
PaletteOptions,
|
|
15
15
|
Shape,
|
|
16
|
+
SxProps,
|
|
16
17
|
Theme,
|
|
17
18
|
} from '@mui/material'
|
|
18
19
|
import type { TypographyOptions } from '@mui/material/styles/createTypography.js'
|
|
@@ -191,6 +192,22 @@ export type WidgetLanguages = {
|
|
|
191
192
|
|
|
192
193
|
export type PoweredByType = 'default' | 'jumper'
|
|
193
194
|
|
|
195
|
+
export interface RouteLabel {
|
|
196
|
+
text: string
|
|
197
|
+
sx?: SxProps<Theme>
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export interface RouteLabelRule {
|
|
201
|
+
label: RouteLabel
|
|
202
|
+
// Matching criteria
|
|
203
|
+
bridges?: AllowDeny<string>
|
|
204
|
+
exchanges?: AllowDeny<string>
|
|
205
|
+
fromChainId?: number[]
|
|
206
|
+
toChainId?: number[]
|
|
207
|
+
fromTokenAddress?: string[]
|
|
208
|
+
toTokenAddress?: string[]
|
|
209
|
+
}
|
|
210
|
+
|
|
194
211
|
export interface WidgetConfig {
|
|
195
212
|
fromChain?: number
|
|
196
213
|
toChain?: number
|
|
@@ -243,6 +260,11 @@ export interface WidgetConfig {
|
|
|
243
260
|
explorerUrls?: Record<number, string[]> &
|
|
244
261
|
Partial<Record<'internal', string[]>>
|
|
245
262
|
poweredBy?: PoweredByType
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Custom labels/badges to show on routes based on specified rules
|
|
266
|
+
*/
|
|
267
|
+
routeLabels?: RouteLabelRule[]
|
|
246
268
|
}
|
|
247
269
|
|
|
248
270
|
export interface FormFieldOptions {
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { compactNumberFormatter } from './compactNumberFormatter.js'
|
|
3
|
+
|
|
4
|
+
describe('Compact Number Formatter', () => {
|
|
5
|
+
describe('basic formatting', () => {
|
|
6
|
+
const testCases = [
|
|
7
|
+
{ input: '0.123456789', expected: '0.123456' },
|
|
8
|
+
{ input: '0.0123456', expected: '0.012345' },
|
|
9
|
+
{ input: '0.00123456', expected: '0.001234' },
|
|
10
|
+
{ input: '0.000123456', expected: '0.000123' },
|
|
11
|
+
{ input: '0.06942', expected: '0.06942' },
|
|
12
|
+
{ input: '0.006942', expected: '0.006942' },
|
|
13
|
+
{ input: '0.0006942', expected: '0.000694' },
|
|
14
|
+
{ input: '0.00006942', expected: '0.0₄6942' },
|
|
15
|
+
{ input: '0.00000123456', expected: '0.0₅1234' },
|
|
16
|
+
{ input: '0.000006942', expected: '0.0₅6942' },
|
|
17
|
+
{ input: '0.00000000001', expected: '0.0₁₀1' },
|
|
18
|
+
{ input: '0.00000006942', expected: '0.0₇6942' },
|
|
19
|
+
{ input: '0.0000000694269', expected: '0.0₇6942' },
|
|
20
|
+
{ input: '0.0000000694069', expected: '0.0₇694' },
|
|
21
|
+
{ input: '0.000000006942', expected: '0.0₈6942' },
|
|
22
|
+
{
|
|
23
|
+
input: '0.000000000000006942',
|
|
24
|
+
expected: '0.0₁₄6942',
|
|
25
|
+
},
|
|
26
|
+
{ input: '-0.0000042', expected: '-0.0₅42' },
|
|
27
|
+
{ input: '-0.000000042', expected: '-0.0₇42' },
|
|
28
|
+
{ input: '1.0000000042', expected: '1.0₈42' },
|
|
29
|
+
{ input: '123456789.00042', expected: '123456789.00042' },
|
|
30
|
+
{
|
|
31
|
+
input: '123456789.000042',
|
|
32
|
+
expected: '123456789.0₄42',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
input: '123456789.0000042',
|
|
36
|
+
expected: '123456789.0₅42',
|
|
37
|
+
},
|
|
38
|
+
{ input: '1234567.000000042', expected: '1234567.0₇42' },
|
|
39
|
+
{
|
|
40
|
+
input: '1234567.000000042',
|
|
41
|
+
expected: '1,234,567.0₇42',
|
|
42
|
+
lng: 'en',
|
|
43
|
+
useGrouping: true,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
input: '1234567.987654321',
|
|
47
|
+
expected: '1,234,567.987654',
|
|
48
|
+
lng: 'en',
|
|
49
|
+
useGrouping: true,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
input: '1234567.000000042',
|
|
53
|
+
expected: '1.234.567,0₇42',
|
|
54
|
+
lng: 'de',
|
|
55
|
+
useGrouping: true,
|
|
56
|
+
},
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
testCases.forEach(({ input, expected, lng, useGrouping = false }) => {
|
|
60
|
+
it(`should format ${input} correctly`, () => {
|
|
61
|
+
expect(compactNumberFormatter(lng, { useGrouping })(input)).toBe(
|
|
62
|
+
expected
|
|
63
|
+
)
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
})
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a non-negative integer to its subscript representation using Unicode subscript characters.
|
|
3
|
+
* @param n - The number to convert to subscript
|
|
4
|
+
* @returns A string where each digit is converted to its subscript equivalent
|
|
5
|
+
* @example
|
|
6
|
+
* toSubscript(5) => '₅'
|
|
7
|
+
* toSubscript(10) => '₁₀'
|
|
8
|
+
*/
|
|
9
|
+
function toSubscript(n: number): string {
|
|
10
|
+
return n
|
|
11
|
+
.toString()
|
|
12
|
+
.split('')
|
|
13
|
+
.map((digit) => String.fromCharCode(8320 + Number(digit)))
|
|
14
|
+
.join('')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Formats numbers with special handling for small decimal values, converting runs of leading zeros
|
|
19
|
+
* into a more compact subscript notation.
|
|
20
|
+
*
|
|
21
|
+
* @param value - The numeric value to format (expected to be a string representing a BigInt)
|
|
22
|
+
* @param lng - The locale string for number formatting
|
|
23
|
+
* @param options - Formatting options including decimals and Intl.NumberFormat options
|
|
24
|
+
* @returns A formatted string with subscript notation for leading zeros
|
|
25
|
+
*/
|
|
26
|
+
export const compactNumberFormatter = (
|
|
27
|
+
lng: string | undefined,
|
|
28
|
+
options: any
|
|
29
|
+
) => {
|
|
30
|
+
const formatter = new Intl.NumberFormat(lng, {
|
|
31
|
+
notation: 'standard',
|
|
32
|
+
roundingPriority: 'morePrecision',
|
|
33
|
+
maximumSignificantDigits: 21,
|
|
34
|
+
maximumFractionDigits: 21,
|
|
35
|
+
...options,
|
|
36
|
+
})
|
|
37
|
+
return (value: any) => {
|
|
38
|
+
if (!Number.parseFloat(value) || Number.isNaN(Number(value))) {
|
|
39
|
+
return '0'
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const parts = formatter.formatToParts(value as Intl.StringNumericLiteral)
|
|
43
|
+
let integerPart = ''
|
|
44
|
+
let fractionPart = ''
|
|
45
|
+
let decimalSeparator = ''
|
|
46
|
+
let minusSign = ''
|
|
47
|
+
|
|
48
|
+
for (const p of parts) {
|
|
49
|
+
switch (p.type) {
|
|
50
|
+
case 'integer':
|
|
51
|
+
case 'group':
|
|
52
|
+
integerPart += p.value
|
|
53
|
+
break
|
|
54
|
+
case 'fraction':
|
|
55
|
+
fractionPart += p.value
|
|
56
|
+
break
|
|
57
|
+
case 'decimal':
|
|
58
|
+
decimalSeparator = p.value
|
|
59
|
+
break
|
|
60
|
+
case 'minusSign':
|
|
61
|
+
minusSign = p.value
|
|
62
|
+
break
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// For numbers with no decimal part, return early
|
|
67
|
+
if (!fractionPart) {
|
|
68
|
+
return `${minusSign}${integerPart}`
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Count consecutive leading zeros in the fraction part
|
|
72
|
+
const match = fractionPart.match(/^0+/)
|
|
73
|
+
const leadingZerosCount = match ? match[0].length : 0
|
|
74
|
+
|
|
75
|
+
// For numbers with few leading zeros (≤ 3), format normally
|
|
76
|
+
// but limit the total length and trim trailing zeros
|
|
77
|
+
if (leadingZerosCount <= 3) {
|
|
78
|
+
fractionPart = fractionPart.slice(0, 6).replace(/0+$/, '')
|
|
79
|
+
return `${minusSign}${integerPart}${decimalSeparator}${fractionPart}`
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// For numbers with many leading zeros (> 3), use subscript notation
|
|
83
|
+
// Format: "0.0₍number_of_zeros₎significant_digits"
|
|
84
|
+
const zerosSubscript = toSubscript(leadingZerosCount)
|
|
85
|
+
const remainder = fractionPart
|
|
86
|
+
.slice(leadingZerosCount, leadingZerosCount + 4)
|
|
87
|
+
.replace(/0+$/, '')
|
|
88
|
+
|
|
89
|
+
return `${minusSign}${integerPart}${decimalSeparator}0${zerosSubscript}${remainder}`
|
|
90
|
+
}
|
|
91
|
+
}
|
package/src/utils/converters.ts
CHANGED
|
@@ -8,8 +8,8 @@ import type {
|
|
|
8
8
|
TokenAmount,
|
|
9
9
|
ToolsResponse,
|
|
10
10
|
} from '@lifi/sdk'
|
|
11
|
-
import { formatUnits } from 'viem'
|
|
12
11
|
import type { RouteExecution } from '../stores/routes/types.js'
|
|
12
|
+
import { formatTokenPrice } from './format.js'
|
|
13
13
|
|
|
14
14
|
const buildProcessFromTxHistory = (tx: FullStatusData): Process[] => {
|
|
15
15
|
const sending = tx.sending as ExtendedTransactionInfo
|
|
@@ -105,9 +105,11 @@ export const buildRouteFromTxHistory = (
|
|
|
105
105
|
: sendingValue
|
|
106
106
|
const sendingFeeAmountUsd =
|
|
107
107
|
sending.gasToken.priceUSD && sendingFeeAmount
|
|
108
|
-
?
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
? formatTokenPrice(
|
|
109
|
+
sendingFeeAmount,
|
|
110
|
+
sending.gasToken.priceUSD,
|
|
111
|
+
sending.gasToken.decimals
|
|
112
|
+
)
|
|
111
113
|
: 0
|
|
112
114
|
|
|
113
115
|
const feeCosts: FeeCost[] | undefined = sendingFeeAmount
|
package/src/utils/fees.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FeeCost, GasCost, RouteExtended, Token } from '@lifi/sdk'
|
|
2
|
-
import {
|
|
2
|
+
import { formatTokenPrice } from './format.js'
|
|
3
3
|
|
|
4
4
|
export interface FeesBreakdown {
|
|
5
5
|
amount: bigint
|
|
@@ -109,9 +109,11 @@ export const getStepFeeCostsBreakdown = (
|
|
|
109
109
|
const { amount, amountUSD } = feeCosts.reduce(
|
|
110
110
|
(acc, feeCost) => {
|
|
111
111
|
const feeAmount = BigInt(Number(feeCost.amount).toFixed(0) || 0)
|
|
112
|
-
const amountUSD =
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
const amountUSD = formatTokenPrice(
|
|
113
|
+
feeAmount,
|
|
114
|
+
feeCost.token.priceUSD,
|
|
115
|
+
feeCost.token.decimals
|
|
116
|
+
)
|
|
115
117
|
|
|
116
118
|
acc.amount += feeAmount
|
|
117
119
|
acc.amountUSD += amountUSD
|