@lifi/widget 4.5.0 → 4.7.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 (522) hide show
  1. package/README.md +26 -3
  2. package/dist/esm/App.d.ts +1 -2
  3. package/dist/esm/App.d.ts.map +1 -1
  4. package/dist/esm/AppDrawer.d.ts +1 -2
  5. package/dist/esm/AppDrawer.d.ts.map +1 -1
  6. package/dist/esm/AppDrawer.js +2 -1
  7. package/dist/esm/AppDrawer.js.map +1 -1
  8. package/dist/esm/components/ActionRow/ActionRow.d.ts +1 -2
  9. package/dist/esm/components/ActionRow/ActionRow.d.ts.map +1 -1
  10. package/dist/esm/components/AmountInput/AmountInput.js +13 -7
  11. package/dist/esm/components/AmountInput/AmountInput.js.map +1 -1
  12. package/dist/esm/components/AmountInput/AmountInput.style.d.ts +2 -2
  13. package/dist/esm/components/AmountInput/AmountInput.style.d.ts.map +1 -1
  14. package/dist/esm/components/AmountInput/AmountInputEndAdornment.d.ts +1 -2
  15. package/dist/esm/components/AmountInput/AmountInputEndAdornment.d.ts.map +1 -1
  16. package/dist/esm/components/AmountInput/AmountInputEndAdornment.js +2 -13
  17. package/dist/esm/components/AmountInput/AmountInputEndAdornment.js.map +1 -1
  18. package/dist/esm/components/AmountInput/PriceFormHelperText.style.d.ts +1 -2
  19. package/dist/esm/components/AmountInput/PriceFormHelperText.style.d.ts.map +1 -1
  20. package/dist/esm/components/AmountInputCard/PercentageChips.js +2 -13
  21. package/dist/esm/components/AmountInputCard/PercentageChips.js.map +1 -1
  22. package/dist/esm/components/AmountInputCard/PercentageChips.style.js +1 -1
  23. package/dist/esm/components/AmountInputCard/PercentageChips.style.js.map +1 -1
  24. package/dist/esm/components/AmountInputCard/ReceiveAmountCard.js +6 -2
  25. package/dist/esm/components/AmountInputCard/ReceiveAmountCard.js.map +1 -1
  26. package/dist/esm/components/AmountInputCard/SendAmountCard.js +13 -6
  27. package/dist/esm/components/AmountInputCard/SendAmountCard.js.map +1 -1
  28. package/dist/esm/components/Avatar/Avatar.d.ts +1 -2
  29. package/dist/esm/components/Avatar/Avatar.d.ts.map +1 -1
  30. package/dist/esm/components/Avatar/TokenAvatar.d.ts +1 -2
  31. package/dist/esm/components/Avatar/TokenAvatar.d.ts.map +1 -1
  32. package/dist/esm/components/BaseTransactionButton/BaseTransactionButton.d.ts +1 -2
  33. package/dist/esm/components/BaseTransactionButton/BaseTransactionButton.d.ts.map +1 -1
  34. package/dist/esm/components/BaseTransactionButton/types.d.ts +1 -2
  35. package/dist/esm/components/BaseTransactionButton/types.d.ts.map +1 -1
  36. package/dist/esm/components/BottomSheet/BottomSheet.js +1 -0
  37. package/dist/esm/components/BottomSheet/BottomSheet.js.map +1 -1
  38. package/dist/esm/components/BottomSheet/types.d.ts +1 -2
  39. package/dist/esm/components/BottomSheet/types.d.ts.map +1 -1
  40. package/dist/esm/components/ButtonTertiary.d.ts +1 -2
  41. package/dist/esm/components/ButtonTertiary.d.ts.map +1 -1
  42. package/dist/esm/components/Card/CardIconButton.d.ts +1 -2
  43. package/dist/esm/components/Card/CardIconButton.d.ts.map +1 -1
  44. package/dist/esm/components/Card/CardTitle.d.ts +1 -2
  45. package/dist/esm/components/Card/CardTitle.d.ts.map +1 -1
  46. package/dist/esm/components/Card/InputCard.d.ts +1 -2
  47. package/dist/esm/components/Card/InputCard.d.ts.map +1 -1
  48. package/dist/esm/components/ChainSelect/ChainSelect.d.ts +1 -2
  49. package/dist/esm/components/ChainSelect/ChainSelect.d.ts.map +1 -1
  50. package/dist/esm/components/ChainSelect/ChainSelect.style.d.ts +1 -2
  51. package/dist/esm/components/ChainSelect/ChainSelect.style.d.ts.map +1 -1
  52. package/dist/esm/components/Chains/AllChainsAvatar.js +7 -1
  53. package/dist/esm/components/Chains/AllChainsAvatar.js.map +1 -1
  54. package/dist/esm/components/ContractComponent/ContractComponent.d.ts +1 -2
  55. package/dist/esm/components/ContractComponent/ContractComponent.d.ts.map +1 -1
  56. package/dist/esm/components/ContractComponent/ItemPrice.d.ts +2 -3
  57. package/dist/esm/components/ContractComponent/ItemPrice.d.ts.map +1 -1
  58. package/dist/esm/components/ContractComponent/NFT/NFT.d.ts +1 -2
  59. package/dist/esm/components/ContractComponent/NFT/NFT.d.ts.map +1 -1
  60. package/dist/esm/components/ContractComponent/NFT/NFTBase.d.ts +1 -2
  61. package/dist/esm/components/ContractComponent/NFT/NFTBase.d.ts.map +1 -1
  62. package/dist/esm/components/ContractComponent/NFT/types.d.ts +3 -4
  63. package/dist/esm/components/ContractComponent/NFT/types.d.ts.map +1 -1
  64. package/dist/esm/components/Dialog/Dialog.js +2 -1
  65. package/dist/esm/components/Dialog/Dialog.js.map +1 -1
  66. package/dist/esm/components/ExecutionStatusCard/ExecutionStatusCard.d.ts +1 -2
  67. package/dist/esm/components/ExecutionStatusCard/ExecutionStatusCard.d.ts.map +1 -1
  68. package/dist/esm/components/ExecutionStatusCard/StatusIcon.d.ts +1 -2
  69. package/dist/esm/components/ExecutionStatusCard/StatusIcon.d.ts.map +1 -1
  70. package/dist/esm/components/Expansion/Expansion.js +4 -3
  71. package/dist/esm/components/Expansion/Expansion.js.map +1 -1
  72. package/dist/esm/components/FeeBreakdownTooltip.d.ts +1 -2
  73. package/dist/esm/components/FeeBreakdownTooltip.d.ts.map +1 -1
  74. package/dist/esm/components/Header/SettingsButton.js +2 -1
  75. package/dist/esm/components/Header/SettingsButton.js.map +1 -1
  76. package/dist/esm/components/IconCircle/IconCircle.d.ts +1 -2
  77. package/dist/esm/components/IconCircle/IconCircle.d.ts.map +1 -1
  78. package/dist/esm/components/IconCircle/IconCircle.js +2 -1
  79. package/dist/esm/components/IconCircle/IconCircle.js.map +1 -1
  80. package/dist/esm/components/IconCircle/statusIcons.d.ts +1 -2
  81. package/dist/esm/components/IconCircle/statusIcons.d.ts.map +1 -1
  82. package/dist/esm/components/IconTypography.d.ts +1 -2
  83. package/dist/esm/components/IconTypography.d.ts.map +1 -1
  84. package/dist/esm/components/ListItem/ListItemButton.d.ts +1 -2
  85. package/dist/esm/components/ListItem/ListItemButton.d.ts.map +1 -1
  86. package/dist/esm/components/Messages/UnsupportedToAddressMessage.js +27 -0
  87. package/dist/esm/components/Messages/UnsupportedToAddressMessage.js.map +1 -0
  88. package/dist/esm/components/Messages/WarningMessages.d.ts +1 -2
  89. package/dist/esm/components/Messages/WarningMessages.d.ts.map +1 -1
  90. package/dist/esm/components/Messages/WarningMessages.js +2 -0
  91. package/dist/esm/components/Messages/WarningMessages.js.map +1 -1
  92. package/dist/esm/components/Messages/useMessageQueue.js +6 -1
  93. package/dist/esm/components/Messages/useMessageQueue.js.map +1 -1
  94. package/dist/esm/components/PageContainer.d.ts +2 -3
  95. package/dist/esm/components/PageContainer.d.ts.map +1 -1
  96. package/dist/esm/components/PoweredBy/PoweredBy.d.ts +1 -2
  97. package/dist/esm/components/PoweredBy/PoweredBy.d.ts.map +1 -1
  98. package/dist/esm/components/ProgressToNextUpdate.d.ts +1 -2
  99. package/dist/esm/components/ProgressToNextUpdate.d.ts.map +1 -1
  100. package/dist/esm/components/RouteCard/RouteCard.d.ts +1 -2
  101. package/dist/esm/components/RouteCard/RouteCard.d.ts.map +1 -1
  102. package/dist/esm/components/RouteCard/RouteCardSkeleton.d.ts +1 -2
  103. package/dist/esm/components/RouteCard/RouteCardSkeleton.d.ts.map +1 -1
  104. package/dist/esm/components/RouteCard/RouteDetails.d.ts +1 -2
  105. package/dist/esm/components/RouteCard/RouteDetails.d.ts.map +1 -1
  106. package/dist/esm/components/RouteCard/RouteDetails.js +2 -1
  107. package/dist/esm/components/RouteCard/RouteDetails.js.map +1 -1
  108. package/dist/esm/components/RouteCard/RouteDetails.style.d.ts +5 -6
  109. package/dist/esm/components/RouteCard/RouteDetails.style.d.ts.map +1 -1
  110. package/dist/esm/components/RouteCard/RouteNotFoundCard.d.ts +1 -2
  111. package/dist/esm/components/RouteCard/RouteNotFoundCard.d.ts.map +1 -1
  112. package/dist/esm/components/RouteCard/RouteTokens.d.ts +1 -2
  113. package/dist/esm/components/RouteCard/RouteTokens.d.ts.map +1 -1
  114. package/dist/esm/components/RouteCard/types.d.ts +2 -3
  115. package/dist/esm/components/RouteCard/types.d.ts.map +1 -1
  116. package/dist/esm/components/Routes/LimitOrderRoutes.js +2 -2
  117. package/dist/esm/components/Routes/LimitOrderRoutes.js.map +1 -1
  118. package/dist/esm/components/Routes/RoutesContent.js +2 -2
  119. package/dist/esm/components/Routes/RoutesContent.js.map +1 -1
  120. package/dist/esm/components/Search/SearchInput.d.ts +1 -2
  121. package/dist/esm/components/Search/SearchInput.d.ts.map +1 -1
  122. package/dist/esm/components/SendToWallet/SendToWalletButton.d.ts +1 -2
  123. package/dist/esm/components/SendToWallet/SendToWalletButton.d.ts.map +1 -1
  124. package/dist/esm/components/SendToWallet/SendToWalletExpandButton.js +2 -2
  125. package/dist/esm/components/SendToWallet/SendToWalletExpandButton.js.map +1 -1
  126. package/dist/esm/components/Skeleton/WidgetSkeleton.d.ts +1 -2
  127. package/dist/esm/components/Skeleton/WidgetSkeleton.d.ts.map +1 -1
  128. package/dist/esm/components/Skeleton/WidgetSkeleton.js +2 -1
  129. package/dist/esm/components/Skeleton/WidgetSkeleton.js.map +1 -1
  130. package/dist/esm/components/Step/StepActions.js +18 -16
  131. package/dist/esm/components/Step/StepActions.js.map +1 -1
  132. package/dist/esm/components/StepActions/SentToWalletRow.d.ts +1 -2
  133. package/dist/esm/components/StepActions/SentToWalletRow.d.ts.map +1 -1
  134. package/dist/esm/components/StepActions/StepActionRow.js +2 -1
  135. package/dist/esm/components/StepActions/StepActionRow.js.map +1 -1
  136. package/dist/esm/components/StepActions/StepActionRow.style.d.ts +1 -2
  137. package/dist/esm/components/StepActions/StepActionRow.style.d.ts.map +1 -1
  138. package/dist/esm/components/StepActions/StepActionsList.d.ts +1 -2
  139. package/dist/esm/components/StepActions/StepActionsList.d.ts.map +1 -1
  140. package/dist/esm/components/StepActions/executionRows.d.ts +2 -3
  141. package/dist/esm/components/StepActions/executionRows.d.ts.map +1 -1
  142. package/dist/esm/components/Switch.js +1 -1
  143. package/dist/esm/components/Switch.js.map +1 -1
  144. package/dist/esm/components/Tabs/Tabs.style.js +4 -2
  145. package/dist/esm/components/Tabs/Tabs.style.js.map +1 -1
  146. package/dist/esm/components/Token/Token.d.ts.map +1 -1
  147. package/dist/esm/components/Token/Token.js +2 -4
  148. package/dist/esm/components/Token/Token.js.map +1 -1
  149. package/dist/esm/components/TokenList/RecentTokensHeader.js +44 -0
  150. package/dist/esm/components/TokenList/RecentTokensHeader.js.map +1 -0
  151. package/dist/esm/components/TokenList/RecentTokensToggle.js +36 -0
  152. package/dist/esm/components/TokenList/RecentTokensToggle.js.map +1 -0
  153. package/dist/esm/components/TokenList/RemoveRecentTokenButton.js +32 -0
  154. package/dist/esm/components/TokenList/RemoveRecentTokenButton.js.map +1 -0
  155. package/dist/esm/components/TokenList/TokenList.js +55 -5
  156. package/dist/esm/components/TokenList/TokenList.js.map +1 -1
  157. package/dist/esm/components/TokenList/TokenListItem.js +81 -27
  158. package/dist/esm/components/TokenList/TokenListItem.js.map +1 -1
  159. package/dist/esm/components/TokenList/TokenNotFound.d.ts +1 -2
  160. package/dist/esm/components/TokenList/TokenNotFound.d.ts.map +1 -1
  161. package/dist/esm/components/TokenList/VirtualizedTokenList.d.ts +1 -2
  162. package/dist/esm/components/TokenList/VirtualizedTokenList.d.ts.map +1 -1
  163. package/dist/esm/components/TokenList/VirtualizedTokenList.js +63 -37
  164. package/dist/esm/components/TokenList/VirtualizedTokenList.js.map +1 -1
  165. package/dist/esm/components/TokenList/types.d.ts +9 -2
  166. package/dist/esm/components/TokenList/types.d.ts.map +1 -1
  167. package/dist/esm/components/TokenList/useTokenSelect.d.ts +1 -2
  168. package/dist/esm/components/TokenList/useTokenSelect.d.ts.map +1 -1
  169. package/dist/esm/components/TokenRate/TokenRate.d.ts +1 -2
  170. package/dist/esm/components/TokenRate/TokenRate.d.ts.map +1 -1
  171. package/dist/esm/components/TokenRate/TokenRate.js +2 -1
  172. package/dist/esm/components/TokenRate/TokenRate.js.map +1 -1
  173. package/dist/esm/components/TransactionCard/ActiveTransactionCard.js +22 -21
  174. package/dist/esm/components/TransactionCard/ActiveTransactionCard.js.map +1 -1
  175. package/dist/esm/components/TransactionCard/TransactionCard.style.d.ts +2 -3
  176. package/dist/esm/components/TransactionCard/TransactionCard.style.d.ts.map +1 -1
  177. package/dist/esm/config/constants.d.ts +1 -2
  178. package/dist/esm/config/constants.d.ts.map +1 -1
  179. package/dist/esm/config/version.d.ts +2 -3
  180. package/dist/esm/config/version.d.ts.map +1 -1
  181. package/dist/esm/config/version.js +1 -1
  182. package/dist/esm/config/version.js.map +1 -1
  183. package/dist/esm/hooks/timer/time.js +2 -2
  184. package/dist/esm/hooks/timer/time.js.map +1 -1
  185. package/dist/esm/hooks/useAddressActivity.d.ts +1 -2
  186. package/dist/esm/hooks/useAddressActivity.d.ts.map +1 -1
  187. package/dist/esm/hooks/useAddressValidation.d.ts +2 -3
  188. package/dist/esm/hooks/useAddressValidation.d.ts.map +1 -1
  189. package/dist/esm/hooks/useAvailableChains.d.ts +1 -2
  190. package/dist/esm/hooks/useAvailableChains.d.ts.map +1 -1
  191. package/dist/esm/hooks/useAvailableChains.js +2 -1
  192. package/dist/esm/hooks/useAvailableChains.js.map +1 -1
  193. package/dist/esm/hooks/useChain.d.ts +1 -2
  194. package/dist/esm/hooks/useChain.d.ts.map +1 -1
  195. package/dist/esm/hooks/useContactSupport.d.ts +1 -2
  196. package/dist/esm/hooks/useContactSupport.d.ts.map +1 -1
  197. package/dist/esm/hooks/useDebouncedWatch.d.ts +1 -2
  198. package/dist/esm/hooks/useDebouncedWatch.d.ts.map +1 -1
  199. package/dist/esm/hooks/useDisplayedTokens.js +6 -4
  200. package/dist/esm/hooks/useDisplayedTokens.js.map +1 -1
  201. package/dist/esm/hooks/useExplorer.d.ts +1 -2
  202. package/dist/esm/hooks/useExplorer.d.ts.map +1 -1
  203. package/dist/esm/hooks/useExplorer.js +4 -0
  204. package/dist/esm/hooks/useExplorer.js.map +1 -1
  205. package/dist/esm/hooks/useFlaggedTokenGuard.js +28 -0
  206. package/dist/esm/hooks/useFlaggedTokenGuard.js.map +1 -0
  207. package/dist/esm/hooks/useGasSufficiency.js +3 -1
  208. package/dist/esm/hooks/useGasSufficiency.js.map +1 -1
  209. package/dist/esm/hooks/useHeader.d.ts +1 -2
  210. package/dist/esm/hooks/useHeader.d.ts.map +1 -1
  211. package/dist/esm/hooks/useListHeight.d.ts +1 -2
  212. package/dist/esm/hooks/useListHeight.d.ts.map +1 -1
  213. package/dist/esm/hooks/useMaxSendAmount.js +15 -0
  214. package/dist/esm/hooks/useMaxSendAmount.js.map +1 -0
  215. package/dist/esm/hooks/useNavigateBack.d.ts +1 -2
  216. package/dist/esm/hooks/useNavigateBack.d.ts.map +1 -1
  217. package/dist/esm/hooks/usePacedRouteExecution.js +3 -2
  218. package/dist/esm/hooks/usePacedRouteExecution.js.map +1 -1
  219. package/dist/esm/hooks/useRecentTokens.js +46 -0
  220. package/dist/esm/hooks/useRecentTokens.js.map +1 -0
  221. package/dist/esm/hooks/useRouteExecution.d.ts +1 -2
  222. package/dist/esm/hooks/useRouteExecution.d.ts.map +1 -1
  223. package/dist/esm/hooks/useRouteExecutionMessage.d.ts +1 -2
  224. package/dist/esm/hooks/useRouteExecutionMessage.d.ts.map +1 -1
  225. package/dist/esm/hooks/useRouteExecutionMessage.js +3 -2
  226. package/dist/esm/hooks/useRouteExecutionMessage.js.map +1 -1
  227. package/dist/esm/hooks/useRoutes.d.ts +1 -2
  228. package/dist/esm/hooks/useRoutes.d.ts.map +1 -1
  229. package/dist/esm/hooks/useRoutes.js +8 -1
  230. package/dist/esm/hooks/useRoutes.js.map +1 -1
  231. package/dist/esm/hooks/useToAddressRequirements.d.ts +3 -2
  232. package/dist/esm/hooks/useToAddressRequirements.d.ts.map +1 -1
  233. package/dist/esm/hooks/useToAddressRequirements.js +13 -1
  234. package/dist/esm/hooks/useToAddressRequirements.js.map +1 -1
  235. package/dist/esm/hooks/useToken.d.ts +1 -2
  236. package/dist/esm/hooks/useToken.d.ts.map +1 -1
  237. package/dist/esm/hooks/useTokenAddressBalance.d.ts +1 -2
  238. package/dist/esm/hooks/useTokenAddressBalance.d.ts.map +1 -1
  239. package/dist/esm/hooks/useTokenBalances.d.ts +3 -2
  240. package/dist/esm/hooks/useTokenBalances.d.ts.map +1 -1
  241. package/dist/esm/hooks/useTokenBalances.js +19 -8
  242. package/dist/esm/hooks/useTokenBalances.js.map +1 -1
  243. package/dist/esm/hooks/useTokenBalancesQueries.js +29 -28
  244. package/dist/esm/hooks/useTokenBalancesQueries.js.map +1 -1
  245. package/dist/esm/hooks/useTokenList.d.ts +1 -2
  246. package/dist/esm/hooks/useTokenList.d.ts.map +1 -1
  247. package/dist/esm/hooks/useTokenSearch.js +5 -6
  248. package/dist/esm/hooks/useTokenSearch.js.map +1 -1
  249. package/dist/esm/hooks/useTokens.js +40 -25
  250. package/dist/esm/hooks/useTokens.js.map +1 -1
  251. package/dist/esm/hooks/useTools.d.ts +1 -2
  252. package/dist/esm/hooks/useTools.d.ts.map +1 -1
  253. package/dist/esm/hooks/useWideVariant.js +2 -1
  254. package/dist/esm/hooks/useWideVariant.js.map +1 -1
  255. package/dist/esm/hooks/useWidgetChains.d.ts +1 -2
  256. package/dist/esm/hooks/useWidgetChains.d.ts.map +1 -1
  257. package/dist/esm/hooks/useWidgetEvents.d.ts +3 -4
  258. package/dist/esm/hooks/useWidgetEvents.d.ts.map +1 -1
  259. package/dist/esm/i18n/bn.json +7 -1
  260. package/dist/esm/i18n/de.json +7 -1
  261. package/dist/esm/i18n/en.json +16 -3
  262. package/dist/esm/i18n/es.json +7 -1
  263. package/dist/esm/i18n/fr.json +7 -1
  264. package/dist/esm/i18n/hi.json +7 -1
  265. package/dist/esm/i18n/id.json +7 -1
  266. package/dist/esm/i18n/it.json +7 -1
  267. package/dist/esm/i18n/ja.json +7 -1
  268. package/dist/esm/i18n/ko.json +7 -1
  269. package/dist/esm/i18n/pl.json +7 -1
  270. package/dist/esm/i18n/pt.json +7 -1
  271. package/dist/esm/i18n/th.json +7 -1
  272. package/dist/esm/i18n/tr.json +7 -1
  273. package/dist/esm/i18n/uk.json +7 -1
  274. package/dist/esm/i18n/vi.json +7 -1
  275. package/dist/esm/i18n/zh.json +7 -1
  276. package/dist/esm/pages/MainPage/MainPage.js +2 -1
  277. package/dist/esm/pages/MainPage/MainPage.js.map +1 -1
  278. package/dist/esm/pages/MainPage/MainWarningMessages.d.ts +1 -2
  279. package/dist/esm/pages/MainPage/MainWarningMessages.d.ts.map +1 -1
  280. package/dist/esm/pages/MainPage/ReviewButton.js +2 -2
  281. package/dist/esm/pages/MainPage/ReviewButton.js.map +1 -1
  282. package/dist/esm/pages/RoutesPage/RoutesPage.js +2 -2
  283. package/dist/esm/pages/RoutesPage/RoutesPage.js.map +1 -1
  284. package/dist/esm/pages/SelectChainPage/SelectChainPage.d.ts +1 -2
  285. package/dist/esm/pages/SelectChainPage/SelectChainPage.d.ts.map +1 -1
  286. package/dist/esm/pages/SelectChainPage/SelectChainPage.js +13 -12
  287. package/dist/esm/pages/SelectChainPage/SelectChainPage.js.map +1 -1
  288. package/dist/esm/pages/SelectChainPage/types.d.ts +1 -2
  289. package/dist/esm/pages/SelectChainPage/types.d.ts.map +1 -1
  290. package/dist/esm/pages/SelectEnabledToolsPage.js +8 -5
  291. package/dist/esm/pages/SelectEnabledToolsPage.js.map +1 -1
  292. package/dist/esm/pages/SelectTokenPage/SearchTokenInput.d.ts +1 -2
  293. package/dist/esm/pages/SelectTokenPage/SearchTokenInput.d.ts.map +1 -1
  294. package/dist/esm/pages/SelectTokenPage/SelectTokenPage.js +2 -1
  295. package/dist/esm/pages/SelectTokenPage/SelectTokenPage.js.map +1 -1
  296. package/dist/esm/pages/SettingsPage/SmallBalanceFilterSettings.js +2 -1
  297. package/dist/esm/pages/SettingsPage/SmallBalanceFilterSettings.js.map +1 -1
  298. package/dist/esm/pages/TransactionDetailsPage/ContactSupportButton.d.ts +1 -2
  299. package/dist/esm/pages/TransactionDetailsPage/ContactSupportButton.d.ts.map +1 -1
  300. package/dist/esm/pages/TransactionDetailsPage/ContactSupportButton.js +2 -1
  301. package/dist/esm/pages/TransactionDetailsPage/ContactSupportButton.js.map +1 -1
  302. package/dist/esm/pages/TransactionDetailsPage/TransactionDetailsPage.js +2 -1
  303. package/dist/esm/pages/TransactionDetailsPage/TransactionDetailsPage.js.map +1 -1
  304. package/dist/esm/pages/TransactionPage/ConfirmToAddressSheet.d.ts +1 -2
  305. package/dist/esm/pages/TransactionPage/ConfirmToAddressSheet.d.ts.map +1 -1
  306. package/dist/esm/pages/TransactionPage/ExchangeRateBottomSheet.d.ts +2 -3
  307. package/dist/esm/pages/TransactionPage/ExchangeRateBottomSheet.d.ts.map +1 -1
  308. package/dist/esm/pages/TransactionPage/ExecutionDoneCard.d.ts +1 -2
  309. package/dist/esm/pages/TransactionPage/ExecutionDoneCard.d.ts.map +1 -1
  310. package/dist/esm/pages/TransactionPage/RouteTracker.d.ts +1 -2
  311. package/dist/esm/pages/TransactionPage/RouteTracker.d.ts.map +1 -1
  312. package/dist/esm/pages/TransactionPage/StartTransactionButton.d.ts +1 -2
  313. package/dist/esm/pages/TransactionPage/StartTransactionButton.d.ts.map +1 -1
  314. package/dist/esm/pages/TransactionPage/StartTransactionButton.js +4 -3
  315. package/dist/esm/pages/TransactionPage/StartTransactionButton.js.map +1 -1
  316. package/dist/esm/pages/TransactionPage/TokenValueBottomSheet.d.ts +1 -2
  317. package/dist/esm/pages/TransactionPage/TokenValueBottomSheet.d.ts.map +1 -1
  318. package/dist/esm/pages/TransactionPage/TokenValueBottomSheet.js +0 -1
  319. package/dist/esm/pages/TransactionPage/TokenValueBottomSheet.js.map +1 -1
  320. package/dist/esm/pages/TransactionPage/TokenVerificationBottomSheet.js +63 -0
  321. package/dist/esm/pages/TransactionPage/TokenVerificationBottomSheet.js.map +1 -0
  322. package/dist/esm/pages/TransactionPage/TransactionDoneButtons.d.ts +1 -2
  323. package/dist/esm/pages/TransactionPage/TransactionDoneButtons.d.ts.map +1 -1
  324. package/dist/esm/pages/TransactionPage/TransactionExecutionContent.js +19 -16
  325. package/dist/esm/pages/TransactionPage/TransactionExecutionContent.js.map +1 -1
  326. package/dist/esm/pages/TransactionPage/TransactionFailedButtons.js +38 -10
  327. package/dist/esm/pages/TransactionPage/TransactionFailedButtons.js.map +1 -1
  328. package/dist/esm/pages/TransactionPage/TransactionReview.js +32 -9
  329. package/dist/esm/pages/TransactionPage/TransactionReview.js.map +1 -1
  330. package/dist/esm/pages/TransactionPage/types.d.ts +1 -2
  331. package/dist/esm/pages/TransactionPage/types.d.ts.map +1 -1
  332. package/dist/esm/pages/TransactionPage/utils.d.ts +2 -3
  333. package/dist/esm/pages/TransactionPage/utils.d.ts.map +1 -1
  334. package/dist/esm/pages/TransactionPage/utils.js +23 -1
  335. package/dist/esm/pages/TransactionPage/utils.js.map +1 -1
  336. package/dist/esm/providers/I18nProvider/I18nProvider.d.ts +1 -2
  337. package/dist/esm/providers/I18nProvider/I18nProvider.d.ts.map +1 -1
  338. package/dist/esm/providers/I18nProvider/I18nProvider.js +37 -36
  339. package/dist/esm/providers/I18nProvider/I18nProvider.js.map +1 -1
  340. package/dist/esm/providers/I18nProvider/types.d.ts +4 -5
  341. package/dist/esm/providers/I18nProvider/types.d.ts.map +1 -1
  342. package/dist/esm/providers/QueryClientProvider.d.ts +1 -2
  343. package/dist/esm/providers/QueryClientProvider.d.ts.map +1 -1
  344. package/dist/esm/providers/SDKClientProvider.d.ts +2 -3
  345. package/dist/esm/providers/SDKClientProvider.d.ts.map +1 -1
  346. package/dist/esm/providers/ThemeProvider/ThemeProvider.js +2 -1
  347. package/dist/esm/providers/ThemeProvider/ThemeProvider.js.map +1 -1
  348. package/dist/esm/providers/WalletProvider/WalletProvider.d.ts +1 -2
  349. package/dist/esm/providers/WalletProvider/WalletProvider.d.ts.map +1 -1
  350. package/dist/esm/providers/WalletProvider/WalletProvider.js +10 -9
  351. package/dist/esm/providers/WalletProvider/WalletProvider.js.map +1 -1
  352. package/dist/esm/providers/WalletProvider/useExternalWalletProvider.js +7 -3
  353. package/dist/esm/providers/WalletProvider/useExternalWalletProvider.js.map +1 -1
  354. package/dist/esm/providers/WidgetProvider/WidgetProvider.d.ts +2 -3
  355. package/dist/esm/providers/WidgetProvider/WidgetProvider.d.ts.map +1 -1
  356. package/dist/esm/providers/WidgetProvider/types.d.ts +2 -3
  357. package/dist/esm/providers/WidgetProvider/types.d.ts.map +1 -1
  358. package/dist/esm/stores/StoreProvider.d.ts +1 -2
  359. package/dist/esm/stores/StoreProvider.d.ts.map +1 -1
  360. package/dist/esm/stores/StoreProvider.js +10 -6
  361. package/dist/esm/stores/StoreProvider.js.map +1 -1
  362. package/dist/esm/stores/bookmarks/BookmarkStore.d.ts +1 -2
  363. package/dist/esm/stores/bookmarks/BookmarkStore.d.ts.map +1 -1
  364. package/dist/esm/stores/bookmarks/types.d.ts +5 -6
  365. package/dist/esm/stores/bookmarks/types.d.ts.map +1 -1
  366. package/dist/esm/stores/chains/ChainOrderStore.d.ts +1 -2
  367. package/dist/esm/stores/chains/ChainOrderStore.d.ts.map +1 -1
  368. package/dist/esm/stores/chains/ChainOrderStore.js +11 -5
  369. package/dist/esm/stores/chains/ChainOrderStore.js.map +1 -1
  370. package/dist/esm/stores/chains/types.d.ts +1 -2
  371. package/dist/esm/stores/chains/types.d.ts.map +1 -1
  372. package/dist/esm/stores/form/types.d.ts +20 -21
  373. package/dist/esm/stores/form/types.d.ts.map +1 -1
  374. package/dist/esm/stores/form/useFieldActions.d.ts +1 -2
  375. package/dist/esm/stores/form/useFieldActions.d.ts.map +1 -1
  376. package/dist/esm/stores/form/useFieldValues.d.ts +1 -2
  377. package/dist/esm/stores/form/useFieldValues.d.ts.map +1 -1
  378. package/dist/esm/stores/header/types.d.ts +1 -2
  379. package/dist/esm/stores/header/types.d.ts.map +1 -1
  380. package/dist/esm/stores/header/useHeaderStore.d.ts +2 -3
  381. package/dist/esm/stores/header/useHeaderStore.d.ts.map +1 -1
  382. package/dist/esm/stores/inputMode/useInputModeStore.d.ts +1 -2
  383. package/dist/esm/stores/inputMode/useInputModeStore.d.ts.map +1 -1
  384. package/dist/esm/stores/recentTokens/RecentTokensStore.js +23 -0
  385. package/dist/esm/stores/recentTokens/RecentTokensStore.js.map +1 -0
  386. package/dist/esm/stores/recentTokens/createRecentTokensStore.js +54 -0
  387. package/dist/esm/stores/recentTokens/createRecentTokensStore.js.map +1 -0
  388. package/dist/esm/stores/recentTokens/utils.js +15 -0
  389. package/dist/esm/stores/recentTokens/utils.js.map +1 -0
  390. package/dist/esm/stores/routes/RouteExecutionStore.d.ts +2 -3
  391. package/dist/esm/stores/routes/RouteExecutionStore.d.ts.map +1 -1
  392. package/dist/esm/stores/routes/types.d.ts +3 -4
  393. package/dist/esm/stores/routes/types.d.ts.map +1 -1
  394. package/dist/esm/stores/routes/useRouteExecutionIndicator.js +4 -3
  395. package/dist/esm/stores/routes/useRouteExecutionIndicator.js.map +1 -1
  396. package/dist/esm/stores/routes/utils.d.ts +4 -5
  397. package/dist/esm/stores/routes/utils.d.ts.map +1 -1
  398. package/dist/esm/stores/settings/SettingsStore.d.ts +2 -3
  399. package/dist/esm/stores/settings/SettingsStore.d.ts.map +1 -1
  400. package/dist/esm/stores/settings/types.d.ts +2 -3
  401. package/dist/esm/stores/settings/types.d.ts.map +1 -1
  402. package/dist/esm/stores/types.d.ts +2 -3
  403. package/dist/esm/stores/types.d.ts.map +1 -1
  404. package/dist/esm/themes/azureLight.d.ts +1 -2
  405. package/dist/esm/themes/azureLight.d.ts.map +1 -1
  406. package/dist/esm/themes/jumper.d.ts +1 -2
  407. package/dist/esm/themes/jumper.d.ts.map +1 -1
  408. package/dist/esm/themes/palettes.d.ts +3 -4
  409. package/dist/esm/themes/palettes.d.ts.map +1 -1
  410. package/dist/esm/themes/types.d.ts +1 -0
  411. package/dist/esm/themes/watermelonLight.d.ts +1 -2
  412. package/dist/esm/themes/watermelonLight.d.ts.map +1 -1
  413. package/dist/esm/themes/windows95.d.ts +1 -2
  414. package/dist/esm/themes/windows95.d.ts.map +1 -1
  415. package/dist/esm/types/events.d.ts +13 -14
  416. package/dist/esm/types/events.d.ts.map +1 -1
  417. package/dist/esm/types/token.d.ts +6 -1
  418. package/dist/esm/types/token.d.ts.map +1 -1
  419. package/dist/esm/types/widget.d.ts +55 -51
  420. package/dist/esm/types/widget.d.ts.map +1 -1
  421. package/dist/esm/types/widget.js.map +1 -1
  422. package/dist/esm/utils/address.js +19 -0
  423. package/dist/esm/utils/address.js.map +1 -0
  424. package/dist/esm/utils/compactNumberFormatter.d.ts +1 -2
  425. package/dist/esm/utils/compactNumberFormatter.d.ts.map +1 -1
  426. package/dist/esm/utils/compactNumberFormatter.js +1 -3
  427. package/dist/esm/utils/compactNumberFormatter.js.map +1 -1
  428. package/dist/esm/utils/converters.d.ts +1 -2
  429. package/dist/esm/utils/converters.d.ts.map +1 -1
  430. package/dist/esm/utils/currencyExtendedFormatter.d.ts +1 -2
  431. package/dist/esm/utils/currencyExtendedFormatter.d.ts.map +1 -1
  432. package/dist/esm/utils/customReceiver.js +12 -0
  433. package/dist/esm/utils/customReceiver.js.map +1 -0
  434. package/dist/esm/utils/elements.d.ts +2 -3
  435. package/dist/esm/utils/elements.d.ts.map +1 -1
  436. package/dist/esm/utils/enum.d.ts +1 -2
  437. package/dist/esm/utils/enum.d.ts.map +1 -1
  438. package/dist/esm/utils/fees.d.ts +2 -3
  439. package/dist/esm/utils/fees.d.ts.map +1 -1
  440. package/dist/esm/utils/format.d.ts +8 -9
  441. package/dist/esm/utils/format.d.ts.map +1 -1
  442. package/dist/esm/utils/gas.js +24 -0
  443. package/dist/esm/utils/gas.js.map +1 -0
  444. package/dist/esm/utils/getErrorMessage.js +0 -1
  445. package/dist/esm/utils/getErrorMessage.js.map +1 -1
  446. package/dist/esm/utils/getPriceImpact.d.ts +1 -2
  447. package/dist/esm/utils/getPriceImpact.d.ts.map +1 -1
  448. package/dist/esm/utils/input.d.ts +1 -2
  449. package/dist/esm/utils/input.d.ts.map +1 -1
  450. package/dist/esm/utils/limitOrder.js +1 -1
  451. package/dist/esm/utils/limitOrder.js.map +1 -1
  452. package/dist/esm/utils/navigationRoutes.d.ts +2 -3
  453. package/dist/esm/utils/navigationRoutes.d.ts.map +1 -1
  454. package/dist/esm/utils/percentFormatter.d.ts +1 -2
  455. package/dist/esm/utils/percentFormatter.d.ts.map +1 -1
  456. package/dist/esm/utils/recentTokens.js +97 -0
  457. package/dist/esm/utils/recentTokens.js.map +1 -0
  458. package/dist/esm/utils/token.js +50 -7
  459. package/dist/esm/utils/token.js.map +1 -1
  460. package/dist/esm/utils/tokenList.js +54 -25
  461. package/dist/esm/utils/tokenList.js.map +1 -1
  462. package/dist/esm/utils/tokenListBands.js +85 -0
  463. package/dist/esm/utils/tokenListBands.js.map +1 -0
  464. package/dist/esm/utils/wallet.d.ts +1 -2
  465. package/dist/esm/utils/wallet.d.ts.map +1 -1
  466. package/package.json +15 -15
  467. package/src/components/AmountInput/AmountInputEndAdornment.tsx +2 -21
  468. package/src/components/AmountInputCard/PercentageChips.tsx +2 -18
  469. package/src/components/AmountInputCard/ReceiveAmountCard.tsx +16 -3
  470. package/src/components/BottomSheet/BottomSheet.tsx +3 -0
  471. package/src/components/Chains/AllChainsAvatar.tsx +7 -1
  472. package/src/components/Messages/UnsupportedToAddressMessage.tsx +24 -0
  473. package/src/components/Messages/WarningMessages.tsx +3 -0
  474. package/src/components/Messages/useMessageQueue.ts +9 -0
  475. package/src/components/Routes/LimitOrderRoutes.tsx +5 -2
  476. package/src/components/Routes/RoutesContent.tsx +5 -2
  477. package/src/components/SendToWallet/SendToWalletExpandButton.tsx +2 -1
  478. package/src/components/Token/Token.tsx +2 -1
  479. package/src/components/TokenList/RecentTokensHeader.tsx +52 -0
  480. package/src/components/TokenList/RecentTokensToggle.tsx +45 -0
  481. package/src/components/TokenList/RemoveRecentTokenButton.tsx +38 -0
  482. package/src/components/TokenList/TokenList.tsx +92 -3
  483. package/src/components/TokenList/TokenListItem.tsx +83 -9
  484. package/src/components/TokenList/VirtualizedTokenList.tsx +86 -103
  485. package/src/components/TokenList/types.ts +13 -0
  486. package/src/config/version.ts +1 -1
  487. package/src/hooks/useAvailableChains.ts +1 -0
  488. package/src/hooks/useDisplayedTokens.ts +17 -3
  489. package/src/hooks/useExplorer.ts +2 -0
  490. package/src/hooks/useFlaggedTokenGuard.ts +33 -0
  491. package/src/hooks/useGasSufficiency.ts +9 -1
  492. package/src/hooks/useMaxSendAmount.ts +21 -0
  493. package/src/hooks/useRecentTokens.ts +88 -0
  494. package/src/hooks/useRoutes.ts +10 -0
  495. package/src/hooks/useToAddressRequirements.ts +23 -1
  496. package/src/hooks/useTokenBalances.ts +38 -19
  497. package/src/hooks/useTokenSearch.ts +6 -19
  498. package/src/hooks/useTokens.ts +97 -40
  499. package/src/pages/MainPage/ReviewButton.tsx +6 -2
  500. package/src/pages/RoutesPage/RoutesPage.tsx +5 -2
  501. package/src/pages/TransactionPage/TokenValueBottomSheet.tsx +1 -1
  502. package/src/pages/TransactionPage/TokenVerificationBottomSheet.tsx +93 -0
  503. package/src/pages/TransactionPage/TransactionFailedButtons.tsx +62 -26
  504. package/src/pages/TransactionPage/TransactionReview.tsx +51 -25
  505. package/src/pages/TransactionPage/utils.ts +78 -0
  506. package/src/providers/WalletProvider/useExternalWalletProvider.ts +9 -1
  507. package/src/stores/StoreProvider.tsx +10 -7
  508. package/src/stores/chains/ChainOrderStore.test.tsx +298 -0
  509. package/src/stores/chains/ChainOrderStore.tsx +25 -6
  510. package/src/stores/recentTokens/RecentTokensStore.tsx +39 -0
  511. package/src/stores/recentTokens/createRecentTokensStore.ts +107 -0
  512. package/src/stores/recentTokens/types.ts +37 -0
  513. package/src/stores/recentTokens/utils.ts +18 -0
  514. package/src/types/token.ts +8 -3
  515. package/src/types/widget.ts +5 -0
  516. package/src/utils/address.ts +21 -0
  517. package/src/utils/customReceiver.ts +31 -0
  518. package/src/utils/gas.ts +41 -0
  519. package/src/utils/recentTokens.ts +187 -0
  520. package/src/utils/token.ts +122 -19
  521. package/src/utils/tokenList.ts +60 -14
  522. package/src/utils/tokenListBands.ts +181 -0
@@ -1 +1 @@
1
- {"version":3,"file":"token.js","names":[],"sources":["../../../src/utils/token.ts"],"sourcesContent":["import type { BaseToken, RouteExtended, Token, TokenAmount } from '@lifi/sdk'\nimport type { FormType } from '../stores/form/types.js'\nimport type { TokensByChain, TokenWithVerified } from '../types/token.js'\nimport type { WidgetChains, WidgetTokens } from '../types/widget.js'\nimport { getConfigItemSets, isFormItemAllowed } from './item.js'\n\n/**\n * Builds per-chain sets of lowercase token addresses from the\n * `tokens.verified` config allowlist.\n */\nexport const getVerifiedTokensSets = (\n configTokens?: WidgetTokens\n): Map<number, Set<string>> | undefined => {\n if (!configTokens?.verified?.length) {\n return undefined\n }\n const sets = new Map<number, Set<string>>()\n for (const token of configTokens.verified) {\n const chainId = Number(token.chainId)\n let addresses = sets.get(chainId)\n if (!addresses) {\n addresses = new Set()\n sets.set(chainId, addresses)\n }\n addresses.add(token.address.toLowerCase())\n }\n return sets\n}\n\n/**\n * Merges verified tokens with search tokens.\n * Verified tokens take priority - search tokens are only added if they don't already exist.\n */\nexport const mergeVerifiedWithSearchTokens = (\n verifiedTokens?: TokensByChain,\n searchTokens?: TokensByChain\n): TokensByChain | undefined => {\n if (!verifiedTokens) {\n return searchTokens\n }\n if (!searchTokens) {\n return verifiedTokens\n }\n\n const result = { ...verifiedTokens }\n\n for (const [chainId, tokens] of Object.entries(searchTokens)) {\n const chainIdNum = Number(chainId)\n const existingTokens = result[chainIdNum] || []\n const existingAddresses = new Set(\n existingTokens.map((t) => t.address.toLowerCase())\n )\n\n const newTokens = tokens.filter(\n (t) => !existingAddresses.has(t.address.toLowerCase())\n )\n\n if (newTokens.length) {\n result[chainIdNum] = [...existingTokens, ...newTokens]\n }\n }\n\n return result\n}\n\n/**\n * Updates a token in the cache by chainId and address.\n * Returns a new cache object with the token updated, or the original if not found.\n */\nexport const updateTokenInCache = (\n data: TokensByChain | undefined,\n token: Token\n): TokensByChain | undefined => {\n if (!data) {\n return data\n }\n const chainTokens = data[token.chainId]\n if (!chainTokens) {\n return data\n }\n const index = chainTokens.findIndex((t) => t.address === token.address)\n if (index < 0) {\n return data\n }\n return {\n ...data,\n [token.chainId]: chainTokens.map((t, i) =>\n i === index ? { ...t, ...token } : t\n ),\n }\n}\n\nexport const filterAllowedTokens = (\n dataTokens: TokensByChain | undefined,\n configTokens?: WidgetTokens,\n chainsConfig?: WidgetChains,\n formType?: FormType\n): TokensByChain | undefined => {\n if (!dataTokens) {\n return\n }\n\n const includedTokens = configTokens?.include || []\n const allChainIds = Array.from(\n new Set([\n ...includedTokens.map((t) => t.chainId),\n ...Object.keys(dataTokens),\n ])\n ).map((chainId) => Number(chainId))\n\n const configChainIdsSet = getConfigItemSets(\n chainsConfig,\n (chainIds: number[]) => new Set(chainIds),\n formType\n )\n\n const allowedChainIds = configChainIdsSet\n ? allChainIds.filter((chainId) =>\n isFormItemAllowed(chainId, configChainIdsSet, formType)\n )\n : allChainIds\n\n const verifiedTokensSets = getVerifiedTokensSets(configTokens)\n\n const allowedTokensByChain: TokensByChain = {}\n for (const chainId of allowedChainIds) {\n const chainIncludedTokens = includedTokens.filter(\n (t) => Number(t.chainId) === chainId\n )\n const includedAddresses = new Set(\n chainIncludedTokens.map((t) => t.address.toLowerCase())\n )\n\n const allowedAddresses = getConfigItemSets(\n configTokens,\n (tokens: BaseToken[]) =>\n new Set(\n tokens\n .filter((t) => Number(t.chainId) === chainId)\n .map((t) => t.address.toLowerCase())\n ),\n formType\n )\n\n const verifiedAddresses = verifiedTokensSets?.get(chainId)\n\n const chainTokens: TokenWithVerified[] = [\n ...(dataTokens[chainId] ?? []),\n ...chainIncludedTokens,\n ]\n\n const filtered: TokenWithVerified[] = []\n const seenAddresses = new Set<string>()\n for (const token of chainTokens) {\n const address = token.address.toLowerCase()\n // Data tokens come first and win over include duplicates,\n // keeping their extended data\n if (seenAddresses.has(address)) {\n continue\n }\n seenAddresses.add(address)\n if (\n !isFormItemAllowed(token, allowedAddresses, formType, (t) =>\n t.address.toLowerCase()\n )\n ) {\n continue\n }\n // Include and verified-allowlist tokens are explicitly set by the\n // integrator, mark them as verified\n if (\n !token.verified &&\n (includedAddresses.has(address) || verifiedAddresses?.has(address))\n ) {\n filtered.push({ ...token, verified: true })\n } else {\n filtered.push(token)\n }\n }\n\n allowedTokensByChain[chainId] = filtered\n }\n\n return allowedTokensByChain\n}\n\nexport const getExecutionToToken = (route: RouteExtended): TokenAmount => ({\n ...(route.steps.at(-1)?.execution?.toToken ?? route.toToken),\n amount: BigInt(\n route.steps.at(-1)?.execution?.toAmount ??\n route.steps.at(-1)?.estimate.toAmount ??\n route.toAmount\n ),\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"token.js","names":[],"sources":["../../../src/utils/token.ts"],"sourcesContent":["import type {\n BaseToken,\n ExtendedChain,\n RouteExtended,\n StaticToken,\n Token,\n TokenAmount,\n TokenExtended,\n} from '@lifi/sdk'\nimport { ChainId } from '@lifi/sdk'\nimport type { FormType } from '../stores/form/types.js'\nimport type { TokensByChain, TokenWithFlags } from '../types/token.js'\nimport type {\n AllowDenySets,\n WidgetChains,\n WidgetTokens,\n} from '../types/widget.js'\nimport { getConfigItemSets, isFormItemAllowed } from './item.js'\n\n// Number(): plain-JS integrators may pass chainId as a string.\nexport const getChainTokenAllowSets = (\n configTokens: WidgetTokens | undefined,\n chainId: number,\n formType?: FormType\n): AllowDenySets | undefined =>\n getConfigItemSets(\n configTokens,\n (tokens: BaseToken[]) =>\n new Set(\n tokens\n .filter((t) => Number(t.chainId) === chainId)\n .map((t) => t.address.toLowerCase())\n ),\n formType\n )\n\n/**\n * Builds per-chain sets of lowercase token addresses from the\n * `tokens.verified` config allowlist.\n */\nexport const getVerifiedTokensSets = (\n configTokens?: WidgetTokens\n): Map<number, Set<string>> | undefined => {\n if (!configTokens?.verified?.length) {\n return undefined\n }\n const sets = new Map<number, Set<string>>()\n for (const token of configTokens.verified) {\n const chainId = Number(token.chainId)\n let addresses = sets.get(chainId)\n if (!addresses) {\n addresses = new Set()\n sets.set(chainId, addresses)\n }\n addresses.add(token.address.toLowerCase())\n }\n return sets\n}\n\n/**\n * Merges verified tokens with search tokens.\n * Verified tokens take priority - search tokens are only added if they don't already exist.\n */\nexport const mergeVerifiedWithSearchTokens = (\n verifiedTokens?: TokensByChain,\n searchTokens?: TokensByChain\n): TokensByChain | undefined => {\n if (!verifiedTokens) {\n return searchTokens\n }\n if (!searchTokens) {\n return verifiedTokens\n }\n\n const result = { ...verifiedTokens }\n\n for (const [chainId, tokens] of Object.entries(searchTokens)) {\n const chainIdNum = Number(chainId)\n const existingTokens = result[chainIdNum] || []\n const existingAddresses = new Set(\n existingTokens.map((t) => t.address.toLowerCase())\n )\n\n const newTokens = tokens.filter(\n (t) => !existingAddresses.has(t.address.toLowerCase())\n )\n\n if (newTokens.length) {\n result[chainIdNum] = [...existingTokens, ...newTokens]\n }\n }\n\n return result\n}\n\n/**\n * `priceUSD` is a required string, so an unknown price arrives as `'0'` or `''`\n * rather than as an absent key — the same reading as in `useLimitMarketRate`. A\n * known price must never lose to one, or a route that cannot price its output\n * blanks a value the cache already knew.\n */\nexport const knownPriceUSD = (fresher: string, cached: string): string =>\n Number(fresher) ? fresher : cached\n\n/**\n * Merges a cached token into a route token for display. The route wins on every\n * field it carries; the cache only fills gaps. Letting the cache win instead\n * swaps the quoted price for a stale feed price.\n */\nexport const mergeFallbackToken = (\n routeToken: TokenAmount,\n cachedToken: TokenExtended | undefined\n): TokenAmount =>\n cachedToken\n ? {\n ...cachedToken,\n ...routeToken,\n priceUSD: knownPriceUSD(routeToken.priceUSD, cachedToken.priceUSD),\n logoURI: routeToken.logoURI || cachedToken.logoURI,\n }\n : routeToken\n\n/**\n * Updates a token in the cache by chainId and address.\n * Returns a new cache object with the token updated, or the original if not found.\n * Addresses are compared case-insensitively: the token list and the route can\n * spell the same address differently. The incoming token only overwrites what it\n * actually knows, so an unpriced or logo-less route cannot blank the cache.\n */\nexport const updateTokenInCache = (\n data: TokensByChain | undefined,\n token: Token\n): TokensByChain | undefined => {\n if (!data) {\n return data\n }\n const chainTokens = data[token.chainId]\n if (!chainTokens) {\n return data\n }\n const address = token.address.toLowerCase()\n const index = chainTokens.findIndex(\n (t) => t.address.toLowerCase() === address\n )\n if (index < 0) {\n return data\n }\n return {\n ...data,\n [token.chainId]: chainTokens.map((t, i) =>\n i === index\n ? {\n ...t,\n ...token,\n // Keep the cached address: other readers hold it as the identity.\n address: t.address,\n priceUSD: knownPriceUSD(token.priceUSD, t.priceUSD),\n logoURI: token.logoURI || t.logoURI,\n }\n : t\n ),\n }\n}\n\nexport const filterAllowedTokens = (\n dataTokens: TokensByChain | undefined,\n configTokens?: WidgetTokens,\n chainsConfig?: WidgetChains,\n formType?: FormType,\n nativeTokenAddresses?: Map<number, string>\n): TokensByChain | undefined => {\n if (!dataTokens) {\n return\n }\n\n const includedTokens = configTokens?.include || []\n const allChainIds = Array.from(\n new Set([\n ...includedTokens.map((t) => t.chainId),\n ...Object.keys(dataTokens),\n ])\n ).map((chainId) => Number(chainId))\n\n const configChainIdsSet = getConfigItemSets(\n chainsConfig,\n (chainIds: number[]) => new Set(chainIds),\n formType\n )\n\n const allowedChainIds = configChainIdsSet\n ? allChainIds.filter((chainId) =>\n isFormItemAllowed(chainId, configChainIdsSet, formType)\n )\n : allChainIds\n\n const verifiedTokensSets = getVerifiedTokensSets(configTokens)\n\n const allowedTokensByChain: TokensByChain = {}\n for (const chainId of allowedChainIds) {\n const chainIncludedTokens = includedTokens.filter(\n (t) => Number(t.chainId) === chainId\n )\n const includedAddresses = new Set(\n chainIncludedTokens.map((t) => t.address.toLowerCase())\n )\n\n const allowedAddresses = getChainTokenAllowSets(\n configTokens,\n chainId,\n formType\n )\n\n const verifiedAddresses = verifiedTokensSets?.get(chainId)\n const nativeAddress = nativeTokenAddresses?.get(chainId)?.toLowerCase()\n\n const chainTokens: TokenWithFlags[] = [\n ...(dataTokens[chainId] ?? []),\n ...chainIncludedTokens,\n ]\n\n const filtered: TokenWithFlags[] = []\n const seenAddresses = new Set<string>()\n for (const token of chainTokens) {\n const address = token.address.toLowerCase()\n // Data tokens come first and win over include duplicates,\n // keeping their extended data\n if (seenAddresses.has(address)) {\n continue\n }\n seenAddresses.add(address)\n if (\n !isFormItemAllowed(token, allowedAddresses, formType, (t) =>\n t.address.toLowerCase()\n )\n ) {\n continue\n }\n // Include and verified-allowlist tokens are explicitly set by the\n // integrator, mark them as verified. Flags are only ever set to true,\n // so a token that earns none keeps its cached object.\n const markVerified =\n !token.verified &&\n (includedAddresses.has(address) || verifiedAddresses?.has(address))\n const markNative = !!nativeAddress && nativeAddress === address\n if (markVerified || markNative) {\n filtered.push({\n ...token,\n ...(markVerified ? { verified: true } : {}),\n ...(markNative ? { native: true } : {}),\n })\n } else {\n filtered.push(token)\n }\n }\n\n allowedTokensByChain[chainId] = filtered\n }\n\n return allowedTokensByChain\n}\n\nexport const getExecutionToToken = (route: RouteExtended): TokenAmount => ({\n ...(route.steps.at(-1)?.execution?.toToken ?? route.toToken),\n amount: BigInt(\n route.steps.at(-1)?.execution?.toAmount ??\n route.steps.at(-1)?.estimate.toAmount ??\n route.toAmount\n ),\n})\n\n/** Capitalized name of the first provider that verified the token. */\nexport const getTokenVerificationProvider = (\n token: Pick<StaticToken, 'verificationStatusBreakdown'>\n): string | undefined => {\n const provider = token.verificationStatusBreakdown?.find(\n (entry) => entry.result === 'verified'\n )?.provider\n return provider\n ? `${provider.charAt(0).toUpperCase()}${provider.slice(1)}`\n : undefined\n}\n\n/**\n * Hyperliquid declares Arbitrum USDC as its native token, Lighter declares\n * Ethereum USDC. Neither is a gas token you hold there. A chain-local\n * stablecoin gas token, as on Arc or Stable, is genuine and stays.\n */\nconst chainIdsWithoutNativeToken = new Set([ChainId.HPL, ChainId.LTR])\n\nexport const getNativeTokenAddresses = (\n chains?: ExtendedChain[]\n): Map<number, string> =>\n new Map(\n chains\n ?.filter((chain) => !chainIdsWithoutNativeToken.has(chain.id))\n .map((chain) => [chain.id, chain.nativeToken.address])\n )\n"],"mappings":"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"}
@@ -3,30 +3,32 @@ import { formatUnits } from "@lifi/sdk";
3
3
  const sortByBalances = (a, b) => Number.parseFloat(formatUnits(b.amount ?? 0n, b.decimals)) * Number.parseFloat(b.priceUSD ?? "0") - Number.parseFloat(formatUnits(a.amount ?? 0n, a.decimals)) * Number.parseFloat(a.priceUSD ?? "0");
4
4
  const sortByVolume = (a, b) => (b.volumeUSD24H ?? 0) - (a.volumeUSD24H ?? 0);
5
5
  const processTokenList = (withoutBalances, noCategories, configTokens, selectedChainId, tokens, tokensWithBalances, isPinnedToken) => {
6
- if (withoutBalances) if (noCategories) {
7
- const sortedTokens = [...tokens ?? []].sort(sortByVolume);
8
- if (isPinnedToken) {
9
- const pinned = [];
10
- const notPinned = [];
11
- for (const token of sortedTokens) if (isPinnedToken(token.chainId, token.address)) {
12
- const pinnedToken = {
13
- ...token,
14
- pinned: true
6
+ if (withoutBalances) {
7
+ if (noCategories) {
8
+ const sortedTokens = [...tokens ?? []].sort(sortByVolume);
9
+ if (isPinnedToken) {
10
+ const pinned = [];
11
+ const notPinned = [];
12
+ for (const token of sortedTokens) if (isPinnedToken(token.chainId, token.address)) {
13
+ const pinnedToken = {
14
+ ...token,
15
+ pinned: true
16
+ };
17
+ pinned.push(pinnedToken);
18
+ } else notPinned.push(token);
19
+ return {
20
+ processedTokens: [...pinned, ...notPinned],
21
+ withCategories: false,
22
+ withPinnedTokens: !!pinned.length
15
23
  };
16
- pinned.push(pinnedToken);
17
- } else notPinned.push(token);
24
+ }
18
25
  return {
19
- processedTokens: [...pinned, ...notPinned],
26
+ processedTokens: sortedTokens,
20
27
  withCategories: false,
21
- withPinnedTokens: !!pinned.length
28
+ withPinnedTokens: false
22
29
  };
23
- }
24
- return {
25
- processedTokens: sortedTokens,
26
- withCategories: false,
27
- withPinnedTokens: false
28
- };
29
- } else return processedTypedTokens(tokens ?? [], [], selectedChainId, configTokens, isPinnedToken);
30
+ } else return processedTypedTokens(tokens ?? [], [], selectedChainId, configTokens, isPinnedToken);
31
+ }
30
32
  const sortedTokensWithBalances = [...tokensWithBalances ?? []].sort(sortByBalances);
31
33
  const tokensWithBalancesSet = new Set(sortedTokensWithBalances.map((token) => `${token.chainId}-${token.address.toLowerCase()}`));
32
34
  const tokensWithoutBalances = tokens?.filter((token) => {
@@ -133,12 +135,39 @@ const processedTypedTokens = (tokens, tokensWithBalances, selectedChainId, confi
133
135
  withPinnedTokens: !!sortedPinnedTokens.length
134
136
  };
135
137
  };
136
- const isSearchMatch = (token, search) => {
137
- if (!search) return true;
138
- const searchLowerCase = search.toLowerCase();
139
- return token.name?.toLowerCase().includes(searchLowerCase) || token.symbol?.replaceAll("₮", "T").toLowerCase().includes(searchLowerCase) || token.address?.toLowerCase().includes(searchLowerCase);
138
+ /**
139
+ * Builds the filter for one search, so the query is normalized once instead of
140
+ * once per token: an all-networks list holds tens of thousands of rows, where
141
+ * normalizing per row costs more than the matching itself. Binding the mode
142
+ * here also keeps every call site of one search on the same rule.
143
+ *
144
+ * An address search matches the token at that address only. Impersonators
145
+ * embed a real address in their name or symbol, so a substring match on
146
+ * those fields would surface them for the very query meant to avoid them.
147
+ */
148
+ const createSearchMatcher = (search, isAddressSearch) => {
149
+ const searchTerm = search?.trim().toLowerCase();
150
+ if (!searchTerm) return () => true;
151
+ if (isAddressSearch) return (token) => token.address?.toLowerCase() === searchTerm;
152
+ return (token) => Boolean(token.name?.toLowerCase().includes(searchTerm) || token.symbol?.replaceAll("₮", "T").toLowerCase().includes(searchTerm) || token.address?.toLowerCase().includes(searchTerm));
153
+ };
154
+ const isHoistableNative = (token, selectedChainId) => !!token?.native && !token.pinned && !token.featured && !token.popular && !token.verified && (selectedChainId === void 0 || token.chainId === selectedChainId);
155
+ /**
156
+ * Moves the chain's native token to the top of its own chain's list, unless an
157
+ * external list already places it. Returns the array unchanged when nothing
158
+ * moves, so a memo downstream keeps its reference.
159
+ */
160
+ const hoistNativeToken = (tokens, selectedChainId) => {
161
+ if (!selectedChainId) return tokens;
162
+ const index = tokens.findIndex((token) => isHoistableNative(token, selectedChainId));
163
+ if (index < 1) return tokens;
164
+ return [
165
+ tokens[index],
166
+ ...tokens.slice(0, index),
167
+ ...tokens.slice(index + 1)
168
+ ];
140
169
  };
141
170
  //#endregion
142
- export { isSearchMatch, processTokenList };
171
+ export { createSearchMatcher, hoistNativeToken, isHoistableNative, processTokenList };
143
172
 
144
173
  //# sourceMappingURL=tokenList.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tokenList.js","names":[],"sources":["../../../src/utils/tokenList.ts"],"sourcesContent":["import { formatUnits, type TokenExtended } from '@lifi/sdk'\nimport type { TokenAmount, TokenAmountExtended } from '../types/token.js'\nimport type { WidgetTokens } from '../types/widget.js'\n\nconst sortByBalances = (a: TokenAmount, b: TokenAmount) =>\n Number.parseFloat(formatUnits(b.amount ?? 0n, b.decimals)) *\n Number.parseFloat(b.priceUSD ?? '0') -\n Number.parseFloat(formatUnits(a.amount ?? 0n, a.decimals)) *\n Number.parseFloat(a.priceUSD ?? '0')\n\nconst sortByVolume = (a: TokenExtended, b: TokenExtended) =>\n (b.volumeUSD24H ?? 0) - (a.volumeUSD24H ?? 0)\n\nexport const processTokenList = (\n withoutBalances: boolean,\n noCategories: boolean,\n configTokens?: WidgetTokens,\n selectedChainId?: number,\n tokens?: TokenExtended[],\n tokensWithBalances?: TokenAmount[],\n isPinnedToken?: (chainId: number, tokenAddress: string) => boolean\n): {\n processedTokens: TokenAmount[]\n withCategories: boolean\n withPinnedTokens: boolean\n} => {\n if (withoutBalances) {\n if (noCategories) {\n const sortedTokens = [...(tokens ?? [])].sort(sortByVolume)\n // Separate pinned tokens\n if (isPinnedToken) {\n const pinned: TokenAmount[] = []\n const notPinned: TokenAmount[] = []\n for (const token of sortedTokens) {\n if (isPinnedToken(token.chainId, token.address)) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinned.push(pinnedToken)\n } else {\n notPinned.push(token)\n }\n }\n return {\n processedTokens: [...pinned, ...notPinned],\n withCategories: false,\n withPinnedTokens: !!pinned.length,\n }\n }\n return {\n processedTokens: sortedTokens,\n withCategories: false,\n withPinnedTokens: false,\n }\n } else {\n return processedTypedTokens(\n tokens ?? [],\n [],\n selectedChainId,\n configTokens,\n isPinnedToken\n )\n }\n }\n\n const sortedTokensWithBalances = [...(tokensWithBalances ?? [])].sort(\n sortByBalances\n )\n\n const tokensWithBalancesSet = new Set(\n sortedTokensWithBalances.map(\n (token) => `${token.chainId}-${token.address.toLowerCase()}`\n )\n )\n const tokensWithoutBalances =\n tokens\n ?.filter((token) => {\n const tokenKey = `${token.chainId}-${token.address.toLowerCase()}`\n return !tokensWithBalancesSet.has(tokenKey)\n })\n .sort(sortByVolume) ?? []\n\n if (noCategories) {\n // Separate pinned tokens\n if (isPinnedToken) {\n const pinnedWithBalances: TokenAmount[] = []\n const notPinnedWithBalances: TokenAmount[] = []\n const pinnedWithoutBalances: TokenAmount[] = []\n const notPinnedWithoutBalances: TokenAmount[] = []\n\n for (const token of sortedTokensWithBalances) {\n if (isPinnedToken(token.chainId, token.address)) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinnedWithBalances.push(pinnedToken)\n } else {\n notPinnedWithBalances.push(token)\n }\n }\n\n for (const token of tokensWithoutBalances) {\n if (isPinnedToken(token.chainId, token.address)) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinnedWithoutBalances.push(pinnedToken)\n } else {\n notPinnedWithoutBalances.push(token)\n }\n }\n\n return {\n processedTokens: [\n ...pinnedWithBalances,\n ...pinnedWithoutBalances,\n ...notPinnedWithBalances,\n ...notPinnedWithoutBalances,\n ],\n withCategories: false,\n withPinnedTokens:\n !!pinnedWithBalances.length || !!pinnedWithoutBalances.length,\n }\n }\n return {\n processedTokens: [...sortedTokensWithBalances, ...tokensWithoutBalances],\n withCategories: false,\n withPinnedTokens: false,\n }\n } else {\n return processedTypedTokens(\n tokensWithoutBalances,\n sortedTokensWithBalances,\n selectedChainId,\n configTokens,\n isPinnedToken\n )\n }\n}\n\n// NB: only for non-all-networks\nconst processedTypedTokens = (\n tokens: TokenAmount[],\n tokensWithBalances: TokenAmount[],\n selectedChainId?: number,\n configTokens?: WidgetTokens,\n isPinnedToken?: (chainId: number, tokenAddress: string) => boolean\n) => {\n const filteredTokensMap = new Map(\n tokens.map((token) => [token.address, token])\n )\n\n const pinnedTokens: TokenAmount[] = []\n const featuredTokensFromConfig: TokenAmount[] = []\n const popularTokensFromConfig: TokenAmount[] = []\n\n ;(['popular', 'featured'] as const).forEach((tokenType) => {\n const typedTokens = configTokens?.[tokenType]?.filter(\n (token) => token.chainId === selectedChainId\n )\n\n typedTokens?.forEach((token) => {\n const tokenAmount = { ...token } as TokenAmount\n tokenAmount[tokenType] = true\n // Config tokens are explicitly set by integrator, mark as verified\n tokenAmount.verified = true\n\n const match = filteredTokensMap.get(token.address)\n if (match?.priceUSD) {\n tokenAmount.priceUSD = match.priceUSD\n }\n if (!token.logoURI && match?.logoURI) {\n tokenAmount.logoURI = match.logoURI\n }\n\n if (tokenType === 'popular') {\n popularTokensFromConfig.push(tokenAmount)\n } else {\n featuredTokensFromConfig.push(tokenAmount)\n }\n\n // Additionally add to pinned tokens if it is pinned\n const isPinned =\n isPinnedToken && selectedChainId\n ? isPinnedToken(selectedChainId, token.address)\n : false\n if (isPinned) {\n const pinnedToken = { ...tokenAmount, pinned: true } as TokenAmount\n pinnedTokens.push(pinnedToken)\n }\n })\n })\n\n // Filter out config-added tokens from main list\n const configTokenAddresses = new Set(\n [...popularTokensFromConfig, ...featuredTokensFromConfig].map((t) =>\n t.address.toLowerCase()\n )\n )\n\n const remainingTokens = tokens.filter(\n (token) => !configTokenAddresses.has(token.address.toLowerCase())\n )\n\n const otherTokens: TokenAmount[] = []\n\n // Separate pinned tokens and categorize remaining tokens\n for (const token of remainingTokens) {\n const isPinned =\n isPinnedToken && selectedChainId\n ? isPinnedToken(selectedChainId, token.address)\n : false\n\n if (isPinned) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinnedTokens.push(pinnedToken)\n } else if (token.featured) {\n featuredTokensFromConfig.push(token)\n } else if (token.popular) {\n popularTokensFromConfig.push(token)\n } else {\n otherTokens.push(token)\n }\n }\n\n // Also check tokens with balances for pinned status\n const pinnedTokensWithBalances: TokenAmount[] = []\n const nonPinnedTokensWithBalances: TokenAmount[] = []\n\n if (isPinnedToken && selectedChainId) {\n for (const token of tokensWithBalances) {\n if (isPinnedToken(selectedChainId, token.address)) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinnedTokensWithBalances.push(pinnedToken)\n } else {\n nonPinnedTokensWithBalances.push(token)\n }\n }\n } else {\n nonPinnedTokensWithBalances.push(...tokensWithBalances)\n }\n\n const sortedPinnedTokens = [\n ...pinnedTokens,\n ...pinnedTokensWithBalances,\n ].sort(sortByVolume)\n const sortedFeaturedTokens = [...featuredTokensFromConfig].sort(sortByVolume)\n const sortedPopularTokens = [...popularTokensFromConfig].sort(sortByVolume)\n const sortedOtherTokens = [...otherTokens].sort(sortByVolume)\n\n return {\n processedTokens: [\n ...sortedPinnedTokens,\n ...sortedFeaturedTokens,\n ...nonPinnedTokensWithBalances,\n ...sortedPopularTokens,\n ...sortedOtherTokens,\n ],\n withCategories:\n !!featuredTokensFromConfig.length || !!popularTokensFromConfig.length,\n withPinnedTokens: !!sortedPinnedTokens.length,\n }\n}\n\nexport const isSearchMatch = (\n token: TokenExtended | TokenAmountExtended,\n search?: string\n): boolean => {\n if (!search) {\n return true\n }\n\n const searchLowerCase = search.toLowerCase()\n return (\n token.name?.toLowerCase().includes(searchLowerCase) ||\n token.symbol\n ?.replaceAll('₮', 'T')\n .toLowerCase()\n .includes(searchLowerCase) ||\n token.address?.toLowerCase().includes(searchLowerCase)\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"tokenList.js","names":[],"sources":["../../../src/utils/tokenList.ts"],"sourcesContent":["import { formatUnits, type TokenExtended } from '@lifi/sdk'\nimport type { TokenAmount, TokenAmountExtended } from '../types/token.js'\nimport type { WidgetTokens } from '../types/widget.js'\n\nconst sortByBalances = (a: TokenAmount, b: TokenAmount) =>\n Number.parseFloat(formatUnits(b.amount ?? 0n, b.decimals)) *\n Number.parseFloat(b.priceUSD ?? '0') -\n Number.parseFloat(formatUnits(a.amount ?? 0n, a.decimals)) *\n Number.parseFloat(a.priceUSD ?? '0')\n\nconst sortByVolume = (a: TokenExtended, b: TokenExtended) =>\n (b.volumeUSD24H ?? 0) - (a.volumeUSD24H ?? 0)\n\nexport const processTokenList = (\n withoutBalances: boolean,\n noCategories: boolean,\n configTokens?: WidgetTokens,\n selectedChainId?: number,\n tokens?: TokenExtended[],\n tokensWithBalances?: TokenAmount[],\n isPinnedToken?: (chainId: number, tokenAddress: string) => boolean\n): {\n processedTokens: TokenAmount[]\n withCategories: boolean\n withPinnedTokens: boolean\n} => {\n if (withoutBalances) {\n if (noCategories) {\n const sortedTokens = [...(tokens ?? [])].sort(sortByVolume)\n // Separate pinned tokens\n if (isPinnedToken) {\n const pinned: TokenAmount[] = []\n const notPinned: TokenAmount[] = []\n for (const token of sortedTokens) {\n if (isPinnedToken(token.chainId, token.address)) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinned.push(pinnedToken)\n } else {\n notPinned.push(token)\n }\n }\n return {\n processedTokens: [...pinned, ...notPinned],\n withCategories: false,\n withPinnedTokens: !!pinned.length,\n }\n }\n return {\n processedTokens: sortedTokens,\n withCategories: false,\n withPinnedTokens: false,\n }\n } else {\n return processedTypedTokens(\n tokens ?? [],\n [],\n selectedChainId,\n configTokens,\n isPinnedToken\n )\n }\n }\n\n const sortedTokensWithBalances = [...(tokensWithBalances ?? [])].sort(\n sortByBalances\n )\n\n const tokensWithBalancesSet = new Set(\n sortedTokensWithBalances.map(\n (token) => `${token.chainId}-${token.address.toLowerCase()}`\n )\n )\n const tokensWithoutBalances =\n tokens\n ?.filter((token) => {\n const tokenKey = `${token.chainId}-${token.address.toLowerCase()}`\n return !tokensWithBalancesSet.has(tokenKey)\n })\n .sort(sortByVolume) ?? []\n\n if (noCategories) {\n // Separate pinned tokens\n if (isPinnedToken) {\n const pinnedWithBalances: TokenAmount[] = []\n const notPinnedWithBalances: TokenAmount[] = []\n const pinnedWithoutBalances: TokenAmount[] = []\n const notPinnedWithoutBalances: TokenAmount[] = []\n\n for (const token of sortedTokensWithBalances) {\n if (isPinnedToken(token.chainId, token.address)) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinnedWithBalances.push(pinnedToken)\n } else {\n notPinnedWithBalances.push(token)\n }\n }\n\n for (const token of tokensWithoutBalances) {\n if (isPinnedToken(token.chainId, token.address)) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinnedWithoutBalances.push(pinnedToken)\n } else {\n notPinnedWithoutBalances.push(token)\n }\n }\n\n return {\n processedTokens: [\n ...pinnedWithBalances,\n ...pinnedWithoutBalances,\n ...notPinnedWithBalances,\n ...notPinnedWithoutBalances,\n ],\n withCategories: false,\n withPinnedTokens:\n !!pinnedWithBalances.length || !!pinnedWithoutBalances.length,\n }\n }\n return {\n processedTokens: [...sortedTokensWithBalances, ...tokensWithoutBalances],\n withCategories: false,\n withPinnedTokens: false,\n }\n } else {\n return processedTypedTokens(\n tokensWithoutBalances,\n sortedTokensWithBalances,\n selectedChainId,\n configTokens,\n isPinnedToken\n )\n }\n}\n\n// NB: only for non-all-networks\nconst processedTypedTokens = (\n tokens: TokenAmount[],\n tokensWithBalances: TokenAmount[],\n selectedChainId?: number,\n configTokens?: WidgetTokens,\n isPinnedToken?: (chainId: number, tokenAddress: string) => boolean\n) => {\n const filteredTokensMap = new Map(\n tokens.map((token) => [token.address, token])\n )\n\n const pinnedTokens: TokenAmount[] = []\n const featuredTokensFromConfig: TokenAmount[] = []\n const popularTokensFromConfig: TokenAmount[] = []\n\n ;(['popular', 'featured'] as const).forEach((tokenType) => {\n const typedTokens = configTokens?.[tokenType]?.filter(\n (token) => token.chainId === selectedChainId\n )\n\n typedTokens?.forEach((token) => {\n const tokenAmount = { ...token } as TokenAmount\n tokenAmount[tokenType] = true\n // Config tokens are explicitly set by integrator, mark as verified\n tokenAmount.verified = true\n\n const match = filteredTokensMap.get(token.address)\n if (match?.priceUSD) {\n tokenAmount.priceUSD = match.priceUSD\n }\n if (!token.logoURI && match?.logoURI) {\n tokenAmount.logoURI = match.logoURI\n }\n\n if (tokenType === 'popular') {\n popularTokensFromConfig.push(tokenAmount)\n } else {\n featuredTokensFromConfig.push(tokenAmount)\n }\n\n // Additionally add to pinned tokens if it is pinned\n const isPinned =\n isPinnedToken && selectedChainId\n ? isPinnedToken(selectedChainId, token.address)\n : false\n if (isPinned) {\n const pinnedToken = { ...tokenAmount, pinned: true } as TokenAmount\n pinnedTokens.push(pinnedToken)\n }\n })\n })\n\n // Filter out config-added tokens from main list\n const configTokenAddresses = new Set(\n [...popularTokensFromConfig, ...featuredTokensFromConfig].map((t) =>\n t.address.toLowerCase()\n )\n )\n\n const remainingTokens = tokens.filter(\n (token) => !configTokenAddresses.has(token.address.toLowerCase())\n )\n\n const otherTokens: TokenAmount[] = []\n\n // Separate pinned tokens and categorize remaining tokens\n for (const token of remainingTokens) {\n const isPinned =\n isPinnedToken && selectedChainId\n ? isPinnedToken(selectedChainId, token.address)\n : false\n\n if (isPinned) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinnedTokens.push(pinnedToken)\n } else if (token.featured) {\n featuredTokensFromConfig.push(token)\n } else if (token.popular) {\n popularTokensFromConfig.push(token)\n } else {\n otherTokens.push(token)\n }\n }\n\n // Also check tokens with balances for pinned status\n const pinnedTokensWithBalances: TokenAmount[] = []\n const nonPinnedTokensWithBalances: TokenAmount[] = []\n\n if (isPinnedToken && selectedChainId) {\n for (const token of tokensWithBalances) {\n if (isPinnedToken(selectedChainId, token.address)) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinnedTokensWithBalances.push(pinnedToken)\n } else {\n nonPinnedTokensWithBalances.push(token)\n }\n }\n } else {\n nonPinnedTokensWithBalances.push(...tokensWithBalances)\n }\n\n const sortedPinnedTokens = [\n ...pinnedTokens,\n ...pinnedTokensWithBalances,\n ].sort(sortByVolume)\n const sortedFeaturedTokens = [...featuredTokensFromConfig].sort(sortByVolume)\n const sortedPopularTokens = [...popularTokensFromConfig].sort(sortByVolume)\n const sortedOtherTokens = [...otherTokens].sort(sortByVolume)\n\n return {\n processedTokens: [\n ...sortedPinnedTokens,\n ...sortedFeaturedTokens,\n ...nonPinnedTokensWithBalances,\n ...sortedPopularTokens,\n ...sortedOtherTokens,\n ],\n withCategories:\n !!featuredTokensFromConfig.length || !!popularTokensFromConfig.length,\n withPinnedTokens: !!sortedPinnedTokens.length,\n }\n}\n\n/**\n * Builds the filter for one search, so the query is normalized once instead of\n * once per token: an all-networks list holds tens of thousands of rows, where\n * normalizing per row costs more than the matching itself. Binding the mode\n * here also keeps every call site of one search on the same rule.\n *\n * An address search matches the token at that address only. Impersonators\n * embed a real address in their name or symbol, so a substring match on\n * those fields would surface them for the very query meant to avoid them.\n */\nexport const createSearchMatcher = (\n search: string | undefined,\n isAddressSearch: boolean\n): ((token: TokenExtended | TokenAmountExtended) => boolean) => {\n const searchTerm = search?.trim().toLowerCase()\n\n if (!searchTerm) {\n return () => true\n }\n\n if (isAddressSearch) {\n return (token) => token.address?.toLowerCase() === searchTerm\n }\n\n return (token) =>\n Boolean(\n token.name?.toLowerCase().includes(searchTerm) ||\n token.symbol?.replaceAll('₮', 'T').toLowerCase().includes(searchTerm) ||\n token.address?.toLowerCase().includes(searchTerm)\n )\n}\n\nexport const isHoistableNative = (\n token: TokenAmount | undefined,\n selectedChainId?: number\n): boolean =>\n !!token?.native &&\n !token.pinned &&\n !token.featured &&\n !token.popular &&\n !token.verified &&\n (selectedChainId === undefined || token.chainId === selectedChainId)\n\n/**\n * Moves the chain's native token to the top of its own chain's list, unless an\n * external list already places it. Returns the array unchanged when nothing\n * moves, so a memo downstream keeps its reference.\n */\nexport const hoistNativeToken = (\n tokens: TokenAmount[],\n selectedChainId?: number\n): TokenAmount[] => {\n if (!selectedChainId) {\n return tokens\n }\n const index = tokens.findIndex((token) =>\n isHoistableNative(token, selectedChainId)\n )\n // -1 is no native token, 0 is already first\n if (index < 1) {\n return tokens\n }\n return [tokens[index], ...tokens.slice(0, index), ...tokens.slice(index + 1)]\n}\n"],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ //#region src/utils/tokenListBands.ts
2
+ const bandHeaderAtListStart = 24;
3
+ const bandHeaderAfterBand = 32;
4
+ const toggleRowHeight = 26;
5
+ const isPromoted = (token) => !!token?.pinned || !!token?.recent;
6
+ const createBandResolver = (tokens, { showCategories, showPinnedTokens, nativeHoisted, recentStartIndex, recentCount, showRecentToggle }) => {
7
+ const listStartIndex = nativeHoisted ? 1 : 0;
8
+ const recentEndIndex = recentStartIndex + recentCount - 1;
9
+ const isFirstPinned = (index) => !!tokens[index]?.pinned && index === listStartIndex;
10
+ const isFirstRecent = (index) => recentCount > 0 && index === recentStartIndex && !!tokens[index]?.recent;
11
+ const isLastRecent = (index) => recentCount > 0 && index === recentEndIndex && !!tokens[index]?.recent;
12
+ const previousOf = (index) => index === listStartIndex ? void 0 : tokens[index - 1];
13
+ const isTransitionFromPromoted = (index) => isPromoted(tokens[index - 1]) && !isPromoted(tokens[index]);
14
+ const isTopRow = (index) => index === 0;
15
+ const headerHeight = (index) => isTopRow(index) ? bandHeaderAtListStart : bandHeaderAfterBand;
16
+ const getRowExtraHeight = (index) => {
17
+ const current = tokens[index];
18
+ const previous = previousOf(index);
19
+ let extra = 0;
20
+ if (showPinnedTokens && isFirstPinned(index)) extra += headerHeight(index);
21
+ if (isFirstRecent(index)) extra += headerHeight(index);
22
+ if (showRecentToggle && isLastRecent(index)) extra += toggleRowHeight;
23
+ if ((showPinnedTokens || showCategories || recentCount > 0) && isTransitionFromPromoted(index)) extra += bandHeaderAfterBand;
24
+ if (!showCategories) return extra;
25
+ if (current?.featured && !isPromoted(current) && index === listStartIndex) extra += headerHeight(index);
26
+ const isNotPromoted = !isPromoted(current) && !isPromoted(previous);
27
+ if (index !== listStartIndex && isNotPromoted && (previous?.amount && !current?.amount || previous?.featured && !current?.featured || previous?.popular && !current?.popular)) extra += bandHeaderAfterBand;
28
+ return extra;
29
+ };
30
+ const getRowBandLabel = (index) => {
31
+ const current = tokens[index];
32
+ const previous = previousOf(index);
33
+ const isListStart = index === listStartIndex;
34
+ const notPromoted = !isPromoted(current);
35
+ const fromPromoted = isPromoted(previous) && notPromoted;
36
+ if (showPinnedTokens && isFirstPinned(index)) return {
37
+ kind: "text",
38
+ key: "main.pinnedTokens",
39
+ atListStart: isTopRow(index)
40
+ };
41
+ if (isFirstRecent(index)) return {
42
+ kind: "recent",
43
+ atListStart: isTopRow(index)
44
+ };
45
+ if ((showPinnedTokens || recentCount > 0) && !showCategories && fromPromoted) return {
46
+ kind: "text",
47
+ key: "main.allTokens",
48
+ atListStart: false
49
+ };
50
+ if (!showCategories) return;
51
+ const fromFeatured = !!previous?.featured && !current?.featured && notPromoted;
52
+ const fromMyTokens = !!previous?.amount && !current?.amount && notPromoted;
53
+ const fromPopular = !!previous?.popular && !current?.popular && notPromoted;
54
+ if (fromPromoted && current?.featured || current?.featured && notPromoted && isListStart) return {
55
+ kind: "text",
56
+ key: "main.featuredTokens",
57
+ atListStart: isTopRow(index)
58
+ };
59
+ if ((fromFeatured || fromPromoted) && current?.amount && notPromoted) return {
60
+ kind: "text",
61
+ key: "main.myTokens",
62
+ atListStart: false
63
+ };
64
+ if ((fromFeatured || fromMyTokens || fromPromoted) && current?.popular && notPromoted) return {
65
+ kind: "text",
66
+ key: "main.popularTokens",
67
+ atListStart: false
68
+ };
69
+ if (fromMyTokens || fromFeatured || fromPromoted || fromPopular) return {
70
+ kind: "text",
71
+ key: "main.allTokens",
72
+ atListStart: false
73
+ };
74
+ };
75
+ const isToggleRow = (index) => showRecentToggle && isLastRecent(index);
76
+ return {
77
+ getRowExtraHeight,
78
+ getRowBandLabel,
79
+ isToggleRow
80
+ };
81
+ };
82
+ //#endregion
83
+ export { createBandResolver };
84
+
85
+ //# sourceMappingURL=tokenListBands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokenListBands.js","names":[],"sources":["../../../src/utils/tokenListBands.ts"],"sourcesContent":["import type { TokenAmount } from '../types/token.js'\n\nconst bandHeaderAtListStart = 24\nconst bandHeaderAfterBand = 32\nconst toggleRowHeight = 26\n\ntype BandLabelKey =\n | 'main.pinnedTokens'\n | 'main.featuredTokens'\n | 'main.myTokens'\n | 'main.popularTokens'\n | 'main.allTokens'\n\nexport type BandLabel =\n | { kind: 'text'; key: BandLabelKey; atListStart: boolean }\n | { kind: 'recent'; atListStart: boolean }\n\nexport interface BandResolverOptions {\n showCategories: boolean\n showPinnedTokens: boolean\n nativeHoisted: boolean\n /** Ignored while `recentCount` is 0. */\n recentStartIndex: number\n recentCount: number\n showRecentToggle: boolean\n}\n\nexport interface BandResolver {\n /** Called for every index by the virtualizer, so it allocates nothing. */\n getRowExtraHeight: (index: number) => number\n /** Called for visible rows only, so the label may be resolved lazily. */\n getRowBandLabel: (index: number) => BandLabel | undefined\n /** The row the toggle hangs below. Shares one definition with the height. */\n isToggleRow: (index: number) => boolean\n}\n\n// Pinned and recent rows sit above the categories; transitions ignore both.\nconst isPromoted = (token: TokenAmount | undefined): boolean =>\n !!token?.pinned || !!token?.recent\n\nexport const createBandResolver = (\n tokens: TokenAmount[],\n {\n showCategories,\n showPinnedTokens,\n nativeHoisted,\n recentStartIndex,\n recentCount,\n showRecentToggle,\n }: BandResolverOptions\n): BandResolver => {\n const listStartIndex = nativeHoisted ? 1 : 0\n const recentEndIndex = recentStartIndex + recentCount - 1\n\n const isFirstPinned = (index: number) =>\n !!tokens[index]?.pinned && index === listStartIndex\n const isFirstRecent = (index: number) =>\n recentCount > 0 && index === recentStartIndex && !!tokens[index]?.recent\n const isLastRecent = (index: number) =>\n recentCount > 0 && index === recentEndIndex && !!tokens[index]?.recent\n // The hoisted native sits above every band, so the list-start row follows none.\n const previousOf = (index: number) =>\n index === listStartIndex ? undefined : tokens[index - 1]\n const isTransitionFromPromoted = (index: number) =>\n isPromoted(tokens[index - 1]) && !isPromoted(tokens[index])\n // Only the top row has no card above it; below the hoisted native one does.\n const isTopRow = (index: number) => index === 0\n const headerHeight = (index: number) =>\n isTopRow(index) ? bandHeaderAtListStart : bandHeaderAfterBand\n\n const getRowExtraHeight = (index: number): number => {\n const current = tokens[index]\n const previous = previousOf(index)\n let extra = 0\n\n if (showPinnedTokens && isFirstPinned(index)) {\n extra += headerHeight(index)\n }\n\n if (isFirstRecent(index)) {\n extra += headerHeight(index)\n }\n\n if (showRecentToggle && isLastRecent(index)) {\n extra += toggleRowHeight\n }\n\n // The recent band needs its own term: all-networks has no categories.\n if (\n (showPinnedTokens || showCategories || recentCount > 0) &&\n isTransitionFromPromoted(index)\n ) {\n extra += bandHeaderAfterBand\n }\n\n if (!showCategories) {\n return extra\n }\n\n if (current?.featured && !isPromoted(current) && index === listStartIndex) {\n extra += headerHeight(index)\n }\n\n const isNotPromoted = !isPromoted(current) && !isPromoted(previous)\n if (\n index !== listStartIndex &&\n isNotPromoted &&\n ((previous?.amount && !current?.amount) ||\n (previous?.featured && !current?.featured) ||\n (previous?.popular && !current?.popular))\n ) {\n extra += bandHeaderAfterBand\n }\n\n return extra\n }\n\n const getRowBandLabel = (index: number): BandLabel | undefined => {\n const current = tokens[index]\n const previous = previousOf(index)\n const isListStart = index === listStartIndex\n const notPromoted = !isPromoted(current)\n const fromPromoted = isPromoted(previous) && notPromoted\n\n if (showPinnedTokens && isFirstPinned(index)) {\n return {\n kind: 'text',\n key: 'main.pinnedTokens',\n atListStart: isTopRow(index),\n }\n }\n if (isFirstRecent(index)) {\n return { kind: 'recent', atListStart: isTopRow(index) }\n }\n if (\n (showPinnedTokens || recentCount > 0) &&\n !showCategories &&\n fromPromoted\n ) {\n return { kind: 'text', key: 'main.allTokens', atListStart: false }\n }\n if (!showCategories) {\n return undefined\n }\n\n const fromFeatured =\n !!previous?.featured && !current?.featured && notPromoted\n const fromMyTokens = !!previous?.amount && !current?.amount && notPromoted\n const fromPopular = !!previous?.popular && !current?.popular && notPromoted\n\n if (\n (fromPromoted && current?.featured) ||\n (current?.featured && notPromoted && isListStart)\n ) {\n return {\n kind: 'text',\n key: 'main.featuredTokens',\n atListStart: isTopRow(index),\n }\n }\n if ((fromFeatured || fromPromoted) && current?.amount && notPromoted) {\n return { kind: 'text', key: 'main.myTokens', atListStart: false }\n }\n if (\n (fromFeatured || fromMyTokens || fromPromoted) &&\n current?.popular &&\n notPromoted\n ) {\n return { kind: 'text', key: 'main.popularTokens', atListStart: false }\n }\n if (fromMyTokens || fromFeatured || fromPromoted || fromPopular) {\n return { kind: 'text', key: 'main.allTokens', atListStart: false }\n }\n return undefined\n }\n\n const isToggleRow = (index: number): boolean =>\n showRecentToggle && isLastRecent(index)\n\n return { getRowExtraHeight, getRowBandLabel, isToggleRow }\n}\n"],"mappings":"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"}
@@ -1,5 +1,4 @@
1
1
  //#region src/utils/wallet.d.ts
2
- declare const shortenAddress: (address?: string) => string | null;
2
+ export declare const shortenAddress: (address?: string) => string | null;
3
3
  //#endregion
4
- export { shortenAddress };
5
4
  //# sourceMappingURL=wallet.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"wallet.d.ts","names":[],"sources":["../../../src/utils/wallet.ts"],"mappings":";cAAa,iBAAkB"}
1
+ {"version":3,"file":"wallet.d.ts","names":[],"sources":["../../../src/utils/wallet.ts"],"mappings":";qBAAa,iBAAkB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifi/widget",
3
- "version": "4.5.0",
3
+ "version": "4.7.0",
4
4
  "description": "LI.FI Widget for cross-chain bridging and swapping. It will drive your multi-chain strategy and attract new users from everywhere.",
5
5
  "type": "module",
6
6
  "main": "./dist/esm/index.js",
@@ -52,22 +52,22 @@
52
52
  "dependencies": {
53
53
  "@emotion/react": "^11.14.0",
54
54
  "@emotion/styled": "^11.14.1",
55
- "@lifi/sdk": "^4.3.0",
56
- "@mui/icons-material": "^9.2.0",
57
- "@mui/material": "^9.2.0",
58
- "@mui/system": "^9.2.0",
59
- "@tanstack/react-router": "^1.170.17",
60
- "@tanstack/react-virtual": "^3.14.5",
55
+ "@lifi/sdk": "^4.8.0",
56
+ "@lifi/wallet-management": "4.3.0",
57
+ "@lifi/widget-provider": "4.5.0",
58
+ "@mui/icons-material": "^9.4.0",
59
+ "@mui/material": "^9.4.0",
60
+ "@mui/system": "^9.4.0",
61
+ "@tanstack/react-router": "^1.170.38",
62
+ "@tanstack/react-virtual": "^3.14.13",
61
63
  "eventemitter3": "^5.0.4",
62
- "i18next": "^26.3.6",
63
- "microdiff": "^1.5.0",
64
- "motion": "^12.42.2",
65
- "react-i18next": "^17.0.9",
66
- "react-intersection-observer": "^10.1.0",
64
+ "i18next": "^26.4.2",
65
+ "microdiff": "^1.6.0",
66
+ "motion": "^13.4.0",
67
+ "react-i18next": "^17.0.14",
68
+ "react-intersection-observer": "^11.0.1",
67
69
  "react-transition-group": "^4.4.5",
68
- "zustand": "^5.0.14",
69
- "@lifi/widget-provider": "4.3.1",
70
- "@lifi/wallet-management": "4.1.3"
70
+ "zustand": "^5.0.15"
71
71
  },
72
72
  "peerDependencies": {
73
73
  "@tanstack/react-query": ">=5.90.0",
@@ -4,7 +4,7 @@ import type React from 'react'
4
4
  import { type JSX, memo } from 'react'
5
5
  import { useTranslation } from 'react-i18next'
6
6
  import { useAvailableChains } from '../../hooks/useAvailableChains.js'
7
- import { useGasRecommendation } from '../../hooks/useGasRecommendation.js'
7
+ import { useMaxSendAmount } from '../../hooks/useMaxSendAmount.js'
8
8
  import { useTokenAddressBalance } from '../../hooks/useTokenAddressBalance.js'
9
9
  import type { FormTypeProps } from '../../stores/form/types.js'
10
10
  import { FormKeyHelper } from '../../stores/form/types.js'
@@ -29,28 +29,10 @@ export const AmountInputEndAdornment: React.NamedExoticComponent<FormTypeProps>
29
29
  const chain = getChainById(chainId)
30
30
  const { account } = useAccount({ chainType: chain?.chainType })
31
31
 
32
- // We get gas recommendations for the source chain to make sure that after pressing the Max button
33
- // the user will have enough funds remaining to cover gas costs
34
- const { data } = useGasRecommendation(chainId)
35
-
36
32
  const { token } = useTokenAddressBalance(chainId, tokenAddress)
37
-
38
- const getMaxAmount = () => {
39
- if (!token?.amount) {
40
- return 0n
41
- }
42
- let maxAmount = token.amount
43
- if (chain?.nativeToken.address === tokenAddress && data?.recommended) {
44
- const recommendedAmount = BigInt(data.recommended.amount)
45
- if (token.amount > recommendedAmount) {
46
- maxAmount = token.amount - recommendedAmount
47
- }
48
- }
49
- return maxAmount
50
- }
33
+ const maxAmount = useMaxSendAmount(chainId, tokenAddress)
51
34
 
52
35
  const handlePercentage = (percentage: number) => {
53
- const maxAmount = getMaxAmount()
54
36
  if (maxAmount && token?.decimals) {
55
37
  const percentageAmount = (maxAmount * BigInt(percentage)) / 100n
56
38
  setFieldValue(
@@ -64,7 +46,6 @@ export const AmountInputEndAdornment: React.NamedExoticComponent<FormTypeProps>
64
46
  }
65
47
 
66
48
  const handleMax = () => {
67
- const maxAmount = getMaxAmount()
68
49
  if (maxAmount && token?.decimals) {
69
50
  setFieldValue(
70
51
  FormKeyHelper.getAmountKey(formType),
@@ -3,8 +3,8 @@ import { useAccount } from '@lifi/wallet-management'
3
3
  import { type JSX, memo } from 'react'
4
4
  import { useTranslation } from 'react-i18next'
5
5
  import { useAvailableChains } from '../../hooks/useAvailableChains.js'
6
- import { useGasRecommendation } from '../../hooks/useGasRecommendation.js'
7
6
  import { useLinkedLimitFields } from '../../hooks/useLinkedLimitFields.js'
7
+ import { useMaxSendAmount } from '../../hooks/useMaxSendAmount.js'
8
8
  import { useTokenAddressBalance } from '../../hooks/useTokenAddressBalance.js'
9
9
  import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
10
10
  import type { FormTypeProps } from '../../stores/form/types.js'
@@ -33,22 +33,8 @@ export const PercentageChips: React.NamedExoticComponent<FormTypeProps> = memo(
33
33
  const chain = getChainById(chainId)
34
34
  const { account } = useAccount({ chainType: chain?.chainType })
35
35
 
36
- const { data } = useGasRecommendation(chainId)
37
36
  const { token } = useTokenAddressBalance(chainId, tokenAddress)
38
-
39
- const getMaxAmount = (): bigint => {
40
- if (!token?.amount) {
41
- return 0n
42
- }
43
- let maxAmount = token.amount
44
- if (chain?.nativeToken.address === tokenAddress && data?.recommended) {
45
- const recommendedAmount = BigInt(data.recommended.amount)
46
- if (token.amount > recommendedAmount) {
47
- maxAmount = token.amount - recommendedAmount
48
- }
49
- }
50
- return maxAmount
51
- }
37
+ const maxAmount = useMaxSendAmount(chainId, tokenAddress)
52
38
 
53
39
  // In limit mode the send amount must flow through the linked-field
54
40
  // derivation so the receive amount recomputes; otherwise it is a plain
@@ -62,7 +48,6 @@ export const PercentageChips: React.NamedExoticComponent<FormTypeProps> = memo(
62
48
  }
63
49
 
64
50
  const handlePercentage = (percentage: number): void => {
65
- const maxAmount = getMaxAmount()
66
51
  if (maxAmount && token?.decimals) {
67
52
  const percentageAmount = (maxAmount * BigInt(percentage)) / 100n
68
53
  applyAmount(formatUnits(percentageAmount, token.decimals))
@@ -70,7 +55,6 @@ export const PercentageChips: React.NamedExoticComponent<FormTypeProps> = memo(
70
55
  }
71
56
 
72
57
  const handleMax = (): void => {
73
- const maxAmount = getMaxAmount()
74
58
  if (maxAmount && token?.decimals) {
75
59
  applyAmount(formatUnits(maxAmount, token.decimals))
76
60
  }
@@ -6,12 +6,14 @@ import { Box, Skeleton, Tooltip } from '@mui/material'
6
6
  import { type JSX, type ReactNode, useLayoutEffect, useRef } from 'react'
7
7
  import { useTranslation } from 'react-i18next'
8
8
  import { useRoutes } from '../../hooks/useRoutes.js'
9
+ import { useToken } from '../../hooks/useToken.js'
9
10
  import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
10
11
  import { useFieldValues } from '../../stores/form/useFieldValues.js'
11
12
  import { useInputModeStore } from '../../stores/inputMode/useInputModeStore.js'
12
13
  import { formatTokenAmount, formatTokenPrice } from '../../utils/format.js'
13
14
  import { getPriceImpact } from '../../utils/getPriceImpact.js'
14
15
  import { fitInputText } from '../../utils/input.js'
16
+ import { mergeFallbackToken } from '../../utils/token.js'
15
17
  import { CardTitle } from '../Card/CardTitle.js'
16
18
  import { ProgressToNextUpdate } from '../ProgressToNextUpdate.js'
17
19
  import { TokenPillButton } from '../TokenPillButton/TokenPillButton.js'
@@ -128,6 +130,17 @@ export const ReceiveAmountCard: React.FC<ReceiveCardProps> = (
128
130
  const priceImpact = useReceivePriceImpact(route)
129
131
  const showPriceImpact = !hiddenUI?.routeCardPriceImpact
130
132
 
133
+ // Value the amount the way a quote card does, so the two sides of the screen
134
+ // cannot disagree: the route's own price wins, and the cache fills only a gap
135
+ // the route leaves — a token it could not price arrives as '0' or ''.
136
+ const { token: cachedToken } = useToken(
137
+ route?.toToken.chainId,
138
+ route?.toToken.address
139
+ )
140
+ const pricedToken = route
141
+ ? mergeFallbackToken(route.toToken, cachedToken)
142
+ : undefined
143
+
131
144
  const receiveAmount = route?.toAmount
132
145
  ? formatTokenAmount(BigInt(route.toAmount), route.toToken.decimals)
133
146
  : undefined
@@ -135,10 +148,10 @@ export const ReceiveAmountCard: React.FC<ReceiveCardProps> = (
135
148
  const showSkeleton = isFetching && !receiveAmount
136
149
 
137
150
  const fiatValue =
138
- receiveAmount && route?.toToken.priceUSD
139
- ? formatTokenPrice(receiveAmount, route.toToken.priceUSD)
151
+ receiveAmount && pricedToken?.priceUSD
152
+ ? formatTokenPrice(receiveAmount, pricedToken.priceUSD)
140
153
  : 0
141
- const canToggle = !!receiveAmount && !!route?.toToken.priceUSD
154
+ const canToggle = !!receiveAmount && !!pricedToken?.priceUSD
142
155
 
143
156
  const mainDisplay = showFiat
144
157
  ? t('format.currency', { value: fiatValue })
@@ -27,6 +27,9 @@ export const BottomSheet = ({
27
27
  const [isInert, setIsInert] = useState(!open)
28
28
 
29
29
  const close = useCallback(() => {
30
+ if (!openRef.current) {
31
+ return
32
+ }
30
33
  // Set inert first to prevent focus issues
31
34
  setIsInert(true)
32
35
  // Push the state update to the next event loop tick
@@ -44,6 +44,12 @@ const chainTypeIcons = [
44
44
  icon: 'https://lifinance.github.io/types/src/assets/icons/chains/tron.svg',
45
45
  defaultChainId: ChainId.TRN,
46
46
  },
47
+ {
48
+ name: 'Stellar',
49
+ chainType: ChainType.STL,
50
+ icon: 'https://lifinance.github.io/types/src/assets/icons/chains/stellar.svg',
51
+ defaultChainId: ChainId.XLM,
52
+ },
47
53
  ]
48
54
 
49
55
  const maxChainAvatarsCount = 4
@@ -93,7 +99,7 @@ export const AllChainsAvatar: React.NamedExoticComponent<AllChainsAvatarProps> =
93
99
  }
94
100
  )
95
101
 
96
- if (existingChainTypeIcons.length === maxChainAvatarsCount) {
102
+ if (existingChainTypeIcons.length >= maxChainAvatarsCount) {
97
103
  return existingChainTypeIcons
98
104
  }
99
105
 
@@ -0,0 +1,24 @@
1
+ import WarningRounded from '@mui/icons-material/WarningRounded'
2
+ import type { BoxProps } from '@mui/material'
3
+ import { Typography } from '@mui/material'
4
+ import { useTranslation } from 'react-i18next'
5
+ import { AlertMessage } from './AlertMessage.js'
6
+
7
+ export const UnsupportedToAddressMessage: React.FC<BoxProps> = ({
8
+ ...props
9
+ }) => {
10
+ const { t } = useTranslation()
11
+ return (
12
+ <AlertMessage
13
+ title={
14
+ <Typography variant="body2" sx={{ px: 1, color: 'text.primary' }}>
15
+ {t('info.message.unsupportedToAddressMessage')}
16
+ </Typography>
17
+ }
18
+ icon={<WarningRounded />}
19
+ severity="warning"
20
+ multiline
21
+ {...props}
22
+ />
23
+ )
24
+ }
@@ -7,6 +7,7 @@ import { FundsSufficiencyMessage } from './FundsSufficiencyMessage.js'
7
7
  import { GasSufficiencyMessage } from './GasSufficiencyMessage.js'
8
8
  import { MinFromAmountUSDMessage } from './MinFromAmountUSDMessage.js'
9
9
  import { MissingRouteRequiredAccountMessage } from './MissingRouteRequiredAccountMessage.js'
10
+ import { UnsupportedToAddressMessage } from './UnsupportedToAddressMessage.js'
10
11
  import { useMessageQueue } from './useMessageQueue.js'
11
12
 
12
13
  type WarningMessagesProps = BoxProps & {
@@ -51,6 +52,8 @@ export const WarningMessages: React.FC<WarningMessagesProps> = ({
51
52
  return <AccountNotDeployedMessage {...props} />
52
53
  case 'ACCOUNT_DEPLOYED':
53
54
  return <AccountDeployedMessage {...props} />
55
+ case 'UNSUPPORTED_TO_ADDRESS':
56
+ return <UnsupportedToAddressMessage {...props} />
54
57
  default:
55
58
  return null
56
59
  }
@@ -22,6 +22,7 @@ export const useMessageQueue = (
22
22
  } => {
23
23
  const {
24
24
  requiredToAddress,
25
+ unsupportedReceiverBlocking,
25
26
  accountNotDeployedAtDestination,
26
27
  accountDeployedAtDestination,
27
28
  isLoading: isToAddressRequirementsLoading,
@@ -37,6 +38,13 @@ export const useMessageQueue = (
37
38
  const messageQueue = useMemo(() => {
38
39
  const queue: QueuedMessage[] = []
39
40
 
41
+ if (unsupportedReceiverBlocking) {
42
+ queue.push({
43
+ id: 'UNSUPPORTED_TO_ADDRESS',
44
+ priority: 7,
45
+ })
46
+ }
47
+
40
48
  if (missingChain) {
41
49
  queue.push({
42
50
  id: 'MISSING_ROUTE_REQUIRED_ACCOUNT',
@@ -94,6 +102,7 @@ export const useMessageQueue = (
94
102
  accountDeployedAtDestination,
95
103
  accountNotDeployedAtDestination,
96
104
  requiredToAddress,
105
+ unsupportedReceiverBlocking,
97
106
  missingChain,
98
107
  missingAccountAddress,
99
108
  belowMinFromAmountUSD,