@lifi/widget 4.8.0 → 4.10.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.
Files changed (136) hide show
  1. package/README.md +26 -3
  2. package/dist/esm/components/AmountInputCard/AmountInputCardPair.js +5 -2
  3. package/dist/esm/components/AmountInputCard/AmountInputCardPair.js.map +1 -1
  4. package/dist/esm/components/SelectChainAndToken.js +5 -2
  5. package/dist/esm/components/SelectChainAndToken.js.map +1 -1
  6. package/dist/esm/components/SendToWallet/SendToWalletButton.js +5 -4
  7. package/dist/esm/components/SendToWallet/SendToWalletButton.js.map +1 -1
  8. package/dist/esm/components/TokenList/TokenDetailsSheet.js +5 -8
  9. package/dist/esm/components/TokenList/TokenDetailsSheet.js.map +1 -1
  10. package/dist/esm/components/TokenList/TokenDetailsSheetContent.js +30 -2
  11. package/dist/esm/components/TokenList/TokenDetailsSheetContent.js.map +1 -1
  12. package/dist/esm/components/TokenList/TokenListItem.js +36 -47
  13. package/dist/esm/components/TokenList/TokenListItem.js.map +1 -1
  14. package/dist/esm/components/TokenList/VirtualizedTokenList.d.ts.map +1 -1
  15. package/dist/esm/components/TokenList/VirtualizedTokenList.js +2 -2
  16. package/dist/esm/components/TokenList/VirtualizedTokenList.js.map +1 -1
  17. package/dist/esm/components/TokenList/tokenVerificationIcons.js +29 -0
  18. package/dist/esm/components/TokenList/tokenVerificationIcons.js.map +1 -0
  19. package/dist/esm/components/TokenList/useTokenSelect.d.ts.map +1 -1
  20. package/dist/esm/components/TokenList/useTokenSelect.js +10 -0
  21. package/dist/esm/components/TokenList/useTokenSelect.js.map +1 -1
  22. package/dist/esm/config/constants.js.map +1 -1
  23. package/dist/esm/config/version.d.ts +1 -1
  24. package/dist/esm/config/version.js +1 -1
  25. package/dist/esm/config/version.js.map +1 -1
  26. package/dist/esm/hooks/useAccountsBalancesData.js +5 -2
  27. package/dist/esm/hooks/useAccountsBalancesData.js.map +1 -1
  28. package/dist/esm/hooks/useAddressValidation.d.ts +3 -2
  29. package/dist/esm/hooks/useAddressValidation.d.ts.map +1 -1
  30. package/dist/esm/hooks/useAddressValidation.js +48 -21
  31. package/dist/esm/hooks/useAddressValidation.js.map +1 -1
  32. package/dist/esm/hooks/useGasRefuel.js +4 -1
  33. package/dist/esm/hooks/useGasRefuel.js.map +1 -1
  34. package/dist/esm/hooks/useRoutes.d.ts.map +1 -1
  35. package/dist/esm/hooks/useRoutes.js +11 -6
  36. package/dist/esm/hooks/useRoutes.js.map +1 -1
  37. package/dist/esm/hooks/useToAddressAutoPopulate.js +6 -8
  38. package/dist/esm/hooks/useToAddressAutoPopulate.js.map +1 -1
  39. package/dist/esm/hooks/useToAddressRequirements.d.ts +3 -2
  40. package/dist/esm/hooks/useToAddressRequirements.d.ts.map +1 -1
  41. package/dist/esm/hooks/useToAddressRequirements.js +8 -7
  42. package/dist/esm/hooks/useToAddressRequirements.js.map +1 -1
  43. package/dist/esm/hooks/useToAddressReset.js +6 -3
  44. package/dist/esm/hooks/useToAddressReset.js.map +1 -1
  45. package/dist/esm/hooks/useTokenBalance.js +2 -1
  46. package/dist/esm/hooks/useTokenBalance.js.map +1 -1
  47. package/dist/esm/hooks/useTokens.js +1 -1
  48. package/dist/esm/i18n/bn.json +7 -2
  49. package/dist/esm/i18n/de.json +7 -2
  50. package/dist/esm/i18n/en.json +7 -2
  51. package/dist/esm/i18n/es.json +7 -2
  52. package/dist/esm/i18n/fr.json +7 -2
  53. package/dist/esm/i18n/hi.json +7 -2
  54. package/dist/esm/i18n/id.json +7 -2
  55. package/dist/esm/i18n/it.json +7 -2
  56. package/dist/esm/i18n/ja.json +7 -2
  57. package/dist/esm/i18n/ko.json +7 -2
  58. package/dist/esm/i18n/pl.json +7 -2
  59. package/dist/esm/i18n/pt.json +7 -2
  60. package/dist/esm/i18n/th.json +7 -2
  61. package/dist/esm/i18n/tr.json +7 -2
  62. package/dist/esm/i18n/uk.json +7 -2
  63. package/dist/esm/i18n/vi.json +7 -2
  64. package/dist/esm/i18n/zh.json +7 -2
  65. package/dist/esm/pages/SendToWallet/BookmarkAddressSheet.js +7 -4
  66. package/dist/esm/pages/SendToWallet/BookmarkAddressSheet.js.map +1 -1
  67. package/dist/esm/pages/SendToWallet/BookmarksPage.js +35 -31
  68. package/dist/esm/pages/SendToWallet/BookmarksPage.js.map +1 -1
  69. package/dist/esm/pages/SendToWallet/ConnectedWalletsPage.js +4 -3
  70. package/dist/esm/pages/SendToWallet/ConnectedWalletsPage.js.map +1 -1
  71. package/dist/esm/pages/SendToWallet/RecentWalletsPage.js +43 -39
  72. package/dist/esm/pages/SendToWallet/RecentWalletsPage.js.map +1 -1
  73. package/dist/esm/pages/SendToWallet/SendToConfiguredWalletPage.js +32 -26
  74. package/dist/esm/pages/SendToWallet/SendToConfiguredWalletPage.js.map +1 -1
  75. package/dist/esm/pages/SendToWallet/SendToWalletPage.js +5 -10
  76. package/dist/esm/pages/SendToWallet/SendToWalletPage.js.map +1 -1
  77. package/dist/esm/providers/WalletProvider/WalletProvider.d.ts.map +1 -1
  78. package/dist/esm/providers/WalletProvider/WalletProvider.js +14 -2
  79. package/dist/esm/providers/WalletProvider/WalletProvider.js.map +1 -1
  80. package/dist/esm/providers/WidgetProvider/WidgetProvider.d.ts.map +1 -1
  81. package/dist/esm/providers/WidgetProvider/WidgetProvider.js +10 -14
  82. package/dist/esm/providers/WidgetProvider/WidgetProvider.js.map +1 -1
  83. package/dist/esm/stores/bookmarks/types.d.ts +3 -0
  84. package/dist/esm/stores/bookmarks/types.d.ts.map +1 -1
  85. package/dist/esm/stores/chains/ChainOrderStore.d.ts.map +1 -1
  86. package/dist/esm/stores/chains/ChainOrderStore.js +4 -1
  87. package/dist/esm/stores/chains/ChainOrderStore.js.map +1 -1
  88. package/dist/esm/stores/form/FormStore.js +3 -2
  89. package/dist/esm/stores/form/FormStore.js.map +1 -1
  90. package/dist/esm/stores/form/URLSearchParamsBuilder.js +7 -3
  91. package/dist/esm/stores/form/URLSearchParamsBuilder.js.map +1 -1
  92. package/dist/esm/stores/form/getDefaultValuesFromQueryString.js +4 -1
  93. package/dist/esm/stores/form/getDefaultValuesFromQueryString.js.map +1 -1
  94. package/dist/esm/utils/chainType.js +32 -1
  95. package/dist/esm/utils/chainType.js.map +1 -1
  96. package/dist/esm/utils/customReceiver.js +6 -1
  97. package/dist/esm/utils/customReceiver.js.map +1 -1
  98. package/dist/esm/utils/token.js +13 -1
  99. package/dist/esm/utils/token.js.map +1 -1
  100. package/package.json +7 -7
  101. package/src/components/AmountInputCard/AmountInputCardPair.tsx +6 -2
  102. package/src/components/SelectChainAndToken.tsx +5 -1
  103. package/src/components/SendToWallet/SendToWalletButton.tsx +13 -8
  104. package/src/components/TokenList/TokenDetailsSheet.tsx +5 -9
  105. package/src/components/TokenList/TokenDetailsSheetContent.tsx +46 -4
  106. package/src/components/TokenList/TokenListItem.tsx +44 -68
  107. package/src/components/TokenList/VirtualizedTokenList.tsx +3 -6
  108. package/src/components/TokenList/tokenVerificationIcons.ts +15 -0
  109. package/src/components/TokenList/types.ts +3 -11
  110. package/src/components/TokenList/useTokenSelect.ts +15 -0
  111. package/src/config/constants.ts +3 -0
  112. package/src/config/version.ts +1 -1
  113. package/src/hooks/useAccountsBalancesData.ts +6 -2
  114. package/src/hooks/useAddressValidation.ts +87 -32
  115. package/src/hooks/useGasRefuel.ts +11 -1
  116. package/src/hooks/useRoutes.ts +23 -18
  117. package/src/hooks/useToAddressAutoPopulate.ts +13 -17
  118. package/src/hooks/useToAddressRequirements.ts +19 -4
  119. package/src/hooks/useToAddressReset.ts +13 -4
  120. package/src/hooks/useTokenBalance.ts +6 -1
  121. package/src/pages/SendToWallet/BookmarkAddressSheet.tsx +5 -1
  122. package/src/pages/SendToWallet/BookmarksPage.tsx +42 -52
  123. package/src/pages/SendToWallet/ConnectedWalletsPage.tsx +4 -14
  124. package/src/pages/SendToWallet/RecentWalletsPage.tsx +59 -66
  125. package/src/pages/SendToWallet/SendToConfiguredWalletPage.tsx +45 -50
  126. package/src/pages/SendToWallet/SendToWalletPage.tsx +4 -16
  127. package/src/providers/WalletProvider/WalletProvider.tsx +28 -3
  128. package/src/providers/WidgetProvider/WidgetProvider.tsx +20 -19
  129. package/src/stores/bookmarks/types.ts +3 -0
  130. package/src/stores/chains/ChainOrderStore.tsx +4 -1
  131. package/src/stores/form/FormStore.tsx +2 -0
  132. package/src/stores/form/URLSearchParamsBuilder.tsx +4 -2
  133. package/src/stores/form/getDefaultValuesFromQueryString.ts +19 -0
  134. package/src/utils/chainType.ts +73 -1
  135. package/src/utils/customReceiver.ts +36 -0
  136. package/src/utils/token.ts +48 -0
@@ -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 { isCustomReceiverBlocked } from '../utils/customReceiver.js'
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 { getChainTypeFromAddress } = useChainTypeFromAddress()
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
- const effectiveFromAddress = account.address ?? quoteFromAddress
169
-
170
- // When we bridge between ecosystems we need to be sure toAddress is set and has the same chainType as toChain
171
- // If toAddress is set, it must have the same chainType as toChain
172
- const hasToAddressAndChainTypeSatisfied: boolean =
173
- !!toChain &&
174
- !!toAddress &&
175
- getChainTypeFromAddress(toAddress) === toChain.chainType
176
- // We need to check for toAddress only if it is set
177
- const isToAddressSatisfied = toAddress
178
- ? hasToAddressAndChainTypeSatisfied
179
- : true
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 { useChainTypeFromAddress } from '@lifi/widget-provider'
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 { getChainTypeFromAddress } = useChainTypeFromAddress()
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
- // Identify the destination chain type based on the bridge direction ('from' or 'to')
56
- const destinationChainType =
57
- formType === 'from'
58
- ? selectedOppositeChain.chainType
59
- : selectedChain.chainType
60
- // If toAddress is already selected, verify that it matches the destination chain type
61
- if (selectedToAddress) {
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) => account.chainType === destinationChainType
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
- getChainTypeFromAddress,
88
+ isAddressForChain,
93
89
  ]
94
90
  )
95
91
  }
@@ -1,6 +1,6 @@
1
- import type { ChainType, RouteExtended } from '@lifi/sdk'
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
- requiredToChainType: ChainType | undefined
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
- requiredToChainType: toChain?.chainType,
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 bookmarkSatisfiesToChainType =
21
- selectedBookmark?.chainType === toChain?.chainType
22
+ const bookmarkSatisfiesToChain = Boolean(
23
+ selectedBookmark && isAddressForChain(selectedBookmark.address, toChain)
24
+ )
22
25
 
23
26
  const shouldResetToAddress =
24
- !requiredToAddress && !bookmarkSatisfiesToChainType
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
- [setFieldValue, setSelectedBookmark, requiredUI, selectedBookmark]
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
- enabled: Boolean(accountAddress && token),
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
  })
@@ -9,6 +9,7 @@ import { BottomSheet } from '../../components/BottomSheet/BottomSheet.js'
9
9
  import type { BottomSheetBase } from '../../components/BottomSheet/types.js'
10
10
  import { Input } from '../../components/Input.js'
11
11
  import { AlertMessage } from '../../components/Messages/AlertMessage.js'
12
+ import { addressInputMaxLength } from '../../config/constants.js'
12
13
  import { useAddressValidation } from '../../hooks/useAddressValidation.js'
13
14
  import type { Bookmark } from '../../stores/bookmarks/types.js'
14
15
  import { useBookmarkActions } from '../../stores/bookmarks/useBookmarkActions.js'
@@ -63,6 +64,7 @@ export const BookmarkAddressSheet = ({
63
64
  name: nameValue,
64
65
  address: validationResult.address,
65
66
  chainType: validationResult.chainType,
67
+ chainId: validationResult.chainId,
66
68
  }
67
69
  }
68
70
 
@@ -74,6 +76,7 @@ export const BookmarkAddressSheet = ({
74
76
  name: nameValue,
75
77
  address: validatedWallet.address,
76
78
  chainType: validatedWallet.chainType,
79
+ chainId: validatedWallet.chainId,
77
80
  }
78
81
  }
79
82
 
@@ -119,6 +122,7 @@ export const BookmarkAddressSheet = ({
119
122
  name: validatedBookmark.name,
120
123
  address: validatedBookmark.address,
121
124
  chainType: validatedBookmark.chainType,
125
+ chainId: validatedBookmark.chainId,
122
126
  })
123
127
  }
124
128
  }
@@ -198,7 +202,7 @@ export const BookmarkAddressSheet = ({
198
202
  context: 'long',
199
203
  })}
200
204
  maxRows={2}
201
- inputProps={{ maxLength: 128 }}
205
+ inputProps={{ maxLength: addressInputMaxLength }}
202
206
  multiline
203
207
  />
204
208
  </SendToWalletCard>
@@ -34,7 +34,7 @@ export const BookmarksPage = (): JSX.Element => {
34
34
  const listParentRef = useRef<HTMLUListElement | null>(null)
35
35
  const buttonRef = useRef<HTMLDivElement | null>(null)
36
36
  const { bookmarks } = useBookmarks()
37
- const { requiredToChainType } = useToAddressRequirements()
37
+ const { isValidReceiver } = useToAddressRequirements()
38
38
  const { addBookmark, removeBookmark, setSelectedBookmark } =
39
39
  useBookmarkActions()
40
40
  const navigate = useNavigate()
@@ -70,59 +70,49 @@ export const BookmarksPage = (): JSX.Element => {
70
70
  disablePadding
71
71
  sx={{ paddingTop: 1.5 }}
72
72
  >
73
- {bookmarks.map((bookmark) => (
74
- <ListItem key={bookmark.address} sx={{ position: 'relative' }}>
75
- <ListItemButton
76
- onClick={() => handleBookmarkSelected(bookmark)}
77
- disabled={
78
- requiredToChainType &&
79
- requiredToChainType !== bookmark.chainType
80
- }
81
- >
82
- <ListItemAvatar>
83
- <AccountAvatar
84
- chainId={defaultChainIdsByType[bookmark.chainType]}
73
+ {bookmarks.map((bookmark) => {
74
+ const disabled = !isValidReceiver(bookmark.address)
75
+ const chainId =
76
+ bookmark.chainId ?? defaultChainIdsByType[bookmark.chainType]
77
+ return (
78
+ <ListItem key={bookmark.address} sx={{ position: 'relative' }}>
79
+ <ListItemButton
80
+ onClick={() => handleBookmarkSelected(bookmark)}
81
+ disabled={disabled}
82
+ >
83
+ <ListItemAvatar>
84
+ <AccountAvatar chainId={chainId} />
85
+ </ListItemAvatar>
86
+ <ListItemText
87
+ primary={bookmark.name}
88
+ secondary={shortenAddress(bookmark.address)}
85
89
  />
86
- </ListItemAvatar>
87
- <ListItemText
88
- primary={bookmark.name}
89
- secondary={shortenAddress(bookmark.address)}
90
+ </ListItemButton>
91
+ <ContextMenu
92
+ disabled={disabled}
93
+ items={[
94
+ {
95
+ icon: <ContentCopyRounded />,
96
+ label: t('button.copyAddress'),
97
+ onClick: () =>
98
+ navigator.clipboard.writeText(bookmark.address),
99
+ },
100
+ {
101
+ icon: <OpenInNewRounded />,
102
+ label: t('button.viewOnExplorer'),
103
+ onClick: () =>
104
+ openInNewTab(getAddressLink(bookmark.address, chainId)),
105
+ },
106
+ {
107
+ icon: <DeleteOutline />,
108
+ label: t('button.delete'),
109
+ onClick: () => removeBookmark(bookmark.address),
110
+ },
111
+ ]}
90
112
  />
91
- </ListItemButton>
92
- <ContextMenu
93
- disabled={
94
- !!(
95
- requiredToChainType &&
96
- requiredToChainType !== bookmark.chainType
97
- )
98
- }
99
- items={[
100
- {
101
- icon: <ContentCopyRounded />,
102
- label: t('button.copyAddress'),
103
- onClick: () =>
104
- navigator.clipboard.writeText(bookmark.address),
105
- },
106
- {
107
- icon: <OpenInNewRounded />,
108
- label: t('button.viewOnExplorer'),
109
- onClick: () =>
110
- openInNewTab(
111
- getAddressLink(
112
- bookmark.address,
113
- defaultChainIdsByType[bookmark.chainType]
114
- )
115
- ),
116
- },
117
- {
118
- icon: <DeleteOutline />,
119
- label: t('button.delete'),
120
- onClick: () => removeBookmark(bookmark.address),
121
- },
122
- ]}
123
- />
124
- </ListItem>
125
- ))}
113
+ </ListItem>
114
+ )
115
+ })}
126
116
  {!bookmarks.length && (
127
117
  <EmptyListIndicator
128
118
  icon={<TurnedIn />}
@@ -27,7 +27,7 @@ export const ConnectedWalletsPage = (): JSX.Element => {
27
27
  const { t } = useTranslation()
28
28
  const { accounts } = useAccount()
29
29
  const { setSelectedBookmark } = useBookmarkActions()
30
- const { requiredToChainType } = useToAddressRequirements()
30
+ const { isValidReceiver } = useToAddressRequirements()
31
31
  const navigate = useNavigate()
32
32
  const { setFieldValue } = useFieldActions()
33
33
  const { getAddressLink } = useExplorer()
@@ -91,15 +91,13 @@ export const ConnectedWalletsPage = (): JSX.Element => {
91
91
  ]
92
92
  : []),
93
93
  ]
94
+ const disabled = !isValidReceiver(account.address)
94
95
 
95
96
  return (
96
97
  <ListItem key={account.address} sx={{ position: 'relative' }}>
97
98
  <ListItemButton
98
99
  onClick={() => handleWalletSelected(account)}
99
- disabled={
100
- requiredToChainType &&
101
- requiredToChainType !== account.chainType
102
- }
100
+ disabled={disabled}
103
101
  >
104
102
  <ListItemAvatar>
105
103
  <AccountAvatar chainId={account.chainId} account={account} />
@@ -109,15 +107,7 @@ export const ConnectedWalletsPage = (): JSX.Element => {
109
107
  secondary={walletAddress}
110
108
  />
111
109
  </ListItemButton>
112
- <ContextMenu
113
- items={menuItems}
114
- disabled={
115
- !!(
116
- requiredToChainType &&
117
- requiredToChainType !== account.chainType
118
- )
119
- }
120
- />
110
+ <ContextMenu items={menuItems} disabled={disabled} />
121
111
  </ListItem>
122
112
  )
123
113
  })}
@@ -35,7 +35,7 @@ export const RecentWalletsPage = (): JSX.Element => {
35
35
  const bookmarkAddressSheetRef = useRef<BottomSheetBase>(null)
36
36
  const listParentRef = useRef<HTMLUListElement | null>(null)
37
37
  const { recentWallets } = useBookmarks()
38
- const { requiredToChainType } = useToAddressRequirements()
38
+ const { isValidReceiver } = useToAddressRequirements()
39
39
  const {
40
40
  removeRecentWallet,
41
41
  addBookmark,
@@ -76,73 +76,66 @@ export const RecentWalletsPage = (): JSX.Element => {
76
76
  disablePadding
77
77
  sx={{ paddingTop: 1.5 }}
78
78
  >
79
- {recentWallets.map((recentWallet) => (
80
- <ListItem key={recentWallet.address} sx={{ position: 'relative' }}>
81
- <ListItemButton
82
- disabled={
83
- requiredToChainType &&
84
- requiredToChainType !== recentWallet.chainType
85
- }
86
- onClick={() => handleRecentSelected(recentWallet)}
87
- >
88
- <ListItemAvatar>
89
- <AccountAvatar
90
- chainId={defaultChainIdsByType[recentWallet.chainType]}
79
+ {recentWallets.map((recentWallet) => {
80
+ const disabled = !isValidReceiver(recentWallet.address)
81
+ const chainId =
82
+ recentWallet.chainId ??
83
+ defaultChainIdsByType[recentWallet.chainType]
84
+ return (
85
+ <ListItem key={recentWallet.address} sx={{ position: 'relative' }}>
86
+ <ListItemButton
87
+ disabled={disabled}
88
+ onClick={() => handleRecentSelected(recentWallet)}
89
+ >
90
+ <ListItemAvatar>
91
+ <AccountAvatar chainId={chainId} />
92
+ </ListItemAvatar>
93
+ <ListItemText
94
+ primary={
95
+ recentWallet.name || shortenAddress(recentWallet.address)
96
+ }
97
+ secondary={
98
+ recentWallet.name
99
+ ? shortenAddress(recentWallet.address)
100
+ : undefined
101
+ }
91
102
  />
92
- </ListItemAvatar>
93
- <ListItemText
94
- primary={
95
- recentWallet.name || shortenAddress(recentWallet.address)
96
- }
97
- secondary={
98
- recentWallet.name
99
- ? shortenAddress(recentWallet.address)
100
- : undefined
101
- }
102
- />
103
- </ListItemButton>
104
- <ContextMenu
105
- disabled={
106
- !!(
107
- requiredToChainType &&
108
- requiredToChainType !== recentWallet.chainType
109
- )
110
- }
111
- items={[
112
- {
113
- icon: <ContentCopyRounded />,
114
- label: t('button.copyAddress'),
115
- onClick: () =>
116
- navigator.clipboard.writeText(recentWallet.address),
117
- },
118
- {
119
- icon: <OpenInNewRounded />,
120
- label: t('button.viewOnExplorer'),
121
- onClick: () =>
122
- openInNewTab(
123
- getAddressLink(
124
- recentWallet.address,
125
- defaultChainIdsByType[recentWallet.chainType]
126
- )
127
- ),
128
- },
129
- {
130
- icon: <TurnedInNot />,
131
- label: t('button.bookmark'),
132
- onClick: () => {
133
- setBookmarkTarget(recentWallet)
134
- bookmarkAddressSheetRef.current?.open()
103
+ </ListItemButton>
104
+ <ContextMenu
105
+ disabled={disabled}
106
+ items={[
107
+ {
108
+ icon: <ContentCopyRounded />,
109
+ label: t('button.copyAddress'),
110
+ onClick: () =>
111
+ navigator.clipboard.writeText(recentWallet.address),
112
+ },
113
+ {
114
+ icon: <OpenInNewRounded />,
115
+ label: t('button.viewOnExplorer'),
116
+ onClick: () =>
117
+ openInNewTab(
118
+ getAddressLink(recentWallet.address, chainId)
119
+ ),
120
+ },
121
+ {
122
+ icon: <TurnedInNot />,
123
+ label: t('button.bookmark'),
124
+ onClick: () => {
125
+ setBookmarkTarget(recentWallet)
126
+ bookmarkAddressSheetRef.current?.open()
127
+ },
135
128
  },
136
- },
137
- {
138
- icon: <DeleteOutline />,
139
- label: t('button.delete'),
140
- onClick: () => removeRecentWallet(recentWallet.address),
141
- },
142
- ]}
143
- />
144
- </ListItem>
145
- ))}
129
+ {
130
+ icon: <DeleteOutline />,
131
+ label: t('button.delete'),
132
+ onClick: () => removeRecentWallet(recentWallet.address),
133
+ },
134
+ ]}
135
+ />
136
+ </ListItem>
137
+ )
138
+ })}
146
139
  {!recentWallets.length && (
147
140
  <EmptyListIndicator
148
141
  icon={<AccountBalanceWalletIcon />}