@lifi/widget 4.6.0 → 4.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (424) 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/components/ActionRow/ActionRow.d.ts +1 -2
  7. package/dist/esm/components/ActionRow/ActionRow.d.ts.map +1 -1
  8. package/dist/esm/components/AmountInput/AmountInput.style.d.ts +2 -2
  9. package/dist/esm/components/AmountInput/AmountInput.style.d.ts.map +1 -1
  10. package/dist/esm/components/AmountInput/AmountInputEndAdornment.d.ts +1 -2
  11. package/dist/esm/components/AmountInput/AmountInputEndAdornment.d.ts.map +1 -1
  12. package/dist/esm/components/AmountInput/PriceFormHelperText.style.d.ts +1 -2
  13. package/dist/esm/components/AmountInput/PriceFormHelperText.style.d.ts.map +1 -1
  14. package/dist/esm/components/AmountInputCard/ReceiveAmountCard.js +6 -2
  15. package/dist/esm/components/AmountInputCard/ReceiveAmountCard.js.map +1 -1
  16. package/dist/esm/components/Avatar/Avatar.d.ts +1 -2
  17. package/dist/esm/components/Avatar/Avatar.d.ts.map +1 -1
  18. package/dist/esm/components/Avatar/TokenAvatar.d.ts +1 -2
  19. package/dist/esm/components/Avatar/TokenAvatar.d.ts.map +1 -1
  20. package/dist/esm/components/BaseTransactionButton/BaseTransactionButton.d.ts +1 -2
  21. package/dist/esm/components/BaseTransactionButton/BaseTransactionButton.d.ts.map +1 -1
  22. package/dist/esm/components/BaseTransactionButton/types.d.ts +1 -2
  23. package/dist/esm/components/BaseTransactionButton/types.d.ts.map +1 -1
  24. package/dist/esm/components/BottomSheet/BottomSheet.js +1 -0
  25. package/dist/esm/components/BottomSheet/BottomSheet.js.map +1 -1
  26. package/dist/esm/components/BottomSheet/types.d.ts +1 -2
  27. package/dist/esm/components/BottomSheet/types.d.ts.map +1 -1
  28. package/dist/esm/components/ButtonTertiary.d.ts +1 -2
  29. package/dist/esm/components/ButtonTertiary.d.ts.map +1 -1
  30. package/dist/esm/components/Card/CardIconButton.d.ts +1 -2
  31. package/dist/esm/components/Card/CardIconButton.d.ts.map +1 -1
  32. package/dist/esm/components/Card/CardTitle.d.ts +1 -2
  33. package/dist/esm/components/Card/CardTitle.d.ts.map +1 -1
  34. package/dist/esm/components/Card/InputCard.d.ts +1 -2
  35. package/dist/esm/components/Card/InputCard.d.ts.map +1 -1
  36. package/dist/esm/components/ChainSelect/ChainSelect.d.ts +1 -2
  37. package/dist/esm/components/ChainSelect/ChainSelect.d.ts.map +1 -1
  38. package/dist/esm/components/ChainSelect/ChainSelect.style.d.ts +1 -2
  39. package/dist/esm/components/ChainSelect/ChainSelect.style.d.ts.map +1 -1
  40. package/dist/esm/components/ContractComponent/ContractComponent.d.ts +1 -2
  41. package/dist/esm/components/ContractComponent/ContractComponent.d.ts.map +1 -1
  42. package/dist/esm/components/ContractComponent/ItemPrice.d.ts +2 -3
  43. package/dist/esm/components/ContractComponent/ItemPrice.d.ts.map +1 -1
  44. package/dist/esm/components/ContractComponent/NFT/NFT.d.ts +1 -2
  45. package/dist/esm/components/ContractComponent/NFT/NFT.d.ts.map +1 -1
  46. package/dist/esm/components/ContractComponent/NFT/NFTBase.d.ts +1 -2
  47. package/dist/esm/components/ContractComponent/NFT/NFTBase.d.ts.map +1 -1
  48. package/dist/esm/components/ContractComponent/NFT/types.d.ts +3 -4
  49. package/dist/esm/components/ContractComponent/NFT/types.d.ts.map +1 -1
  50. package/dist/esm/components/ExecutionStatusCard/ExecutionStatusCard.d.ts +1 -2
  51. package/dist/esm/components/ExecutionStatusCard/ExecutionStatusCard.d.ts.map +1 -1
  52. package/dist/esm/components/ExecutionStatusCard/StatusIcon.d.ts +1 -2
  53. package/dist/esm/components/ExecutionStatusCard/StatusIcon.d.ts.map +1 -1
  54. package/dist/esm/components/FeeBreakdownTooltip.d.ts +1 -2
  55. package/dist/esm/components/FeeBreakdownTooltip.d.ts.map +1 -1
  56. package/dist/esm/components/IconCircle/IconCircle.d.ts +1 -2
  57. package/dist/esm/components/IconCircle/IconCircle.d.ts.map +1 -1
  58. package/dist/esm/components/IconCircle/statusIcons.d.ts +1 -2
  59. package/dist/esm/components/IconCircle/statusIcons.d.ts.map +1 -1
  60. package/dist/esm/components/IconTypography.d.ts +1 -2
  61. package/dist/esm/components/IconTypography.d.ts.map +1 -1
  62. package/dist/esm/components/ListItem/ListItemButton.d.ts +1 -2
  63. package/dist/esm/components/ListItem/ListItemButton.d.ts.map +1 -1
  64. package/dist/esm/components/Messages/WarningMessages.d.ts +1 -2
  65. package/dist/esm/components/Messages/WarningMessages.d.ts.map +1 -1
  66. package/dist/esm/components/PageContainer.d.ts +2 -3
  67. package/dist/esm/components/PageContainer.d.ts.map +1 -1
  68. package/dist/esm/components/PoweredBy/PoweredBy.d.ts +1 -2
  69. package/dist/esm/components/PoweredBy/PoweredBy.d.ts.map +1 -1
  70. package/dist/esm/components/ProgressToNextUpdate.d.ts +1 -2
  71. package/dist/esm/components/ProgressToNextUpdate.d.ts.map +1 -1
  72. package/dist/esm/components/RouteCard/RouteCard.d.ts +1 -2
  73. package/dist/esm/components/RouteCard/RouteCard.d.ts.map +1 -1
  74. package/dist/esm/components/RouteCard/RouteCardSkeleton.d.ts +1 -2
  75. package/dist/esm/components/RouteCard/RouteCardSkeleton.d.ts.map +1 -1
  76. package/dist/esm/components/RouteCard/RouteDetails.d.ts +1 -2
  77. package/dist/esm/components/RouteCard/RouteDetails.d.ts.map +1 -1
  78. package/dist/esm/components/RouteCard/RouteDetails.style.d.ts +5 -6
  79. package/dist/esm/components/RouteCard/RouteDetails.style.d.ts.map +1 -1
  80. package/dist/esm/components/RouteCard/RouteNotFoundCard.d.ts +1 -2
  81. package/dist/esm/components/RouteCard/RouteNotFoundCard.d.ts.map +1 -1
  82. package/dist/esm/components/RouteCard/RouteTokens.d.ts +1 -2
  83. package/dist/esm/components/RouteCard/RouteTokens.d.ts.map +1 -1
  84. package/dist/esm/components/RouteCard/types.d.ts +2 -3
  85. package/dist/esm/components/RouteCard/types.d.ts.map +1 -1
  86. package/dist/esm/components/Search/SearchInput.d.ts +1 -2
  87. package/dist/esm/components/Search/SearchInput.d.ts.map +1 -1
  88. package/dist/esm/components/SendToWallet/SendToWalletButton.d.ts +1 -2
  89. package/dist/esm/components/SendToWallet/SendToWalletButton.d.ts.map +1 -1
  90. package/dist/esm/components/Skeleton/WidgetSkeleton.d.ts +1 -2
  91. package/dist/esm/components/Skeleton/WidgetSkeleton.d.ts.map +1 -1
  92. package/dist/esm/components/StepActions/SentToWalletRow.d.ts +1 -2
  93. package/dist/esm/components/StepActions/SentToWalletRow.d.ts.map +1 -1
  94. package/dist/esm/components/StepActions/StepActionRow.style.d.ts +1 -2
  95. package/dist/esm/components/StepActions/StepActionRow.style.d.ts.map +1 -1
  96. package/dist/esm/components/StepActions/StepActionsList.d.ts +1 -2
  97. package/dist/esm/components/StepActions/StepActionsList.d.ts.map +1 -1
  98. package/dist/esm/components/StepActions/executionRows.d.ts +2 -3
  99. package/dist/esm/components/StepActions/executionRows.d.ts.map +1 -1
  100. package/dist/esm/components/Token/Token.d.ts.map +1 -1
  101. package/dist/esm/components/Token/Token.js +2 -4
  102. package/dist/esm/components/Token/Token.js.map +1 -1
  103. package/dist/esm/components/TokenList/RecentTokensHeader.js +44 -0
  104. package/dist/esm/components/TokenList/RecentTokensHeader.js.map +1 -0
  105. package/dist/esm/components/TokenList/RecentTokensToggle.js +36 -0
  106. package/dist/esm/components/TokenList/RecentTokensToggle.js.map +1 -0
  107. package/dist/esm/components/TokenList/RemoveRecentTokenButton.js +32 -0
  108. package/dist/esm/components/TokenList/RemoveRecentTokenButton.js.map +1 -0
  109. package/dist/esm/components/TokenList/TokenList.js +55 -5
  110. package/dist/esm/components/TokenList/TokenList.js.map +1 -1
  111. package/dist/esm/components/TokenList/TokenListItem.js +35 -19
  112. package/dist/esm/components/TokenList/TokenListItem.js.map +1 -1
  113. package/dist/esm/components/TokenList/TokenNotFound.d.ts +1 -2
  114. package/dist/esm/components/TokenList/TokenNotFound.d.ts.map +1 -1
  115. package/dist/esm/components/TokenList/VirtualizedTokenList.d.ts +1 -2
  116. package/dist/esm/components/TokenList/VirtualizedTokenList.d.ts.map +1 -1
  117. package/dist/esm/components/TokenList/VirtualizedTokenList.js +55 -42
  118. package/dist/esm/components/TokenList/VirtualizedTokenList.js.map +1 -1
  119. package/dist/esm/components/TokenList/types.d.ts +9 -2
  120. package/dist/esm/components/TokenList/types.d.ts.map +1 -1
  121. package/dist/esm/components/TokenList/useTokenSelect.d.ts +1 -2
  122. package/dist/esm/components/TokenList/useTokenSelect.d.ts.map +1 -1
  123. package/dist/esm/components/TokenRate/TokenRate.d.ts +1 -2
  124. package/dist/esm/components/TokenRate/TokenRate.d.ts.map +1 -1
  125. package/dist/esm/components/TransactionCard/TransactionCard.style.d.ts +2 -3
  126. package/dist/esm/components/TransactionCard/TransactionCard.style.d.ts.map +1 -1
  127. package/dist/esm/config/constants.d.ts +1 -2
  128. package/dist/esm/config/constants.d.ts.map +1 -1
  129. package/dist/esm/config/version.d.ts +2 -3
  130. package/dist/esm/config/version.d.ts.map +1 -1
  131. package/dist/esm/config/version.js +1 -1
  132. package/dist/esm/config/version.js.map +1 -1
  133. package/dist/esm/hooks/useAddressActivity.d.ts +1 -2
  134. package/dist/esm/hooks/useAddressActivity.d.ts.map +1 -1
  135. package/dist/esm/hooks/useAddressValidation.d.ts +2 -3
  136. package/dist/esm/hooks/useAddressValidation.d.ts.map +1 -1
  137. package/dist/esm/hooks/useAvailableChains.d.ts +1 -2
  138. package/dist/esm/hooks/useAvailableChains.d.ts.map +1 -1
  139. package/dist/esm/hooks/useChain.d.ts +1 -2
  140. package/dist/esm/hooks/useChain.d.ts.map +1 -1
  141. package/dist/esm/hooks/useContactSupport.d.ts +1 -2
  142. package/dist/esm/hooks/useContactSupport.d.ts.map +1 -1
  143. package/dist/esm/hooks/useDebouncedWatch.d.ts +1 -2
  144. package/dist/esm/hooks/useDebouncedWatch.d.ts.map +1 -1
  145. package/dist/esm/hooks/useDisplayedTokens.js +6 -4
  146. package/dist/esm/hooks/useDisplayedTokens.js.map +1 -1
  147. package/dist/esm/hooks/useExplorer.d.ts +2 -3
  148. package/dist/esm/hooks/useExplorer.d.ts.map +1 -1
  149. package/dist/esm/hooks/useExplorer.js +7 -3
  150. package/dist/esm/hooks/useExplorer.js.map +1 -1
  151. package/dist/esm/hooks/useHeader.d.ts +1 -2
  152. package/dist/esm/hooks/useHeader.d.ts.map +1 -1
  153. package/dist/esm/hooks/useListHeight.d.ts +1 -2
  154. package/dist/esm/hooks/useListHeight.d.ts.map +1 -1
  155. package/dist/esm/hooks/useNavigateBack.d.ts +1 -2
  156. package/dist/esm/hooks/useNavigateBack.d.ts.map +1 -1
  157. package/dist/esm/hooks/useRecentTokens.js +46 -0
  158. package/dist/esm/hooks/useRecentTokens.js.map +1 -0
  159. package/dist/esm/hooks/useRouteExecution.d.ts +1 -2
  160. package/dist/esm/hooks/useRouteExecution.d.ts.map +1 -1
  161. package/dist/esm/hooks/useRouteExecutionMessage.d.ts +1 -2
  162. package/dist/esm/hooks/useRouteExecutionMessage.d.ts.map +1 -1
  163. package/dist/esm/hooks/useRoutes.d.ts +1 -2
  164. package/dist/esm/hooks/useRoutes.d.ts.map +1 -1
  165. package/dist/esm/hooks/useRoutes.js +7 -2
  166. package/dist/esm/hooks/useRoutes.js.map +1 -1
  167. package/dist/esm/hooks/useToAddressRequirements.d.ts +1 -2
  168. package/dist/esm/hooks/useToAddressRequirements.d.ts.map +1 -1
  169. package/dist/esm/hooks/useToAddressRequirements.js +8 -2
  170. package/dist/esm/hooks/useToAddressRequirements.js.map +1 -1
  171. package/dist/esm/hooks/useToken.d.ts +1 -2
  172. package/dist/esm/hooks/useToken.d.ts.map +1 -1
  173. package/dist/esm/hooks/useTokenAddressBalance.d.ts +1 -2
  174. package/dist/esm/hooks/useTokenAddressBalance.d.ts.map +1 -1
  175. package/dist/esm/hooks/useTokenBalances.d.ts +3 -2
  176. package/dist/esm/hooks/useTokenBalances.d.ts.map +1 -1
  177. package/dist/esm/hooks/useTokenBalances.js +15 -9
  178. package/dist/esm/hooks/useTokenBalances.js.map +1 -1
  179. package/dist/esm/hooks/useTokenList.d.ts +1 -2
  180. package/dist/esm/hooks/useTokenList.d.ts.map +1 -1
  181. package/dist/esm/hooks/useTokenSearch.js +3 -2
  182. package/dist/esm/hooks/useTokenSearch.js.map +1 -1
  183. package/dist/esm/hooks/useTokens.js +26 -15
  184. package/dist/esm/hooks/useTokens.js.map +1 -1
  185. package/dist/esm/hooks/useTools.d.ts +1 -2
  186. package/dist/esm/hooks/useTools.d.ts.map +1 -1
  187. package/dist/esm/hooks/useWidgetChains.d.ts +1 -2
  188. package/dist/esm/hooks/useWidgetChains.d.ts.map +1 -1
  189. package/dist/esm/hooks/useWidgetEvents.d.ts +3 -4
  190. package/dist/esm/hooks/useWidgetEvents.d.ts.map +1 -1
  191. package/dist/esm/i18n/bn.json +4 -0
  192. package/dist/esm/i18n/de.json +4 -0
  193. package/dist/esm/i18n/en.json +4 -0
  194. package/dist/esm/i18n/es.json +4 -0
  195. package/dist/esm/i18n/fr.json +4 -0
  196. package/dist/esm/i18n/hi.json +4 -0
  197. package/dist/esm/i18n/id.json +4 -0
  198. package/dist/esm/i18n/it.json +4 -0
  199. package/dist/esm/i18n/ja.json +4 -0
  200. package/dist/esm/i18n/ko.json +4 -0
  201. package/dist/esm/i18n/pl.json +4 -0
  202. package/dist/esm/i18n/pt.json +4 -0
  203. package/dist/esm/i18n/th.json +4 -0
  204. package/dist/esm/i18n/tr.json +4 -0
  205. package/dist/esm/i18n/uk.json +4 -0
  206. package/dist/esm/i18n/vi.json +4 -0
  207. package/dist/esm/i18n/zh.json +4 -0
  208. package/dist/esm/pages/MainPage/MainWarningMessages.d.ts +1 -2
  209. package/dist/esm/pages/MainPage/MainWarningMessages.d.ts.map +1 -1
  210. package/dist/esm/pages/SelectChainPage/SelectChainPage.d.ts +1 -2
  211. package/dist/esm/pages/SelectChainPage/SelectChainPage.d.ts.map +1 -1
  212. package/dist/esm/pages/SelectChainPage/types.d.ts +1 -2
  213. package/dist/esm/pages/SelectChainPage/types.d.ts.map +1 -1
  214. package/dist/esm/pages/SelectTokenPage/SearchTokenInput.d.ts +1 -2
  215. package/dist/esm/pages/SelectTokenPage/SearchTokenInput.d.ts.map +1 -1
  216. package/dist/esm/pages/SendToWallet/BookmarksPage.js +2 -1
  217. package/dist/esm/pages/SendToWallet/BookmarksPage.js.map +1 -1
  218. package/dist/esm/pages/SendToWallet/ConnectedWalletsPage.js +2 -1
  219. package/dist/esm/pages/SendToWallet/ConnectedWalletsPage.js.map +1 -1
  220. package/dist/esm/pages/SendToWallet/RecentWalletsPage.js +2 -1
  221. package/dist/esm/pages/SendToWallet/RecentWalletsPage.js.map +1 -1
  222. package/dist/esm/pages/SendToWallet/SendToConfiguredWalletPage.js +2 -1
  223. package/dist/esm/pages/SendToWallet/SendToConfiguredWalletPage.js.map +1 -1
  224. package/dist/esm/pages/TransactionDetailsPage/ContactSupportButton.d.ts +1 -2
  225. package/dist/esm/pages/TransactionDetailsPage/ContactSupportButton.d.ts.map +1 -1
  226. package/dist/esm/pages/TransactionDetailsPage/TransactionDetailsPage.js +2 -1
  227. package/dist/esm/pages/TransactionDetailsPage/TransactionDetailsPage.js.map +1 -1
  228. package/dist/esm/pages/TransactionDetailsPage/TransferIdCard.js +2 -1
  229. package/dist/esm/pages/TransactionDetailsPage/TransferIdCard.js.map +1 -1
  230. package/dist/esm/pages/TransactionPage/ConfirmToAddressSheet.d.ts +1 -2
  231. package/dist/esm/pages/TransactionPage/ConfirmToAddressSheet.d.ts.map +1 -1
  232. package/dist/esm/pages/TransactionPage/ExchangeRateBottomSheet.d.ts +2 -3
  233. package/dist/esm/pages/TransactionPage/ExchangeRateBottomSheet.d.ts.map +1 -1
  234. package/dist/esm/pages/TransactionPage/ExecutionDoneCard.d.ts +1 -2
  235. package/dist/esm/pages/TransactionPage/ExecutionDoneCard.d.ts.map +1 -1
  236. package/dist/esm/pages/TransactionPage/RouteTracker.d.ts +1 -2
  237. package/dist/esm/pages/TransactionPage/RouteTracker.d.ts.map +1 -1
  238. package/dist/esm/pages/TransactionPage/StartTransactionButton.d.ts +1 -2
  239. package/dist/esm/pages/TransactionPage/StartTransactionButton.d.ts.map +1 -1
  240. package/dist/esm/pages/TransactionPage/TokenValueBottomSheet.d.ts +1 -2
  241. package/dist/esm/pages/TransactionPage/TokenValueBottomSheet.d.ts.map +1 -1
  242. package/dist/esm/pages/TransactionPage/TokenValueBottomSheet.js +0 -1
  243. package/dist/esm/pages/TransactionPage/TokenValueBottomSheet.js.map +1 -1
  244. package/dist/esm/pages/TransactionPage/TokenVerificationBottomSheet.js +0 -1
  245. package/dist/esm/pages/TransactionPage/TokenVerificationBottomSheet.js.map +1 -1
  246. package/dist/esm/pages/TransactionPage/TransactionDoneButtons.d.ts +1 -2
  247. package/dist/esm/pages/TransactionPage/TransactionDoneButtons.d.ts.map +1 -1
  248. package/dist/esm/pages/TransactionPage/TransactionFailedButtons.js +38 -12
  249. package/dist/esm/pages/TransactionPage/TransactionFailedButtons.js.map +1 -1
  250. package/dist/esm/pages/TransactionPage/TransactionReview.js +22 -21
  251. package/dist/esm/pages/TransactionPage/TransactionReview.js.map +1 -1
  252. package/dist/esm/pages/TransactionPage/types.d.ts +1 -2
  253. package/dist/esm/pages/TransactionPage/types.d.ts.map +1 -1
  254. package/dist/esm/pages/TransactionPage/utils.d.ts +2 -3
  255. package/dist/esm/pages/TransactionPage/utils.d.ts.map +1 -1
  256. package/dist/esm/pages/TransactionPage/utils.js +23 -1
  257. package/dist/esm/pages/TransactionPage/utils.js.map +1 -1
  258. package/dist/esm/providers/I18nProvider/I18nProvider.d.ts +1 -2
  259. package/dist/esm/providers/I18nProvider/I18nProvider.d.ts.map +1 -1
  260. package/dist/esm/providers/I18nProvider/types.d.ts +4 -5
  261. package/dist/esm/providers/I18nProvider/types.d.ts.map +1 -1
  262. package/dist/esm/providers/QueryClientProvider.d.ts +1 -2
  263. package/dist/esm/providers/QueryClientProvider.d.ts.map +1 -1
  264. package/dist/esm/providers/SDKClientProvider.d.ts +2 -3
  265. package/dist/esm/providers/SDKClientProvider.d.ts.map +1 -1
  266. package/dist/esm/providers/WalletProvider/WalletProvider.d.ts +1 -2
  267. package/dist/esm/providers/WalletProvider/WalletProvider.d.ts.map +1 -1
  268. package/dist/esm/providers/WidgetProvider/WidgetProvider.d.ts +2 -3
  269. package/dist/esm/providers/WidgetProvider/WidgetProvider.d.ts.map +1 -1
  270. package/dist/esm/providers/WidgetProvider/types.d.ts +2 -3
  271. package/dist/esm/providers/WidgetProvider/types.d.ts.map +1 -1
  272. package/dist/esm/shared.d.ts +2 -1
  273. package/dist/esm/shared.js +2 -1
  274. package/dist/esm/stores/StoreProvider.d.ts +1 -2
  275. package/dist/esm/stores/StoreProvider.d.ts.map +1 -1
  276. package/dist/esm/stores/StoreProvider.js +10 -6
  277. package/dist/esm/stores/StoreProvider.js.map +1 -1
  278. package/dist/esm/stores/bookmarks/BookmarkStore.d.ts +1 -2
  279. package/dist/esm/stores/bookmarks/BookmarkStore.d.ts.map +1 -1
  280. package/dist/esm/stores/bookmarks/types.d.ts +5 -6
  281. package/dist/esm/stores/bookmarks/types.d.ts.map +1 -1
  282. package/dist/esm/stores/chains/ChainOrderStore.d.ts +1 -2
  283. package/dist/esm/stores/chains/ChainOrderStore.d.ts.map +1 -1
  284. package/dist/esm/stores/chains/ChainOrderStore.js +11 -5
  285. package/dist/esm/stores/chains/ChainOrderStore.js.map +1 -1
  286. package/dist/esm/stores/chains/types.d.ts +1 -2
  287. package/dist/esm/stores/chains/types.d.ts.map +1 -1
  288. package/dist/esm/stores/form/types.d.ts +20 -21
  289. package/dist/esm/stores/form/types.d.ts.map +1 -1
  290. package/dist/esm/stores/form/useFieldActions.d.ts +1 -2
  291. package/dist/esm/stores/form/useFieldActions.d.ts.map +1 -1
  292. package/dist/esm/stores/form/useFieldValues.d.ts +1 -2
  293. package/dist/esm/stores/form/useFieldValues.d.ts.map +1 -1
  294. package/dist/esm/stores/header/types.d.ts +1 -2
  295. package/dist/esm/stores/header/types.d.ts.map +1 -1
  296. package/dist/esm/stores/header/useHeaderStore.d.ts +2 -3
  297. package/dist/esm/stores/header/useHeaderStore.d.ts.map +1 -1
  298. package/dist/esm/stores/inputMode/useInputModeStore.d.ts +1 -2
  299. package/dist/esm/stores/inputMode/useInputModeStore.d.ts.map +1 -1
  300. package/dist/esm/stores/recentTokens/RecentTokensStore.js +23 -0
  301. package/dist/esm/stores/recentTokens/RecentTokensStore.js.map +1 -0
  302. package/dist/esm/stores/recentTokens/createRecentTokensStore.js +54 -0
  303. package/dist/esm/stores/recentTokens/createRecentTokensStore.js.map +1 -0
  304. package/dist/esm/stores/recentTokens/utils.js +15 -0
  305. package/dist/esm/stores/recentTokens/utils.js.map +1 -0
  306. package/dist/esm/stores/routes/RouteExecutionStore.d.ts +2 -3
  307. package/dist/esm/stores/routes/RouteExecutionStore.d.ts.map +1 -1
  308. package/dist/esm/stores/routes/types.d.ts +3 -4
  309. package/dist/esm/stores/routes/types.d.ts.map +1 -1
  310. package/dist/esm/stores/routes/utils.d.ts +4 -5
  311. package/dist/esm/stores/routes/utils.d.ts.map +1 -1
  312. package/dist/esm/stores/settings/SettingsStore.d.ts +2 -3
  313. package/dist/esm/stores/settings/SettingsStore.d.ts.map +1 -1
  314. package/dist/esm/stores/settings/types.d.ts +2 -3
  315. package/dist/esm/stores/settings/types.d.ts.map +1 -1
  316. package/dist/esm/stores/types.d.ts +2 -3
  317. package/dist/esm/stores/types.d.ts.map +1 -1
  318. package/dist/esm/themes/azureLight.d.ts +1 -2
  319. package/dist/esm/themes/azureLight.d.ts.map +1 -1
  320. package/dist/esm/themes/jumper.d.ts +1 -2
  321. package/dist/esm/themes/jumper.d.ts.map +1 -1
  322. package/dist/esm/themes/palettes.d.ts +3 -4
  323. package/dist/esm/themes/palettes.d.ts.map +1 -1
  324. package/dist/esm/themes/types.d.ts +1 -0
  325. package/dist/esm/themes/watermelonLight.d.ts +1 -2
  326. package/dist/esm/themes/watermelonLight.d.ts.map +1 -1
  327. package/dist/esm/themes/windows95.d.ts +1 -2
  328. package/dist/esm/themes/windows95.d.ts.map +1 -1
  329. package/dist/esm/types/events.d.ts +13 -14
  330. package/dist/esm/types/events.d.ts.map +1 -1
  331. package/dist/esm/types/token.d.ts +4 -2
  332. package/dist/esm/types/token.d.ts.map +1 -1
  333. package/dist/esm/types/widget.d.ts +55 -51
  334. package/dist/esm/types/widget.d.ts.map +1 -1
  335. package/dist/esm/types/widget.js.map +1 -1
  336. package/dist/esm/utils/address.js +19 -0
  337. package/dist/esm/utils/address.js.map +1 -0
  338. package/dist/esm/utils/compactNumberFormatter.d.ts +1 -2
  339. package/dist/esm/utils/compactNumberFormatter.d.ts.map +1 -1
  340. package/dist/esm/utils/converters.d.ts +1 -2
  341. package/dist/esm/utils/converters.d.ts.map +1 -1
  342. package/dist/esm/utils/currencyExtendedFormatter.d.ts +1 -2
  343. package/dist/esm/utils/currencyExtendedFormatter.d.ts.map +1 -1
  344. package/dist/esm/utils/customReceiver.js +6 -1
  345. package/dist/esm/utils/customReceiver.js.map +1 -1
  346. package/dist/esm/utils/elements.d.ts +2 -3
  347. package/dist/esm/utils/elements.d.ts.map +1 -1
  348. package/dist/esm/utils/enum.d.ts +1 -2
  349. package/dist/esm/utils/enum.d.ts.map +1 -1
  350. package/dist/esm/utils/fees.d.ts +2 -3
  351. package/dist/esm/utils/fees.d.ts.map +1 -1
  352. package/dist/esm/utils/format.d.ts +8 -9
  353. package/dist/esm/utils/format.d.ts.map +1 -1
  354. package/dist/esm/utils/getPriceImpact.d.ts +1 -2
  355. package/dist/esm/utils/getPriceImpact.d.ts.map +1 -1
  356. package/dist/esm/utils/input.d.ts +1 -2
  357. package/dist/esm/utils/input.d.ts.map +1 -1
  358. package/dist/esm/utils/navigationRoutes.d.ts +2 -3
  359. package/dist/esm/utils/navigationRoutes.d.ts.map +1 -1
  360. package/dist/esm/utils/percentFormatter.d.ts +1 -2
  361. package/dist/esm/utils/percentFormatter.d.ts.map +1 -1
  362. package/dist/esm/utils/recentTokens.js +97 -0
  363. package/dist/esm/utils/recentTokens.js.map +1 -0
  364. package/dist/esm/utils/token.js +30 -4
  365. package/dist/esm/utils/token.js.map +1 -1
  366. package/dist/esm/utils/tokenList.js +18 -6
  367. package/dist/esm/utils/tokenList.js.map +1 -1
  368. package/dist/esm/utils/tokenListBands.js +85 -0
  369. package/dist/esm/utils/tokenListBands.js.map +1 -0
  370. package/dist/esm/utils/url.d.ts +5 -0
  371. package/dist/esm/utils/url.d.ts.map +1 -0
  372. package/dist/esm/utils/url.js +17 -0
  373. package/dist/esm/utils/url.js.map +1 -0
  374. package/dist/esm/utils/wallet.d.ts +1 -2
  375. package/dist/esm/utils/wallet.d.ts.map +1 -1
  376. package/package.json +9 -9
  377. package/src/components/AmountInputCard/ReceiveAmountCard.tsx +16 -3
  378. package/src/components/BottomSheet/BottomSheet.tsx +3 -0
  379. package/src/components/Token/Token.tsx +2 -1
  380. package/src/components/TokenList/RecentTokensHeader.tsx +52 -0
  381. package/src/components/TokenList/RecentTokensToggle.tsx +45 -0
  382. package/src/components/TokenList/RemoveRecentTokenButton.tsx +38 -0
  383. package/src/components/TokenList/TokenList.tsx +92 -3
  384. package/src/components/TokenList/TokenListItem.tsx +15 -0
  385. package/src/components/TokenList/VirtualizedTokenList.tsx +84 -125
  386. package/src/components/TokenList/types.ts +9 -0
  387. package/src/config/version.ts +1 -1
  388. package/src/hooks/useDisplayedTokens.ts +17 -3
  389. package/src/hooks/useExplorer.test.tsx +106 -0
  390. package/src/hooks/useExplorer.ts +11 -5
  391. package/src/hooks/useRecentTokens.ts +88 -0
  392. package/src/hooks/useRoutes.ts +8 -5
  393. package/src/hooks/useToAddressRequirements.ts +11 -5
  394. package/src/hooks/useTokenBalances.ts +34 -31
  395. package/src/hooks/useTokenSearch.ts +5 -14
  396. package/src/hooks/useTokens.ts +69 -27
  397. package/src/pages/SendToWallet/BookmarksPage.tsx +3 -3
  398. package/src/pages/SendToWallet/ConnectedWalletsPage.tsx +3 -3
  399. package/src/pages/SendToWallet/RecentWalletsPage.tsx +3 -3
  400. package/src/pages/SendToWallet/SendToConfiguredWalletPage.tsx +3 -3
  401. package/src/pages/TransactionDetailsPage/TransactionDetailsPage.tsx +5 -5
  402. package/src/pages/TransactionDetailsPage/TransferIdCard.tsx +2 -1
  403. package/src/pages/TransactionPage/TokenValueBottomSheet.tsx +1 -1
  404. package/src/pages/TransactionPage/TokenVerificationBottomSheet.tsx +1 -1
  405. package/src/pages/TransactionPage/TransactionFailedButtons.tsx +62 -26
  406. package/src/pages/TransactionPage/TransactionReview.tsx +36 -37
  407. package/src/pages/TransactionPage/utils.ts +78 -0
  408. package/src/shared.ts +1 -0
  409. package/src/stores/StoreProvider.tsx +10 -7
  410. package/src/stores/chains/ChainOrderStore.test.tsx +298 -0
  411. package/src/stores/chains/ChainOrderStore.tsx +25 -6
  412. package/src/stores/recentTokens/RecentTokensStore.tsx +39 -0
  413. package/src/stores/recentTokens/createRecentTokensStore.ts +107 -0
  414. package/src/stores/recentTokens/types.ts +37 -0
  415. package/src/stores/recentTokens/utils.ts +18 -0
  416. package/src/types/token.ts +2 -0
  417. package/src/types/widget.ts +5 -0
  418. package/src/utils/address.ts +21 -0
  419. package/src/utils/customReceiver.ts +24 -0
  420. package/src/utils/recentTokens.ts +187 -0
  421. package/src/utils/token.ts +69 -10
  422. package/src/utils/tokenList.ts +41 -23
  423. package/src/utils/tokenListBands.ts +181 -0
  424. package/src/utils/url.ts +17 -0
@@ -4,7 +4,11 @@ import type { FC } from 'react'
4
4
  import { useCallback, useEffect, useMemo, useRef } from 'react'
5
5
  import { useTranslation } from 'react-i18next'
6
6
  import { useAvailableChains } from '../../hooks/useAvailableChains.js'
7
- import type { TokenAmount } from '../../types/token.js'
7
+ import { getTokenKey } from '../../stores/recentTokens/utils.js'
8
+ import { isHoistableNative } from '../../utils/tokenList.js'
9
+ import { createBandResolver } from '../../utils/tokenListBands.js'
10
+ import { RecentTokensHeader } from './RecentTokensHeader.js'
11
+ import { RecentTokensToggle } from './RecentTokensToggle.js'
8
12
  import { TokenDetailsSheet } from './TokenDetailsSheet.js'
9
13
  import { List } from './TokenList.style.js'
10
14
  import { TokenListItem, TokenListItemSkeleton } from './TokenListItem.js'
@@ -26,6 +30,13 @@ export const VirtualizedTokenList: FC<VirtualizedTokenListProps> = ({
26
30
  showPinnedTokens,
27
31
  onClick,
28
32
  isAllNetworks,
33
+ recentStartIndex = 0,
34
+ recentCount = 0,
35
+ hiddenRecentCount = 0,
36
+ recentExpanded = false,
37
+ onToggleRecent,
38
+ onClearRecent,
39
+ nativeHoisted,
29
40
  }) => {
30
41
  const { t } = useTranslation()
31
42
 
@@ -52,74 +63,65 @@ export const VirtualizedTokenList: FC<VirtualizedTokenListProps> = ({
52
63
  []
53
64
  )
54
65
 
66
+ // Rows below the band keep their key as it changes size, so none remount.
55
67
  const getItemKey = useCallback(
56
68
  (index: number) => {
57
69
  const token = tokens[index]
58
- return `${token.chainId}-${token.address}-${index}`
70
+ if (token.recent) {
71
+ return `recent-${token.chainId}-${token.address}`
72
+ }
73
+ const shift = index >= recentStartIndex + recentCount ? recentCount : 0
74
+ return `${token.chainId}-${token.address}-${index - shift}`
59
75
  },
60
- [tokens]
76
+ [tokens, recentStartIndex, recentCount]
61
77
  )
62
78
 
63
- // The hoisted native token sits above every category, so the row after it
64
- // opens the first one. Only a row the hoist could have moved counts.
65
- const nativeTokenHoisted = useMemo(() => {
66
- const first = tokens[0]
67
- return (
68
- !!first?.native &&
69
- !first.pinned &&
70
- !first.featured &&
71
- !first.popular &&
72
- !first.verified
73
- )
74
- }, [tokens])
75
-
76
- const isListStartIndex = useCallback(
77
- (index: number) => index === 0 || (index === 1 && nativeTokenHoisted),
78
- [nativeTokenHoisted]
79
+ const resolvedNativeHoisted = useMemo(
80
+ () => nativeHoisted ?? isHoistableNative(tokens[0]),
81
+ [nativeHoisted, tokens]
79
82
  )
80
83
 
81
- const estimateSize = useCallback(
82
- (index: number) => {
83
- const currentToken = tokens[index]
84
- const previousToken = tokens[index - 1]
85
- let size = tokenItemHeight
86
-
87
- // Pinned tokens (always shown, even in all networks mode)
88
- if (currentToken.pinned && isListStartIndex(index)) {
89
- size += 24
90
- }
91
- if (previousToken?.pinned && !currentToken.pinned) {
92
- size += 32
93
- }
94
-
95
- if (!showCategories) {
96
- return size
97
- }
98
-
99
- if (
100
- currentToken.featured &&
101
- !currentToken.pinned &&
102
- isListStartIndex(index)
103
- ) {
104
- size += 24
105
- }
84
+ const showRecentToggle = recentCount > 0 && !!onToggleRecent
106
85
 
107
- // Category transition (excluding pinned tokens). The hoisted native row
108
- // sits above every band, so the row after it ends nothing.
109
- const isNotPinned = !currentToken.pinned && !previousToken?.pinned
110
- if (
111
- !isListStartIndex(index) &&
112
- isNotPinned &&
113
- ((previousToken?.amount && !currentToken.amount) ||
114
- (previousToken?.featured && !currentToken.featured) ||
115
- (previousToken?.popular && !currentToken.popular))
116
- ) {
117
- size += 32
86
+ // The band copy is on screen; its list row may be far below the fold.
87
+ const bandKeys = useMemo(() => {
88
+ if (!recentCount) {
89
+ return undefined
90
+ }
91
+ const keys = new Set<string>()
92
+ for (let i = recentStartIndex; i < recentStartIndex + recentCount; i++) {
93
+ const token = tokens[i]
94
+ if (token) {
95
+ keys.add(getTokenKey(token.chainId, token.address))
118
96
  }
97
+ }
98
+ return keys
99
+ }, [tokens, recentStartIndex, recentCount])
100
+
101
+ const bands = useMemo(
102
+ () =>
103
+ createBandResolver(tokens, {
104
+ showCategories: !!showCategories,
105
+ showPinnedTokens: !!showPinnedTokens,
106
+ nativeHoisted: resolvedNativeHoisted,
107
+ recentStartIndex,
108
+ recentCount,
109
+ showRecentToggle,
110
+ }),
111
+ [
112
+ tokens,
113
+ showCategories,
114
+ showPinnedTokens,
115
+ resolvedNativeHoisted,
116
+ recentStartIndex,
117
+ recentCount,
118
+ showRecentToggle,
119
+ ]
120
+ )
119
121
 
120
- return size
121
- },
122
- [tokens, showCategories, isListStartIndex]
122
+ const estimateSize = useCallback(
123
+ (index: number) => tokenItemHeight + bands.getRowExtraHeight(index),
124
+ [bands]
123
125
  )
124
126
 
125
127
  const virtualizerConfig = useMemo(
@@ -163,70 +165,16 @@ export const VirtualizedTokenList: FC<VirtualizedTokenListProps> = ({
163
165
  >
164
166
  {getVirtualItems().map((item) => {
165
167
  const currentToken = tokens[item.index]
166
- const previousToken: TokenAmount | undefined = tokens[item.index - 1]
167
168
  const chain = chainsSet?.get(currentToken.chainId)
168
-
169
- const isListStart = isListStartIndex(item.index)
170
- const isNotPinned = !currentToken.pinned
171
- const isFirstPinnedToken = currentToken.pinned && isListStart
172
- const isTransitionFromPinned = previousToken?.pinned && isNotPinned
173
-
174
- // Category transitions (excluding pinned)
175
- const isTransitionFromFeatured =
176
- previousToken?.featured && !currentToken.featured && isNotPinned
177
- const isTransitionFromMyTokens =
178
- previousToken?.amount && !currentToken.amount && isNotPinned
179
- const isTransitionFromPopular =
180
- previousToken?.popular && !currentToken.popular && isNotPinned
181
-
182
- // Determine which category label to show
183
- const startAdornmentLabel = (() => {
184
- if (showPinnedTokens && isFirstPinnedToken) {
185
- return t('main.pinnedTokens')
186
- }
187
- if (showPinnedTokens && !showCategories && isTransitionFromPinned) {
188
- return t('main.allTokens')
189
- }
190
- if (!showCategories) {
191
- return null
192
- }
193
-
194
- if (
195
- (isTransitionFromPinned && currentToken.featured) ||
196
- (currentToken.featured && isNotPinned && isListStart)
197
- ) {
198
- return t('main.featuredTokens')
199
- }
200
- if (
201
- (isTransitionFromFeatured || isTransitionFromPinned) &&
202
- currentToken.amount &&
203
- isNotPinned
204
- ) {
205
- return t('main.myTokens')
206
- }
207
- if (
208
- (isTransitionFromFeatured ||
209
- isTransitionFromMyTokens ||
210
- isTransitionFromPinned) &&
211
- currentToken.popular &&
212
- isNotPinned
213
- ) {
214
- return t('main.popularTokens')
215
- }
216
- if (
217
- isTransitionFromMyTokens ||
218
- isTransitionFromFeatured ||
219
- isTransitionFromPinned ||
220
- isTransitionFromPopular
221
- ) {
222
- return t('main.allTokens')
223
- }
224
- return null
225
- })()
169
+ const band = bands.getRowBandLabel(item.index)
226
170
 
227
171
  const isSelected =
228
172
  selectedTokenAddress === currentToken.address &&
229
- chainId === currentToken.chainId
173
+ chainId === currentToken.chainId &&
174
+ (!!currentToken.recent ||
175
+ !bandKeys?.has(
176
+ getTokenKey(currentToken.chainId, currentToken.address)
177
+ ))
230
178
 
231
179
  return (
232
180
  <TokenListItem
@@ -241,25 +189,36 @@ export const VirtualizedTokenList: FC<VirtualizedTokenListProps> = ({
241
189
  onShowTokenDetails={onShowTokenDetails}
242
190
  isBalanceLoading={isBalanceLoading}
243
191
  startAdornment={
244
- startAdornmentLabel ? (
192
+ band?.kind === 'recent' ? (
193
+ // Unconditional: the resolver always reserves this height.
194
+ <RecentTokensHeader
195
+ atListStart={band.atListStart}
196
+ onClear={onClearRecent}
197
+ />
198
+ ) : band?.kind === 'text' ? (
245
199
  <Typography
246
200
  sx={{
247
201
  fontSize: 14,
248
202
  fontWeight: 600,
249
203
  lineHeight: '16px',
250
204
  px: 1.5,
251
- pt:
252
- isFirstPinnedToken ||
253
- (currentToken.featured && isNotPinned && isListStart)
254
- ? 0
255
- : 1,
205
+ pt: band.atListStart ? 0 : 1,
256
206
  pb: 1,
257
207
  }}
258
208
  >
259
- {startAdornmentLabel}
209
+ {t(band.key)}
260
210
  </Typography>
261
211
  ) : null
262
212
  }
213
+ endAdornment={
214
+ onToggleRecent && bands.isToggleRow(item.index) ? (
215
+ <RecentTokensToggle
216
+ expanded={recentExpanded}
217
+ hiddenCount={hiddenRecentCount}
218
+ onToggle={onToggleRecent}
219
+ />
220
+ ) : null
221
+ }
263
222
  />
264
223
  )
265
224
  })}
@@ -19,6 +19,15 @@ export interface VirtualizedTokenListProps {
19
19
  onClick(tokenAddress: string, chainId?: number): void
20
20
  selectedTokenAddress?: string
21
21
  isAllNetworks: boolean
22
+ // Optional: public via shared.ts, and widget-checkout passes none of these.
23
+ recentStartIndex?: number
24
+ recentCount?: number
25
+ hiddenRecentCount?: number
26
+ recentExpanded?: boolean
27
+ onToggleRecent?: () => void
28
+ onClearRecent?: () => void
29
+ /** Defaults to this component's own `isHoistableNative(tokens[0])`. */
30
+ nativeHoisted?: boolean
22
31
  }
23
32
 
24
33
  interface TokenListItemBaseProps {
@@ -1,2 +1,2 @@
1
1
  export const name = '@lifi/widget'
2
- export const version = '4.6.0'
2
+ export const version = '4.7.1'
@@ -2,11 +2,13 @@ import type { TokenExtended } from '@lifi/sdk'
2
2
  import { useMemo } from 'react'
3
3
  import type { FormType } from '../stores/form/types.js'
4
4
  import { usePinnedTokensStore } from '../stores/pinnedTokens/PinnedTokensStore.js'
5
- import { isSearchMatch } from '../utils/tokenList.js'
5
+ import { createSearchMatcher } from '../utils/tokenList.js'
6
6
  import { useTokens } from './useTokens.js'
7
7
 
8
8
  export type IsPinnedToken = (chainId: number, tokenAddress: string) => boolean
9
9
 
10
+ export type MatchesSearch = ReturnType<typeof createSearchMatcher>
11
+
10
12
  // Balance-free core shared by useTokenList and useTokenBalances.
11
13
  export const useDisplayedTokens = (
12
14
  selectedChainId?: number,
@@ -17,6 +19,11 @@ export const useDisplayedTokens = (
17
19
  allTokens: Record<number, TokenExtended[]> | undefined
18
20
  displayedTokensList: TokenExtended[]
19
21
  isPinnedToken: IsPinnedToken | undefined
22
+ /**
23
+ * The filter this search applies, built once here so every list that shows
24
+ * the same search — with balances or without — cannot disagree with it.
25
+ */
26
+ matchesSearch: MatchesSearch
20
27
  isTokensLoading: boolean
21
28
  isSearchLoading: boolean
22
29
  } => {
@@ -24,6 +31,7 @@ export const useDisplayedTokens = (
24
31
  allTokens,
25
32
  isLoading: isTokensLoading,
26
33
  isSearchLoading,
34
+ isAddressSearch,
27
35
  } = useTokens(formType, search, isAllNetworks ? undefined : selectedChainId)
28
36
 
29
37
  const pinnedTokens = usePinnedTokensStore((state) => state.pinnedTokens)
@@ -51,19 +59,25 @@ export const useDisplayedTokens = (
51
59
  return undefined
52
60
  }, [isAllNetworks, selectedChainId, pinnedTokens])
53
61
 
62
+ const matchesSearch = useMemo<MatchesSearch>(
63
+ () => createSearchMatcher(search, isAddressSearch),
64
+ [search, isAddressSearch]
65
+ )
66
+
54
67
  const displayedTokensList = useMemo(() => {
55
68
  const tokensByChain = isAllNetworks
56
69
  ? Object.values(allTokens ?? {}).flat()
57
70
  : selectedChainId
58
71
  ? allTokens?.[selectedChainId]
59
72
  : undefined
60
- return tokensByChain?.filter((t) => isSearchMatch(t, search)) ?? []
61
- }, [allTokens, isAllNetworks, selectedChainId, search])
73
+ return tokensByChain?.filter(matchesSearch) ?? []
74
+ }, [allTokens, isAllNetworks, selectedChainId, matchesSearch])
62
75
 
63
76
  return {
64
77
  allTokens,
65
78
  displayedTokensList,
66
79
  isPinnedToken,
80
+ matchesSearch,
67
81
  isTokensLoading,
68
82
  isSearchLoading,
69
83
  }
@@ -0,0 +1,106 @@
1
+ /** @vitest-environment happy-dom */
2
+
3
+ import type { ExtendedChain } from '@lifi/sdk'
4
+ import { act } from 'react'
5
+ import { createRoot, type Root } from 'react-dom/client'
6
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
7
+ import type { WidgetConfig } from '../types/widget.js'
8
+
9
+ const txHash = `0x${'ab'.repeat(32)}`
10
+
11
+ let explorerUrls: WidgetConfig['explorerUrls']
12
+ let blockExplorerUrls: string[] = []
13
+
14
+ const getChainById = (id: number) =>
15
+ ({
16
+ id,
17
+ chainType: 'EVM',
18
+ metamask: { blockExplorerUrls },
19
+ }) as unknown as ExtendedChain
20
+
21
+ vi.mock('../providers/WidgetProvider/WidgetProvider.js', () => ({
22
+ useWidgetConfig: () => ({ explorerUrls }),
23
+ }))
24
+
25
+ vi.mock('./useAvailableChains.js', () => ({
26
+ useAvailableChains: () => ({ getChainById }),
27
+ }))
28
+
29
+ const { useExplorer } = await import('./useExplorer.js')
30
+
31
+ let explorer: ReturnType<typeof useExplorer>
32
+
33
+ const ExplorerHandle = (): null => {
34
+ explorer = useExplorer()
35
+ return null
36
+ }
37
+
38
+ let root: Root
39
+
40
+ beforeEach(() => {
41
+ explorerUrls = undefined
42
+ blockExplorerUrls = ['https://etherscan.io/']
43
+ root = createRoot(document.createElement('div'))
44
+ })
45
+
46
+ afterEach(() => {
47
+ act(() => {
48
+ root.unmount()
49
+ })
50
+ })
51
+
52
+ const render = () => {
53
+ act(() => {
54
+ root.render(<ExplorerHandle />)
55
+ })
56
+ }
57
+
58
+ describe('useExplorer', () => {
59
+ it('builds links from an https explorer base', () => {
60
+ render()
61
+ expect(explorer.getAddressLink('0xabc', 1)).toBe(
62
+ 'https://etherscan.io/address/0xabc'
63
+ )
64
+ expect(explorer.getTransactionLink({ txHash, chain: 1 })).toBe(
65
+ `https://etherscan.io/tx/${txHash}`
66
+ )
67
+ })
68
+
69
+ it('returns no link when chain metadata carries a javascript: explorer base', () => {
70
+ blockExplorerUrls = ['javascript:alert(document.domain)//']
71
+ render()
72
+ expect(explorer.getAddressLink('0xabc', 1)).toBeUndefined()
73
+ expect(explorer.getTransactionLink({ txHash, chain: 1 })).toBeUndefined()
74
+ })
75
+
76
+ it('returns no link when the integrator config carries a javascript: explorer base', () => {
77
+ explorerUrls = {
78
+ 1: ['javascript:alert(document.domain)'],
79
+ internal: ['javascript:alert(document.domain)'],
80
+ }
81
+ render()
82
+ expect(explorer.getAddressLink('0xabc', 1)).toBeUndefined()
83
+ expect(explorer.getTransactionLink({ txHash })).toBeUndefined()
84
+ })
85
+
86
+ it('falls back to an https txLink when the hash link is rejected', () => {
87
+ blockExplorerUrls = ['javascript:alert(document.domain)//']
88
+ render()
89
+ expect(
90
+ explorer.getTransactionLink({
91
+ txHash,
92
+ txLink: 'https://scan.li.fi/tx/0xabc',
93
+ chain: 1,
94
+ })
95
+ ).toBe('https://scan.li.fi/tx/0xabc')
96
+ })
97
+
98
+ it('rejects a javascript: txLink fallback', () => {
99
+ render()
100
+ expect(
101
+ explorer.getTransactionLink({
102
+ txLink: 'javascript:alert(document.domain)',
103
+ })
104
+ ).toBeUndefined()
105
+ })
106
+ })
@@ -4,6 +4,7 @@ import { useCallback } from 'react'
4
4
  import { internalExplorerUrl } from '../config/constants.js'
5
5
  import { useAvailableChains } from '../hooks/useAvailableChains.js'
6
6
  import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
7
+ import { isHttpUrl } from '../utils/url.js'
7
8
 
8
9
  const sanitiseBaseUrl = (baseUrl: string) => baseUrl.trim().replace(/\/+$/, '')
9
10
 
@@ -25,7 +26,10 @@ type TransactionLinkProps = {
25
26
 
26
27
  export const useExplorer = (): {
27
28
  getTransactionLink: (props: TransactionLinkProps) => string | undefined
28
- getAddressLink: (address: string, chain?: Chain | number) => string
29
+ getAddressLink: (
30
+ address: string,
31
+ chain?: Chain | number
32
+ ) => string | undefined
29
33
  } => {
30
34
  const { explorerUrls } = useWidgetConfig()
31
35
  const { getChainById } = useAvailableChains()
@@ -82,11 +86,12 @@ export const useExplorer = (): {
82
86
  config.resolvedChain?.chainType !== ChainType.EVM ||
83
87
  config.hasOverride ||
84
88
  isHex(txHash, { strict: true })
85
- if (validForEvm) {
86
- return `${config.url}/${config.txPath}/${txHash}`
89
+ const link = `${config.url}/${config.txPath}/${txHash}`
90
+ if (validForEvm && isHttpUrl(link)) {
91
+ return link
87
92
  }
88
93
  }
89
- return txLink
94
+ return isHttpUrl(txLink) ? txLink : undefined
90
95
  },
91
96
  [getExplorerConfig]
92
97
  )
@@ -94,7 +99,8 @@ export const useExplorer = (): {
94
99
  const getAddressLink = useCallback(
95
100
  (address: string, chain?: Chain | number) => {
96
101
  const config = getExplorerConfig(chain)
97
- return `${config.url}/${config.addressPath}/${address}`
102
+ const link = `${config.url}/${config.addressPath}/${address}`
103
+ return isHttpUrl(link) ? link : undefined
98
104
  },
99
105
  [getExplorerConfig]
100
106
  )
@@ -0,0 +1,88 @@
1
+ import { useMemo } from 'react'
2
+ import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
3
+ import type { FormType } from '../stores/form/types.js'
4
+ import { useRecentTokensStore } from '../stores/recentTokens/RecentTokensStore.js'
5
+ import type { RecentTokenId } from '../stores/recentTokens/types.js'
6
+ import type { TokenAmount } from '../types/token.js'
7
+ import type { RecentTokensResult } from '../utils/recentTokens.js'
8
+ import { resolveRecentRows, spliceRecentRows } from '../utils/recentTokens.js'
9
+ import { useChains } from './useChains.js'
10
+
11
+ export interface UseRecentTokensOptions {
12
+ selectedChainId?: number
13
+ isAllNetworks?: boolean
14
+ search?: string
15
+ expanded: boolean
16
+ formType: FormType
17
+ isTokensLoading: boolean
18
+ nativeHoisted: boolean
19
+ }
20
+
21
+ export interface UseRecentTokensResult extends RecentTokensResult {
22
+ bandEntries: RecentTokenId[]
23
+ recentStartIndex: number
24
+ }
25
+
26
+ export const useRecentTokens = (
27
+ tokens: TokenAmount[],
28
+ {
29
+ selectedChainId,
30
+ isAllNetworks,
31
+ search,
32
+ expanded,
33
+ formType,
34
+ isTokensLoading,
35
+ nativeHoisted,
36
+ }: UseRecentTokensOptions
37
+ ): UseRecentTokensResult => {
38
+ const { hiddenUI, tokens: configTokens } = useWidgetConfig()
39
+ // useChains, not useAvailableChains: only it applies chains.allow/deny.
40
+ const { chains } = useChains(formType)
41
+ const recentTokens = useRecentTokensStore((state) => state.recentTokens)
42
+
43
+ const availableChainIds = useMemo(
44
+ () => new Set(chains?.map((chain) => chain.id)),
45
+ [chains]
46
+ )
47
+
48
+ // Untrimmed, like the list layout; wait for tokens (no amber flash) and chains.
49
+ const disabled =
50
+ !!search || !!hiddenUI?.recentSearches || isTokensLoading || !chains
51
+
52
+ const resolved = useMemo(
53
+ () =>
54
+ resolveRecentRows(tokens, {
55
+ recentTokens,
56
+ availableChainIds,
57
+ configTokens,
58
+ formType,
59
+ selectedChainId,
60
+ isAllNetworks,
61
+ nativeHoisted,
62
+ disabled,
63
+ }),
64
+ [
65
+ tokens,
66
+ recentTokens,
67
+ availableChainIds,
68
+ configTokens,
69
+ formType,
70
+ selectedChainId,
71
+ isAllNetworks,
72
+ nativeHoisted,
73
+ disabled,
74
+ ]
75
+ )
76
+
77
+ // Separate, so a Show more/less click redoes the slice but not the scan.
78
+ const spliced = useMemo(
79
+ () => spliceRecentRows(tokens, resolved, expanded),
80
+ [tokens, resolved, expanded]
81
+ )
82
+
83
+ return {
84
+ ...spliced,
85
+ bandEntries: resolved.bandEntries,
86
+ recentStartIndex: resolved.recentStartIndex,
87
+ }
88
+ }
@@ -29,7 +29,7 @@ import { defaultSlippage } from '../stores/settings/createSettingsStore.js'
29
29
  import { useSettings } from '../stores/settings/useSettings.js'
30
30
  import { WidgetEvent } from '../types/events.js'
31
31
  import type { TokensByChain } from '../types/token.js'
32
- import { isCustomReceiverUnsupported } from '../utils/customReceiver.js'
32
+ import { isCustomReceiverBlocked } from '../utils/customReceiver.js'
33
33
  import { getQueryKey } from '../utils/queries.js'
34
34
  import { updateTokenInCache } from '../utils/token.js'
35
35
  import { useChain } from './useChain.js'
@@ -178,10 +178,13 @@ export const useRoutes = ({
178
178
  ? hasToAddressAndChainTypeSatisfied
179
179
  : true
180
180
 
181
- // Stellar settles to the signer, so a set receiver makes the route unfulfillable.
182
- const customReceiverBlocked =
183
- isCustomReceiverUnsupported(fromChain?.chainType, toChain?.chainType) &&
184
- Boolean(toAddress)
181
+ // Not `effectiveFromAddress`: a quote placeholder must never match a receiver.
182
+ const customReceiverBlocked = isCustomReceiverBlocked({
183
+ fromChainType: fromChain?.chainType,
184
+ toChainType: toChain?.chainType,
185
+ toAddress,
186
+ signerAddress: account.address,
187
+ })
185
188
 
186
189
  // toAddress might be an empty string, but we need to pass undefined if there is no value
187
190
  const toWalletAddress = toAddress || undefined
@@ -4,7 +4,10 @@ import { useEthereumContext } from '@lifi/widget-provider'
4
4
  import { useChain } from '../hooks/useChain.js'
5
5
  import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
6
6
  import { useFieldValues } from '../stores/form/useFieldValues.js'
7
- import { isCustomReceiverUnsupported } from '../utils/customReceiver.js'
7
+ import {
8
+ isCustomReceiverBlocked,
9
+ isCustomReceiverUnsupported,
10
+ } from '../utils/customReceiver.js'
8
11
  import { useIsContractAddress } from './useIsContractAddress.js'
9
12
 
10
13
  export const useToAddressRequirements = (
@@ -76,10 +79,13 @@ export const useToAddressRequirements = (
76
79
  toChain?.chainType
77
80
  )
78
81
 
79
- // Stellar routes settle to the signer, so a required receiver is never honoured.
80
- const unsupportedReceiverBlocking = Boolean(
81
- unsupportedToAddress && (toAddress || requiredUI?.toAddress)
82
- )
82
+ const unsupportedReceiverBlocking = isCustomReceiverBlocked({
83
+ fromChainType: fromChain?.chainType,
84
+ toChainType: toChain?.chainType,
85
+ toAddress,
86
+ signerAddress: fromAddress,
87
+ receiverRequired: requiredUI?.toAddress,
88
+ })
83
89
 
84
90
  const requiredToAddress = Boolean(
85
91
  (isDifferentChainType ||