@lifi/widget 4.8.0 → 4.9.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/dist/esm/components/AmountInputCard/AmountInputCardPair.js +5 -2
- package/dist/esm/components/AmountInputCard/AmountInputCardPair.js.map +1 -1
- package/dist/esm/components/SelectChainAndToken.js +5 -2
- package/dist/esm/components/SelectChainAndToken.js.map +1 -1
- package/dist/esm/components/SendToWallet/SendToWalletButton.js +5 -4
- package/dist/esm/components/SendToWallet/SendToWalletButton.js.map +1 -1
- package/dist/esm/components/TokenList/useTokenSelect.d.ts.map +1 -1
- package/dist/esm/components/TokenList/useTokenSelect.js +10 -0
- package/dist/esm/components/TokenList/useTokenSelect.js.map +1 -1
- package/dist/esm/config/constants.js.map +1 -1
- package/dist/esm/config/version.d.ts +1 -1
- package/dist/esm/config/version.js +1 -1
- package/dist/esm/config/version.js.map +1 -1
- package/dist/esm/hooks/useAccountsBalancesData.js +5 -2
- package/dist/esm/hooks/useAccountsBalancesData.js.map +1 -1
- package/dist/esm/hooks/useAddressValidation.d.ts +3 -2
- package/dist/esm/hooks/useAddressValidation.d.ts.map +1 -1
- package/dist/esm/hooks/useAddressValidation.js +48 -21
- package/dist/esm/hooks/useAddressValidation.js.map +1 -1
- package/dist/esm/hooks/useGasRefuel.js +4 -1
- package/dist/esm/hooks/useGasRefuel.js.map +1 -1
- package/dist/esm/hooks/useRoutes.d.ts.map +1 -1
- package/dist/esm/hooks/useRoutes.js +11 -6
- package/dist/esm/hooks/useRoutes.js.map +1 -1
- package/dist/esm/hooks/useToAddressAutoPopulate.js +6 -8
- package/dist/esm/hooks/useToAddressAutoPopulate.js.map +1 -1
- package/dist/esm/hooks/useToAddressRequirements.d.ts +3 -2
- package/dist/esm/hooks/useToAddressRequirements.d.ts.map +1 -1
- package/dist/esm/hooks/useToAddressRequirements.js +8 -7
- package/dist/esm/hooks/useToAddressRequirements.js.map +1 -1
- package/dist/esm/hooks/useToAddressReset.js +6 -3
- package/dist/esm/hooks/useToAddressReset.js.map +1 -1
- package/dist/esm/hooks/useTokenBalance.js +2 -1
- package/dist/esm/hooks/useTokenBalance.js.map +1 -1
- package/dist/esm/hooks/useTokens.js +1 -1
- package/dist/esm/i18n/bn.json +2 -1
- package/dist/esm/i18n/de.json +2 -1
- package/dist/esm/i18n/en.json +2 -1
- package/dist/esm/i18n/es.json +2 -1
- package/dist/esm/i18n/fr.json +2 -1
- package/dist/esm/i18n/hi.json +2 -1
- package/dist/esm/i18n/id.json +2 -1
- package/dist/esm/i18n/it.json +2 -1
- package/dist/esm/i18n/ja.json +2 -1
- package/dist/esm/i18n/ko.json +2 -1
- package/dist/esm/i18n/pl.json +2 -1
- package/dist/esm/i18n/pt.json +2 -1
- package/dist/esm/i18n/th.json +2 -1
- package/dist/esm/i18n/tr.json +2 -1
- package/dist/esm/i18n/uk.json +2 -1
- package/dist/esm/i18n/vi.json +2 -1
- package/dist/esm/i18n/zh.json +2 -1
- package/dist/esm/pages/SendToWallet/BookmarkAddressSheet.js +7 -4
- package/dist/esm/pages/SendToWallet/BookmarkAddressSheet.js.map +1 -1
- package/dist/esm/pages/SendToWallet/BookmarksPage.js +35 -31
- package/dist/esm/pages/SendToWallet/BookmarksPage.js.map +1 -1
- package/dist/esm/pages/SendToWallet/ConnectedWalletsPage.js +4 -3
- package/dist/esm/pages/SendToWallet/ConnectedWalletsPage.js.map +1 -1
- package/dist/esm/pages/SendToWallet/RecentWalletsPage.js +43 -39
- package/dist/esm/pages/SendToWallet/RecentWalletsPage.js.map +1 -1
- package/dist/esm/pages/SendToWallet/SendToConfiguredWalletPage.js +32 -26
- package/dist/esm/pages/SendToWallet/SendToConfiguredWalletPage.js.map +1 -1
- package/dist/esm/pages/SendToWallet/SendToWalletPage.js +5 -10
- package/dist/esm/pages/SendToWallet/SendToWalletPage.js.map +1 -1
- package/dist/esm/providers/WalletProvider/WalletProvider.d.ts.map +1 -1
- package/dist/esm/providers/WalletProvider/WalletProvider.js +14 -2
- package/dist/esm/providers/WalletProvider/WalletProvider.js.map +1 -1
- package/dist/esm/providers/WidgetProvider/WidgetProvider.d.ts.map +1 -1
- package/dist/esm/providers/WidgetProvider/WidgetProvider.js +10 -14
- package/dist/esm/providers/WidgetProvider/WidgetProvider.js.map +1 -1
- package/dist/esm/stores/bookmarks/types.d.ts +3 -0
- package/dist/esm/stores/bookmarks/types.d.ts.map +1 -1
- package/dist/esm/stores/chains/ChainOrderStore.d.ts.map +1 -1
- package/dist/esm/stores/chains/ChainOrderStore.js +4 -1
- package/dist/esm/stores/chains/ChainOrderStore.js.map +1 -1
- package/dist/esm/stores/form/FormStore.js +3 -2
- package/dist/esm/stores/form/FormStore.js.map +1 -1
- package/dist/esm/stores/form/URLSearchParamsBuilder.js +7 -3
- package/dist/esm/stores/form/URLSearchParamsBuilder.js.map +1 -1
- package/dist/esm/stores/form/getDefaultValuesFromQueryString.js +4 -1
- package/dist/esm/stores/form/getDefaultValuesFromQueryString.js.map +1 -1
- package/dist/esm/utils/chainType.js +32 -1
- package/dist/esm/utils/chainType.js.map +1 -1
- package/dist/esm/utils/customReceiver.js +6 -1
- package/dist/esm/utils/customReceiver.js.map +1 -1
- package/package.json +7 -7
- package/src/components/AmountInputCard/AmountInputCardPair.tsx +6 -2
- package/src/components/SelectChainAndToken.tsx +5 -1
- package/src/components/SendToWallet/SendToWalletButton.tsx +13 -8
- package/src/components/TokenList/useTokenSelect.ts +15 -0
- package/src/config/constants.ts +3 -0
- package/src/config/version.ts +1 -1
- package/src/hooks/useAccountsBalancesData.ts +6 -2
- package/src/hooks/useAddressValidation.ts +87 -32
- package/src/hooks/useGasRefuel.ts +11 -1
- package/src/hooks/useRoutes.ts +23 -18
- package/src/hooks/useToAddressAutoPopulate.ts +13 -17
- package/src/hooks/useToAddressRequirements.ts +19 -4
- package/src/hooks/useToAddressReset.ts +13 -4
- package/src/hooks/useTokenBalance.ts +6 -1
- package/src/pages/SendToWallet/BookmarkAddressSheet.tsx +5 -1
- package/src/pages/SendToWallet/BookmarksPage.tsx +42 -52
- package/src/pages/SendToWallet/ConnectedWalletsPage.tsx +4 -14
- package/src/pages/SendToWallet/RecentWalletsPage.tsx +59 -66
- package/src/pages/SendToWallet/SendToConfiguredWalletPage.tsx +45 -50
- package/src/pages/SendToWallet/SendToWalletPage.tsx +4 -16
- package/src/providers/WalletProvider/WalletProvider.tsx +28 -3
- package/src/providers/WidgetProvider/WidgetProvider.tsx +20 -19
- package/src/stores/bookmarks/types.ts +3 -0
- package/src/stores/chains/ChainOrderStore.tsx +4 -1
- package/src/stores/form/FormStore.tsx +2 -0
- package/src/stores/form/URLSearchParamsBuilder.tsx +4 -2
- package/src/stores/form/getDefaultValuesFromQueryString.ts +19 -0
- package/src/utils/chainType.ts +73 -1
- package/src/utils/customReceiver.ts +36 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lifi/widget",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.9.0",
|
|
4
4
|
"description": "LI.FI Widget for cross-chain bridging and swapping. It will drive your multi-chain strategy and attract new users from everywhere.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/esm/index.js",
|
|
@@ -57,19 +57,19 @@
|
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"@emotion/react": "^11.14.0",
|
|
59
59
|
"@emotion/styled": "^11.14.1",
|
|
60
|
-
"@lifi/sdk": "^4.
|
|
61
|
-
"@lifi/wallet-management": "4.3.
|
|
62
|
-
"@lifi/widget-provider": "4.
|
|
60
|
+
"@lifi/sdk": "^4.11.0",
|
|
61
|
+
"@lifi/wallet-management": "4.3.2",
|
|
62
|
+
"@lifi/widget-provider": "4.6.0",
|
|
63
63
|
"@mui/icons-material": "^9.4.0",
|
|
64
64
|
"@mui/material": "^9.4.0",
|
|
65
65
|
"@mui/system": "^9.4.0",
|
|
66
|
-
"@tanstack/react-router": "^1.170.
|
|
66
|
+
"@tanstack/react-router": "^1.170.41",
|
|
67
67
|
"@tanstack/react-virtual": "^3.14.13",
|
|
68
68
|
"eventemitter3": "^5.0.4",
|
|
69
69
|
"i18next": "^26.4.2",
|
|
70
70
|
"microdiff": "^1.6.0",
|
|
71
|
-
"motion": "^
|
|
72
|
-
"react-i18next": "^17.0.
|
|
71
|
+
"motion": "^14.0.0",
|
|
72
|
+
"react-i18next": "^17.0.16",
|
|
73
73
|
"react-intersection-observer": "^11.0.1",
|
|
74
74
|
"react-transition-group": "^4.4.5",
|
|
75
75
|
"zustand": "^5.0.15"
|
|
@@ -2,6 +2,8 @@ import type { BoxProps } from '@mui/material'
|
|
|
2
2
|
import type React from 'react'
|
|
3
3
|
import type { JSX } from 'react'
|
|
4
4
|
import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
|
|
5
|
+
import { useFieldValues } from '../../stores/form/useFieldValues.js'
|
|
6
|
+
import { isDestinationAllowedAsSource } from '../../utils/chainType.js'
|
|
5
7
|
import { LimitPriceCard } from '../LimitPriceCard/LimitPriceCard.js'
|
|
6
8
|
import { SwapButton } from '../SwapButton/SwapButton.js'
|
|
7
9
|
import { CardContainer } from './AmountInputCard.style.js'
|
|
@@ -11,8 +13,10 @@ import { SendAmountCard } from './SendAmountCard.js'
|
|
|
11
13
|
export const AmountInputCardPair: React.FC<BoxProps> = (props): JSX.Element => {
|
|
12
14
|
// Refuel's destination is fixed to native gas, so reversing from/to is
|
|
13
15
|
// meaningless — hide the swap button (and the notch mask it sits in).
|
|
14
|
-
const { mode } = useWidgetConfig()
|
|
15
|
-
const
|
|
16
|
+
const { mode, chains } = useWidgetConfig()
|
|
17
|
+
const [toChainId] = useFieldValues('toChain')
|
|
18
|
+
const showSwapButton =
|
|
19
|
+
mode !== 'refuel' && isDestinationAllowedAsSource(toChainId, chains)
|
|
16
20
|
|
|
17
21
|
return (
|
|
18
22
|
<CardContainer {...props}>
|
|
@@ -3,13 +3,17 @@ import { Box } from '@mui/material'
|
|
|
3
3
|
import { ReverseTokensButton } from '../components/ReverseTokensButton/ReverseTokensButton.js'
|
|
4
4
|
import { SelectTokenButton } from '../components/SelectTokenButton/SelectTokenButton.js'
|
|
5
5
|
import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
|
|
6
|
+
import { useFieldValues } from '../stores/form/useFieldValues.js'
|
|
7
|
+
import { isDestinationAllowedAsSource } from '../utils/chainType.js'
|
|
6
8
|
import { ReverseTokensButtonEmpty } from './ReverseTokensButton/ReverseTokensButton.style.js'
|
|
7
9
|
|
|
8
10
|
export const SelectChainAndToken: React.FC<BoxProps> = (props) => {
|
|
9
|
-
const { disabledUI, hiddenUI, mode } = useWidgetConfig()
|
|
11
|
+
const { disabledUI, hiddenUI, mode, chains } = useWidgetConfig()
|
|
12
|
+
const [toChainId] = useFieldValues('toChain')
|
|
10
13
|
|
|
11
14
|
const hiddenReverse =
|
|
12
15
|
mode === 'refuel' ||
|
|
16
|
+
!isDestinationAllowedAsSource(toChainId, chains) ||
|
|
13
17
|
disabledUI?.fromToken ||
|
|
14
18
|
disabledUI?.toToken ||
|
|
15
19
|
hiddenUI?.fromToken ||
|
|
@@ -50,7 +50,7 @@ export const SendToWalletButton: React.FC<
|
|
|
50
50
|
const { selectedBookmark } = useBookmarks()
|
|
51
51
|
const { setSelectedBookmark } = useBookmarkActions()
|
|
52
52
|
const { accounts } = useAccount()
|
|
53
|
-
const {
|
|
53
|
+
const { getChainFromAddress } = useChainTypeFromAddress()
|
|
54
54
|
const { requiredToAddress } = useToAddressRequirements()
|
|
55
55
|
const disabledToAddress = disabledUI?.toAddress
|
|
56
56
|
const hiddenToAddress = hiddenUI?.toAddress
|
|
@@ -65,11 +65,16 @@ export const SendToWalletButton: React.FC<
|
|
|
65
65
|
(account) => account.address === toAddressFieldValue
|
|
66
66
|
)
|
|
67
67
|
|
|
68
|
+
// A bookmark from the config or the URL may carry no chainId.
|
|
69
|
+
const detectedChain =
|
|
70
|
+
!matchingConnectedAccount &&
|
|
71
|
+
selectedBookmark?.chainId === undefined &&
|
|
72
|
+
toAddressFieldValue
|
|
73
|
+
? getChainFromAddress(toAddressFieldValue)
|
|
74
|
+
: undefined
|
|
75
|
+
|
|
68
76
|
const chainType = !matchingConnectedAccount
|
|
69
|
-
? selectedBookmark?.chainType ||
|
|
70
|
-
(toAddressFieldValue
|
|
71
|
-
? getChainTypeFromAddress(toAddressFieldValue)
|
|
72
|
-
: undefined)
|
|
77
|
+
? selectedBookmark?.chainType || detectedChain?.chainType
|
|
73
78
|
: undefined
|
|
74
79
|
|
|
75
80
|
const chainId =
|
|
@@ -77,9 +82,9 @@ export const SendToWalletButton: React.FC<
|
|
|
77
82
|
? toChainId
|
|
78
83
|
: matchingConnectedAccount
|
|
79
84
|
? matchingConnectedAccount.chainId
|
|
80
|
-
:
|
|
81
|
-
|
|
82
|
-
: undefined
|
|
85
|
+
: (selectedBookmark?.chainId ??
|
|
86
|
+
detectedChain?.chainId ??
|
|
87
|
+
(chainType ? defaultChainIdsByType[chainType] : undefined))
|
|
83
88
|
|
|
84
89
|
const isConnectedAccount =
|
|
85
90
|
selectedBookmark?.isConnectedAccount &&
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { useCallback } from 'react'
|
|
2
|
+
import { useAvailableChains } from '../../hooks/useAvailableChains.js'
|
|
2
3
|
import { useToAddressAutoPopulate } from '../../hooks/useToAddressAutoPopulate.js'
|
|
4
|
+
import { useToAddressReset } from '../../hooks/useToAddressReset.js'
|
|
3
5
|
import { useWidgetEvents } from '../../hooks/useWidgetEvents.js'
|
|
4
6
|
import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
|
|
5
7
|
import { useChainOrderStore } from '../../stores/chains/ChainOrderStore.js'
|
|
@@ -20,6 +22,8 @@ export const useTokenSelect = (
|
|
|
20
22
|
const emitter = useWidgetEvents()
|
|
21
23
|
const { setFieldValue, getFieldValues } = useFieldActions()
|
|
22
24
|
const autoPopulateToAddress = useToAddressAutoPopulate()
|
|
25
|
+
const { tryResetToAddress } = useToAddressReset()
|
|
26
|
+
const { getChainById } = useAvailableChains()
|
|
23
27
|
const [setChain, setIsAllNetworks] = useChainOrderStore((state) => [
|
|
24
28
|
state.setChain,
|
|
25
29
|
state.setIsAllNetworks,
|
|
@@ -29,6 +33,7 @@ export const useTokenSelect = (
|
|
|
29
33
|
|
|
30
34
|
return useCallback(
|
|
31
35
|
(tokenAddress: string, chainId?: number) => {
|
|
36
|
+
const [toChainIdBefore] = getFieldValues('toChain')
|
|
32
37
|
setFieldValue(tokenKey, tokenAddress, { isDirty: true, isTouched: true })
|
|
33
38
|
const selectedChainId =
|
|
34
39
|
chainId ?? getFieldValues(FormKeyHelper.getChainKey(formType))[0]
|
|
@@ -93,6 +98,14 @@ export const useTokenSelect = (
|
|
|
93
98
|
setChain(selectedChainId, oppositeFormType)
|
|
94
99
|
}
|
|
95
100
|
|
|
101
|
+
// A token from "All networks" moves the destination without the chain select,
|
|
102
|
+
// which resets a receiver the new chain cannot take.
|
|
103
|
+
const [toChainId] = getFieldValues('toChain')
|
|
104
|
+
const toChain = getChainById(toChainId)
|
|
105
|
+
if (toChainId !== toChainIdBefore && toChain) {
|
|
106
|
+
tryResetToAddress(toChain)
|
|
107
|
+
}
|
|
108
|
+
|
|
96
109
|
// Automatically populate toAddress field if bridging across ecosystems and compatible wallet is connected
|
|
97
110
|
autoPopulateToAddress({
|
|
98
111
|
formType,
|
|
@@ -118,6 +131,8 @@ export const useTokenSelect = (
|
|
|
118
131
|
},
|
|
119
132
|
[
|
|
120
133
|
autoPopulateToAddress,
|
|
134
|
+
tryResetToAddress,
|
|
135
|
+
getChainById,
|
|
121
136
|
disabledUI,
|
|
122
137
|
emitter,
|
|
123
138
|
formType,
|
package/src/config/constants.ts
CHANGED
package/src/config/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export const name = '@lifi/widget'
|
|
2
|
-
export const version = '4.
|
|
2
|
+
export const version = '4.9.0'
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { TokenExtended } from '@lifi/sdk'
|
|
2
2
|
import { useAccount } from '@lifi/wallet-management'
|
|
3
|
+
import { useAddressForChain } from '@lifi/widget-provider'
|
|
3
4
|
import { useMemo } from 'react'
|
|
4
5
|
import type { FormType } from '../stores/form/types.js'
|
|
5
6
|
import { useChains } from './useChains.js'
|
|
@@ -30,6 +31,7 @@ export const useAccountsBalancesData = (
|
|
|
30
31
|
const { accounts: allAccounts, account: currentAccount } = useAccount(
|
|
31
32
|
isAllNetworks ? undefined : { chainType: currentChain?.chainType }
|
|
32
33
|
)
|
|
34
|
+
const { isAddressForChain } = useAddressForChain()
|
|
33
35
|
const accounts = useMemo(() => {
|
|
34
36
|
return isAllNetworks
|
|
35
37
|
? allAccounts
|
|
@@ -48,7 +50,9 @@ export const useAccountsBalancesData = (
|
|
|
48
50
|
(acc, account) => {
|
|
49
51
|
if (account.address) {
|
|
50
52
|
const accountChains = chains?.filter(
|
|
51
|
-
(chain) =>
|
|
53
|
+
(chain) =>
|
|
54
|
+
account.chainType === chain?.chainType &&
|
|
55
|
+
isAddressForChain(account.address as string, chain)
|
|
52
56
|
)
|
|
53
57
|
if (accountChains) {
|
|
54
58
|
const chainIdSet = new Set(accountChains.map((chain) => chain.id))
|
|
@@ -68,7 +72,7 @@ export const useAccountsBalancesData = (
|
|
|
68
72
|
},
|
|
69
73
|
{} as Record<string, Record<number, TokenExtended[]>>
|
|
70
74
|
)
|
|
71
|
-
}, [accounts, chains, allTokens])
|
|
75
|
+
}, [accounts, chains, allTokens, isAddressForChain])
|
|
72
76
|
|
|
73
77
|
return {
|
|
74
78
|
data: accountsWithTokens,
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { Chain, ChainType } from '@lifi/sdk'
|
|
2
|
-
import { getNameServiceAddress } from '@lifi/sdk'
|
|
3
|
-
import {
|
|
2
|
+
import { ChainId, getNameServiceAddress } from '@lifi/sdk'
|
|
3
|
+
import {
|
|
4
|
+
useAddressForChain,
|
|
5
|
+
useChainTypeFromAddress,
|
|
6
|
+
} from '@lifi/widget-provider'
|
|
4
7
|
import { useMutation } from '@tanstack/react-query'
|
|
5
8
|
import { useTranslation } from 'react-i18next'
|
|
6
9
|
import { useSDKClient } from '../providers/SDKClientProvider.js'
|
|
@@ -12,7 +15,7 @@ export enum AddressType {
|
|
|
12
15
|
|
|
13
16
|
type ValidationArgs = {
|
|
14
17
|
value: string
|
|
15
|
-
|
|
18
|
+
/** Strict: the receiver must be valid on this chain. */
|
|
16
19
|
chain?: Chain
|
|
17
20
|
}
|
|
18
21
|
|
|
@@ -20,6 +23,7 @@ type ValidResponse = {
|
|
|
20
23
|
address: string
|
|
21
24
|
addressType: AddressType
|
|
22
25
|
chainType: ChainType
|
|
26
|
+
chainId?: ChainId
|
|
23
27
|
isValid: true
|
|
24
28
|
}
|
|
25
29
|
|
|
@@ -35,49 +39,100 @@ export const useAddressValidation = (): {
|
|
|
35
39
|
isValidating: boolean
|
|
36
40
|
} => {
|
|
37
41
|
const { t } = useTranslation()
|
|
38
|
-
const {
|
|
42
|
+
const { getChainFromAddress } = useChainTypeFromAddress()
|
|
43
|
+
const { isAddressForChain } = useAddressForChain()
|
|
39
44
|
const sdkClient = useSDKClient()
|
|
40
45
|
|
|
46
|
+
const validFor = (
|
|
47
|
+
address: string,
|
|
48
|
+
addressType: AddressType,
|
|
49
|
+
chain: Chain
|
|
50
|
+
): ValidResponse => ({
|
|
51
|
+
address,
|
|
52
|
+
addressType,
|
|
53
|
+
chainType: chain.chainType,
|
|
54
|
+
chainId: getChainFromAddress(address)?.chainId,
|
|
55
|
+
isValid: true,
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
const validateForChain = async (
|
|
59
|
+
value: string,
|
|
60
|
+
chain: Chain
|
|
61
|
+
): Promise<ValidResponse | InvalidResponse> => {
|
|
62
|
+
if (isAddressForChain(value, chain)) {
|
|
63
|
+
return validFor(value, AddressType.Address, chain)
|
|
64
|
+
}
|
|
65
|
+
// A recognised address is no name to resolve.
|
|
66
|
+
if (getChainFromAddress(value)) {
|
|
67
|
+
return {
|
|
68
|
+
isValid: false,
|
|
69
|
+
error: t('error.title.walletChainTypeInvalid', {
|
|
70
|
+
chainName: chain.name,
|
|
71
|
+
}),
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const resolved = await getNameServiceAddress(
|
|
75
|
+
sdkClient,
|
|
76
|
+
value,
|
|
77
|
+
chain.chainType
|
|
78
|
+
)
|
|
79
|
+
if (resolved && isAddressForChain(resolved, chain)) {
|
|
80
|
+
return validFor(resolved, AddressType.NameService, chain)
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
isValid: false,
|
|
84
|
+
error:
|
|
85
|
+
chain.id === ChainId.ZEC
|
|
86
|
+
? t('error.title.zcashAddressInvalid')
|
|
87
|
+
: t('error.title.walletAddressInvalid', {
|
|
88
|
+
context: 'chain',
|
|
89
|
+
chainName: chain.name,
|
|
90
|
+
}),
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const validateWithoutChain = async (
|
|
95
|
+
value: string
|
|
96
|
+
): Promise<ValidResponse | undefined> => {
|
|
97
|
+
const detected = getChainFromAddress(value)
|
|
98
|
+
if (detected) {
|
|
99
|
+
return {
|
|
100
|
+
address: value,
|
|
101
|
+
addressType: AddressType.Address,
|
|
102
|
+
...detected,
|
|
103
|
+
isValid: true,
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const address = await getNameServiceAddress(sdkClient, value)
|
|
107
|
+
const resolved = address ? getChainFromAddress(address) : undefined
|
|
108
|
+
if (address && resolved) {
|
|
109
|
+
return {
|
|
110
|
+
address,
|
|
111
|
+
addressType: AddressType.NameService,
|
|
112
|
+
...resolved,
|
|
113
|
+
isValid: true,
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return undefined
|
|
117
|
+
}
|
|
118
|
+
|
|
41
119
|
const { mutateAsync: validateAddress, isPending: isValidating } = useMutation(
|
|
42
120
|
{
|
|
43
121
|
mutationFn: async ({
|
|
44
122
|
value,
|
|
45
|
-
chainType,
|
|
46
123
|
chain,
|
|
47
124
|
}: ValidationArgs): Promise<ValidResponse | InvalidResponse> => {
|
|
48
125
|
try {
|
|
49
126
|
if (!value) {
|
|
50
127
|
throw new Error()
|
|
51
128
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (_chainType) {
|
|
55
|
-
return {
|
|
56
|
-
address: value,
|
|
57
|
-
addressType: AddressType.Address,
|
|
58
|
-
chainType: _chainType,
|
|
59
|
-
isValid: true,
|
|
60
|
-
}
|
|
129
|
+
if (chain) {
|
|
130
|
+
return await validateForChain(value, chain)
|
|
61
131
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
value,
|
|
66
|
-
chainType
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
if (address) {
|
|
70
|
-
const _chainType = getChainTypeFromAddress(address)
|
|
71
|
-
if (_chainType) {
|
|
72
|
-
return {
|
|
73
|
-
address: address,
|
|
74
|
-
addressType: AddressType.NameService,
|
|
75
|
-
chainType: _chainType,
|
|
76
|
-
isValid: true,
|
|
77
|
-
}
|
|
78
|
-
}
|
|
132
|
+
const result = await validateWithoutChain(value)
|
|
133
|
+
if (result) {
|
|
134
|
+
return result
|
|
79
135
|
}
|
|
80
|
-
|
|
81
136
|
throw new Error()
|
|
82
137
|
} catch (_) {
|
|
83
138
|
return {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ExtendedChain } from '@lifi/sdk'
|
|
2
2
|
import { useAccount } from '@lifi/wallet-management'
|
|
3
|
+
import { useAddressForChain } from '@lifi/widget-provider'
|
|
3
4
|
import { useMemo } from 'react'
|
|
4
5
|
import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
|
|
5
6
|
import { useFieldValues } from '../stores/form/useFieldValues.js'
|
|
@@ -28,8 +29,17 @@ export const useGasRefuel = (): {
|
|
|
28
29
|
const fromChain = getChainById(fromChainId)
|
|
29
30
|
|
|
30
31
|
const { account: toAccount } = useAccount({ chainType: toChain?.chainType })
|
|
32
|
+
const { isAddressForChain } = useAddressForChain()
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
// A shared chain type is not a shared format: a Bitcoin account cannot receive on ZEC.
|
|
35
|
+
const toAccountAddress =
|
|
36
|
+
toAccount?.address &&
|
|
37
|
+
toChain &&
|
|
38
|
+
isAddressForChain(toAccount.address, toChain)
|
|
39
|
+
? toAccount.address
|
|
40
|
+
: undefined
|
|
41
|
+
|
|
42
|
+
const effectiveToAddress = toAddress || toAccountAddress
|
|
33
43
|
|
|
34
44
|
const { isContractAddress: isToContractAddress } = useIsContractAddress(
|
|
35
45
|
effectiveToAddress,
|
package/src/hooks/useRoutes.ts
CHANGED
|
@@ -9,10 +9,7 @@ import {
|
|
|
9
9
|
parseUnits,
|
|
10
10
|
} from '@lifi/sdk'
|
|
11
11
|
import { useAccount } from '@lifi/wallet-management'
|
|
12
|
-
import {
|
|
13
|
-
useChainTypeFromAddress,
|
|
14
|
-
useEthereumContext,
|
|
15
|
-
} from '@lifi/widget-provider'
|
|
12
|
+
import { useAddressForChain, useEthereumContext } from '@lifi/widget-provider'
|
|
16
13
|
import {
|
|
17
14
|
keepPreviousData,
|
|
18
15
|
useQuery,
|
|
@@ -29,7 +26,11 @@ import { defaultSlippage } from '../stores/settings/createSettingsStore.js'
|
|
|
29
26
|
import { useSettings } from '../stores/settings/useSettings.js'
|
|
30
27
|
import { WidgetEvent } from '../types/events.js'
|
|
31
28
|
import type { TokensByChain } from '../types/token.js'
|
|
32
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
canQuoteWithToAddress,
|
|
31
|
+
isCustomReceiverBlocked,
|
|
32
|
+
isSignerInvalidReceiver,
|
|
33
|
+
} from '../utils/customReceiver.js'
|
|
33
34
|
import { getQueryKey } from '../utils/queries.js'
|
|
34
35
|
import { updateTokenInCache } from '../utils/token.js'
|
|
35
36
|
import { useChain } from './useChain.js'
|
|
@@ -138,7 +139,7 @@ export const useRoutes = ({
|
|
|
138
139
|
const { chain: toChain } = useChain(toChainId)
|
|
139
140
|
const { enabled: enabledRefuel, fromAmount: gasRecommendationFromAmount } =
|
|
140
141
|
useGasRefuel()
|
|
141
|
-
const {
|
|
142
|
+
const { isAddressForChain } = useAddressForChain()
|
|
142
143
|
const { isGaslessStep, disableMessageSigning } = useEthereumContext()
|
|
143
144
|
const { account } = useAccount({ chainType: fromChain?.chainType })
|
|
144
145
|
const { isBatchingSupported, isBatchingSupportedLoading } =
|
|
@@ -165,18 +166,22 @@ export const useRoutes = ({
|
|
|
165
166
|
? Boolean(account.address)
|
|
166
167
|
: mode !== 'custom' || customType === 'deposit'
|
|
167
168
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
169
|
+
// Quote as if disconnected while the signer would be taken as the receiver.
|
|
170
|
+
const effectiveFromAddress = isSignerInvalidReceiver({
|
|
171
|
+
signerAddress: account.address,
|
|
172
|
+
toAddress,
|
|
173
|
+
fromChain,
|
|
174
|
+
toChain,
|
|
175
|
+
isAddressForChain,
|
|
176
|
+
})
|
|
177
|
+
? quoteFromAddress
|
|
178
|
+
: (account.address ?? quoteFromAddress)
|
|
179
|
+
|
|
180
|
+
const isToAddressSatisfied = canQuoteWithToAddress(
|
|
181
|
+
toAddress,
|
|
182
|
+
toChain,
|
|
183
|
+
isAddressForChain
|
|
184
|
+
)
|
|
180
185
|
|
|
181
186
|
// Not `effectiveFromAddress`: a quote placeholder must never match a receiver.
|
|
182
187
|
const customReceiverBlocked = isCustomReceiverBlocked({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useAccount } from '@lifi/wallet-management'
|
|
2
|
-
import {
|
|
2
|
+
import { useAddressForChain } from '@lifi/widget-provider'
|
|
3
3
|
import { useCallback } from 'react'
|
|
4
4
|
import { useBookmarkActions } from '../stores/bookmarks/useBookmarkActions.js'
|
|
5
5
|
import type { FormType } from '../stores/form/types.js'
|
|
@@ -24,7 +24,7 @@ export const useToAddressAutoPopulate = (): ((
|
|
|
24
24
|
const { setSelectedBookmark } = useBookmarkActions()
|
|
25
25
|
const { getChainById } = useAvailableChains()
|
|
26
26
|
const { accounts } = useAccount()
|
|
27
|
-
const {
|
|
27
|
+
const { isAddressForChain } = useAddressForChain()
|
|
28
28
|
|
|
29
29
|
return useCallback(
|
|
30
30
|
({
|
|
@@ -52,22 +52,18 @@ export const useToAddressAutoPopulate = (): ((
|
|
|
52
52
|
) {
|
|
53
53
|
return
|
|
54
54
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const selectedToAddressChainType =
|
|
63
|
-
getChainTypeFromAddress(selectedToAddress)
|
|
64
|
-
if (destinationChainType === selectedToAddressChainType) {
|
|
65
|
-
return
|
|
66
|
-
}
|
|
55
|
+
const destinationChain =
|
|
56
|
+
formType === 'from' ? selectedOppositeChain : selectedChain
|
|
57
|
+
if (
|
|
58
|
+
selectedToAddress &&
|
|
59
|
+
isAddressForChain(selectedToAddress, destinationChain)
|
|
60
|
+
) {
|
|
61
|
+
return
|
|
67
62
|
}
|
|
68
|
-
// Find connected account compatible with the destination chain type
|
|
69
63
|
const destinationAccount = accounts?.find(
|
|
70
|
-
(account) =>
|
|
64
|
+
(account) =>
|
|
65
|
+
account.address &&
|
|
66
|
+
isAddressForChain(account.address, destinationChain)
|
|
71
67
|
)
|
|
72
68
|
// If a compatible destination account is found, set toAddress as if selecting it from the "Send to Wallet" connected wallets page
|
|
73
69
|
if (destinationAccount?.address) {
|
|
@@ -89,7 +85,7 @@ export const useToAddressAutoPopulate = (): ((
|
|
|
89
85
|
getChainById,
|
|
90
86
|
setFieldValue,
|
|
91
87
|
setSelectedBookmark,
|
|
92
|
-
|
|
88
|
+
isAddressForChain,
|
|
93
89
|
]
|
|
94
90
|
)
|
|
95
91
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RouteExtended } from '@lifi/sdk'
|
|
2
2
|
import { useAccount } from '@lifi/wallet-management'
|
|
3
|
-
import { useEthereumContext } from '@lifi/widget-provider'
|
|
3
|
+
import { useAddressForChain, useEthereumContext } from '@lifi/widget-provider'
|
|
4
4
|
import { useChain } from '../hooks/useChain.js'
|
|
5
5
|
import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
|
|
6
6
|
import { useFieldValues } from '../stores/form/useFieldValues.js'
|
|
@@ -16,7 +16,8 @@ export const useToAddressRequirements = (
|
|
|
16
16
|
requiredToAddress: boolean
|
|
17
17
|
unsupportedToAddress: boolean
|
|
18
18
|
unsupportedReceiverBlocking: boolean
|
|
19
|
-
|
|
19
|
+
/** Whether a saved or connected address can receive on the destination chain. */
|
|
20
|
+
isValidReceiver: (address?: string) => boolean
|
|
20
21
|
accountNotDeployedAtDestination: boolean
|
|
21
22
|
accountDeployedAtDestination: boolean
|
|
22
23
|
toAddress: string | undefined
|
|
@@ -32,6 +33,7 @@ export const useToAddressRequirements = (
|
|
|
32
33
|
'toAddress'
|
|
33
34
|
)
|
|
34
35
|
const { isDelegationDesignatorCode } = useEthereumContext()
|
|
36
|
+
const { isAddressForChain } = useAddressForChain()
|
|
35
37
|
|
|
36
38
|
const fromChainId = route?.fromChainId ?? formFromChainId
|
|
37
39
|
const toChainId = route?.toChainId ?? formToChainId
|
|
@@ -87,14 +89,27 @@ export const useToAddressRequirements = (
|
|
|
87
89
|
receiverRequired: requiredUI?.toAddress,
|
|
88
90
|
})
|
|
89
91
|
|
|
92
|
+
// A shared chain type is not a shared format: a Bitcoin signer cannot receive on ZEC.
|
|
93
|
+
const isSignerAddressInvalidAtDestination = Boolean(
|
|
94
|
+
fromAddress &&
|
|
95
|
+
fromChain &&
|
|
96
|
+
toChain &&
|
|
97
|
+
isAddressForChain(fromAddress, fromChain) &&
|
|
98
|
+
!isAddressForChain(fromAddress, toChain)
|
|
99
|
+
)
|
|
100
|
+
|
|
90
101
|
const requiredToAddress = Boolean(
|
|
91
102
|
(isDifferentChainType ||
|
|
92
103
|
isCrossChainContractAddress ||
|
|
104
|
+
isSignerAddressInvalidAtDestination ||
|
|
93
105
|
requiredUI?.toAddress) &&
|
|
94
106
|
!hiddenUI?.toAddress &&
|
|
95
107
|
!unsupportedToAddress
|
|
96
108
|
)
|
|
97
109
|
|
|
110
|
+
const isValidReceiver = (address?: string): boolean =>
|
|
111
|
+
!toChain || (!!address && isAddressForChain(address, toChain))
|
|
112
|
+
|
|
98
113
|
const accountNotDeployedAtDestination = Boolean(
|
|
99
114
|
isFromContractAddress &&
|
|
100
115
|
!fromContractCodeHasDelegationIndicator &&
|
|
@@ -113,7 +128,7 @@ export const useToAddressRequirements = (
|
|
|
113
128
|
requiredToAddress,
|
|
114
129
|
unsupportedToAddress,
|
|
115
130
|
unsupportedReceiverBlocking,
|
|
116
|
-
|
|
131
|
+
isValidReceiver,
|
|
117
132
|
accountNotDeployedAtDestination,
|
|
118
133
|
accountDeployedAtDestination,
|
|
119
134
|
toAddress,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ExtendedChain } from '@lifi/sdk'
|
|
2
|
+
import { useAddressForChain } from '@lifi/widget-provider'
|
|
2
3
|
import { useCallback } from 'react'
|
|
3
4
|
import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
|
|
4
5
|
import { useBookmarkActions } from '../stores/bookmarks/useBookmarkActions.js'
|
|
@@ -12,16 +13,18 @@ export const useToAddressReset = (): {
|
|
|
12
13
|
const { setFieldValue } = useFieldActions()
|
|
13
14
|
const { selectedBookmark } = useBookmarks()
|
|
14
15
|
const { setSelectedBookmark } = useBookmarkActions()
|
|
16
|
+
const { isAddressForChain } = useAddressForChain()
|
|
15
17
|
|
|
16
18
|
const tryResetToAddress = useCallback(
|
|
17
19
|
(toChain: ExtendedChain) => {
|
|
18
20
|
const requiredToAddress = requiredUI?.toAddress
|
|
19
21
|
|
|
20
|
-
const
|
|
21
|
-
selectedBookmark
|
|
22
|
+
const bookmarkSatisfiesToChain = Boolean(
|
|
23
|
+
selectedBookmark && isAddressForChain(selectedBookmark.address, toChain)
|
|
24
|
+
)
|
|
22
25
|
|
|
23
26
|
const shouldResetToAddress =
|
|
24
|
-
!requiredToAddress && !
|
|
27
|
+
!requiredToAddress && !bookmarkSatisfiesToChain
|
|
25
28
|
|
|
26
29
|
// We reset toAddress on each chain change if it's no longer required, ensuring that
|
|
27
30
|
// switching chain types doesn't leave a stale toAddress from a different ecosystem.
|
|
@@ -30,7 +33,13 @@ export const useToAddressReset = (): {
|
|
|
30
33
|
setSelectedBookmark()
|
|
31
34
|
}
|
|
32
35
|
},
|
|
33
|
-
[
|
|
36
|
+
[
|
|
37
|
+
setFieldValue,
|
|
38
|
+
setSelectedBookmark,
|
|
39
|
+
requiredUI,
|
|
40
|
+
selectedBookmark,
|
|
41
|
+
isAddressForChain,
|
|
42
|
+
]
|
|
34
43
|
)
|
|
35
44
|
|
|
36
45
|
return {
|
|
@@ -8,6 +8,7 @@ import { useQuery, useQueryClient } from '@tanstack/react-query'
|
|
|
8
8
|
import { useCallback, useMemo } from 'react'
|
|
9
9
|
import { useSDKClient } from '../providers/SDKClientProvider.js'
|
|
10
10
|
import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
|
|
11
|
+
import { isDestinationOnlyChain } from '../utils/chainType.js'
|
|
11
12
|
import { getQueryKey } from '../utils/queries.js'
|
|
12
13
|
|
|
13
14
|
const defaultRefetchInterval = 30_000
|
|
@@ -93,7 +94,11 @@ export const useTokenBalance = (
|
|
|
93
94
|
} as TokenAmount
|
|
94
95
|
},
|
|
95
96
|
|
|
96
|
-
|
|
97
|
+
// No provider reads a balance on a destination-only chain, and a balance
|
|
98
|
+
// without a block number is retried until the backoff gives up.
|
|
99
|
+
enabled:
|
|
100
|
+
Boolean(accountAddress && token) &&
|
|
101
|
+
!isDestinationOnlyChain(token?.chainId),
|
|
97
102
|
refetchInterval: defaultRefetchInterval,
|
|
98
103
|
staleTime: defaultRefetchInterval,
|
|
99
104
|
})
|