@lifi/widget 4.5.0 → 4.6.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 (199) hide show
  1. package/dist/esm/AppDrawer.js +2 -1
  2. package/dist/esm/AppDrawer.js.map +1 -1
  3. package/dist/esm/components/AmountInput/AmountInput.js +13 -7
  4. package/dist/esm/components/AmountInput/AmountInput.js.map +1 -1
  5. package/dist/esm/components/AmountInput/AmountInputEndAdornment.js +2 -13
  6. package/dist/esm/components/AmountInput/AmountInputEndAdornment.js.map +1 -1
  7. package/dist/esm/components/AmountInputCard/PercentageChips.js +2 -13
  8. package/dist/esm/components/AmountInputCard/PercentageChips.js.map +1 -1
  9. package/dist/esm/components/AmountInputCard/PercentageChips.style.js +1 -1
  10. package/dist/esm/components/AmountInputCard/PercentageChips.style.js.map +1 -1
  11. package/dist/esm/components/AmountInputCard/SendAmountCard.js +13 -6
  12. package/dist/esm/components/AmountInputCard/SendAmountCard.js.map +1 -1
  13. package/dist/esm/components/Chains/AllChainsAvatar.js +7 -1
  14. package/dist/esm/components/Chains/AllChainsAvatar.js.map +1 -1
  15. package/dist/esm/components/Dialog/Dialog.js +2 -1
  16. package/dist/esm/components/Dialog/Dialog.js.map +1 -1
  17. package/dist/esm/components/Expansion/Expansion.js +4 -3
  18. package/dist/esm/components/Expansion/Expansion.js.map +1 -1
  19. package/dist/esm/components/Header/SettingsButton.js +2 -1
  20. package/dist/esm/components/Header/SettingsButton.js.map +1 -1
  21. package/dist/esm/components/IconCircle/IconCircle.js +2 -1
  22. package/dist/esm/components/IconCircle/IconCircle.js.map +1 -1
  23. package/dist/esm/components/Messages/UnsupportedToAddressMessage.js +27 -0
  24. package/dist/esm/components/Messages/UnsupportedToAddressMessage.js.map +1 -0
  25. package/dist/esm/components/Messages/WarningMessages.d.ts.map +1 -1
  26. package/dist/esm/components/Messages/WarningMessages.js +2 -0
  27. package/dist/esm/components/Messages/WarningMessages.js.map +1 -1
  28. package/dist/esm/components/Messages/useMessageQueue.js +6 -1
  29. package/dist/esm/components/Messages/useMessageQueue.js.map +1 -1
  30. package/dist/esm/components/RouteCard/RouteDetails.js +2 -1
  31. package/dist/esm/components/RouteCard/RouteDetails.js.map +1 -1
  32. package/dist/esm/components/Routes/LimitOrderRoutes.js +2 -2
  33. package/dist/esm/components/Routes/LimitOrderRoutes.js.map +1 -1
  34. package/dist/esm/components/Routes/RoutesContent.js +2 -2
  35. package/dist/esm/components/Routes/RoutesContent.js.map +1 -1
  36. package/dist/esm/components/SendToWallet/SendToWalletExpandButton.js +2 -2
  37. package/dist/esm/components/SendToWallet/SendToWalletExpandButton.js.map +1 -1
  38. package/dist/esm/components/Skeleton/WidgetSkeleton.js +2 -1
  39. package/dist/esm/components/Skeleton/WidgetSkeleton.js.map +1 -1
  40. package/dist/esm/components/Step/StepActions.js +18 -16
  41. package/dist/esm/components/Step/StepActions.js.map +1 -1
  42. package/dist/esm/components/StepActions/StepActionRow.js +2 -1
  43. package/dist/esm/components/StepActions/StepActionRow.js.map +1 -1
  44. package/dist/esm/components/Switch.js +1 -1
  45. package/dist/esm/components/Switch.js.map +1 -1
  46. package/dist/esm/components/Tabs/Tabs.style.js +4 -2
  47. package/dist/esm/components/Tabs/Tabs.style.js.map +1 -1
  48. package/dist/esm/components/TokenList/TokenListItem.js +46 -8
  49. package/dist/esm/components/TokenList/TokenListItem.js.map +1 -1
  50. package/dist/esm/components/TokenList/VirtualizedTokenList.js +22 -9
  51. package/dist/esm/components/TokenList/VirtualizedTokenList.js.map +1 -1
  52. package/dist/esm/components/TokenRate/TokenRate.js +2 -1
  53. package/dist/esm/components/TokenRate/TokenRate.js.map +1 -1
  54. package/dist/esm/components/TransactionCard/ActiveTransactionCard.js +22 -21
  55. package/dist/esm/components/TransactionCard/ActiveTransactionCard.js.map +1 -1
  56. package/dist/esm/config/version.d.ts +1 -1
  57. package/dist/esm/config/version.js +1 -1
  58. package/dist/esm/config/version.js.map +1 -1
  59. package/dist/esm/hooks/timer/time.js +2 -2
  60. package/dist/esm/hooks/timer/time.js.map +1 -1
  61. package/dist/esm/hooks/useAvailableChains.d.ts.map +1 -1
  62. package/dist/esm/hooks/useAvailableChains.js +2 -1
  63. package/dist/esm/hooks/useAvailableChains.js.map +1 -1
  64. package/dist/esm/hooks/useExplorer.d.ts.map +1 -1
  65. package/dist/esm/hooks/useExplorer.js +4 -0
  66. package/dist/esm/hooks/useExplorer.js.map +1 -1
  67. package/dist/esm/hooks/useFlaggedTokenGuard.js +28 -0
  68. package/dist/esm/hooks/useFlaggedTokenGuard.js.map +1 -0
  69. package/dist/esm/hooks/useGasSufficiency.js +3 -1
  70. package/dist/esm/hooks/useGasSufficiency.js.map +1 -1
  71. package/dist/esm/hooks/useMaxSendAmount.js +15 -0
  72. package/dist/esm/hooks/useMaxSendAmount.js.map +1 -0
  73. package/dist/esm/hooks/usePacedRouteExecution.js +3 -2
  74. package/dist/esm/hooks/usePacedRouteExecution.js.map +1 -1
  75. package/dist/esm/hooks/useRouteExecutionMessage.js +3 -2
  76. package/dist/esm/hooks/useRouteExecutionMessage.js.map +1 -1
  77. package/dist/esm/hooks/useRoutes.d.ts.map +1 -1
  78. package/dist/esm/hooks/useRoutes.js +3 -1
  79. package/dist/esm/hooks/useRoutes.js.map +1 -1
  80. package/dist/esm/hooks/useToAddressRequirements.d.ts +2 -0
  81. package/dist/esm/hooks/useToAddressRequirements.d.ts.map +1 -1
  82. package/dist/esm/hooks/useToAddressRequirements.js +7 -1
  83. package/dist/esm/hooks/useToAddressRequirements.js.map +1 -1
  84. package/dist/esm/hooks/useTokenBalances.d.ts.map +1 -1
  85. package/dist/esm/hooks/useTokenBalances.js +7 -2
  86. package/dist/esm/hooks/useTokenBalances.js.map +1 -1
  87. package/dist/esm/hooks/useTokenBalancesQueries.js +29 -28
  88. package/dist/esm/hooks/useTokenBalancesQueries.js.map +1 -1
  89. package/dist/esm/hooks/useTokenSearch.js +3 -5
  90. package/dist/esm/hooks/useTokenSearch.js.map +1 -1
  91. package/dist/esm/hooks/useTokens.js +14 -10
  92. package/dist/esm/hooks/useTokens.js.map +1 -1
  93. package/dist/esm/hooks/useWideVariant.js +2 -1
  94. package/dist/esm/hooks/useWideVariant.js.map +1 -1
  95. package/dist/esm/i18n/bn.json +3 -1
  96. package/dist/esm/i18n/de.json +3 -1
  97. package/dist/esm/i18n/en.json +12 -3
  98. package/dist/esm/i18n/es.json +3 -1
  99. package/dist/esm/i18n/fr.json +3 -1
  100. package/dist/esm/i18n/hi.json +3 -1
  101. package/dist/esm/i18n/id.json +3 -1
  102. package/dist/esm/i18n/it.json +3 -1
  103. package/dist/esm/i18n/ja.json +3 -1
  104. package/dist/esm/i18n/ko.json +3 -1
  105. package/dist/esm/i18n/pl.json +3 -1
  106. package/dist/esm/i18n/pt.json +3 -1
  107. package/dist/esm/i18n/th.json +3 -1
  108. package/dist/esm/i18n/tr.json +3 -1
  109. package/dist/esm/i18n/uk.json +3 -1
  110. package/dist/esm/i18n/vi.json +3 -1
  111. package/dist/esm/i18n/zh.json +3 -1
  112. package/dist/esm/pages/MainPage/MainPage.js +2 -1
  113. package/dist/esm/pages/MainPage/MainPage.js.map +1 -1
  114. package/dist/esm/pages/MainPage/ReviewButton.js +2 -2
  115. package/dist/esm/pages/MainPage/ReviewButton.js.map +1 -1
  116. package/dist/esm/pages/RoutesPage/RoutesPage.js +2 -2
  117. package/dist/esm/pages/RoutesPage/RoutesPage.js.map +1 -1
  118. package/dist/esm/pages/SelectChainPage/SelectChainPage.js +13 -12
  119. package/dist/esm/pages/SelectChainPage/SelectChainPage.js.map +1 -1
  120. package/dist/esm/pages/SelectEnabledToolsPage.js +8 -5
  121. package/dist/esm/pages/SelectEnabledToolsPage.js.map +1 -1
  122. package/dist/esm/pages/SelectTokenPage/SelectTokenPage.js +2 -1
  123. package/dist/esm/pages/SelectTokenPage/SelectTokenPage.js.map +1 -1
  124. package/dist/esm/pages/SettingsPage/SmallBalanceFilterSettings.js +2 -1
  125. package/dist/esm/pages/SettingsPage/SmallBalanceFilterSettings.js.map +1 -1
  126. package/dist/esm/pages/TransactionDetailsPage/ContactSupportButton.js +2 -1
  127. package/dist/esm/pages/TransactionDetailsPage/ContactSupportButton.js.map +1 -1
  128. package/dist/esm/pages/TransactionDetailsPage/TransactionDetailsPage.js +2 -1
  129. package/dist/esm/pages/TransactionDetailsPage/TransactionDetailsPage.js.map +1 -1
  130. package/dist/esm/pages/TransactionPage/StartTransactionButton.js +4 -3
  131. package/dist/esm/pages/TransactionPage/StartTransactionButton.js.map +1 -1
  132. package/dist/esm/pages/TransactionPage/TokenVerificationBottomSheet.js +64 -0
  133. package/dist/esm/pages/TransactionPage/TokenVerificationBottomSheet.js.map +1 -0
  134. package/dist/esm/pages/TransactionPage/TransactionExecutionContent.js +19 -16
  135. package/dist/esm/pages/TransactionPage/TransactionExecutionContent.js.map +1 -1
  136. package/dist/esm/pages/TransactionPage/TransactionFailedButtons.js +3 -1
  137. package/dist/esm/pages/TransactionPage/TransactionFailedButtons.js.map +1 -1
  138. package/dist/esm/pages/TransactionPage/TransactionReview.js +24 -2
  139. package/dist/esm/pages/TransactionPage/TransactionReview.js.map +1 -1
  140. package/dist/esm/providers/I18nProvider/I18nProvider.js +37 -36
  141. package/dist/esm/providers/I18nProvider/I18nProvider.js.map +1 -1
  142. package/dist/esm/providers/ThemeProvider/ThemeProvider.js +2 -1
  143. package/dist/esm/providers/ThemeProvider/ThemeProvider.js.map +1 -1
  144. package/dist/esm/providers/WalletProvider/WalletProvider.js +10 -9
  145. package/dist/esm/providers/WalletProvider/WalletProvider.js.map +1 -1
  146. package/dist/esm/providers/WalletProvider/useExternalWalletProvider.js +7 -3
  147. package/dist/esm/providers/WalletProvider/useExternalWalletProvider.js.map +1 -1
  148. package/dist/esm/stores/routes/useRouteExecutionIndicator.js +4 -3
  149. package/dist/esm/stores/routes/useRouteExecutionIndicator.js.map +1 -1
  150. package/dist/esm/types/token.d.ts +4 -1
  151. package/dist/esm/types/token.d.ts.map +1 -1
  152. package/dist/esm/utils/compactNumberFormatter.js +1 -3
  153. package/dist/esm/utils/compactNumberFormatter.js.map +1 -1
  154. package/dist/esm/utils/customReceiver.js +7 -0
  155. package/dist/esm/utils/customReceiver.js.map +1 -0
  156. package/dist/esm/utils/gas.js +24 -0
  157. package/dist/esm/utils/gas.js.map +1 -0
  158. package/dist/esm/utils/getErrorMessage.js +0 -1
  159. package/dist/esm/utils/getErrorMessage.js.map +1 -1
  160. package/dist/esm/utils/limitOrder.js +1 -1
  161. package/dist/esm/utils/limitOrder.js.map +1 -1
  162. package/dist/esm/utils/token.js +21 -4
  163. package/dist/esm/utils/token.js.map +1 -1
  164. package/dist/esm/utils/tokenList.js +38 -21
  165. package/dist/esm/utils/tokenList.js.map +1 -1
  166. package/package.json +15 -15
  167. package/src/components/AmountInput/AmountInputEndAdornment.tsx +2 -21
  168. package/src/components/AmountInputCard/PercentageChips.tsx +2 -18
  169. package/src/components/Chains/AllChainsAvatar.tsx +7 -1
  170. package/src/components/Messages/UnsupportedToAddressMessage.tsx +24 -0
  171. package/src/components/Messages/WarningMessages.tsx +3 -0
  172. package/src/components/Messages/useMessageQueue.ts +9 -0
  173. package/src/components/Routes/LimitOrderRoutes.tsx +5 -2
  174. package/src/components/Routes/RoutesContent.tsx +5 -2
  175. package/src/components/SendToWallet/SendToWalletExpandButton.tsx +2 -1
  176. package/src/components/TokenList/TokenListItem.tsx +68 -9
  177. package/src/components/TokenList/VirtualizedTokenList.tsx +33 -9
  178. package/src/components/TokenList/types.ts +4 -0
  179. package/src/config/version.ts +1 -1
  180. package/src/hooks/useAvailableChains.ts +1 -0
  181. package/src/hooks/useExplorer.ts +2 -0
  182. package/src/hooks/useFlaggedTokenGuard.ts +33 -0
  183. package/src/hooks/useGasSufficiency.ts +9 -1
  184. package/src/hooks/useMaxSendAmount.ts +21 -0
  185. package/src/hooks/useRoutes.ts +7 -0
  186. package/src/hooks/useToAddressRequirements.ts +17 -1
  187. package/src/hooks/useTokenBalances.ts +18 -2
  188. package/src/hooks/useTokenSearch.ts +1 -5
  189. package/src/hooks/useTokens.ts +28 -13
  190. package/src/pages/MainPage/ReviewButton.tsx +6 -2
  191. package/src/pages/RoutesPage/RoutesPage.tsx +5 -2
  192. package/src/pages/TransactionPage/TokenVerificationBottomSheet.tsx +93 -0
  193. package/src/pages/TransactionPage/TransactionReview.tsx +28 -1
  194. package/src/providers/WalletProvider/useExternalWalletProvider.ts +9 -1
  195. package/src/types/token.ts +6 -3
  196. package/src/utils/customReceiver.ts +7 -0
  197. package/src/utils/gas.ts +41 -0
  198. package/src/utils/token.ts +53 -9
  199. package/src/utils/tokenList.ts +28 -0
@@ -0,0 +1,93 @@
1
+ import type { Token } from '@lifi/sdk'
2
+ import { Button, Checkbox, FormControlLabel } from '@mui/material'
3
+ import type { JSX, Ref, RefObject } from 'react'
4
+ import { useRef, useState } from 'react'
5
+ import { useTranslation } from 'react-i18next'
6
+ import { BottomSheet } from '../../components/BottomSheet/BottomSheet.js'
7
+ import type { BottomSheetBase } from '../../components/BottomSheet/types.js'
8
+ import { IconCircle } from '../../components/IconCircle/IconCircle.js'
9
+ import { useSetContentHeight } from '../../hooks/useSetContentHeight.js'
10
+ import {
11
+ ButtonRow,
12
+ CenterContainer,
13
+ ContentContainer,
14
+ WarningMessage,
15
+ WarningTitle,
16
+ } from './TokenValueBottomSheet.style.js'
17
+
18
+ interface TokenVerificationBottomSheetProps {
19
+ /** Source and/or destination tokens the screening provider flagged. */
20
+ tokens: Token[]
21
+ onContinue(): void
22
+ onCancel?(): void
23
+ }
24
+
25
+ export const TokenVerificationBottomSheet = ({
26
+ tokens,
27
+ onContinue,
28
+ onCancel,
29
+ ref,
30
+ }: TokenVerificationBottomSheetProps & {
31
+ ref?: Ref<BottomSheetBase>
32
+ }): JSX.Element => {
33
+ const handleCancel = () => {
34
+ ;(ref as RefObject<BottomSheetBase>).current?.close()
35
+ onCancel?.()
36
+ }
37
+
38
+ return (
39
+ <BottomSheet ref={ref} onClose={onCancel}>
40
+ <TokenVerificationBottomSheetContent
41
+ tokens={tokens}
42
+ onContinue={onContinue}
43
+ onCancel={handleCancel}
44
+ />
45
+ </BottomSheet>
46
+ )
47
+ }
48
+
49
+ const TokenVerificationBottomSheetContent: React.FC<
50
+ TokenVerificationBottomSheetProps
51
+ > = ({ tokens, onCancel, onContinue }) => {
52
+ const [accepted, setAccepted] = useState(false)
53
+ const { t } = useTranslation()
54
+ const ref = useRef<HTMLElement>(null)
55
+ useSetContentHeight(ref)
56
+
57
+ return (
58
+ <ContentContainer ref={ref}>
59
+ <CenterContainer>
60
+ <IconCircle status="error" sx={{ mb: 2.5 }} />
61
+ <WarningTitle>{t('warning.title.tokenFlagged')}</WarningTitle>
62
+ </CenterContainer>
63
+ {tokens.map((token) => (
64
+ <WarningMessage key={`${token.chainId}-${token.address}`}>
65
+ {t('warning.message.tokenFlagged', { tokenSymbol: token.symbol })}
66
+ </WarningMessage>
67
+ ))}
68
+ <FormControlLabel
69
+ control={
70
+ <Checkbox
71
+ checked={accepted}
72
+ onChange={(_, checked) => setAccepted(checked)}
73
+ />
74
+ }
75
+ label={t('warning.checkbox.tokenFlagged')}
76
+ sx={{ mt: 1 }}
77
+ />
78
+ <ButtonRow>
79
+ <Button variant="text" onClick={onCancel} fullWidth>
80
+ {t('button.cancel')}
81
+ </Button>
82
+ <Button
83
+ variant="contained"
84
+ onClick={onContinue}
85
+ disabled={!accepted}
86
+ fullWidth
87
+ >
88
+ {t('button.continue')}
89
+ </Button>
90
+ </ButtonRow>
91
+ </ContentContainer>
92
+ )
93
+ }
@@ -8,6 +8,7 @@ import { Card } from '../../components/Card/Card.js'
8
8
  import { WarningMessages } from '../../components/Messages/WarningMessages.js'
9
9
  import { RouteTokens } from '../../components/RouteCard/RouteTokens.js'
10
10
  import { useAddressActivity } from '../../hooks/useAddressActivity.js'
11
+ import { useFlaggedTokenGuard } from '../../hooks/useFlaggedTokenGuard.js'
11
12
  import { useWidgetEvents } from '../../hooks/useWidgetEvents.js'
12
13
  import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
13
14
  import { useFieldActions } from '../../stores/form/useFieldActions.js'
@@ -18,6 +19,7 @@ import { navigationRoutes } from '../../utils/navigationRoutes.js'
18
19
  import { ConfirmToAddressSheet } from './ConfirmToAddressSheet.js'
19
20
  import { StartTransactionButton } from './StartTransactionButton.js'
20
21
  import { TokenValueBottomSheet } from './TokenValueBottomSheet.js'
22
+ import { TokenVerificationBottomSheet } from './TokenVerificationBottomSheet.js'
21
23
  import {
22
24
  calculateValueLossPercentage,
23
25
  getTokenValueLossThreshold,
@@ -44,6 +46,8 @@ export const TransactionReview: React.FC<TransactionReviewProps> = ({
44
46
  const tokenValueBottomSheetRef = useRef<BottomSheetBase>(null)
45
47
  const confirmToAddressSheetRef = useRef<BottomSheetBase>(null)
46
48
 
49
+ const { flaggedTokens, flaggedTokenSheetRef } = useFlaggedTokenGuard(route)
50
+
47
51
  const {
48
52
  toAddress,
49
53
  hasActivity,
@@ -70,6 +74,7 @@ export const TransactionReview: React.FC<TransactionReviewProps> = ({
70
74
  })
71
75
  }
72
76
  tokenValueBottomSheetRef.current?.close()
77
+ flaggedTokenSheetRef.current?.close()
73
78
  executeRoute()
74
79
  setFieldValue('fromAmount', '')
75
80
  if (mode === 'custom') {
@@ -81,7 +86,9 @@ export const TransactionReview: React.FC<TransactionReviewProps> = ({
81
86
  })
82
87
  }
83
88
 
84
- const handleStartClick = () => {
89
+ // Runs the remaining gates. The flagged-token sheet continues into this
90
+ // rather than executing, so accepting it does not skip the others.
91
+ const handleStartAfterTokenCheck = () => {
85
92
  if (
86
93
  toAddress &&
87
94
  !hasActivity &&
@@ -109,6 +116,19 @@ export const TransactionReview: React.FC<TransactionReviewProps> = ({
109
116
  }
110
117
  }
111
118
 
119
+ const handleStartClick = () => {
120
+ if (flaggedTokens.length) {
121
+ flaggedTokenSheetRef.current?.open()
122
+ return
123
+ }
124
+ handleStartAfterTokenCheck()
125
+ }
126
+
127
+ const handleFlaggedTokensContinue = () => {
128
+ flaggedTokenSheetRef.current?.close()
129
+ handleStartAfterTokenCheck()
130
+ }
131
+
112
132
  const getButtonText = (): string => {
113
133
  switch (mode) {
114
134
  case 'custom':
@@ -150,6 +170,13 @@ export const TransactionReview: React.FC<TransactionReviewProps> = ({
150
170
  onContinue={handleExecuteRoute}
151
171
  />
152
172
  ) : null}
173
+ {flaggedTokens.length ? (
174
+ <TokenVerificationBottomSheet
175
+ ref={flaggedTokenSheetRef}
176
+ tokens={flaggedTokens}
177
+ onContinue={handleFlaggedTokensContinue}
178
+ />
179
+ ) : null}
153
180
  {!hiddenUI?.lowAddressActivityConfirmation ? (
154
181
  <ConfirmToAddressSheet
155
182
  ref={confirmToAddressSheetRef}
@@ -3,6 +3,7 @@ import {
3
3
  useBitcoinContext,
4
4
  useEthereumContext,
5
5
  useSolanaContext,
6
+ useStellarContext,
6
7
  useSuiContext,
7
8
  useTronContext,
8
9
  } from '@lifi/widget-provider'
@@ -21,6 +22,7 @@ const internalChainTypes = [
21
22
  ChainType.UTXO,
22
23
  ChainType.MVM,
23
24
  ChainType.TVM,
25
+ ChainType.STL,
24
26
  ]
25
27
 
26
28
  export function useExternalWalletProvider(): ExternalWalletProvider {
@@ -30,6 +32,7 @@ export function useExternalWalletProvider(): ExternalWalletProvider {
30
32
  const { isExternalContext: hasExternalBitcoinContext } = useBitcoinContext()
31
33
  const { isExternalContext: hasExternalSuiContext } = useSuiContext()
32
34
  const { isExternalContext: hasExternalTronContext } = useTronContext()
35
+ const { isExternalContext: hasExternalStellarContext } = useStellarContext()
33
36
  const data = useMemo(() => {
34
37
  const providers: ChainType[] = []
35
38
  if (hasExternalEthereumContext) {
@@ -47,12 +50,16 @@ export function useExternalWalletProvider(): ExternalWalletProvider {
47
50
  if (hasExternalTronContext) {
48
51
  providers.push(ChainType.TVM)
49
52
  }
53
+ if (hasExternalStellarContext) {
54
+ providers.push(ChainType.STL)
55
+ }
50
56
  const hasExternalProvider =
51
57
  hasExternalEthereumContext ||
52
58
  hasExternalSolanaContext ||
53
59
  hasExternalBitcoinContext ||
54
60
  hasExternalSuiContext ||
55
- hasExternalTronContext
61
+ hasExternalTronContext ||
62
+ hasExternalStellarContext
56
63
 
57
64
  const useExternalWalletProvidersOnly =
58
65
  hasExternalProvider &&
@@ -71,6 +78,7 @@ export function useExternalWalletProvider(): ExternalWalletProvider {
71
78
  hasExternalBitcoinContext,
72
79
  hasExternalSuiContext,
73
80
  hasExternalTronContext,
81
+ hasExternalStellarContext,
74
82
  walletConfig?.usePartialWalletManagement,
75
83
  walletConfig?.forceInternalWalletManagement,
76
84
  ])
@@ -4,11 +4,14 @@ import type {
4
4
  TokenExtended,
5
5
  } from '@lifi/sdk'
6
6
 
7
- interface TokenFlags {
7
+ export interface TokenFlags {
8
8
  featured?: boolean
9
9
  popular?: boolean
10
10
  pinned?: boolean
11
11
  verified?: boolean
12
+ native?: boolean
13
+ /** Came from the main token list rather than from a search. */
14
+ listed?: boolean
12
15
  }
13
16
 
14
17
  export interface TokenAmount extends SDKTokenAmount, TokenFlags {}
@@ -17,5 +20,5 @@ export interface TokenAmountExtended
17
20
  extends SDKTokenAmountExtended,
18
21
  TokenFlags {}
19
22
 
20
- export type TokenWithVerified = TokenExtended & { verified?: boolean }
21
- export type TokensByChain = Record<number, TokenWithVerified[]>
23
+ export type TokenWithFlags = TokenExtended & TokenFlags
24
+ export type TokensByChain = Record<number, TokenWithFlags[]>
@@ -0,0 +1,7 @@
1
+ import { ChainType } from '@lifi/sdk'
2
+
3
+ // Stellar routes always settle to the account that signs them.
4
+ export const isCustomReceiverUnsupported = (
5
+ fromChainType?: ChainType,
6
+ toChainType?: ChainType
7
+ ): boolean => fromChainType === ChainType.STL && toChainType === ChainType.STL
@@ -0,0 +1,41 @@
1
+ import type { LiFiStepExtended, RouteExtended } from '@lifi/sdk'
2
+ import { ChainType } from '@lifi/sdk'
3
+
4
+ /**
5
+ * Returns how much of a step's gas token the route itself delivers before that
6
+ * step runs. The previous step must output the gas token on the same chain, and
7
+ * whatever this step consumes as input is not available for gas.
8
+ * Stellar routes only — they bridge XLM to the receiver and hold back a buffer
9
+ * for the account reserve and the destination step fees.
10
+ */
11
+ export const getSelfFundedGasAmount = (
12
+ route: RouteExtended,
13
+ step: LiFiStepExtended,
14
+ gasChainType?: ChainType
15
+ ): bigint => {
16
+ // TODO: remove the chain gate when the backend reserves gas on every chain
17
+ if (gasChainType !== ChainType.STL) {
18
+ return 0n
19
+ }
20
+ const gasToken = step.estimate.gasCosts?.[0]?.token
21
+ const previousStep = route.steps[route.steps.indexOf(step) - 1]
22
+ if (!gasToken || !previousStep) {
23
+ return 0n
24
+ }
25
+ const { toToken } = previousStep.action
26
+ if (
27
+ toToken.chainId !== gasToken.chainId ||
28
+ toToken.address.toLowerCase() !== gasToken.address.toLowerCase()
29
+ ) {
30
+ return 0n
31
+ }
32
+ const deliveredAmount = BigInt(previousStep.estimate.toAmountMin)
33
+ const consumedAmount =
34
+ step.action.fromToken.address.toLowerCase() ===
35
+ gasToken.address.toLowerCase()
36
+ ? BigInt(step.action.fromAmount)
37
+ : 0n
38
+ return deliveredAmount > consumedAmount
39
+ ? deliveredAmount - consumedAmount
40
+ : 0n
41
+ }
@@ -1,6 +1,14 @@
1
- import type { BaseToken, RouteExtended, Token, TokenAmount } from '@lifi/sdk'
1
+ import type {
2
+ BaseToken,
3
+ ExtendedChain,
4
+ RouteExtended,
5
+ StaticToken,
6
+ Token,
7
+ TokenAmount,
8
+ } from '@lifi/sdk'
9
+ import { ChainId } from '@lifi/sdk'
2
10
  import type { FormType } from '../stores/form/types.js'
3
- import type { TokensByChain, TokenWithVerified } from '../types/token.js'
11
+ import type { TokensByChain, TokenWithFlags } from '../types/token.js'
4
12
  import type { WidgetChains, WidgetTokens } from '../types/widget.js'
5
13
  import { getConfigItemSets, isFormItemAllowed } from './item.js'
6
14
 
@@ -94,7 +102,8 @@ export const filterAllowedTokens = (
94
102
  dataTokens: TokensByChain | undefined,
95
103
  configTokens?: WidgetTokens,
96
104
  chainsConfig?: WidgetChains,
97
- formType?: FormType
105
+ formType?: FormType,
106
+ nativeTokenAddresses?: Map<number, string>
98
107
  ): TokensByChain | undefined => {
99
108
  if (!dataTokens) {
100
109
  return
@@ -143,13 +152,14 @@ export const filterAllowedTokens = (
143
152
  )
144
153
 
145
154
  const verifiedAddresses = verifiedTokensSets?.get(chainId)
155
+ const nativeAddress = nativeTokenAddresses?.get(chainId)?.toLowerCase()
146
156
 
147
- const chainTokens: TokenWithVerified[] = [
157
+ const chainTokens: TokenWithFlags[] = [
148
158
  ...(dataTokens[chainId] ?? []),
149
159
  ...chainIncludedTokens,
150
160
  ]
151
161
 
152
- const filtered: TokenWithVerified[] = []
162
+ const filtered: TokenWithFlags[] = []
153
163
  const seenAddresses = new Set<string>()
154
164
  for (const token of chainTokens) {
155
165
  const address = token.address.toLowerCase()
@@ -167,12 +177,18 @@ export const filterAllowedTokens = (
167
177
  continue
168
178
  }
169
179
  // Include and verified-allowlist tokens are explicitly set by the
170
- // integrator, mark them as verified
171
- if (
180
+ // integrator, mark them as verified. Flags are only ever set to true,
181
+ // so a token that earns none keeps its cached object.
182
+ const markVerified =
172
183
  !token.verified &&
173
184
  (includedAddresses.has(address) || verifiedAddresses?.has(address))
174
- ) {
175
- filtered.push({ ...token, verified: true })
185
+ const markNative = !!nativeAddress && nativeAddress === address
186
+ if (markVerified || markNative) {
187
+ filtered.push({
188
+ ...token,
189
+ ...(markVerified ? { verified: true } : {}),
190
+ ...(markNative ? { native: true } : {}),
191
+ })
176
192
  } else {
177
193
  filtered.push(token)
178
194
  }
@@ -192,3 +208,31 @@ export const getExecutionToToken = (route: RouteExtended): TokenAmount => ({
192
208
  route.toAmount
193
209
  ),
194
210
  })
211
+
212
+ /** Capitalized name of the first provider that verified the token. */
213
+ export const getTokenVerificationProvider = (
214
+ token: Pick<StaticToken, 'verificationStatusBreakdown'>
215
+ ): string | undefined => {
216
+ const provider = token.verificationStatusBreakdown?.find(
217
+ (entry) => entry.result === 'verified'
218
+ )?.provider
219
+ return provider
220
+ ? `${provider.charAt(0).toUpperCase()}${provider.slice(1)}`
221
+ : undefined
222
+ }
223
+
224
+ /**
225
+ * Hyperliquid declares Arbitrum USDC as its native token, Lighter declares
226
+ * Ethereum USDC. Neither is a gas token you hold there. A chain-local
227
+ * stablecoin gas token, as on Arc or Stable, is genuine and stays.
228
+ */
229
+ const chainIdsWithoutNativeToken = new Set([ChainId.HPL, ChainId.LTR])
230
+
231
+ export const getNativeTokenAddresses = (
232
+ chains?: ExtendedChain[]
233
+ ): Map<number, string> =>
234
+ new Map(
235
+ chains
236
+ ?.filter((chain) => !chainIdsWithoutNativeToken.has(chain.id))
237
+ .map((chain) => [chain.id, chain.nativeToken.address])
238
+ )
@@ -274,3 +274,31 @@ export const isSearchMatch = (
274
274
  token.address?.toLowerCase().includes(searchLowerCase)
275
275
  )
276
276
  }
277
+
278
+ /**
279
+ * Moves the chain's native token to the top of its own chain's list, unless an
280
+ * external list already places it. Returns the array unchanged when nothing
281
+ * moves, so a memo downstream keeps its reference.
282
+ */
283
+ export const hoistNativeToken = (
284
+ tokens: TokenAmount[],
285
+ selectedChainId?: number
286
+ ): TokenAmount[] => {
287
+ if (!selectedChainId) {
288
+ return tokens
289
+ }
290
+ const index = tokens.findIndex(
291
+ (token) =>
292
+ token.native &&
293
+ token.chainId === selectedChainId &&
294
+ !token.featured &&
295
+ !token.popular &&
296
+ !token.pinned &&
297
+ !token.verified
298
+ )
299
+ // -1 is no native token, 0 is already first
300
+ if (index < 1) {
301
+ return tokens
302
+ }
303
+ return [tokens[index], ...tokens.slice(0, index), ...tokens.slice(index + 1)]
304
+ }