@lifi/widget 3.37.0 → 3.38.1

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 (81) hide show
  1. package/CHANGELOG.md +26 -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/pages/LanguagesPage.js +6 -7
  38. package/dist/esm/pages/LanguagesPage.js.map +1 -1
  39. package/dist/esm/stores/StoreProvider.js +2 -1
  40. package/dist/esm/stores/StoreProvider.js.map +1 -1
  41. package/dist/esm/stores/chains/ChainOrderStore.js +20 -2
  42. package/dist/esm/stores/chains/ChainOrderStore.js.map +1 -1
  43. package/dist/esm/types/token.d.ts +6 -1
  44. package/dist/esm/types/widget.d.ts +11 -2
  45. package/dist/esm/types/widget.js +1 -0
  46. package/dist/esm/types/widget.js.map +1 -1
  47. package/dist/esm/utils/token.d.ts +12 -1
  48. package/dist/esm/utils/token.js +44 -0
  49. package/dist/esm/utils/token.js.map +1 -1
  50. package/dist/esm/utils/tokenList.js +2 -0
  51. package/dist/esm/utils/tokenList.js.map +1 -1
  52. package/dist/esm/utils/variant.d.ts +2 -0
  53. package/dist/esm/utils/variant.js +10 -0
  54. package/dist/esm/utils/variant.js.map +1 -0
  55. package/package.json +7 -7
  56. package/package.json.tmp +9 -9
  57. package/src/components/AmountInput/PriceFormHelperText.style.tsx +3 -0
  58. package/src/components/AppContainer.tsx +1 -0
  59. package/src/components/Avatar/AccountAvatar.tsx +3 -15
  60. package/src/components/Avatar/Avatar.tsx +6 -7
  61. package/src/components/Avatar/ChainBadgeContent.tsx +22 -0
  62. package/src/components/Avatar/TokenAvatar.tsx +3 -11
  63. package/src/components/Chains/AllChainsAvatar.tsx +56 -8
  64. package/src/components/Chains/ChainSearchInput.tsx +9 -1
  65. package/src/components/Header/NavigationHeader.tsx +5 -1
  66. package/src/components/SelectTokenButton/SelectTokenButton.tsx +9 -1
  67. package/src/components/TokenList/TokenListItem.tsx +23 -1
  68. package/src/config/version.ts +1 -1
  69. package/src/hooks/useFilteredByTokenBalances.ts +8 -4
  70. package/src/hooks/useRoutes.ts +17 -23
  71. package/src/hooks/useTokenSearch.ts +17 -10
  72. package/src/hooks/useTokens.ts +51 -83
  73. package/src/i18n/en.json +1 -0
  74. package/src/pages/LanguagesPage.tsx +1 -2
  75. package/src/stores/StoreProvider.tsx +2 -1
  76. package/src/stores/chains/ChainOrderStore.tsx +24 -2
  77. package/src/types/token.ts +5 -0
  78. package/src/types/widget.ts +10 -1
  79. package/src/utils/token.ts +65 -1
  80. package/src/utils/tokenList.ts +2 -0
  81. package/src/utils/variant.ts +16 -0
@@ -4,17 +4,23 @@ import {
4
4
  getTokens,
5
5
  type TokensExtendedResponse,
6
6
  } from '@lifi/sdk'
7
- import { useQuery, useQueryClient } from '@tanstack/react-query'
7
+ import { useQuery } from '@tanstack/react-query'
8
8
  import { useMemo } from 'react'
9
9
  import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
10
10
  import type { FormType } from '../stores/form/types.js'
11
+ import type { TokensByChain } from '../types/token.js'
11
12
  import {
12
13
  defaultChainIdsByType,
13
14
  getChainTypeFromAddress,
14
15
  } from '../utils/chainType.js'
15
16
  import { isItemAllowed } from '../utils/item.js'
16
17
  import { getQueryKey } from '../utils/queries.js'
17
- import { filterAllowedTokens } from '../utils/token.js'
18
+ import {
19
+ filterAllowedTokens,
20
+ mergeVerifiedWithSearchTokens,
21
+ } from '../utils/token.js'
22
+
23
+ const refetchInterval = 300_000
18
24
 
19
25
  export const useTokens = (
20
26
  formType?: FormType,
@@ -27,12 +33,8 @@ export const useTokens = (
27
33
  keyPrefix,
28
34
  } = useWidgetConfig()
29
35
 
30
- const { isLoading: isSearchLoading } = useBackgroundTokenSearch(
31
- search,
32
- chainId
33
- )
34
-
35
- const { data, isLoading } = useQuery({
36
+ // Main tokens cache - verified tokens from API
37
+ const { data: verifiedTokens, isLoading } = useQuery({
36
38
  queryKey: [getQueryKey('tokens', keyPrefix)],
37
39
  queryFn: async ({ signal }) => {
38
40
  const chainTypes = [
@@ -41,6 +43,7 @@ export const useTokens = (
41
43
  ChainType.UTXO,
42
44
  ChainType.MVM,
43
45
  ].filter((chainType) => isItemAllowed(chainType, chainsConfig?.types))
46
+
44
47
  const tokensResponse: TokensExtendedResponse = await getTokens(
45
48
  {
46
49
  chainTypes,
@@ -50,45 +53,37 @@ export const useTokens = (
50
53
  },
51
54
  { signal }
52
55
  )
53
- return tokensResponse
54
- },
55
- refetchInterval: 300_000,
56
- staleTime: 300_000,
57
- })
58
-
59
- const allTokens = useMemo(() => {
60
- return filterAllowedTokens(
61
- data?.tokens,
62
- configTokens,
63
- chainsConfig,
64
- formType
65
- )
66
- }, [data?.tokens, configTokens, chainsConfig, formType])
67
56
 
68
- return {
69
- allTokens,
70
- isLoading,
71
- isSearchLoading,
72
- }
73
- }
57
+ // Mark all tokens as verified
58
+ const tokens: TokensByChain = Object.fromEntries(
59
+ Object.entries(tokensResponse.tokens).map(([chainId, tokens]) => [
60
+ chainId,
61
+ tokens.map((token) => ({ ...token, verified: true })),
62
+ ])
63
+ )
74
64
 
75
- /** This hook is used to search for tokens in the background.
76
- * It updates the main tokens cache with the search results,
77
- * if any of the tokens are not already in the cache. */
78
- const useBackgroundTokenSearch = (search?: string, chainId?: number) => {
79
- const { chains: chainsConfig, keyPrefix } = useWidgetConfig()
80
- const queryClient = useQueryClient()
65
+ return tokens
66
+ },
67
+ refetchInterval,
68
+ staleTime: refetchInterval,
69
+ })
81
70
 
82
- const { isLoading: isSearchLoading } = useQuery({
71
+ // Search tokens cache - unverified tokens from search
72
+ const { data: searchTokens, isLoading: isSearchLoading } = useQuery({
83
73
  queryKey: [getQueryKey('tokens-search', keyPrefix), search, chainId],
84
74
  queryFn: async ({ queryKey, signal }) => {
85
- const [, searchQuery, chainId] = queryKey as [string, string, number]
75
+ const [, searchQuery, searchChainId] = queryKey as [
76
+ string,
77
+ string,
78
+ number,
79
+ ]
86
80
  const chainTypes = [
87
81
  ChainType.EVM,
88
82
  ChainType.SVM,
89
83
  ChainType.UTXO,
90
84
  ChainType.MVM,
91
85
  ].filter((chainType) => isItemAllowed(chainType, chainsConfig?.types))
86
+
92
87
  const tokensResponse: TokensExtendedResponse = await getTokens(
93
88
  {
94
89
  chainTypes,
@@ -101,7 +96,7 @@ const useBackgroundTokenSearch = (search?: string, chainId?: number) => {
101
96
  )
102
97
 
103
98
  // If the chainId is not provided, try to get it from the search query
104
- let _chainId = chainId
99
+ let _chainId = searchChainId
105
100
  if (!_chainId) {
106
101
  const chainType = getChainTypeFromAddress(searchQuery)
107
102
  if (chainType) {
@@ -121,57 +116,30 @@ const useBackgroundTokenSearch = (search?: string, chainId?: number) => {
121
116
  }
122
117
  }
123
118
 
124
- // Merge search results into main tokens cache
125
- if (searchQuery) {
126
- queryClient.setQueriesData<TokensExtendedResponse>(
127
- { queryKey: [getQueryKey('tokens', keyPrefix)] },
128
- (data) => {
129
- if (!data) {
130
- return data
131
- }
132
-
133
- const clonedData = { ...data, tokens: { ...data.tokens } }
134
-
135
- Object.entries(tokensResponse.tokens).forEach(
136
- ([chainId, searchTokens]) => {
137
- const chainIdNum = Number(chainId)
138
- const existingTokens = clonedData.tokens[chainIdNum] || []
139
-
140
- const existingTokenAddresses = new Set(
141
- existingTokens.map((token) => token.address.toLowerCase())
142
- )
143
-
144
- // Find tokens in search results that don't exist in the main list
145
- const newTokens = searchTokens.filter(
146
- (searchToken) =>
147
- !existingTokenAddresses.has(
148
- searchToken.address.toLowerCase()
149
- )
150
- )
151
-
152
- // Add new tokens to the main list
153
- if (newTokens.length > 0) {
154
- clonedData.tokens[chainIdNum] = [
155
- ...existingTokens,
156
- ...newTokens,
157
- ]
158
- }
159
- }
160
- )
161
-
162
- return clonedData
163
- }
164
- )
165
- }
119
+ // Mark all search tokens as unverified
120
+ const tokens: TokensByChain = Object.fromEntries(
121
+ Object.entries(tokensResponse.tokens).map(([chainId, tokens]) => [
122
+ chainId,
123
+ tokens.map((token) => ({ ...token, verified: false })),
124
+ ])
125
+ )
166
126
 
167
- return tokensResponse
127
+ return tokens
168
128
  },
169
129
  enabled: !!search,
170
- refetchInterval: 300_000,
171
- staleTime: 300_000,
130
+ refetchInterval,
131
+ staleTime: refetchInterval,
172
132
  })
173
133
 
134
+ // Merge tokens at read time - single place where caches are combined
135
+ const allTokens = useMemo(() => {
136
+ const merged = mergeVerifiedWithSearchTokens(verifiedTokens, searchTokens)
137
+ return filterAllowedTokens(merged, configTokens, chainsConfig, formType)
138
+ }, [verifiedTokens, searchTokens, configTokens, chainsConfig, formType])
139
+
174
140
  return {
175
- isLoading: isSearchLoading,
141
+ allTokens,
142
+ isLoading,
143
+ isSearchLoading,
176
144
  }
177
145
  }
package/src/i18n/en.json CHANGED
@@ -127,6 +127,7 @@
127
127
  "deleteTransactionHistory": "Transaction history is only stored locally and can't be recovered if you delete it.",
128
128
  "fundsLossPrevention": "Always ensure smart contract accounts are properly set up on the destination chain and avoid direct transfers to exchanges to prevent fund loss.",
129
129
  "highValueLoss": "The value of the received tokens is significantly lower than the exchanged tokens and transaction cost.",
130
+ "unverifiedToken": "Unverified token. Always do your own research before proceeding.",
130
131
  "insufficientFunds": "You don't have enough funds to complete the transaction.",
131
132
  "insufficientGas": "You don't have enough gas to complete the transaction. You need to add at least:",
132
133
  "minFromAmountUSD": "Minimum amount is {{amount, currencyExt(currency: USD)}}. Please enter a higher amount.",
@@ -1,4 +1,3 @@
1
- import Check from '@mui/icons-material/Check'
2
1
  import { List } from '@mui/material'
3
2
  import { useTranslation } from 'react-i18next'
4
3
  import { ListItemText } from '../components/ListItemText.js'
@@ -33,6 +32,7 @@ export const LanguagesPage: React.FC = () => {
33
32
  <SettingsListItemButton
34
33
  key={language}
35
34
  onClick={() => setLanguageWithCode(language)}
35
+ selected={selectedLanguageCode === language}
36
36
  >
37
37
  <ListItemText
38
38
  primary={
@@ -43,7 +43,6 @@ export const LanguagesPage: React.FC = () => {
43
43
  language
44
44
  }
45
45
  />
46
- {selectedLanguageCode === language && <Check color="primary" />}
47
46
  </SettingsListItemButton>
48
47
  ))}
49
48
  </List>
@@ -1,5 +1,6 @@
1
1
  import type { PropsWithChildren } from 'react'
2
2
  import type { WidgetConfigProps } from '../types/widget.js'
3
+ import { getSplitSubvariant } from '../utils/variant.js'
3
4
  import { BookmarkStoreProvider } from './bookmarks/BookmarkStore.js'
4
5
  import { ChainOrderStoreProvider } from './chains/ChainOrderStore.js'
5
6
  import { FormStoreProvider } from './form/FormStore.js'
@@ -16,7 +17,7 @@ export const StoreProvider: React.FC<PropsWithChildren<WidgetConfigProps>> = ({
16
17
  <SplitSubvariantStoreProvider
17
18
  state={
18
19
  config.subvariant === 'split'
19
- ? config.subvariantOptions?.split || 'swap'
20
+ ? getSplitSubvariant(config.subvariantOptions?.split)
20
21
  : undefined
21
22
  }
22
23
  >
@@ -3,8 +3,10 @@ import type { StoreApi } from 'zustand'
3
3
  import { useShallow } from 'zustand/shallow'
4
4
  import type { UseBoundStoreWithEqualityFn } from 'zustand/traditional'
5
5
  import { useChains } from '../../hooks/useChains.js'
6
+ import { useSwapOnly } from '../../hooks/useSwapOnly.js'
6
7
  import { useExternalWalletProvider } from '../../providers/WalletProvider/useExternalWalletProvider.js'
7
8
  import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
9
+ import { HiddenUI } from '../../types/widget.js'
8
10
  import { getConfigItemSets, isItemAllowedForSets } from '../../utils/item.js'
9
11
  import type { FormType } from '../form/types.js'
10
12
  import { useFieldActions } from '../form/useFieldActions.js'
@@ -20,10 +22,11 @@ export function ChainOrderStoreProvider({
20
22
  children,
21
23
  ...props
22
24
  }: PersistStoreProviderProps) {
23
- const { chains: chainsConfig } = useWidgetConfig()
25
+ const { chains: chainsConfig, hiddenUI } = useWidgetConfig()
24
26
  const storeRef = useRef<ChainOrderStore>(null)
25
27
  const { chains } = useChains()
26
28
  const { setFieldValue, getFieldValues } = useFieldActions()
29
+ const swapOnly = useSwapOnly()
27
30
  const { variant, subvariantOptions } = useWidgetConfig()
28
31
  const { externalChainTypes, useExternalWalletProvidersOnly } =
29
32
  useExternalWalletProvider()
@@ -62,18 +65,35 @@ export function ChainOrderStoreProvider({
62
65
  key
63
66
  )
64
67
 
68
+ const isSwapTo = swapOnly && key === 'to'
69
+
65
70
  // Show "All networks" button if there are multiple networks
66
- const showAllNetworks = filteredChains.length > 1
71
+ const showAllNetworks =
72
+ filteredChains.length > 1 &&
73
+ !hiddenUI?.includes(HiddenUI.AllNetworks) &&
74
+ !isSwapTo
67
75
  if (!showAllNetworks) {
68
76
  storeRef.current?.getState().setIsAllNetworks(false, key)
69
77
  }
70
78
  storeRef.current?.getState().setShowAllNetworks(showAllNetworks, key)
71
79
 
80
+ // If swap only, set the to chain to the from chain
81
+ if (isSwapTo) {
82
+ const [fromChainValue] = getFieldValues('fromChain')
83
+ setFieldValue('toChain', fromChainValue)
84
+ }
85
+
72
86
  const [chainValue] = getFieldValues(`${key}Chain`)
73
87
  if (chainValue) {
74
88
  return
75
89
  }
76
90
 
91
+ // If no chain is selected (e.g., removed from URL params) and
92
+ // showAllNetworks is enabled, reset isAllNetworks to true
93
+ if (showAllNetworks) {
94
+ storeRef.current?.getState().setIsAllNetworks(true, key)
95
+ }
96
+
77
97
  const firstAllowedPinnedChain = storeRef.current
78
98
  ?.getState()
79
99
  .pinnedChains?.find((chainId) =>
@@ -99,6 +119,8 @@ export function ChainOrderStoreProvider({
99
119
  useExternalWalletProvidersOnly,
100
120
  variant,
101
121
  subvariantOptions?.wide?.enableChainSidebar,
122
+ hiddenUI,
123
+ swapOnly,
102
124
  ])
103
125
 
104
126
  return (
@@ -1,11 +1,13 @@
1
1
  import type {
2
2
  TokenAmount as SDKTokenAmount,
3
3
  TokenAmountExtended as SDKTokenAmountExtended,
4
+ TokenExtended,
4
5
  } from '@lifi/sdk'
5
6
 
6
7
  interface TokenFlags {
7
8
  featured?: boolean
8
9
  popular?: boolean
10
+ verified?: boolean
9
11
  }
10
12
 
11
13
  export interface TokenAmount extends SDKTokenAmount, TokenFlags {}
@@ -13,3 +15,6 @@ export interface TokenAmount extends SDKTokenAmount, TokenFlags {}
13
15
  export interface TokenAmountExtended
14
16
  extends SDKTokenAmountExtended,
15
17
  TokenFlags {}
18
+
19
+ export type TokenWithVerified = TokenExtended & { verified?: boolean }
20
+ export type TokensByChain = Record<number, TokenWithVerified[]>
@@ -38,12 +38,20 @@ import type { DefaultFieldValues } from '../stores/form/types.js'
38
38
  export type WidgetVariant = 'compact' | 'wide' | 'drawer'
39
39
  export type WidgetSubvariant = 'default' | 'split' | 'custom' | 'refuel'
40
40
  export type SplitSubvariant = 'bridge' | 'swap'
41
+ export type SplitSubvariantOptions = {
42
+ defaultTab: SplitSubvariant
43
+ }
41
44
  export type CustomSubvariant = 'checkout' | 'deposit'
42
45
  export type WideSubvariant = {
43
46
  enableChainSidebar?: boolean
44
47
  }
45
48
  export interface SubvariantOptions {
46
- split?: SplitSubvariant
49
+ /**
50
+ * Configure split subvariant behavior:
51
+ * - 'bridge' | 'swap': Single mode without tabs
52
+ * - { defaultTab: 'bridge' | 'swap' }: Tabs mode with configurable default tab
53
+ */
54
+ split?: SplitSubvariant | SplitSubvariantOptions
47
55
  custom?: CustomSubvariant
48
56
  wide?: WideSubvariant
49
57
  }
@@ -125,6 +133,7 @@ export enum HiddenUI {
125
133
  SearchTokenInput = 'searchTokenInput',
126
134
  InsufficientGasMessage = 'insufficientGasMessage',
127
135
  ContactSupport = 'contactSupport',
136
+ AllNetworks = 'allNetworks',
128
137
  }
129
138
  export type HiddenUIType = `${HiddenUI}`
130
139
 
@@ -1,8 +1,72 @@
1
- import type { BaseToken, TokenExtended } from '@lifi/sdk'
1
+ import type { BaseToken, Token, TokenExtended } from '@lifi/sdk'
2
2
  import type { FormType } from '../stores/form/types.js'
3
+ import type { TokensByChain } from '../types/token.js'
3
4
  import type { WidgetChains, WidgetTokens } from '../types/widget.js'
4
5
  import { getConfigItemSets, isFormItemAllowed } from './item.js'
5
6
 
7
+ /**
8
+ * Merges verified tokens with search tokens.
9
+ * Verified tokens take priority - search tokens are only added if they don't already exist.
10
+ */
11
+ export const mergeVerifiedWithSearchTokens = (
12
+ verifiedTokens?: TokensByChain,
13
+ searchTokens?: TokensByChain
14
+ ): TokensByChain | undefined => {
15
+ if (!verifiedTokens) {
16
+ return searchTokens
17
+ }
18
+ if (!searchTokens) {
19
+ return verifiedTokens
20
+ }
21
+
22
+ const result = { ...verifiedTokens }
23
+
24
+ for (const [chainId, tokens] of Object.entries(searchTokens)) {
25
+ const chainIdNum = Number(chainId)
26
+ const existingTokens = result[chainIdNum] || []
27
+ const existingAddresses = new Set(
28
+ existingTokens.map((t) => t.address.toLowerCase())
29
+ )
30
+
31
+ const newTokens = tokens.filter(
32
+ (t) => !existingAddresses.has(t.address.toLowerCase())
33
+ )
34
+
35
+ if (newTokens.length) {
36
+ result[chainIdNum] = [...existingTokens, ...newTokens]
37
+ }
38
+ }
39
+
40
+ return result
41
+ }
42
+
43
+ /**
44
+ * Updates a token in the cache by chainId and address.
45
+ * Returns a new cache object with the token updated, or the original if not found.
46
+ */
47
+ export const updateTokenInCache = (
48
+ data: TokensByChain | undefined,
49
+ token: Token
50
+ ): TokensByChain | undefined => {
51
+ if (!data) {
52
+ return data
53
+ }
54
+ const chainTokens = data[token.chainId]
55
+ if (!chainTokens) {
56
+ return data
57
+ }
58
+ const index = chainTokens.findIndex((t) => t.address === token.address)
59
+ if (index < 0) {
60
+ return data
61
+ }
62
+ return {
63
+ ...data,
64
+ [token.chainId]: chainTokens.map((t, i) =>
65
+ i === index ? { ...t, ...token } : t
66
+ ),
67
+ }
68
+ }
69
+
6
70
  export const filterAllowedTokens = (
7
71
  dataTokens: { [chainId: number]: TokenExtended[] } | undefined,
8
72
  configTokens?: WidgetTokens,
@@ -91,6 +91,8 @@ const processedTypedTokens = (
91
91
  typedTokens?.forEach((token) => {
92
92
  const tokenAmount = { ...token } as TokenAmount
93
93
  tokenAmount[tokenType] = true
94
+ // Config tokens are explicitly set by integrator, mark as verified
95
+ tokenAmount.verified = true
94
96
 
95
97
  const match = filteredTokensMap.get(token.address)
96
98
  if (match?.priceUSD) {
@@ -0,0 +1,16 @@
1
+ import type {
2
+ SplitSubvariant,
3
+ SplitSubvariantOptions,
4
+ } from '../types/widget.js'
5
+
6
+ export const getSplitSubvariant = (
7
+ split?: SplitSubvariant | SplitSubvariantOptions
8
+ ): SplitSubvariant => {
9
+ if (!split) {
10
+ return 'swap'
11
+ }
12
+ if (typeof split === 'string') {
13
+ return split
14
+ }
15
+ return split.defaultTab
16
+ }