@lifi/widget 3.0.0-alpha.21 → 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 (147) hide show
  1. package/App.tsx +16 -17
  2. package/AppDrawer.tsx +2 -2
  3. package/_cjs/App.js +2 -3
  4. package/_cjs/App.js.map +1 -1
  5. package/_cjs/AppDrawer.d.ts +2 -2
  6. package/_cjs/components/AccountAvatar.js +2 -2
  7. package/_cjs/components/AccountAvatar.js.map +1 -1
  8. package/_cjs/components/AmountInput/AmountInputEndAdornment.js +11 -7
  9. package/_cjs/components/AmountInput/AmountInputEndAdornment.js.map +1 -1
  10. package/_cjs/components/Routes/RoutesExpanded.js +1 -1
  11. package/_cjs/components/Routes/RoutesExpanded.js.map +1 -1
  12. package/_cjs/components/TokenAvatar/TokenAvatar.style.js +4 -0
  13. package/_cjs/components/TokenAvatar/TokenAvatar.style.js.map +1 -1
  14. package/_cjs/components/TokenList/TokenList.js +5 -2
  15. package/_cjs/components/TokenList/TokenList.js.map +1 -1
  16. package/_cjs/components/TokenList/VirtualizedTokenList.js +41 -19
  17. package/_cjs/components/TokenList/VirtualizedTokenList.js.map +1 -1
  18. package/_cjs/components/TokenList/types.d.ts +3 -2
  19. package/_cjs/config/version.d.ts +1 -1
  20. package/_cjs/config/version.js +1 -1
  21. package/_cjs/hooks/index.d.ts +0 -1
  22. package/_cjs/hooks/index.js +0 -1
  23. package/_cjs/hooks/index.js.map +1 -1
  24. package/_cjs/hooks/useAvailableChains.js +3 -3
  25. package/_cjs/hooks/useAvailableChains.js.map +1 -1
  26. package/_cjs/hooks/useDebouncedWatch.d.ts +1 -1
  27. package/_cjs/hooks/useRoutes.js +49 -25
  28. package/_cjs/hooks/useRoutes.js.map +1 -1
  29. package/_cjs/hooks/useSettingMonitor.js +8 -12
  30. package/_cjs/hooks/useSettingMonitor.js.map +1 -1
  31. package/_cjs/hooks/useToken.d.ts +1 -1
  32. package/_cjs/hooks/useTokenAddressBalance.d.ts +3 -2
  33. package/_cjs/hooks/useTokenAddressBalance.js.map +1 -1
  34. package/_cjs/hooks/useTokenBalances.d.ts +3 -2
  35. package/_cjs/hooks/useTokenBalances.js +33 -14
  36. package/_cjs/hooks/useTokenBalances.js.map +1 -1
  37. package/_cjs/hooks/useTokens.d.ts +3 -1
  38. package/_cjs/hooks/useTokens.js +44 -31
  39. package/_cjs/hooks/useTokens.js.map +1 -1
  40. package/_cjs/i18n/en.json +3 -1
  41. package/_cjs/pages/RoutesPage/RoutesPage.js +1 -1
  42. package/_cjs/pages/RoutesPage/RoutesPage.js.map +1 -1
  43. package/_cjs/pages/SelectEnabledToolsPage/SelectEnabledToolsPage.js +10 -27
  44. package/_cjs/pages/SelectEnabledToolsPage/SelectEnabledToolsPage.js.map +1 -1
  45. package/_cjs/pages/SettingsPage/BridgeAndExchangeSettings.js +2 -2
  46. package/_cjs/pages/SettingsPage/BridgeAndExchangeSettings.js.map +1 -1
  47. package/_cjs/stores/form/createFormStore.js +10 -6
  48. package/_cjs/stores/form/createFormStore.js.map +1 -1
  49. package/_cjs/stores/form/types.d.ts +3 -14
  50. package/_cjs/stores/form/types.js.map +1 -1
  51. package/_cjs/stores/form/useFieldController.d.ts +1 -1
  52. package/_cjs/stores/form/useFieldValues.d.ts +1 -1
  53. package/_cjs/stores/settings/types.d.ts +10 -9
  54. package/_cjs/stores/settings/types.js.map +1 -1
  55. package/_cjs/stores/settings/useSettingsStore.js +40 -30
  56. package/_cjs/stores/settings/useSettingsStore.js.map +1 -1
  57. package/_cjs/types/token.d.ts +1 -0
  58. package/_cjs/types/widget.d.ts +9 -8
  59. package/_esm/App.js +2 -3
  60. package/_esm/App.js.map +1 -1
  61. package/_esm/AppDrawer.d.ts +2 -2
  62. package/_esm/components/AccountAvatar.js +2 -2
  63. package/_esm/components/AccountAvatar.js.map +1 -1
  64. package/_esm/components/AmountInput/AmountInputEndAdornment.js +11 -7
  65. package/_esm/components/AmountInput/AmountInputEndAdornment.js.map +1 -1
  66. package/_esm/components/Routes/RoutesExpanded.js +1 -1
  67. package/_esm/components/Routes/RoutesExpanded.js.map +1 -1
  68. package/_esm/components/TokenAvatar/TokenAvatar.style.js +4 -0
  69. package/_esm/components/TokenAvatar/TokenAvatar.style.js.map +1 -1
  70. package/_esm/components/TokenList/TokenList.js +6 -3
  71. package/_esm/components/TokenList/TokenList.js.map +1 -1
  72. package/_esm/components/TokenList/VirtualizedTokenList.js +41 -19
  73. package/_esm/components/TokenList/VirtualizedTokenList.js.map +1 -1
  74. package/_esm/components/TokenList/types.d.ts +3 -2
  75. package/_esm/config/version.d.ts +1 -1
  76. package/_esm/config/version.js +1 -1
  77. package/_esm/hooks/index.d.ts +0 -1
  78. package/_esm/hooks/index.js +0 -1
  79. package/_esm/hooks/index.js.map +1 -1
  80. package/_esm/hooks/useAvailableChains.js +3 -3
  81. package/_esm/hooks/useAvailableChains.js.map +1 -1
  82. package/_esm/hooks/useDebouncedWatch.d.ts +1 -1
  83. package/_esm/hooks/useRoutes.js +49 -25
  84. package/_esm/hooks/useRoutes.js.map +1 -1
  85. package/_esm/hooks/useSettingMonitor.js +8 -12
  86. package/_esm/hooks/useSettingMonitor.js.map +1 -1
  87. package/_esm/hooks/useToken.d.ts +1 -1
  88. package/_esm/hooks/useTokenAddressBalance.d.ts +3 -2
  89. package/_esm/hooks/useTokenAddressBalance.js.map +1 -1
  90. package/_esm/hooks/useTokenBalances.d.ts +3 -2
  91. package/_esm/hooks/useTokenBalances.js +33 -14
  92. package/_esm/hooks/useTokenBalances.js.map +1 -1
  93. package/_esm/hooks/useTokens.d.ts +3 -1
  94. package/_esm/hooks/useTokens.js +44 -31
  95. package/_esm/hooks/useTokens.js.map +1 -1
  96. package/_esm/i18n/en.json +3 -1
  97. package/_esm/pages/RoutesPage/RoutesPage.js +1 -1
  98. package/_esm/pages/RoutesPage/RoutesPage.js.map +1 -1
  99. package/_esm/pages/SelectEnabledToolsPage/SelectEnabledToolsPage.js +10 -27
  100. package/_esm/pages/SelectEnabledToolsPage/SelectEnabledToolsPage.js.map +1 -1
  101. package/_esm/pages/SettingsPage/BridgeAndExchangeSettings.js +2 -2
  102. package/_esm/pages/SettingsPage/BridgeAndExchangeSettings.js.map +1 -1
  103. package/_esm/stores/form/createFormStore.js +10 -6
  104. package/_esm/stores/form/createFormStore.js.map +1 -1
  105. package/_esm/stores/form/types.d.ts +3 -14
  106. package/_esm/stores/form/types.js.map +1 -1
  107. package/_esm/stores/form/useFieldController.d.ts +1 -1
  108. package/_esm/stores/form/useFieldValues.d.ts +1 -1
  109. package/_esm/stores/settings/types.d.ts +10 -9
  110. package/_esm/stores/settings/types.js.map +1 -1
  111. package/_esm/stores/settings/useSettingsStore.js +40 -30
  112. package/_esm/stores/settings/useSettingsStore.js.map +1 -1
  113. package/_esm/types/token.d.ts +1 -0
  114. package/_esm/types/widget.d.ts +9 -8
  115. package/components/AccountAvatar.tsx +4 -7
  116. package/components/AmountInput/AmountInputEndAdornment.tsx +15 -12
  117. package/components/Routes/RoutesExpanded.tsx +1 -1
  118. package/components/TokenAvatar/TokenAvatar.style.tsx +4 -0
  119. package/components/TokenList/TokenList.tsx +8 -2
  120. package/components/TokenList/VirtualizedTokenList.tsx +63 -41
  121. package/components/TokenList/types.ts +3 -2
  122. package/config/version.ts +1 -1
  123. package/hooks/index.ts +0 -1
  124. package/hooks/useAvailableChains.ts +7 -4
  125. package/hooks/useRoutes.ts +58 -31
  126. package/hooks/useSettingMonitor.ts +20 -21
  127. package/hooks/useTokenAddressBalance.ts +2 -1
  128. package/hooks/useTokenBalances.ts +34 -29
  129. package/hooks/useTokens.ts +65 -36
  130. package/i18n/en.json +3 -1
  131. package/package.json +9 -9
  132. package/pages/RoutesPage/RoutesPage.tsx +1 -1
  133. package/pages/SelectEnabledToolsPage/SelectEnabledToolsPage.tsx +16 -35
  134. package/pages/SettingsPage/BridgeAndExchangeSettings.tsx +2 -2
  135. package/stores/form/createFormStore.ts +15 -15
  136. package/stores/form/types.ts +3 -14
  137. package/stores/settings/types.ts +10 -13
  138. package/stores/settings/useSettingsStore.ts +51 -53
  139. package/types/token.ts +1 -0
  140. package/types/widget.ts +10 -9
  141. package/_cjs/hooks/useFeaturedTokens.d.ts +0 -1
  142. package/_cjs/hooks/useFeaturedTokens.js +0 -11
  143. package/_cjs/hooks/useFeaturedTokens.js.map +0 -1
  144. package/_esm/hooks/useFeaturedTokens.d.ts +0 -1
  145. package/_esm/hooks/useFeaturedTokens.js +0 -7
  146. package/_esm/hooks/useFeaturedTokens.js.map +0 -1
  147. package/hooks/useFeaturedTokens.ts +0 -12
@@ -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.21",
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",
@@ -35,8 +35,8 @@
35
35
  "dependencies": {
36
36
  "@emotion/react": "^11.11.3",
37
37
  "@emotion/styled": "^11.11.0",
38
- "@lifi/sdk": "^3.0.0-alpha.53",
39
- "@lifi/wallet-management": "^3.0.0-alpha.14",
38
+ "@lifi/sdk": "^3.0.0-alpha.54",
39
+ "@lifi/wallet-management": "^3.0.0-alpha.15",
40
40
  "@mui/icons-material": "^5.15.7",
41
41
  "@mui/lab": "^5.0.0-alpha.163",
42
42
  "@mui/material": "^5.15.7",
@@ -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",
56
- "react-router-dom": "^6.21.3",
54
+ "react-i18next": "^14.0.3",
55
+ "react-intersection-observer": "^9.7.0",
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
 
@@ -7,7 +7,6 @@ import type {
7
7
  FormValueControl,
8
8
  FormValues,
9
9
  FormValuesState,
10
- GenericFormValue,
11
10
  } from './types';
12
11
 
13
12
  export const formDefaultValues: DefaultValues = {
@@ -21,9 +20,7 @@ export const formDefaultValues: DefaultValues = {
21
20
  toAmount: '',
22
21
  };
23
22
 
24
- const defaultValueToFormValue = (
25
- value: GenericFormValue,
26
- ): FormValueControl<GenericFormValue> => ({
23
+ const defaultValueToFormValue = <T>(value: T): FormValueControl<T> => ({
27
24
  isTouched: false,
28
25
  isDirty: false,
29
26
  value,
@@ -31,22 +28,25 @@ const defaultValueToFormValue = (
31
28
 
32
29
  const valuesToFormValues = (defaultValues: DefaultValues): FormValues => {
33
30
  return (Object.keys(defaultValues) as FormFieldNames[]).reduce(
34
- (accum, key) => {
35
- return { ...accum, [key]: defaultValueToFormValue(defaultValues[key]) };
36
- },
37
- {},
38
- ) as FormValues;
31
+ (accum, key) => ({
32
+ ...accum,
33
+ [key]: defaultValueToFormValue(defaultValues[key]),
34
+ }),
35
+ {} as FormValues,
36
+ );
39
37
  };
40
38
 
41
39
  const isString = (str: any) => typeof str === 'string' || str instanceof String;
42
40
 
43
41
  const getUpdatedTouchedFields = (userValues: FormValues) => {
44
- return Object.keys(userValues).reduce(
45
- (accum, key) =>
46
- userValues[key as FormFieldNames]?.isTouched
47
- ? { ...accum, [key]: true }
48
- : accum,
49
- {},
42
+ return (Object.keys(userValues) as FormFieldNames[]).reduce(
43
+ (accum, key) => {
44
+ if (userValues[key]?.isTouched) {
45
+ accum[key] = true;
46
+ }
47
+ return accum;
48
+ },
49
+ {} as Record<FormFieldNames, boolean>,
50
50
  );
51
51
  };
52
52
 
@@ -23,20 +23,9 @@ export interface FormValueControl<T> {
23
23
  value: T;
24
24
  }
25
25
 
26
- export interface FormValues {
27
- contractOutputsToken: FormValueControl<string>;
28
- fromAmount: FormValueControl<string>;
29
- fromChain?: FormValueControl<number | undefined>;
30
- fromToken?: FormValueControl<string | undefined>;
31
- toAddress: FormValueControl<string>;
32
- toAmount: FormValueControl<string>;
33
- toChain?: FormValueControl<number | undefined>;
34
- toContractAddress: FormValueControl<string>;
35
- toContractCallData: FormValueControl<string>;
36
- toContractGasLimit: FormValueControl<string>;
37
- toToken?: FormValueControl<string | undefined>;
38
- tokenSearchFilter: FormValueControl<string>;
39
- }
26
+ export type FormValues = {
27
+ [Property in keyof DefaultValues]: FormValueControl<DefaultValues[Property]>;
28
+ };
40
29
 
41
30
  export type FormFieldNames = keyof FormValues;
42
31
  export type ExtractValueType<T> =
@@ -2,7 +2,7 @@ import type { Order } from '@lifi/sdk';
2
2
  import type { PropsWithChildren } from 'react';
3
3
  import type { StoreApi } from 'zustand';
4
4
  import type { UseBoundStoreWithEqualityFn } from 'zustand/traditional';
5
- import type { Appearance, WidgetConfig } from '../../types';
5
+ import type { Appearance } from '../../types';
6
6
 
7
7
  export type ValueSetter<S> = <K extends keyof S>(
8
8
  key: K,
@@ -13,8 +13,8 @@ export type ValuesSetter<S> = <K extends keyof S>(
13
13
  values: Record<K, S[Extract<K, string>]>,
14
14
  ) => void;
15
15
 
16
- export type SettingsToolType = 'Bridges' | 'Exchanges';
17
- export const SettingsToolTypes: SettingsToolType[] = ['Bridges', 'Exchanges'];
16
+ export const SettingsToolTypes = ['Bridges', 'Exchanges'] as const;
17
+ export type SettingsToolType = (typeof SettingsToolTypes)[number];
18
18
 
19
19
  export interface SettingsProps {
20
20
  appearance: Appearance;
@@ -24,10 +24,10 @@ export interface SettingsProps {
24
24
  enabledAutoRefuel: boolean;
25
25
  showDestinationWallet: boolean;
26
26
  slippage?: string;
27
- enabledBridges: string[];
28
- _enabledBridges?: Record<string, boolean>;
29
- enabledExchanges: string[];
30
- _enabledExchanges?: Record<string, boolean>;
27
+ disabledBridges: string[];
28
+ enabledBridges: Record<string, boolean>;
29
+ disabledExchanges: string[];
30
+ enabledExchanges: Record<string, boolean>;
31
31
  }
32
32
 
33
33
  export interface SettingsState extends SettingsProps {
@@ -38,12 +38,9 @@ export interface SettingsState extends SettingsProps {
38
38
  tools: string[],
39
39
  reset?: boolean,
40
40
  ): void;
41
- setTools(
42
- toolType: SettingsToolType,
43
- tools: string[],
44
- availableTools: string[],
45
- ): void;
46
- reset(config: WidgetConfig, bridges: string[], exchanges: string[]): void;
41
+ setToolValue(toolType: SettingsToolType, tool: string, value: boolean): void;
42
+ toggleTools(toolType: SettingsToolType): void;
43
+ reset(bridges: string[], exchanges: string[]): void;
47
44
  }
48
45
 
49
46
  export interface SendToWalletState {
@@ -21,8 +21,10 @@ export const defaultSettings: SettingsProps = {
21
21
  gasPrice: 'normal',
22
22
  enabledAutoRefuel: true,
23
23
  showDestinationWallet: true,
24
- enabledBridges: [],
25
- enabledExchanges: [],
24
+ disabledBridges: [],
25
+ disabledExchanges: [],
26
+ enabledBridges: {},
27
+ enabledExchanges: {},
26
28
  };
27
29
 
28
30
  export const useSettingsStore = createWithEqualityFn<SettingsState>(
@@ -49,60 +51,57 @@ export const useSettingsStore = createWithEqualityFn<SettingsState>(
49
51
  }
50
52
  set((state) => {
51
53
  const updatedState = { ...state };
52
- if (updatedState[`_enabled${toolType}`] && !reset) {
54
+ if (!reset) {
53
55
  // Add new tools
54
- const enabledTools = tools
55
- .filter(
56
- (tool) =>
57
- !Object.hasOwn(
58
- updatedState[`_enabled${toolType}`] as object,
59
- tool,
60
- ),
61
- )
62
- .reduce(
63
- (values, tool) => {
64
- values[tool] = true;
65
- return values;
66
- },
67
- updatedState[`_enabled${toolType}`] as Record<string, boolean>,
68
- );
56
+ tools.forEach((tool) => {
57
+ if (!Object.hasOwn(updatedState[`enabled${toolType}`], tool)) {
58
+ updatedState[`enabled${toolType}`][tool] = true;
59
+ }
60
+ });
69
61
  // Filter tools we no longer have
70
- updatedState[`_enabled${toolType}`] = Object.fromEntries(
71
- Object.entries(enabledTools).filter(([key]) =>
72
- tools.includes(key),
62
+ updatedState[`enabled${toolType}`] = Object.fromEntries(
63
+ Object.entries(updatedState[`enabled${toolType}`]).filter(
64
+ ([key]) => tools.includes(key),
73
65
  ),
74
66
  );
75
67
  } else {
76
- updatedState[`_enabled${toolType}`] = tools.reduce(
77
- (values, tool) => {
78
- values[tool] = true;
79
- return values;
80
- },
81
- {} as Record<string, boolean>,
82
- );
68
+ tools.forEach((tool) => {
69
+ updatedState[`enabled${toolType}`][tool] = true;
70
+ });
83
71
  }
84
- updatedState[`enabled${toolType}`] = Object.entries(
85
- updatedState[`_enabled${toolType}`]!,
86
- )
87
- .filter(([_, value]) => value)
88
- .map(([key]) => key);
72
+ updatedState[`disabled${toolType}`] = Object.keys(
73
+ updatedState[`enabled${toolType}`],
74
+ ).filter((key) => !updatedState[`enabled${toolType}`][key]);
89
75
  return updatedState;
90
76
  });
91
77
  },
92
- setTools: (toolType, tools, availableTools) =>
93
- set(() => ({
94
- [`enabled${toolType}`]: tools,
95
- [`_enabled${toolType}`]: availableTools.reduce(
96
- (values, toolKey) => {
97
- values[toolKey] = tools.includes(toolKey);
98
- return values;
99
- },
100
- {} as Record<string, boolean>,
101
- ),
102
- })),
103
- reset: (config, bridges, exchanges) => {
78
+ setToolValue: (toolType, tool, value) =>
79
+ set((state) => {
80
+ const enabledTools = {
81
+ ...state[`enabled${toolType}`],
82
+ [tool]: value,
83
+ };
84
+ return {
85
+ [`enabled${toolType}`]: enabledTools,
86
+ [`disabled${toolType}`]: Object.keys(enabledTools).filter(
87
+ (key) => !enabledTools[key],
88
+ ),
89
+ };
90
+ }),
91
+ toggleTools: (toolType) =>
92
+ set((state) => {
93
+ const enabledTools = { ...state[`enabled${toolType}`] };
94
+ const enableAll = Boolean(state[`disabled${toolType}`].length);
95
+ for (const toolKey in enabledTools) {
96
+ enabledTools[toolKey] = enableAll;
97
+ }
98
+ return {
99
+ [`enabled${toolType}`]: enabledTools,
100
+ [`disabled${toolType}`]: enableAll ? [] : Object.keys(enabledTools),
101
+ };
102
+ }),
103
+ reset: (bridges, exchanges) => {
104
104
  const { appearance, ...restDefaultSettings } = defaultSettings;
105
-
106
105
  set(() => ({
107
106
  ...restDefaultSettings,
108
107
  ...defaultConfigurableSettings,
@@ -113,19 +112,18 @@ export const useSettingsStore = createWithEqualityFn<SettingsState>(
113
112
  }),
114
113
  {
115
114
  name: `li.fi-widget-settings`,
116
- version: 2,
115
+ version: 3,
117
116
  partialize: (state) => {
118
- const { enabledBridges, enabledExchanges, ...partializedState } = state;
117
+ const { disabledBridges, disabledExchanges, ...partializedState } =
118
+ state;
119
119
  return partializedState;
120
120
  },
121
121
  merge: (persistedState: any, currentState: SettingsState) => {
122
122
  const state = { ...currentState, ...persistedState };
123
123
  SettingsToolTypes.forEach((toolType) => {
124
- state[`enabled${toolType}`] = Object.entries(
125
- persistedState[`_enabled${toolType}`],
126
- )
127
- .filter(([_, value]) => value)
128
- .map(([key]) => key);
124
+ state[`disabled${toolType}`] = Object.keys(
125
+ persistedState[`enabled${toolType}`],
126
+ ).filter((key) => !persistedState[`enabled${toolType}`][key]);
129
127
  });
130
128
  return state;
131
129
  },
package/types/token.ts CHANGED
@@ -2,4 +2,5 @@ import type { TokenAmount as SDKTokenAmount } from '@lifi/sdk';
2
2
 
3
3
  export interface TokenAmount extends SDKTokenAmount {
4
4
  featured?: boolean;
5
+ popular?: boolean;
5
6
  }
package/types/widget.ts CHANGED
@@ -160,6 +160,7 @@ export interface WidgetConfig {
160
160
  tokens?: {
161
161
  featured?: StaticToken[];
162
162
  include?: Token[];
163
+ popular?: StaticToken[];
163
164
  } & AllowDeny<BaseToken>;
164
165
  languages?: {
165
166
  default?: LanguageKey;
@@ -167,15 +168,6 @@ export interface WidgetConfig {
167
168
  languageResources?: LanguageResources;
168
169
  }
169
170
 
170
- export type WidgetDrawerProps = {
171
- elementRef?: RefObject<HTMLDivElement>;
172
- open?: boolean;
173
- /**
174
- * Make sure to make the onClose callback stable (e.g. using useCallback) to avoid causing re-renders of the entire widget
175
- */
176
- onClose?(): void;
177
- };
178
-
179
171
  export interface WidgetConfigProps {
180
172
  config: WidgetConfig;
181
173
  }
@@ -187,3 +179,12 @@ export interface WidgetConfigPartialProps {
187
179
  export type WidgetProps = WidgetDrawerProps &
188
180
  WidgetConfig &
189
181
  WidgetConfigPartialProps;
182
+
183
+ export interface WidgetDrawerProps extends WidgetConfigPartialProps {
184
+ elementRef?: RefObject<HTMLDivElement>;
185
+ open?: boolean;
186
+ /**
187
+ * Make sure to make the onClose callback stable (e.g. using useCallback) to avoid causing re-renders of the entire widget
188
+ */
189
+ onClose?(): void;
190
+ }
@@ -1 +0,0 @@
1
- export declare const useFeaturedTokens: (selectedChainId?: number) => import("@lifi/types").StaticToken[] | undefined;
@@ -1,11 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useFeaturedTokens = void 0;
4
- const react_1 = require("react");
5
- const providers_1 = require("../providers");
6
- const useFeaturedTokens = (selectedChainId) => {
7
- const { tokens } = (0, providers_1.useWidgetConfig)();
8
- return (0, react_1.useMemo)(() => tokens?.featured?.filter((token) => token.chainId === selectedChainId), [selectedChainId, tokens?.featured]);
9
- };
10
- exports.useFeaturedTokens = useFeaturedTokens;
11
- //# sourceMappingURL=useFeaturedTokens.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useFeaturedTokens.js","sourceRoot":"","sources":["../../hooks/useFeaturedTokens.ts"],"names":[],"mappings":";;;AAAA,iCAAgC;AAChC,4CAA+C;AAExC,MAAM,iBAAiB,GAAG,CAAC,eAAwB,EAAE,EAAE;IAC5D,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,2BAAe,GAAE,CAAC;IAErC,OAAO,IAAA,eAAO,EACZ,GAAG,EAAE,CACH,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,KAAK,eAAe,CAAC,EACxE,CAAC,eAAe,EAAE,MAAM,EAAE,QAAQ,CAAC,CACpC,CAAC;AACJ,CAAC,CAAC;AARW,QAAA,iBAAiB,qBAQ5B"}
@@ -1 +0,0 @@
1
- export declare const useFeaturedTokens: (selectedChainId?: number) => import("@lifi/types").StaticToken[] | undefined;