@lifi/widget 3.37.0 → 3.38.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 (78) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/esm/components/AmountInput/PriceFormHelperText.style.js +3 -0
  3. package/dist/esm/components/AmountInput/PriceFormHelperText.style.js.map +1 -1
  4. package/dist/esm/components/AppContainer.js +1 -0
  5. package/dist/esm/components/AppContainer.js.map +1 -1
  6. package/dist/esm/components/Avatar/AccountAvatar.js +3 -3
  7. package/dist/esm/components/Avatar/AccountAvatar.js.map +1 -1
  8. package/dist/esm/components/Avatar/Avatar.d.ts +2 -0
  9. package/dist/esm/components/Avatar/Avatar.js +4 -3
  10. package/dist/esm/components/Avatar/Avatar.js.map +1 -1
  11. package/dist/esm/components/Avatar/ChainBadgeContent.d.ts +7 -0
  12. package/dist/esm/components/Avatar/ChainBadgeContent.js +10 -0
  13. package/dist/esm/components/Avatar/ChainBadgeContent.js.map +1 -0
  14. package/dist/esm/components/Avatar/TokenAvatar.js +3 -3
  15. package/dist/esm/components/Avatar/TokenAvatar.js.map +1 -1
  16. package/dist/esm/components/Chains/AllChainsAvatar.js +31 -5
  17. package/dist/esm/components/Chains/AllChainsAvatar.js.map +1 -1
  18. package/dist/esm/components/Chains/ChainSearchInput.js +7 -1
  19. package/dist/esm/components/Chains/ChainSearchInput.js.map +1 -1
  20. package/dist/esm/components/Header/NavigationHeader.js +5 -1
  21. package/dist/esm/components/Header/NavigationHeader.js.map +1 -1
  22. package/dist/esm/components/SelectTokenButton/SelectTokenButton.js +3 -1
  23. package/dist/esm/components/SelectTokenButton/SelectTokenButton.js.map +1 -1
  24. package/dist/esm/components/TokenList/TokenListItem.js +8 -2
  25. package/dist/esm/components/TokenList/TokenListItem.js.map +1 -1
  26. package/dist/esm/config/version.d.ts +1 -1
  27. package/dist/esm/config/version.js +1 -1
  28. package/dist/esm/hooks/useFilteredByTokenBalances.js +8 -2
  29. package/dist/esm/hooks/useFilteredByTokenBalances.js.map +1 -1
  30. package/dist/esm/hooks/useRoutes.js +10 -19
  31. package/dist/esm/hooks/useRoutes.js.map +1 -1
  32. package/dist/esm/hooks/useTokenSearch.js +15 -8
  33. package/dist/esm/hooks/useTokenSearch.js.map +1 -1
  34. package/dist/esm/hooks/useTokens.js +33 -53
  35. package/dist/esm/hooks/useTokens.js.map +1 -1
  36. package/dist/esm/i18n/en.json +1 -0
  37. package/dist/esm/stores/StoreProvider.js +2 -1
  38. package/dist/esm/stores/StoreProvider.js.map +1 -1
  39. package/dist/esm/stores/chains/ChainOrderStore.js +15 -2
  40. package/dist/esm/stores/chains/ChainOrderStore.js.map +1 -1
  41. package/dist/esm/types/token.d.ts +6 -1
  42. package/dist/esm/types/widget.d.ts +11 -2
  43. package/dist/esm/types/widget.js +1 -0
  44. package/dist/esm/types/widget.js.map +1 -1
  45. package/dist/esm/utils/token.d.ts +12 -1
  46. package/dist/esm/utils/token.js +44 -0
  47. package/dist/esm/utils/token.js.map +1 -1
  48. package/dist/esm/utils/tokenList.js +2 -0
  49. package/dist/esm/utils/tokenList.js.map +1 -1
  50. package/dist/esm/utils/variant.d.ts +2 -0
  51. package/dist/esm/utils/variant.js +10 -0
  52. package/dist/esm/utils/variant.js.map +1 -0
  53. package/package.json +7 -7
  54. package/package.json.tmp +9 -9
  55. package/src/components/AmountInput/PriceFormHelperText.style.tsx +3 -0
  56. package/src/components/AppContainer.tsx +1 -0
  57. package/src/components/Avatar/AccountAvatar.tsx +3 -15
  58. package/src/components/Avatar/Avatar.tsx +6 -7
  59. package/src/components/Avatar/ChainBadgeContent.tsx +22 -0
  60. package/src/components/Avatar/TokenAvatar.tsx +3 -11
  61. package/src/components/Chains/AllChainsAvatar.tsx +56 -8
  62. package/src/components/Chains/ChainSearchInput.tsx +9 -1
  63. package/src/components/Header/NavigationHeader.tsx +5 -1
  64. package/src/components/SelectTokenButton/SelectTokenButton.tsx +9 -1
  65. package/src/components/TokenList/TokenListItem.tsx +23 -1
  66. package/src/config/version.ts +1 -1
  67. package/src/hooks/useFilteredByTokenBalances.ts +8 -4
  68. package/src/hooks/useRoutes.ts +17 -23
  69. package/src/hooks/useTokenSearch.ts +17 -10
  70. package/src/hooks/useTokens.ts +51 -83
  71. package/src/i18n/en.json +1 -0
  72. package/src/stores/StoreProvider.tsx +2 -1
  73. package/src/stores/chains/ChainOrderStore.tsx +18 -2
  74. package/src/types/token.ts +5 -0
  75. package/src/types/widget.ts +10 -1
  76. package/src/utils/token.ts +65 -1
  77. package/src/utils/tokenList.ts +2 -0
  78. package/src/utils/variant.ts +16 -0
package/package.json.tmp CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifi/widget",
3
- "version": "3.37.0",
3
+ "version": "3.38.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": "./src/index.ts",
@@ -53,7 +53,7 @@
53
53
  "@bigmi/core": "^0.6.3",
54
54
  "@emotion/react": "^11.14.0",
55
55
  "@emotion/styled": "^11.14.1",
56
- "@lifi/sdk": "^3.13.7",
56
+ "@lifi/sdk": "^3.14.1",
57
57
  "@lifi/wallet-management": "workspace:^",
58
58
  "@mui/icons-material": "^7.3.6",
59
59
  "@mui/material": "^7.3.6",
@@ -62,25 +62,25 @@
62
62
  "@solana/wallet-adapter-base": "^0.9.27",
63
63
  "@solana/wallet-adapter-coinbase": "^0.1.23",
64
64
  "@solana/web3.js": "^1.98.4",
65
- "@tanstack/react-virtual": "^3.13.12",
66
- "i18next": "^25.7.1",
65
+ "@tanstack/react-virtual": "^3.13.13",
66
+ "i18next": "^25.7.3",
67
67
  "microdiff": "^1.5.0",
68
68
  "mitt": "^3.0.1",
69
- "react-i18next": "^16.3.5",
69
+ "react-i18next": "^16.5.0",
70
70
  "react-intersection-observer": "^9.16.0",
71
71
  "react-router-dom": "^6.30.1",
72
72
  "react-transition-group": "^4.4.5",
73
- "viem": "^2.41.2",
73
+ "viem": "^2.42.1",
74
74
  "zustand": "^5.0.9"
75
75
  },
76
76
  "devDependencies": {
77
77
  "@types/react-transition-group": "^4.4.12",
78
78
  "cpy-cli": "^6.0.0",
79
79
  "madge": "^8.0.0",
80
- "react": "^19.2.1",
81
- "react-dom": "^19.2.1",
80
+ "react": "^19.2.3",
81
+ "react-dom": "^19.2.3",
82
82
  "typescript": "^5.9.3",
83
- "vitest": "^4.0.15"
83
+ "vitest": "^4.0.16"
84
84
  },
85
85
  "peerDependencies": {
86
86
  "@bigmi/react": ">=0.6.0",
@@ -25,5 +25,8 @@ export const InputPriceButton = styled(Button)(({ theme, onClick }) => ({
25
25
  cursor: 'text',
26
26
  userSelect: 'text',
27
27
  pointerEvents: 'none',
28
+ ...theme.applyStyles('dark', {
29
+ backgroundColor: 'transparent',
30
+ }),
28
31
  }),
29
32
  }))
@@ -114,6 +114,7 @@ const CssBaselineContainer = styled(ScopedCssBaseline, {
114
114
  defaultMaxHeight,
115
115
  },
116
116
  '&:has(.long-list)': {
117
+ minHeight: getWidgetMaxHeight(theme),
117
118
  maxHeight: getWidgetMaxHeight(theme),
118
119
  },
119
120
  }
@@ -4,12 +4,8 @@ import Wallet from '@mui/icons-material/Wallet'
4
4
  import { Badge } from '@mui/material'
5
5
  import { useChain } from '../../hooks/useChain.js'
6
6
  import type { ToAddress } from '../../types/widget.js'
7
- import {
8
- AvatarDefault,
9
- AvatarDefaultBadge,
10
- AvatarMasked,
11
- } from './Avatar.style.js'
12
- import { SmallAvatar } from './SmallAvatar.js'
7
+ import { AvatarDefault, AvatarMasked } from './Avatar.style.js'
8
+ import { ChainBadgeContent } from './ChainBadgeContent.js'
13
9
 
14
10
  interface AccountAvatarProps {
15
11
  chainId?: number
@@ -45,15 +41,7 @@ export const AccountAvatar = ({
45
41
  <Badge
46
42
  overlap="circular"
47
43
  anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
48
- badgeContent={
49
- chain ? (
50
- <SmallAvatar src={chain.logoURI} alt={chain.name}>
51
- {chain.name[0]}
52
- </SmallAvatar>
53
- ) : (
54
- <AvatarDefaultBadge />
55
- )
56
- }
44
+ badgeContent={<ChainBadgeContent chain={chain} />}
57
45
  >
58
46
  {avatar}
59
47
  </Badge>
@@ -1,20 +1,19 @@
1
+ import type { ExtendedChain } from '@lifi/sdk'
1
2
  import type { SxProps, Theme } from '@mui/material'
2
3
  import { Badge, Skeleton } from '@mui/material'
3
- import {
4
- AvatarDefault,
5
- AvatarDefaultBadge,
6
- AvatarSkeletonMaskedContainer,
7
- } from './Avatar.style.js'
4
+ import { AvatarDefault, AvatarSkeletonMaskedContainer } from './Avatar.style.js'
5
+ import { ChainBadgeContent } from './ChainBadgeContent.js'
8
6
  import { SmallAvatarSkeleton } from './SmallAvatar.js'
9
7
 
10
8
  export const AvatarBadgedDefault: React.FC<{
11
9
  sx?: SxProps<Theme>
12
- }> = ({ sx }) => {
10
+ chain?: ExtendedChain
11
+ }> = ({ sx, chain }) => {
13
12
  return (
14
13
  <Badge
15
14
  overlap="circular"
16
15
  anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
17
- badgeContent={<AvatarDefaultBadge />}
16
+ badgeContent={<ChainBadgeContent chain={chain} />}
18
17
  sx={sx}
19
18
  >
20
19
  <AvatarDefault />
@@ -0,0 +1,22 @@
1
+ import type { Chain, ExtendedChain } from '@lifi/sdk'
2
+ import { AvatarDefaultBadge } from './Avatar.style.js'
3
+ import { SmallAvatar } from './SmallAvatar.js'
4
+
5
+ interface ChainBadgeContentProps {
6
+ chain?: Chain | ExtendedChain
7
+ size?: number
8
+ }
9
+
10
+ export const ChainBadgeContent: React.FC<ChainBadgeContentProps> = ({
11
+ chain,
12
+ size = 16,
13
+ }) => {
14
+ if (chain?.logoURI) {
15
+ return (
16
+ <SmallAvatar src={chain.logoURI} alt={chain.name} size={size}>
17
+ {chain.name?.[0]}
18
+ </SmallAvatar>
19
+ )
20
+ }
21
+ return <AvatarDefaultBadge size={size} />
22
+ }
@@ -4,8 +4,8 @@ import { Badge } from '@mui/material'
4
4
  import { useChain } from '../../hooks/useChain.js'
5
5
  import { useToken } from '../../hooks/useToken.js'
6
6
  import { AvatarBadgedSkeleton } from './Avatar.js'
7
- import { AvatarDefaultBadge, AvatarMasked } from './Avatar.style.js'
8
- import { SmallAvatar } from './SmallAvatar.js'
7
+ import { AvatarMasked } from './Avatar.style.js'
8
+ import { ChainBadgeContent } from './ChainBadgeContent.js'
9
9
 
10
10
  export const TokenAvatar: React.FC<{
11
11
  token?: StaticToken
@@ -83,15 +83,7 @@ const TokenAvatarBase: React.FC<{
83
83
  <Badge
84
84
  overlap="circular"
85
85
  anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
86
- badgeContent={
87
- chain ? (
88
- <SmallAvatar src={chain.logoURI} alt={chain.name} size={badgeSize}>
89
- {chain.name[0]}
90
- </SmallAvatar>
91
- ) : (
92
- <AvatarDefaultBadge size={badgeSize} />
93
- )
94
- }
86
+ badgeContent={<ChainBadgeContent chain={chain} size={badgeSize} />}
95
87
  sx={sx}
96
88
  >
97
89
  <AvatarMasked
@@ -1,4 +1,9 @@
1
- import { ChainType, type EVMChain, type ExtendedChain } from '@lifi/sdk'
1
+ import {
2
+ ChainId,
3
+ ChainType,
4
+ type EVMChain,
5
+ type ExtendedChain,
6
+ } from '@lifi/sdk'
2
7
  import { Avatar, Box } from '@mui/material'
3
8
  import { memo, useMemo } from 'react'
4
9
 
@@ -12,21 +17,25 @@ const chainTypeIcons = [
12
17
  name: 'Ethereum',
13
18
  chainType: ChainType.EVM,
14
19
  icon: 'https://lifinance.github.io/types/src/assets/icons/chains/ethereum.svg',
20
+ defaultChainId: ChainId.ETH,
15
21
  },
16
22
  {
17
23
  name: 'Solana',
18
24
  chainType: ChainType.SVM,
19
25
  icon: 'https://lifinance.github.io/types/src/assets/icons/chains/solana.svg',
26
+ defaultChainId: ChainId.SOL,
20
27
  },
21
28
  {
22
29
  name: 'Bitcoin',
23
30
  chainType: ChainType.UTXO,
24
31
  icon: 'https://lifinance.github.io/types/src/assets/icons/chains/bitcoin.svg',
32
+ defaultChainId: ChainId.BTC,
25
33
  },
26
34
  {
27
35
  name: 'Sui',
28
36
  chainType: ChainType.MVM,
29
37
  icon: 'https://lifinance.github.io/types/src/assets/icons/chains/sui.svg',
38
+ defaultChainId: ChainId.SUI,
30
39
  },
31
40
  ]
32
41
 
@@ -35,10 +44,48 @@ const maxChainAvatarsCount = chainTypeIcons.length
35
44
  export const AllChainsAvatar = memo(
36
45
  ({ chains, size }: AllChainsAvatarProps) => {
37
46
  const icons = useMemo(() => {
47
+ // Create maps for efficient lookups
48
+ const chainsPerChainType = new Map<ChainType, number>()
49
+ const chainsByChainType = new Map<
50
+ ChainType,
51
+ (ExtendedChain | EVMChain)[]
52
+ >()
53
+
54
+ chains.forEach((chain) => {
55
+ chainsPerChainType.set(
56
+ chain.chainType,
57
+ (chainsPerChainType.get(chain.chainType) || 0) + 1
58
+ )
59
+ const chainsOfType = chainsByChainType.get(chain.chainType) || []
60
+ chainsOfType.push(chain)
61
+ chainsByChainType.set(chain.chainType, chainsOfType)
62
+ })
63
+
38
64
  // Get existing ecosystem icons
39
- const existingChainTypeIcons = chainTypeIcons.filter((predefinedChain) =>
40
- chains.some((chain) => chain.chainType === predefinedChain.chainType)
65
+ const existingChainTypeIcons = chainTypeIcons.filter(
66
+ (predefinedChain) => {
67
+ const numberOfChains =
68
+ chainsPerChainType.get(predefinedChain.chainType) ?? 0
69
+
70
+ // If there's only one chain of this type, check if it's not the default
71
+ if (numberOfChains === 1) {
72
+ const chainsOfType = chainsByChainType.get(
73
+ predefinedChain.chainType
74
+ )
75
+ const singleChain = chainsOfType?.[0]
76
+ // Exclude the predefined icon if the single chain is not the default
77
+ if (
78
+ singleChain &&
79
+ singleChain.id !== predefinedChain.defaultChainId
80
+ ) {
81
+ return false
82
+ }
83
+ }
84
+
85
+ return numberOfChains > 0
86
+ }
41
87
  )
88
+
42
89
  if (existingChainTypeIcons.length === maxChainAvatarsCount) {
43
90
  return existingChainTypeIcons
44
91
  }
@@ -46,13 +93,14 @@ export const AllChainsAvatar = memo(
46
93
  const remainingSlots =
47
94
  maxChainAvatarsCount - existingChainTypeIcons.length
48
95
 
96
+ // Create a Set for O(1) lookup of predefined icon names
97
+ const predefinedIconNames = new Set(
98
+ existingChainTypeIcons.map((icon) => icon.name)
99
+ )
100
+
49
101
  const chainsWithLogos = chains.filter((chain) => {
50
102
  // Filter out chain icons matching ecosystem icons
51
- const hasPredefinedIcon = existingChainTypeIcons.some(
52
- (icon) => icon.name === chain.name
53
- )
54
- const hasLogoURI = Boolean(chain.logoURI)
55
- return !hasPredefinedIcon && hasLogoURI
103
+ return !predefinedIconNames.has(chain.name) && Boolean(chain.logoURI)
56
104
  })
57
105
 
58
106
  const additionalIcons = chainsWithLogos
@@ -29,7 +29,15 @@ export const ChainSearchInput = ({
29
29
 
30
30
  if (inExpansion) {
31
31
  return (
32
- <Box sx={{ pt: 3, pb: 2, px: 2.5, height: searchHeaderHeight }}>
32
+ <Box
33
+ sx={{
34
+ pt: 3,
35
+ pb: 2,
36
+ px: 2.5,
37
+ height: searchHeaderHeight,
38
+ boxSizing: 'border-box',
39
+ }}
40
+ >
33
41
  <SearchInput
34
42
  inputRef={inputRef}
35
43
  onChange={onChange}
@@ -34,8 +34,12 @@ export const NavigationHeader: React.FC = () => {
34
34
  const path = cleanedPathname.substring(cleanedPathname.lastIndexOf('/') + 1)
35
35
  const hasPath = navigationRoutesValues.includes(path)
36
36
 
37
+ // Show tabs when split is undefined (default tabs) or an object with defaultTab
38
+ // Hide tabs when split is a string ('bridge' or 'swap' - single mode)
37
39
  const showSplitOptions =
38
- subvariant === 'split' && !hasPath && !subvariantOptions?.split
40
+ subvariant === 'split' &&
41
+ !hasPath &&
42
+ typeof subvariantOptions?.split !== 'string'
39
43
 
40
44
  return (
41
45
  <HeaderAppBar elevation={0} sx={{ paddingTop: 1, paddingBottom: 0.5 }}>
@@ -5,6 +5,8 @@ import { useChain } from '../../hooks/useChain.js'
5
5
  import { useSwapOnly } from '../../hooks/useSwapOnly.js'
6
6
  import { useToken } from '../../hooks/useToken.js'
7
7
  import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
8
+ import { useChainOrderStore } from '../../stores/chains/ChainOrderStore.js'
9
+ import type { ChainOrderState } from '../../stores/chains/types.js'
8
10
  import type { FormTypeProps } from '../../stores/form/types.js'
9
11
  import { FormKeyHelper } from '../../stores/form/types.js'
10
12
  import { useFieldValues } from '../../stores/form/useFieldValues.js'
@@ -37,6 +39,10 @@ export const SelectTokenButton: React.FC<
37
39
  const { chain, isLoading: isChainLoading } = useChain(chainId)
38
40
  const { token, isLoading: isTokenLoading } = useToken(chainId, tokenAddress)
39
41
 
42
+ const isAllNetworks = useChainOrderStore(
43
+ (state: ChainOrderState) => state[`${formType}IsAllNetworks`]
44
+ )
45
+
40
46
  const handleClick = () => {
41
47
  navigate(
42
48
  formType === 'from'
@@ -77,7 +83,9 @@ export const SelectTokenButton: React.FC<
77
83
  isSelected ? (
78
84
  <TokenAvatar token={token} chain={chain} />
79
85
  ) : (
80
- <AvatarBadgedDefault />
86
+ <AvatarBadgedDefault
87
+ chain={isAllNetworks ? undefined : chain}
88
+ />
81
89
  )
82
90
  }
83
91
  title={isSelected ? token.symbol : defaultPlaceholder}
@@ -1,6 +1,7 @@
1
1
  import type { StaticToken } from '@lifi/sdk'
2
2
  import { ChainType } from '@lifi/sdk'
3
3
  import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'
4
+ import WarningRounded from '@mui/icons-material/WarningRounded'
4
5
  import {
5
6
  Avatar,
6
7
  Box,
@@ -9,6 +10,7 @@ import {
9
10
  listItemSecondaryActionClasses,
10
11
  Skeleton,
11
12
  Slide,
13
+ Tooltip,
12
14
  Typography,
13
15
  } from '@mui/material'
14
16
  import type { MouseEventHandler } from 'react'
@@ -189,7 +191,27 @@ const TokenListItemButton: React.FC<TokenListItemButtonProps> = memo(
189
191
  )}
190
192
  </ListItemAvatar>
191
193
  <ListItemText
192
- primary={token.symbol}
194
+ primary={
195
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
196
+ {token.symbol}
197
+ {!token.verified && (
198
+ <Tooltip
199
+ title={t('warning.message.unverifiedToken')}
200
+ placement="top"
201
+ arrow
202
+ >
203
+ <WarningRounded
204
+ sx={{
205
+ display: 'flex',
206
+ fontSize: 16,
207
+ color: 'warning.main',
208
+ cursor: 'help',
209
+ }}
210
+ />
211
+ </Tooltip>
212
+ )}
213
+ </Box>
214
+ }
193
215
  slotProps={{
194
216
  secondary: {
195
217
  component: 'div',
@@ -1,2 +1,2 @@
1
1
  export const name = '@lifi/widget'
2
- export const version = '3.37.0'
2
+ export const version = '3.38.0'
@@ -96,8 +96,8 @@ export const useFilteredTokensByBalance = (
96
96
  formType
97
97
  )
98
98
 
99
- const additionalTokens = balances.filter(
100
- (balance: WalletTokenExtended) => {
99
+ const additionalTokens = balances
100
+ .filter((balance: WalletTokenExtended) => {
101
101
  const balanceKey = balance.address.toLowerCase()
102
102
  return (
103
103
  !chainTokenSet.has(balanceKey) &&
@@ -109,8 +109,12 @@ export const useFilteredTokensByBalance = (
109
109
  (t) => t.address.toLowerCase()
110
110
  )
111
111
  )
112
- }
113
- ) as TokenExtended[]
112
+ })
113
+ // Mark tokens from wallet balances as unverified
114
+ .map((token: WalletTokenExtended) => ({
115
+ ...token,
116
+ verified: false,
117
+ })) as TokenExtended[]
114
118
 
115
119
  // Combine both sets of tokens - convert WalletTokenExtended to TokenAmount
116
120
  const allTokens = [
@@ -1,4 +1,4 @@
1
- import type { Route, Token, TokensResponse } from '@lifi/sdk'
1
+ import type { Route, Token } from '@lifi/sdk'
2
2
  import {
3
3
  ChainType,
4
4
  convertQuoteToRoute,
@@ -6,7 +6,6 @@ import {
6
6
  getRelayerQuote,
7
7
  getRoutes,
8
8
  isGaslessStep,
9
- isTokenMessageSigningAllowed,
10
9
  LiFiErrorCode,
11
10
  } from '@lifi/sdk'
12
11
  import { useAccount } from '@lifi/wallet-management'
@@ -20,8 +19,10 @@ import { useSetExecutableRoute } from '../stores/routes/useSetExecutableRoute.js
20
19
  import { defaultSlippage } from '../stores/settings/createSettingsStore.js'
21
20
  import { useSettings } from '../stores/settings/useSettings.js'
22
21
  import { WidgetEvent } from '../types/events.js'
22
+ import type { TokensByChain } from '../types/token.js'
23
23
  import { getChainTypeFromAddress } from '../utils/chainType.js'
24
24
  import { getQueryKey } from '../utils/queries.js'
25
+ import { updateTokenInCache } from '../utils/token.js'
25
26
  import { useChain } from './useChain.js'
26
27
  import { useDebouncedWatch } from './useDebouncedWatch.js'
27
28
  import { useGasRefuel } from './useGasRefuel.js'
@@ -352,8 +353,7 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
352
353
  fromChain.nativeToken.address !== fromTokenAddress &&
353
354
  useRelayerRoutes &&
354
355
  !isBatchingSupported &&
355
- (!observableRoute || isObservableRelayerRoute) &&
356
- isTokenMessageSigningAllowed(fromToken!)
356
+ (!observableRoute || isObservableRelayerRoute)
357
357
 
358
358
  const mainRoutesPromise = shouldUseMainRoutes
359
359
  ? getRoutes(
@@ -447,27 +447,21 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
447
447
  // Update local tokens cache to keep priceUSD in sync
448
448
  const { fromToken, toToken } = routesResult.routes[0]
449
449
  ;[fromToken, toToken].forEach((token) => {
450
- queryClient.setQueriesData<TokensResponse>(
450
+ // Update main tokens cache (verified)
451
+ queryClient.setQueriesData<TokensByChain>(
451
452
  { queryKey: [getQueryKey('tokens', keyPrefix)] },
452
- (data) => {
453
- if (data) {
454
- const clonedData = { ...data, tokens: { ...data.tokens } }
455
- const index = clonedData.tokens?.[token.chainId]?.findIndex(
456
- (dataToken) => dataToken.address === token.address
457
- )
458
- if (index >= 0) {
459
- clonedData.tokens[token.chainId] = [
460
- ...clonedData.tokens[token.chainId],
461
- ]
462
- clonedData.tokens[token.chainId][index] = {
463
- ...clonedData.tokens[token.chainId][index],
464
- ...token,
465
- }
466
- }
467
- return clonedData
468
- }
469
- }
453
+ (data) => updateTokenInCache(data, token)
454
+ )
455
+
456
+ // Update search tokens cache (unverified) - matches any search query
457
+ queryClient.setQueriesData<TokensByChain>(
458
+ {
459
+ queryKey: [getQueryKey('tokens-search', keyPrefix)],
460
+ exact: false,
461
+ },
462
+ (data) => updateTokenInCache(data, token)
470
463
  )
464
+
471
465
  queryClient.setQueriesData<Token[]>(
472
466
  {
473
467
  queryKey: [
@@ -3,11 +3,11 @@ import {
3
3
  type ChainId,
4
4
  getToken,
5
5
  type TokenExtended,
6
- type TokensResponse,
7
6
  } from '@lifi/sdk'
8
7
  import { useQuery, useQueryClient } from '@tanstack/react-query'
9
8
  import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
10
9
  import type { FormType } from '../stores/form/types.js'
10
+ import type { TokensByChain } from '../types/token.js'
11
11
  import { getConfigItemSets, isFormItemAllowed } from '../utils/item.js'
12
12
  import { getQueryKey } from '../utils/queries.js'
13
13
 
@@ -49,21 +49,28 @@ export const useTokenSearch = (
49
49
  return null
50
50
  }
51
51
 
52
- queryClient.setQueriesData<TokensResponse>(
52
+ // Add token to main tokens cache
53
+ queryClient.setQueriesData<TokensByChain>(
53
54
  { queryKey: [getQueryKey('tokens', keyPrefix)] },
54
55
  (data) => {
56
+ if (!data) {
57
+ return data
58
+ }
59
+ const chainTokens = data[chainId as number]
55
60
  if (
56
- data &&
57
- !data.tokens[chainId as number]?.some(
61
+ chainTokens?.some(
58
62
  (t) => t.address.toLowerCase() === token.address.toLowerCase()
59
63
  )
60
64
  ) {
61
- const clonedData = { ...data, tokens: { ...data.tokens } }
62
- clonedData.tokens[chainId as number] = [
63
- ...(clonedData.tokens[chainId as number] ?? []),
64
- token,
65
- ]
66
- return clonedData
65
+ return data
66
+ }
67
+ // Mark token from search as unverified
68
+ return {
69
+ ...data,
70
+ [chainId as number]: [
71
+ ...(chainTokens ?? []),
72
+ { ...token, verified: false },
73
+ ],
67
74
  }
68
75
  }
69
76
  )