@lifi/widget 3.0.0-alpha.22 → 3.0.0-alpha.23

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 (124) hide show
  1. package/_cjs/components/AmountInput/AmountInputEndAdornment.js +11 -7
  2. package/_cjs/components/AmountInput/AmountInputEndAdornment.js.map +1 -1
  3. package/_cjs/components/Routes/RoutesExpanded.js +1 -1
  4. package/_cjs/components/Routes/RoutesExpanded.js.map +1 -1
  5. package/_cjs/components/TokenList/TokenList.js +5 -2
  6. package/_cjs/components/TokenList/TokenList.js.map +1 -1
  7. package/_cjs/components/TokenList/VirtualizedTokenList.js +41 -19
  8. package/_cjs/components/TokenList/VirtualizedTokenList.js.map +1 -1
  9. package/_cjs/components/TokenList/types.d.ts +3 -2
  10. package/_cjs/config/version.d.ts +1 -1
  11. package/_cjs/config/version.js +1 -1
  12. package/_cjs/hooks/index.d.ts +0 -1
  13. package/_cjs/hooks/index.js +0 -1
  14. package/_cjs/hooks/index.js.map +1 -1
  15. package/_cjs/hooks/useDebouncedWatch.d.ts +1 -1
  16. package/_cjs/hooks/useRoutes.js +49 -25
  17. package/_cjs/hooks/useRoutes.js.map +1 -1
  18. package/_cjs/hooks/useSettingMonitor.js +8 -12
  19. package/_cjs/hooks/useSettingMonitor.js.map +1 -1
  20. package/_cjs/hooks/useToken.d.ts +1 -1
  21. package/_cjs/hooks/useTokenAddressBalance.d.ts +3 -2
  22. package/_cjs/hooks/useTokenAddressBalance.js.map +1 -1
  23. package/_cjs/hooks/useTokenBalances.d.ts +3 -2
  24. package/_cjs/hooks/useTokenBalances.js +33 -14
  25. package/_cjs/hooks/useTokenBalances.js.map +1 -1
  26. package/_cjs/hooks/useTokens.d.ts +3 -1
  27. package/_cjs/hooks/useTokens.js +44 -31
  28. package/_cjs/hooks/useTokens.js.map +1 -1
  29. package/_cjs/i18n/en.json +3 -1
  30. package/_cjs/pages/RoutesPage/RoutesPage.js +1 -1
  31. package/_cjs/pages/RoutesPage/RoutesPage.js.map +1 -1
  32. package/_cjs/pages/SelectEnabledToolsPage/SelectEnabledToolsPage.js +10 -27
  33. package/_cjs/pages/SelectEnabledToolsPage/SelectEnabledToolsPage.js.map +1 -1
  34. package/_cjs/pages/SettingsPage/BridgeAndExchangeSettings.js +2 -2
  35. package/_cjs/pages/SettingsPage/BridgeAndExchangeSettings.js.map +1 -1
  36. package/_cjs/stores/form/createFormStore.js +10 -6
  37. package/_cjs/stores/form/createFormStore.js.map +1 -1
  38. package/_cjs/stores/form/types.d.ts +3 -14
  39. package/_cjs/stores/form/types.js.map +1 -1
  40. package/_cjs/stores/form/useFieldController.d.ts +1 -1
  41. package/_cjs/stores/form/useFieldValues.d.ts +1 -1
  42. package/_cjs/stores/settings/types.d.ts +10 -9
  43. package/_cjs/stores/settings/types.js.map +1 -1
  44. package/_cjs/stores/settings/useSettingsStore.js +40 -30
  45. package/_cjs/stores/settings/useSettingsStore.js.map +1 -1
  46. package/_cjs/types/token.d.ts +1 -0
  47. package/_cjs/types/widget.d.ts +1 -0
  48. package/_esm/components/AmountInput/AmountInputEndAdornment.js +11 -7
  49. package/_esm/components/AmountInput/AmountInputEndAdornment.js.map +1 -1
  50. package/_esm/components/Routes/RoutesExpanded.js +1 -1
  51. package/_esm/components/Routes/RoutesExpanded.js.map +1 -1
  52. package/_esm/components/TokenList/TokenList.js +6 -3
  53. package/_esm/components/TokenList/TokenList.js.map +1 -1
  54. package/_esm/components/TokenList/VirtualizedTokenList.js +41 -19
  55. package/_esm/components/TokenList/VirtualizedTokenList.js.map +1 -1
  56. package/_esm/components/TokenList/types.d.ts +3 -2
  57. package/_esm/config/version.d.ts +1 -1
  58. package/_esm/config/version.js +1 -1
  59. package/_esm/hooks/index.d.ts +0 -1
  60. package/_esm/hooks/index.js +0 -1
  61. package/_esm/hooks/index.js.map +1 -1
  62. package/_esm/hooks/useDebouncedWatch.d.ts +1 -1
  63. package/_esm/hooks/useRoutes.js +49 -25
  64. package/_esm/hooks/useRoutes.js.map +1 -1
  65. package/_esm/hooks/useSettingMonitor.js +8 -12
  66. package/_esm/hooks/useSettingMonitor.js.map +1 -1
  67. package/_esm/hooks/useToken.d.ts +1 -1
  68. package/_esm/hooks/useTokenAddressBalance.d.ts +3 -2
  69. package/_esm/hooks/useTokenAddressBalance.js.map +1 -1
  70. package/_esm/hooks/useTokenBalances.d.ts +3 -2
  71. package/_esm/hooks/useTokenBalances.js +33 -14
  72. package/_esm/hooks/useTokenBalances.js.map +1 -1
  73. package/_esm/hooks/useTokens.d.ts +3 -1
  74. package/_esm/hooks/useTokens.js +44 -31
  75. package/_esm/hooks/useTokens.js.map +1 -1
  76. package/_esm/i18n/en.json +3 -1
  77. package/_esm/pages/RoutesPage/RoutesPage.js +1 -1
  78. package/_esm/pages/RoutesPage/RoutesPage.js.map +1 -1
  79. package/_esm/pages/SelectEnabledToolsPage/SelectEnabledToolsPage.js +10 -27
  80. package/_esm/pages/SelectEnabledToolsPage/SelectEnabledToolsPage.js.map +1 -1
  81. package/_esm/pages/SettingsPage/BridgeAndExchangeSettings.js +2 -2
  82. package/_esm/pages/SettingsPage/BridgeAndExchangeSettings.js.map +1 -1
  83. package/_esm/stores/form/createFormStore.js +10 -6
  84. package/_esm/stores/form/createFormStore.js.map +1 -1
  85. package/_esm/stores/form/types.d.ts +3 -14
  86. package/_esm/stores/form/types.js.map +1 -1
  87. package/_esm/stores/form/useFieldController.d.ts +1 -1
  88. package/_esm/stores/form/useFieldValues.d.ts +1 -1
  89. package/_esm/stores/settings/types.d.ts +10 -9
  90. package/_esm/stores/settings/types.js.map +1 -1
  91. package/_esm/stores/settings/useSettingsStore.js +40 -30
  92. package/_esm/stores/settings/useSettingsStore.js.map +1 -1
  93. package/_esm/types/token.d.ts +1 -0
  94. package/_esm/types/widget.d.ts +1 -0
  95. package/components/AmountInput/AmountInputEndAdornment.tsx +15 -12
  96. package/components/Routes/RoutesExpanded.tsx +1 -1
  97. package/components/TokenList/TokenList.tsx +8 -2
  98. package/components/TokenList/VirtualizedTokenList.tsx +63 -41
  99. package/components/TokenList/types.ts +3 -2
  100. package/config/version.ts +1 -1
  101. package/hooks/index.ts +0 -1
  102. package/hooks/useRoutes.ts +58 -31
  103. package/hooks/useSettingMonitor.ts +20 -21
  104. package/hooks/useTokenAddressBalance.ts +2 -1
  105. package/hooks/useTokenBalances.ts +34 -29
  106. package/hooks/useTokens.ts +65 -36
  107. package/i18n/en.json +3 -1
  108. package/package.json +6 -6
  109. package/pages/RoutesPage/RoutesPage.tsx +1 -1
  110. package/pages/SelectEnabledToolsPage/SelectEnabledToolsPage.tsx +16 -35
  111. package/pages/SettingsPage/BridgeAndExchangeSettings.tsx +2 -2
  112. package/stores/form/createFormStore.ts +15 -15
  113. package/stores/form/types.ts +3 -14
  114. package/stores/settings/types.ts +10 -13
  115. package/stores/settings/useSettingsStore.ts +51 -53
  116. package/types/token.ts +1 -0
  117. package/types/widget.ts +1 -0
  118. package/_cjs/hooks/useFeaturedTokens.d.ts +0 -1
  119. package/_cjs/hooks/useFeaturedTokens.js +0 -11
  120. package/_cjs/hooks/useFeaturedTokens.js.map +0 -1
  121. package/_esm/hooks/useFeaturedTokens.d.ts +0 -1
  122. package/_esm/hooks/useFeaturedTokens.js +0 -7
  123. package/_esm/hooks/useFeaturedTokens.js.map +0 -1
  124. package/hooks/useFeaturedTokens.ts +0 -12
@@ -21,17 +21,17 @@ export const useRoutes = ({ insurableRoute }: RoutesProps = {}) => {
21
21
  const queryClient = useQueryClient();
22
22
  const swapOnly = useSwapOnly();
23
23
  const {
24
- slippage,
25
- enabledBridges,
24
+ disabledBridges,
25
+ disabledExchanges,
26
26
  enabledAutoRefuel,
27
- enabledExchanges,
28
27
  routePriority,
28
+ slippage,
29
29
  } = useSettings([
30
- 'slippage',
31
- 'routePriority',
30
+ 'disabledBridges',
31
+ 'disabledExchanges',
32
32
  'enabledAutoRefuel',
33
- 'enabledBridges',
34
- 'enabledExchanges',
33
+ 'routePriority',
34
+ 'slippage',
35
35
  ]);
36
36
  const [fromTokenAmount] = useDebouncedWatch(320, 'fromAmount');
37
37
  const [
@@ -117,8 +117,9 @@ export const useRoutes = ({ insurableRoute }: RoutesProps = {}) => {
117
117
  toContractCallData,
118
118
  toContractGasLimit,
119
119
  slippage,
120
- swapOnly ? [] : enabledBridges,
121
- enabledExchanges,
120
+ swapOnly,
121
+ disabledBridges,
122
+ disabledExchanges,
122
123
  routePriority,
123
124
  subvariant,
124
125
  sdkConfig?.routeOptions?.allowSwitchChain,
@@ -146,8 +147,9 @@ export const useRoutes = ({ insurableRoute }: RoutesProps = {}) => {
146
147
  toContractCallData,
147
148
  toContractGasLimit,
148
149
  slippage = defaultSlippage,
149
- enabledBridges,
150
- enabledExchanges,
150
+ swapOnly,
151
+ disabledBridges,
152
+ disabledExchanges,
151
153
  routePriority,
152
154
  subvariant,
153
155
  allowSwitchChain,
@@ -165,21 +167,29 @@ export const useRoutes = ({ insurableRoute }: RoutesProps = {}) => {
165
167
  ).toString();
166
168
  const formattedSlippage = parseFloat(slippage) / 100;
167
169
 
168
- const allowedBridges: string[] = insurableRoute
169
- ? insurableRoute.steps.flatMap((step) =>
170
- step.includedSteps
171
- .filter((includedStep) => includedStep.type === 'cross')
172
- .map((includedStep) => includedStep.toolDetails.key),
173
- )
174
- : enabledBridges;
170
+ const allowedBridges = swapOnly
171
+ ? []
172
+ : insurableRoute
173
+ ? insurableRoute.steps.flatMap((step) =>
174
+ step.includedSteps.reduce((toolKeys, includedStep) => {
175
+ if (includedStep.type === 'cross') {
176
+ toolKeys.push(includedStep.toolDetails.key);
177
+ }
178
+ return toolKeys;
179
+ }, [] as string[]),
180
+ )
181
+ : undefined;
175
182
 
176
- const allowedExchanges: string[] = insurableRoute
183
+ const allowedExchanges = insurableRoute
177
184
  ? insurableRoute.steps.flatMap((step) =>
178
- step.includedSteps
179
- .filter((includedStep) => includedStep.type === 'swap')
180
- .map((includedStep) => includedStep.toolDetails.key),
185
+ step.includedSteps.reduce((toolKeys, includedStep) => {
186
+ if (includedStep.type === 'swap') {
187
+ toolKeys.push(includedStep.toolDetails.key);
188
+ }
189
+ return toolKeys;
190
+ }, [] as string[]),
181
191
  )
182
- : enabledExchanges;
192
+ : undefined;
183
193
 
184
194
  if (subvariant === 'nft') {
185
195
  const contractCallQuote = await getContractCallQuote(
@@ -194,7 +204,12 @@ export const useRoutes = ({ insurableRoute }: RoutesProps = {}) => {
194
204
  toContractAddress,
195
205
  toContractCallData,
196
206
  toContractGasLimit,
207
+ denyBridges: disabledBridges.length ? disabledBridges : undefined,
208
+ denyExchanges: disabledExchanges.length
209
+ ? disabledExchanges
210
+ : undefined,
197
211
  allowBridges: allowedBridges,
212
+ allowExchanges: allowedExchanges,
198
213
  toFallbackAddress: toWalletAddress,
199
214
  slippage: formattedSlippage,
200
215
  },
@@ -255,17 +270,29 @@ export const useRoutes = ({ insurableRoute }: RoutesProps = {}) => {
255
270
  ? gasRecommendationFromAmount
256
271
  : undefined,
257
272
  options: {
258
- slippage: formattedSlippage,
259
- bridges: {
260
- allow: allowedBridges,
261
- },
262
- exchanges: {
263
- allow: allowedExchanges,
264
- },
265
- order: routePriority,
266
273
  allowSwitchChain:
267
274
  subvariant === 'refuel' ? false : allowSwitchChain,
275
+ bridges:
276
+ allowedBridges?.length || disabledBridges.length
277
+ ? {
278
+ allow: allowedBridges,
279
+ deny: disabledBridges.length
280
+ ? disabledBridges
281
+ : undefined,
282
+ }
283
+ : undefined,
284
+ exchanges:
285
+ allowedExchanges?.length || disabledExchanges.length
286
+ ? {
287
+ allow: allowedExchanges,
288
+ deny: disabledExchanges.length
289
+ ? disabledExchanges
290
+ : undefined,
291
+ }
292
+ : undefined,
268
293
  insurance: insurance ? Boolean(insurableRoute) : undefined,
294
+ order: routePriority,
295
+ slippage: formattedSlippage,
269
296
  },
270
297
  },
271
298
  { signal },
@@ -1,24 +1,29 @@
1
+ import { shallow } from 'zustand/shallow';
2
+ import { useWidgetConfig } from '../providers';
1
3
  import {
2
4
  defaultConfigurableSettings,
3
5
  setDefaultSettings,
4
6
  useSettingsStore,
5
7
  } from '../stores';
6
- import { shallow } from 'zustand/shallow';
7
8
  import { useTools } from './useTools';
8
- import { useWidgetConfig } from '../providers';
9
9
 
10
10
  export const useSettingMonitor = () => {
11
- const [enabledBridges, enabledExchanges, routePriority, slippage, gasPrice] =
12
- useSettingsStore(
13
- (state) => [
14
- state.enabledBridges,
15
- state.enabledExchanges,
16
- state.routePriority,
17
- state.slippage,
18
- state.gasPrice,
19
- ],
20
- shallow,
21
- );
11
+ const [
12
+ disabledBridges,
13
+ disabledExchanges,
14
+ routePriority,
15
+ slippage,
16
+ gasPrice,
17
+ ] = useSettingsStore(
18
+ (state) => [
19
+ state.disabledBridges,
20
+ state.disabledExchanges,
21
+ state.routePriority,
22
+ state.slippage,
23
+ state.gasPrice,
24
+ ],
25
+ shallow,
26
+ );
22
27
  const { tools } = useTools();
23
28
  const resetSettings = useSettingsStore((state) => state.reset);
24
29
  const config = useWidgetConfig();
@@ -36,13 +41,8 @@ export const useSettingMonitor = () => {
36
41
 
37
42
  const isGasPriceChanged = gasPrice !== defaultConfigurableSettings.gasPrice;
38
43
 
39
- const isBridgesChanged = tools?.bridges
40
- ? tools?.bridges?.length !== enabledBridges?.length
41
- : false;
42
-
43
- const isExchangesChanged = tools?.exchanges
44
- ? tools?.exchanges?.length !== enabledExchanges?.length
45
- : false;
44
+ const isBridgesChanged = Boolean(disabledBridges.length);
45
+ const isExchangesChanged = Boolean(disabledExchanges.length);
46
46
 
47
47
  const isCustomRouteSettings =
48
48
  isBridgesChanged ||
@@ -56,7 +56,6 @@ export const useSettingMonitor = () => {
56
56
  const reset = () => {
57
57
  if (tools) {
58
58
  resetSettings(
59
- config,
60
59
  tools.bridges.map((tool) => tool.key),
61
60
  tools.exchanges.map((tool) => tool.key),
62
61
  );
@@ -1,4 +1,5 @@
1
1
  import { useMemo } from 'react';
2
+ import type { TokenAmount } from '../types';
2
3
  import { useTokenBalances } from './useTokenBalances';
3
4
 
4
5
  export const useTokenAddressBalance = (
@@ -13,7 +14,7 @@ export const useTokenAddressBalance = (
13
14
  const token = (tokensWithBalance ?? tokens)?.find(
14
15
  (token) => token.address === tokenAddress && token.chainId === chainId,
15
16
  );
16
- return token;
17
+ return token as TokenAmount;
17
18
  }
18
19
  }, [chainId, tokenAddress, tokens, tokensWithBalance]);
19
20
 
@@ -3,14 +3,13 @@ import { useQuery } from '@tanstack/react-query';
3
3
  import { formatUnits } from 'viem';
4
4
  import type { TokenAmount } from '../types';
5
5
  import { useAccount } from './useAccount';
6
- import { useFeaturedTokens } from './useFeaturedTokens';
7
6
  import { useTokens } from './useTokens';
8
7
 
9
8
  const defaultRefetchInterval = 32_000;
10
9
 
11
10
  export const useTokenBalances = (selectedChainId?: number) => {
12
- const featuredTokens = useFeaturedTokens(selectedChainId);
13
- const { tokens, chain, isLoading } = useTokens(selectedChainId);
11
+ const { tokens, featuredTokens, popularTokens, chain, isLoading } =
12
+ useTokens(selectedChainId);
14
13
  const { account } = useAccount({ chainType: chain?.chainType });
15
14
 
16
15
  const isBalanceLoadingEnabled =
@@ -30,44 +29,49 @@ export const useTokenBalances = (selectedChainId?: number) => {
30
29
  tokens?.length,
31
30
  ],
32
31
  queryFn: async ({ queryKey: [, accountAddress] }) => {
33
- const tokenBalances = await getTokenBalances(
32
+ const tokensWithBalance: TokenAmount[] = await getTokenBalances(
34
33
  accountAddress as string,
35
34
  tokens!,
36
35
  );
37
36
 
38
- const featuredTokenAddresses = new Set(
39
- featuredTokens?.map((token) => token.address),
40
- );
37
+ if (!tokensWithBalance?.length) {
38
+ return tokens as TokenAmount[];
39
+ }
41
40
 
42
41
  const sortFn = (a: TokenAmount, b: TokenAmount) =>
43
42
  parseFloat(formatUnits(b.amount ?? 0n, b.decimals)) *
44
43
  parseFloat(b.priceUSD ?? '0') -
45
44
  parseFloat(formatUnits(a.amount ?? 0n, a.decimals)) *
46
45
  parseFloat(a.priceUSD ?? '0');
47
- const formattedTokens = (
48
- tokenBalances.length === 0 ? tokens : tokenBalances
49
- ) as TokenAmount[];
46
+
47
+ const featuredTokens: TokenAmount[] = [];
48
+ const tokensWithAmount: TokenAmount[] = [];
49
+ const popularTokens: TokenAmount[] = [];
50
+ const allTokens: TokenAmount[] = [];
51
+
52
+ tokensWithBalance.forEach((token) => {
53
+ if (token.amount) {
54
+ token.featured = false;
55
+ token.popular = false;
56
+ }
57
+ if (token.featured) {
58
+ featuredTokens.push(token);
59
+ } else if (token.amount) {
60
+ tokensWithAmount.push(token);
61
+ } else if (token.popular) {
62
+ popularTokens.push(token);
63
+ } else {
64
+ allTokens.push(token);
65
+ }
66
+ });
67
+
68
+ tokensWithAmount.sort(sortFn);
50
69
 
51
70
  const result = [
52
- ...formattedTokens
53
- .filter(
54
- (token) =>
55
- token.amount && featuredTokenAddresses.has(token.address),
56
- )
57
- .sort(sortFn),
58
- ...formattedTokens.filter(
59
- (token) => !token.amount && featuredTokenAddresses.has(token.address),
60
- ),
61
- ...formattedTokens
62
- .filter(
63
- (token) =>
64
- token.amount && !featuredTokenAddresses.has(token.address),
65
- )
66
- .sort(sortFn),
67
- ...formattedTokens.filter(
68
- (token) =>
69
- !token.amount && !featuredTokenAddresses.has(token.address),
70
- ),
71
+ ...featuredTokens,
72
+ ...tokensWithAmount,
73
+ ...popularTokens,
74
+ ...allTokens,
71
75
  ];
72
76
  return result;
73
77
  },
@@ -80,6 +84,7 @@ export const useTokenBalances = (selectedChainId?: number) => {
80
84
  tokens,
81
85
  tokensWithBalance,
82
86
  featuredTokens,
87
+ popularTokens,
83
88
  chain,
84
89
  isLoading,
85
90
  isBalanceLoading: isBalanceLoading && isBalanceLoadingEnabled,
@@ -4,7 +4,6 @@ import { useMemo } from 'react';
4
4
  import { useWidgetConfig } from '../providers';
5
5
  import type { TokenAmount } from '../types';
6
6
  import { useChains } from './useChains';
7
- import { useFeaturedTokens } from './useFeaturedTokens';
8
7
 
9
8
  export const useTokens = (selectedChainId?: number) => {
10
9
  const { tokens: configTokens } = useWidgetConfig();
@@ -18,10 +17,9 @@ export const useTokens = (selectedChainId?: number) => {
18
17
  isLoading: isSupportedChainsLoading,
19
18
  getChainById,
20
19
  } = useChains();
21
- const featuredTokens = useFeaturedTokens(selectedChainId);
22
20
 
23
21
  const filteredData = useMemo(() => {
24
- if (isSupportedChainsLoading) {
22
+ if (isSupportedChainsLoading || !data) {
25
23
  return;
26
24
  }
27
25
  const chain = getChainById(selectedChainId, chains);
@@ -29,55 +27,84 @@ export const useTokens = (selectedChainId?: number) => {
29
27
  if (!chainAllowed) {
30
28
  return;
31
29
  }
32
- let filteredTokens = data?.tokens[selectedChainId];
30
+ let filteredTokens = data.tokens?.[selectedChainId] || [];
33
31
  const includedTokens = configTokens?.include?.filter(
34
32
  (token) => token.chainId === selectedChainId,
35
33
  );
36
34
  if (includedTokens?.length) {
37
- filteredTokens = filteredTokens
38
- ? [...includedTokens, ...filteredTokens]
39
- : includedTokens;
35
+ filteredTokens = [...includedTokens, ...filteredTokens];
40
36
  }
41
- const allowedTokens = configTokens?.allow
42
- ?.filter((token) => token.chainId === selectedChainId)
43
- .map((token) => token.address);
44
- if (allowedTokens?.length) {
45
- filteredTokens = filteredTokens?.filter((token) =>
46
- allowedTokens.includes(token.address),
37
+
38
+ if (configTokens?.allow?.length || configTokens?.deny?.length) {
39
+ const allowedTokensSet = new Set(
40
+ configTokens?.allow
41
+ ?.filter((token) => token.chainId === selectedChainId)
42
+ .map((token) => token.address),
47
43
  );
48
- }
49
- const deniedTokenAddresses = configTokens?.deny
50
- ?.filter((token) => token.chainId === selectedChainId)
51
- .map((token) => token.address);
52
- if (deniedTokenAddresses?.length) {
53
- filteredTokens = filteredTokens?.filter(
54
- (token) => !deniedTokenAddresses.includes(token.address),
44
+
45
+ const deniedTokenAddressesSet = new Set(
46
+ configTokens?.deny
47
+ ?.filter((token) => token.chainId === selectedChainId)
48
+ .map((token) => token.address),
55
49
  );
50
+
51
+ if (allowedTokensSet.size || deniedTokenAddressesSet.size) {
52
+ filteredTokens = filteredTokens.filter(
53
+ (token) =>
54
+ (!allowedTokensSet.size || allowedTokensSet.has(token.address)) &&
55
+ !deniedTokenAddressesSet.has(token.address),
56
+ );
57
+ }
56
58
  }
57
- const featuredTokenAddresses = new Set(
58
- featuredTokens?.map((token) => token.address),
59
+ const filteredTokensMap = new Map(
60
+ filteredTokens.map((token) => [token.address, token]),
59
61
  );
60
- const tokens = [
61
- ...(featuredTokens?.map((token) => {
62
- (token as TokenAmount).featured = true;
62
+
63
+ const [popularTokens, featuredTokens] = (
64
+ ['popular', 'featured'] as ('popular' | 'featured')[]
65
+ ).map((tokenType) => {
66
+ const typedConfigTokens = configTokens?.[tokenType]?.filter(
67
+ (token) => token.chainId === selectedChainId,
68
+ );
69
+
70
+ const populatedConfigTokens = typedConfigTokens?.map((token) => {
71
+ // Mark token as popular
72
+ (token as TokenAmount)[tokenType] = true;
73
+ // Check if this token exists in the filteredTokensMap and add priceUSD if it does
74
+ const matchingFilteredToken = filteredTokensMap.get(token.address);
75
+ if (matchingFilteredToken?.priceUSD) {
76
+ (token as TokenAmount).priceUSD = matchingFilteredToken.priceUSD;
77
+ }
78
+ if (!token.logoURI && matchingFilteredToken) {
79
+ (token as TokenAmount).logoURI = matchingFilteredToken.logoURI;
80
+ }
63
81
  return token;
64
- }) ?? []),
65
- ...(filteredTokens?.filter(
66
- (token) => !featuredTokenAddresses.has(token.address),
67
- ) ?? []),
68
- ] as TokenAmount[];
82
+ }) as TokenAmount[];
83
+
84
+ if (populatedConfigTokens?.length) {
85
+ const configTokenAddresses = new Set(
86
+ populatedConfigTokens?.map((token) => token.address),
87
+ );
88
+ filteredTokens = filteredTokens.filter(
89
+ (token) => !configTokenAddresses.has(token.address),
90
+ );
91
+ populatedConfigTokens.push(...filteredTokens);
92
+ filteredTokens = populatedConfigTokens;
93
+ }
94
+
95
+ return populatedConfigTokens;
96
+ });
69
97
 
70
98
  return {
71
- tokens,
99
+ tokens: filteredTokens,
100
+ featuredTokens,
101
+ popularTokens,
72
102
  chain,
73
103
  };
74
104
  }, [
75
105
  chains,
76
- configTokens?.allow,
77
- configTokens?.deny,
78
- configTokens?.include,
79
- data?.tokens,
80
- featuredTokens,
106
+ configTokens,
107
+ data,
81
108
  getChainById,
82
109
  isSupportedChainsLoading,
83
110
  selectedChainId,
@@ -85,6 +112,8 @@ export const useTokens = (selectedChainId?: number) => {
85
112
 
86
113
  return {
87
114
  tokens: filteredData?.tokens,
115
+ featuredTokens: filteredData?.featuredTokens,
116
+ popularTokens: filteredData?.popularTokens,
88
117
  chain: filteredData?.chain,
89
118
  isLoading,
90
119
  };
package/i18n/en.json CHANGED
@@ -174,6 +174,7 @@
174
174
  "deselectAll": "Deselect all"
175
175
  },
176
176
  "main": {
177
+ "allTokens": "All tokens",
177
178
  "crossStepDetails": "Bridge from {{from}} to {{to}} via {{tool}}",
178
179
  "currentAmount": "Current amount",
179
180
  "featuredTokens": "Featured tokens",
@@ -189,10 +190,11 @@
189
190
  "fromAmount": "You pay",
190
191
  "gasCost": "Gas cost",
191
192
  "inProgress": "in progress",
193
+ "myTokens": "My tokens",
192
194
  "nftStepDetails": "Purchase NFT via {{tool}}",
193
195
  "onChain": "on {{chainName}}",
194
- "otherTokens": "Other tokens",
195
196
  "ownedBy": "Owned by",
197
+ "popularTokens": "Popular tokens",
196
198
  "process": {
197
199
  "crossChain": {
198
200
  "actionRequired": "Please sign the transaction",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifi/widget",
3
- "version": "3.0.0-alpha.22",
3
+ "version": "3.0.0-alpha.23",
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": "./_cjs/index.js",
@@ -44,19 +44,19 @@
44
44
  "@solana/wallet-adapter-react": "^0.15.35",
45
45
  "@solana/wallet-adapter-wallets": "^0.19.26",
46
46
  "@solana/web3.js": "^1.89.1",
47
- "@tanstack/react-query": "^5.18.0",
48
- "@tanstack/react-virtual": "^3.0.2",
47
+ "@tanstack/react-query": "^5.18.1",
48
+ "@tanstack/react-virtual": "^3.0.4",
49
49
  "i18next": "^23.8.2",
50
50
  "microdiff": "^1.3.2",
51
51
  "mitt": "^3.0.1",
52
52
  "react": "^18.2.0",
53
53
  "react-dom": "^18.2.0",
54
- "react-i18next": "^14.0.1",
55
- "react-intersection-observer": "^9.6.0",
54
+ "react-i18next": "^14.0.3",
55
+ "react-intersection-observer": "^9.7.0",
56
56
  "react-router-dom": "^6.22.0",
57
57
  "react-timer-hook": "^3.0.7",
58
58
  "uuid": "^9.0.1",
59
- "viem": "^2.7.1",
59
+ "viem": "^2.7.6",
60
60
  "wagmi": "^2.5.5",
61
61
  "zustand": "^4.5.0"
62
62
  },
@@ -63,7 +63,7 @@ export const RoutesPage: React.FC<BoxProps> = () => {
63
63
  ) : (
64
64
  routes?.map((route: Route, index: number) => (
65
65
  <RouteCard
66
- key={route.id}
66
+ key={index}
67
67
  route={route}
68
68
  onClick={() => handleRouteClick(route)}
69
69
  active={index === 0}
@@ -55,52 +55,33 @@ export const SelectEnabledToolsPage: React.FC<{
55
55
  }> = ({ type }) => {
56
56
  const typeKey = type.toLowerCase() as 'bridges' | 'exchanges';
57
57
  const { tools } = useTools();
58
- const [enabledTools, setTools] = useSettingsStore(
59
- (state) => [state[`enabled${type}`], state.setTools],
60
- shallow,
61
- );
58
+ const [enabledTools, disabledTools, setToolValue, toggleTools] =
59
+ useSettingsStore(
60
+ (state) => [
61
+ state[`enabled${type}`],
62
+ state[`disabled${type}`],
63
+ state.setToolValue,
64
+ state.toggleTools,
65
+ ],
66
+ shallow,
67
+ );
62
68
  const headerStoreContext = useHeaderStoreContext();
63
69
 
64
70
  const handleClick = (key: string) => {
65
- if (!tools) {
66
- return;
67
- }
68
- const toolKeys = tools[typeKey].map((tool) => tool.key);
69
- if (enabledTools?.includes(key)) {
70
- setTools(
71
- type,
72
- enabledTools.filter((toolKey) => toolKey !== key),
73
- toolKeys,
74
- );
75
- } else {
76
- setTools(type, [...enabledTools, key], toolKeys);
77
- }
71
+ setToolValue(type, key, !enabledTools[key]);
78
72
  };
79
73
 
80
74
  useEffect(() => {
81
- const allToolsSelected = tools?.[typeKey].length === enabledTools.length;
82
- const toggleCheckboxes = () => {
83
- if (!tools) {
84
- return;
85
- }
86
- const toolKeys = tools[typeKey].map((tool) => tool.key);
87
- if (allToolsSelected) {
88
- setTools(type, [], toolKeys);
89
- } else {
90
- setTools(type, toolKeys, toolKeys);
91
- }
92
- };
93
-
94
75
  return headerStoreContext
95
76
  .getState()
96
77
  .setAction(
97
78
  <SelectAllCheckbox
98
- allCheckboxesSelected={allToolsSelected}
99
- anyCheckboxesSelected={!!enabledTools.length}
100
- onClick={toggleCheckboxes}
79
+ allCheckboxesSelected={!disabledTools.length}
80
+ anyCheckboxesSelected={Boolean(disabledTools.length)}
81
+ onClick={() => toggleTools(type)}
101
82
  />,
102
83
  );
103
- }, [enabledTools.length, headerStoreContext, setTools, tools, type, typeKey]);
84
+ }, [disabledTools.length, headerStoreContext, toggleTools, type]);
104
85
 
105
86
  return (
106
87
  <PageContainer disableGutters>
@@ -121,7 +102,7 @@ export const SelectEnabledToolsPage: React.FC<{
121
102
  </Avatar>
122
103
  </ListItemAvatar>
123
104
  <ListItemText primary={tool.name} />
124
- {enabledTools?.includes(tool.key) && <CheckIcon color="primary" />}
105
+ {enabledTools[tool.key] && <CheckIcon color="primary" />}
125
106
  </SettingsListItemButton>
126
107
  ))}
127
108
  </List>
@@ -3,10 +3,10 @@ import SwapHorizIcon from '@mui/icons-material/SwapHoriz';
3
3
  import { useTranslation } from 'react-i18next';
4
4
  import { useNavigate } from 'react-router-dom';
5
5
  import { shallow } from 'zustand/shallow';
6
+ import { CardButton } from '../../components/Card';
6
7
  import { useSettingMonitor } from '../../hooks';
7
8
  import { useSettingsStore } from '../../stores';
8
9
  import { navigationRoutes } from '../../utils';
9
- import { CardButton } from '../../components/Card';
10
10
  import { BadgedValue } from './SettingsCard';
11
11
 
12
12
  const supportedIcons = {
@@ -21,7 +21,7 @@ export const BridgeAndExchangeSettings: React.FC<{
21
21
  const { t } = useTranslation();
22
22
  const navigate = useNavigate();
23
23
  const [enabledTools, tools] = useSettingsStore((state) => {
24
- const enabledTools = Object.values(state[`_enabled${type}`] ?? {});
24
+ const enabledTools = Object.values(state[`enabled${type}`]);
25
25
  return [enabledTools.filter(Boolean).length, enabledTools.length];
26
26
  }, shallow);
27
27