@lifi/widget 4.6.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 (394) 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 +1 -2
  148. package/dist/esm/hooks/useExplorer.d.ts.map +1 -1
  149. package/dist/esm/hooks/useHeader.d.ts +1 -2
  150. package/dist/esm/hooks/useHeader.d.ts.map +1 -1
  151. package/dist/esm/hooks/useListHeight.d.ts +1 -2
  152. package/dist/esm/hooks/useListHeight.d.ts.map +1 -1
  153. package/dist/esm/hooks/useNavigateBack.d.ts +1 -2
  154. package/dist/esm/hooks/useNavigateBack.d.ts.map +1 -1
  155. package/dist/esm/hooks/useRecentTokens.js +46 -0
  156. package/dist/esm/hooks/useRecentTokens.js.map +1 -0
  157. package/dist/esm/hooks/useRouteExecution.d.ts +1 -2
  158. package/dist/esm/hooks/useRouteExecution.d.ts.map +1 -1
  159. package/dist/esm/hooks/useRouteExecutionMessage.d.ts +1 -2
  160. package/dist/esm/hooks/useRouteExecutionMessage.d.ts.map +1 -1
  161. package/dist/esm/hooks/useRoutes.d.ts +1 -2
  162. package/dist/esm/hooks/useRoutes.d.ts.map +1 -1
  163. package/dist/esm/hooks/useRoutes.js +7 -2
  164. package/dist/esm/hooks/useRoutes.js.map +1 -1
  165. package/dist/esm/hooks/useToAddressRequirements.d.ts +1 -2
  166. package/dist/esm/hooks/useToAddressRequirements.d.ts.map +1 -1
  167. package/dist/esm/hooks/useToAddressRequirements.js +8 -2
  168. package/dist/esm/hooks/useToAddressRequirements.js.map +1 -1
  169. package/dist/esm/hooks/useToken.d.ts +1 -2
  170. package/dist/esm/hooks/useToken.d.ts.map +1 -1
  171. package/dist/esm/hooks/useTokenAddressBalance.d.ts +1 -2
  172. package/dist/esm/hooks/useTokenAddressBalance.d.ts.map +1 -1
  173. package/dist/esm/hooks/useTokenBalances.d.ts +3 -2
  174. package/dist/esm/hooks/useTokenBalances.d.ts.map +1 -1
  175. package/dist/esm/hooks/useTokenBalances.js +15 -9
  176. package/dist/esm/hooks/useTokenBalances.js.map +1 -1
  177. package/dist/esm/hooks/useTokenList.d.ts +1 -2
  178. package/dist/esm/hooks/useTokenList.d.ts.map +1 -1
  179. package/dist/esm/hooks/useTokenSearch.js +3 -2
  180. package/dist/esm/hooks/useTokenSearch.js.map +1 -1
  181. package/dist/esm/hooks/useTokens.js +26 -15
  182. package/dist/esm/hooks/useTokens.js.map +1 -1
  183. package/dist/esm/hooks/useTools.d.ts +1 -2
  184. package/dist/esm/hooks/useTools.d.ts.map +1 -1
  185. package/dist/esm/hooks/useWidgetChains.d.ts +1 -2
  186. package/dist/esm/hooks/useWidgetChains.d.ts.map +1 -1
  187. package/dist/esm/hooks/useWidgetEvents.d.ts +3 -4
  188. package/dist/esm/hooks/useWidgetEvents.d.ts.map +1 -1
  189. package/dist/esm/i18n/bn.json +4 -0
  190. package/dist/esm/i18n/de.json +4 -0
  191. package/dist/esm/i18n/en.json +4 -0
  192. package/dist/esm/i18n/es.json +4 -0
  193. package/dist/esm/i18n/fr.json +4 -0
  194. package/dist/esm/i18n/hi.json +4 -0
  195. package/dist/esm/i18n/id.json +4 -0
  196. package/dist/esm/i18n/it.json +4 -0
  197. package/dist/esm/i18n/ja.json +4 -0
  198. package/dist/esm/i18n/ko.json +4 -0
  199. package/dist/esm/i18n/pl.json +4 -0
  200. package/dist/esm/i18n/pt.json +4 -0
  201. package/dist/esm/i18n/th.json +4 -0
  202. package/dist/esm/i18n/tr.json +4 -0
  203. package/dist/esm/i18n/uk.json +4 -0
  204. package/dist/esm/i18n/vi.json +4 -0
  205. package/dist/esm/i18n/zh.json +4 -0
  206. package/dist/esm/pages/MainPage/MainWarningMessages.d.ts +1 -2
  207. package/dist/esm/pages/MainPage/MainWarningMessages.d.ts.map +1 -1
  208. package/dist/esm/pages/SelectChainPage/SelectChainPage.d.ts +1 -2
  209. package/dist/esm/pages/SelectChainPage/SelectChainPage.d.ts.map +1 -1
  210. package/dist/esm/pages/SelectChainPage/types.d.ts +1 -2
  211. package/dist/esm/pages/SelectChainPage/types.d.ts.map +1 -1
  212. package/dist/esm/pages/SelectTokenPage/SearchTokenInput.d.ts +1 -2
  213. package/dist/esm/pages/SelectTokenPage/SearchTokenInput.d.ts.map +1 -1
  214. package/dist/esm/pages/TransactionDetailsPage/ContactSupportButton.d.ts +1 -2
  215. package/dist/esm/pages/TransactionDetailsPage/ContactSupportButton.d.ts.map +1 -1
  216. package/dist/esm/pages/TransactionPage/ConfirmToAddressSheet.d.ts +1 -2
  217. package/dist/esm/pages/TransactionPage/ConfirmToAddressSheet.d.ts.map +1 -1
  218. package/dist/esm/pages/TransactionPage/ExchangeRateBottomSheet.d.ts +2 -3
  219. package/dist/esm/pages/TransactionPage/ExchangeRateBottomSheet.d.ts.map +1 -1
  220. package/dist/esm/pages/TransactionPage/ExecutionDoneCard.d.ts +1 -2
  221. package/dist/esm/pages/TransactionPage/ExecutionDoneCard.d.ts.map +1 -1
  222. package/dist/esm/pages/TransactionPage/RouteTracker.d.ts +1 -2
  223. package/dist/esm/pages/TransactionPage/RouteTracker.d.ts.map +1 -1
  224. package/dist/esm/pages/TransactionPage/StartTransactionButton.d.ts +1 -2
  225. package/dist/esm/pages/TransactionPage/StartTransactionButton.d.ts.map +1 -1
  226. package/dist/esm/pages/TransactionPage/TokenValueBottomSheet.d.ts +1 -2
  227. package/dist/esm/pages/TransactionPage/TokenValueBottomSheet.d.ts.map +1 -1
  228. package/dist/esm/pages/TransactionPage/TokenValueBottomSheet.js +0 -1
  229. package/dist/esm/pages/TransactionPage/TokenValueBottomSheet.js.map +1 -1
  230. package/dist/esm/pages/TransactionPage/TokenVerificationBottomSheet.js +0 -1
  231. package/dist/esm/pages/TransactionPage/TokenVerificationBottomSheet.js.map +1 -1
  232. package/dist/esm/pages/TransactionPage/TransactionDoneButtons.d.ts +1 -2
  233. package/dist/esm/pages/TransactionPage/TransactionDoneButtons.d.ts.map +1 -1
  234. package/dist/esm/pages/TransactionPage/TransactionFailedButtons.js +38 -12
  235. package/dist/esm/pages/TransactionPage/TransactionFailedButtons.js.map +1 -1
  236. package/dist/esm/pages/TransactionPage/TransactionReview.js +22 -21
  237. package/dist/esm/pages/TransactionPage/TransactionReview.js.map +1 -1
  238. package/dist/esm/pages/TransactionPage/types.d.ts +1 -2
  239. package/dist/esm/pages/TransactionPage/types.d.ts.map +1 -1
  240. package/dist/esm/pages/TransactionPage/utils.d.ts +2 -3
  241. package/dist/esm/pages/TransactionPage/utils.d.ts.map +1 -1
  242. package/dist/esm/pages/TransactionPage/utils.js +23 -1
  243. package/dist/esm/pages/TransactionPage/utils.js.map +1 -1
  244. package/dist/esm/providers/I18nProvider/I18nProvider.d.ts +1 -2
  245. package/dist/esm/providers/I18nProvider/I18nProvider.d.ts.map +1 -1
  246. package/dist/esm/providers/I18nProvider/types.d.ts +4 -5
  247. package/dist/esm/providers/I18nProvider/types.d.ts.map +1 -1
  248. package/dist/esm/providers/QueryClientProvider.d.ts +1 -2
  249. package/dist/esm/providers/QueryClientProvider.d.ts.map +1 -1
  250. package/dist/esm/providers/SDKClientProvider.d.ts +2 -3
  251. package/dist/esm/providers/SDKClientProvider.d.ts.map +1 -1
  252. package/dist/esm/providers/WalletProvider/WalletProvider.d.ts +1 -2
  253. package/dist/esm/providers/WalletProvider/WalletProvider.d.ts.map +1 -1
  254. package/dist/esm/providers/WidgetProvider/WidgetProvider.d.ts +2 -3
  255. package/dist/esm/providers/WidgetProvider/WidgetProvider.d.ts.map +1 -1
  256. package/dist/esm/providers/WidgetProvider/types.d.ts +2 -3
  257. package/dist/esm/providers/WidgetProvider/types.d.ts.map +1 -1
  258. package/dist/esm/stores/StoreProvider.d.ts +1 -2
  259. package/dist/esm/stores/StoreProvider.d.ts.map +1 -1
  260. package/dist/esm/stores/StoreProvider.js +10 -6
  261. package/dist/esm/stores/StoreProvider.js.map +1 -1
  262. package/dist/esm/stores/bookmarks/BookmarkStore.d.ts +1 -2
  263. package/dist/esm/stores/bookmarks/BookmarkStore.d.ts.map +1 -1
  264. package/dist/esm/stores/bookmarks/types.d.ts +5 -6
  265. package/dist/esm/stores/bookmarks/types.d.ts.map +1 -1
  266. package/dist/esm/stores/chains/ChainOrderStore.d.ts +1 -2
  267. package/dist/esm/stores/chains/ChainOrderStore.d.ts.map +1 -1
  268. package/dist/esm/stores/chains/ChainOrderStore.js +11 -5
  269. package/dist/esm/stores/chains/ChainOrderStore.js.map +1 -1
  270. package/dist/esm/stores/chains/types.d.ts +1 -2
  271. package/dist/esm/stores/chains/types.d.ts.map +1 -1
  272. package/dist/esm/stores/form/types.d.ts +20 -21
  273. package/dist/esm/stores/form/types.d.ts.map +1 -1
  274. package/dist/esm/stores/form/useFieldActions.d.ts +1 -2
  275. package/dist/esm/stores/form/useFieldActions.d.ts.map +1 -1
  276. package/dist/esm/stores/form/useFieldValues.d.ts +1 -2
  277. package/dist/esm/stores/form/useFieldValues.d.ts.map +1 -1
  278. package/dist/esm/stores/header/types.d.ts +1 -2
  279. package/dist/esm/stores/header/types.d.ts.map +1 -1
  280. package/dist/esm/stores/header/useHeaderStore.d.ts +2 -3
  281. package/dist/esm/stores/header/useHeaderStore.d.ts.map +1 -1
  282. package/dist/esm/stores/inputMode/useInputModeStore.d.ts +1 -2
  283. package/dist/esm/stores/inputMode/useInputModeStore.d.ts.map +1 -1
  284. package/dist/esm/stores/recentTokens/RecentTokensStore.js +23 -0
  285. package/dist/esm/stores/recentTokens/RecentTokensStore.js.map +1 -0
  286. package/dist/esm/stores/recentTokens/createRecentTokensStore.js +54 -0
  287. package/dist/esm/stores/recentTokens/createRecentTokensStore.js.map +1 -0
  288. package/dist/esm/stores/recentTokens/utils.js +15 -0
  289. package/dist/esm/stores/recentTokens/utils.js.map +1 -0
  290. package/dist/esm/stores/routes/RouteExecutionStore.d.ts +2 -3
  291. package/dist/esm/stores/routes/RouteExecutionStore.d.ts.map +1 -1
  292. package/dist/esm/stores/routes/types.d.ts +3 -4
  293. package/dist/esm/stores/routes/types.d.ts.map +1 -1
  294. package/dist/esm/stores/routes/utils.d.ts +4 -5
  295. package/dist/esm/stores/routes/utils.d.ts.map +1 -1
  296. package/dist/esm/stores/settings/SettingsStore.d.ts +2 -3
  297. package/dist/esm/stores/settings/SettingsStore.d.ts.map +1 -1
  298. package/dist/esm/stores/settings/types.d.ts +2 -3
  299. package/dist/esm/stores/settings/types.d.ts.map +1 -1
  300. package/dist/esm/stores/types.d.ts +2 -3
  301. package/dist/esm/stores/types.d.ts.map +1 -1
  302. package/dist/esm/themes/azureLight.d.ts +1 -2
  303. package/dist/esm/themes/azureLight.d.ts.map +1 -1
  304. package/dist/esm/themes/jumper.d.ts +1 -2
  305. package/dist/esm/themes/jumper.d.ts.map +1 -1
  306. package/dist/esm/themes/palettes.d.ts +3 -4
  307. package/dist/esm/themes/palettes.d.ts.map +1 -1
  308. package/dist/esm/themes/types.d.ts +1 -0
  309. package/dist/esm/themes/watermelonLight.d.ts +1 -2
  310. package/dist/esm/themes/watermelonLight.d.ts.map +1 -1
  311. package/dist/esm/themes/windows95.d.ts +1 -2
  312. package/dist/esm/themes/windows95.d.ts.map +1 -1
  313. package/dist/esm/types/events.d.ts +13 -14
  314. package/dist/esm/types/events.d.ts.map +1 -1
  315. package/dist/esm/types/token.d.ts +4 -2
  316. package/dist/esm/types/token.d.ts.map +1 -1
  317. package/dist/esm/types/widget.d.ts +55 -51
  318. package/dist/esm/types/widget.d.ts.map +1 -1
  319. package/dist/esm/types/widget.js.map +1 -1
  320. package/dist/esm/utils/address.js +19 -0
  321. package/dist/esm/utils/address.js.map +1 -0
  322. package/dist/esm/utils/compactNumberFormatter.d.ts +1 -2
  323. package/dist/esm/utils/compactNumberFormatter.d.ts.map +1 -1
  324. package/dist/esm/utils/converters.d.ts +1 -2
  325. package/dist/esm/utils/converters.d.ts.map +1 -1
  326. package/dist/esm/utils/currencyExtendedFormatter.d.ts +1 -2
  327. package/dist/esm/utils/currencyExtendedFormatter.d.ts.map +1 -1
  328. package/dist/esm/utils/customReceiver.js +6 -1
  329. package/dist/esm/utils/customReceiver.js.map +1 -1
  330. package/dist/esm/utils/elements.d.ts +2 -3
  331. package/dist/esm/utils/elements.d.ts.map +1 -1
  332. package/dist/esm/utils/enum.d.ts +1 -2
  333. package/dist/esm/utils/enum.d.ts.map +1 -1
  334. package/dist/esm/utils/fees.d.ts +2 -3
  335. package/dist/esm/utils/fees.d.ts.map +1 -1
  336. package/dist/esm/utils/format.d.ts +8 -9
  337. package/dist/esm/utils/format.d.ts.map +1 -1
  338. package/dist/esm/utils/getPriceImpact.d.ts +1 -2
  339. package/dist/esm/utils/getPriceImpact.d.ts.map +1 -1
  340. package/dist/esm/utils/input.d.ts +1 -2
  341. package/dist/esm/utils/input.d.ts.map +1 -1
  342. package/dist/esm/utils/navigationRoutes.d.ts +2 -3
  343. package/dist/esm/utils/navigationRoutes.d.ts.map +1 -1
  344. package/dist/esm/utils/percentFormatter.d.ts +1 -2
  345. package/dist/esm/utils/percentFormatter.d.ts.map +1 -1
  346. package/dist/esm/utils/recentTokens.js +97 -0
  347. package/dist/esm/utils/recentTokens.js.map +1 -0
  348. package/dist/esm/utils/token.js +30 -4
  349. package/dist/esm/utils/token.js.map +1 -1
  350. package/dist/esm/utils/tokenList.js +18 -6
  351. package/dist/esm/utils/tokenList.js.map +1 -1
  352. package/dist/esm/utils/tokenListBands.js +85 -0
  353. package/dist/esm/utils/tokenListBands.js.map +1 -0
  354. package/dist/esm/utils/wallet.d.ts +1 -2
  355. package/dist/esm/utils/wallet.d.ts.map +1 -1
  356. package/package.json +9 -9
  357. package/src/components/AmountInputCard/ReceiveAmountCard.tsx +16 -3
  358. package/src/components/BottomSheet/BottomSheet.tsx +3 -0
  359. package/src/components/Token/Token.tsx +2 -1
  360. package/src/components/TokenList/RecentTokensHeader.tsx +52 -0
  361. package/src/components/TokenList/RecentTokensToggle.tsx +45 -0
  362. package/src/components/TokenList/RemoveRecentTokenButton.tsx +38 -0
  363. package/src/components/TokenList/TokenList.tsx +92 -3
  364. package/src/components/TokenList/TokenListItem.tsx +15 -0
  365. package/src/components/TokenList/VirtualizedTokenList.tsx +84 -125
  366. package/src/components/TokenList/types.ts +9 -0
  367. package/src/config/version.ts +1 -1
  368. package/src/hooks/useDisplayedTokens.ts +17 -3
  369. package/src/hooks/useRecentTokens.ts +88 -0
  370. package/src/hooks/useRoutes.ts +8 -5
  371. package/src/hooks/useToAddressRequirements.ts +11 -5
  372. package/src/hooks/useTokenBalances.ts +34 -31
  373. package/src/hooks/useTokenSearch.ts +5 -14
  374. package/src/hooks/useTokens.ts +69 -27
  375. package/src/pages/TransactionPage/TokenValueBottomSheet.tsx +1 -1
  376. package/src/pages/TransactionPage/TokenVerificationBottomSheet.tsx +1 -1
  377. package/src/pages/TransactionPage/TransactionFailedButtons.tsx +62 -26
  378. package/src/pages/TransactionPage/TransactionReview.tsx +36 -37
  379. package/src/pages/TransactionPage/utils.ts +78 -0
  380. package/src/stores/StoreProvider.tsx +10 -7
  381. package/src/stores/chains/ChainOrderStore.test.tsx +298 -0
  382. package/src/stores/chains/ChainOrderStore.tsx +25 -6
  383. package/src/stores/recentTokens/RecentTokensStore.tsx +39 -0
  384. package/src/stores/recentTokens/createRecentTokensStore.ts +107 -0
  385. package/src/stores/recentTokens/types.ts +37 -0
  386. package/src/stores/recentTokens/utils.ts +18 -0
  387. package/src/types/token.ts +2 -0
  388. package/src/types/widget.ts +5 -0
  389. package/src/utils/address.ts +21 -0
  390. package/src/utils/customReceiver.ts +24 -0
  391. package/src/utils/recentTokens.ts +187 -0
  392. package/src/utils/token.ts +69 -10
  393. package/src/utils/tokenList.ts +41 -23
  394. package/src/utils/tokenListBands.ts +181 -0
@@ -0,0 +1,298 @@
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
+
8
+ /**
9
+ * Regression cover for JUM-1541.
10
+ *
11
+ * Mock identities must stay stable. In production this effect's dependencies
12
+ * are config values, so it does not re-run when a form field changes; mocks
13
+ * that rebuild objects each render would re-run it and prove behaviour the
14
+ * widget does not have.
15
+ */
16
+
17
+ const ETHEREUM = 1
18
+ const ARBITRUM = 42161
19
+ const POLYGON = 137
20
+ const SOLANA = 1151111081099710
21
+
22
+ const chain = (id: number): ExtendedChain =>
23
+ ({ id, name: `Chain ${id}`, chainType: 'EVM' }) as unknown as ExtendedChain
24
+
25
+ let chainsConfig: { from?: { allow: number[] } } | undefined
26
+ let availableChains: ExtendedChain[] = []
27
+ let swapOnly = false
28
+ let fromChainConfig: number | undefined
29
+
30
+ const formValues = new Map<string, unknown>()
31
+ const touchedFields = new Set<string>()
32
+ const setFieldValue = vi.fn(
33
+ (key: string, value: unknown, options?: { isTouched?: boolean }) => {
34
+ formValues.set(key, value)
35
+ if (options?.isTouched === true) {
36
+ touchedFields.add(key)
37
+ } else if (options?.isTouched === false) {
38
+ touchedFields.delete(key)
39
+ }
40
+ }
41
+ )
42
+
43
+ vi.mock('../../hooks/useChains.js', () => ({
44
+ useChains: () => ({ chains: availableChains }),
45
+ }))
46
+
47
+ vi.mock('../../hooks/useSwapOnly.js', () => ({
48
+ useSwapOnly: () => swapOnly,
49
+ }))
50
+
51
+ // Stable identities, as production has them: `useExternalWalletProvider`
52
+ // memoizes and `useFieldActions` selects store methods through `useShallow`.
53
+ // Returning fresh objects here would put a new value in the effect's
54
+ // dependency array on every render and re-run it — which production does not
55
+ // do, and which would make these tests prove something the widget does not.
56
+ const externalWalletProvider = {
57
+ externalChainTypes: [] as string[],
58
+ useExternalWalletProvidersOnly: false,
59
+ }
60
+ vi.mock('../../providers/WalletProvider/useExternalWalletProvider.js', () => ({
61
+ useExternalWalletProvider: () => externalWalletProvider,
62
+ }))
63
+
64
+ vi.mock('../../providers/WidgetProvider/WidgetProvider.js', () => ({
65
+ useWidgetConfig: () => ({
66
+ chains: chainsConfig,
67
+ hiddenUI: undefined,
68
+ fromChain: fromChainConfig,
69
+ toChain: undefined,
70
+ buildUrl: true,
71
+ variant: 'compact',
72
+ }),
73
+ }))
74
+
75
+ const getFieldValues = (key: string) => [formValues.get(key)]
76
+ const isTouched = (key: string) => touchedFields.has(key)
77
+ const fieldActions = { setFieldValue, getFieldValues, isTouched }
78
+ vi.mock('../form/useFieldActions.js', () => ({
79
+ useFieldActions: () => fieldActions,
80
+ }))
81
+
82
+ const { ChainOrderStoreProvider, useChainOrderStore } = await import(
83
+ './ChainOrderStore.js'
84
+ )
85
+
86
+ /** What `selectAllNetworks` does to the form: clear the field, as touched. */
87
+ const clickAllNetworks = (key: 'from' | 'to') => {
88
+ formValues.set(`${key}Chain`, '')
89
+ touchedFields.add(`${key}Chain`)
90
+ }
91
+
92
+ let fromIsAllNetworks: boolean | undefined
93
+
94
+ const StoreHandle = (): null => {
95
+ fromIsAllNetworks = useChainOrderStore((state) => state.fromIsAllNetworks)
96
+ return null
97
+ }
98
+
99
+ let root: Root
100
+
101
+ const render = () => {
102
+ act(() => {
103
+ root.render(
104
+ <ChainOrderStoreProvider namePrefix="test">
105
+ <StoreHandle />
106
+ </ChainOrderStoreProvider>
107
+ )
108
+ })
109
+ }
110
+
111
+ const fromChainWrites = () =>
112
+ setFieldValue.mock.calls.filter(([key]) => key === 'fromChain')
113
+
114
+ const toChainWrites = () =>
115
+ setFieldValue.mock.calls.filter(([key]) => key === 'toChain')
116
+
117
+ /** An integrator that derives `chains` from form state rebuilds it on every write. */
118
+ const rebuildHostConfig = () => {
119
+ chainsConfig = { ...chainsConfig }
120
+ render()
121
+ }
122
+
123
+ beforeEach(() => {
124
+ ;(
125
+ globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT: boolean }
126
+ ).IS_REACT_ACT_ENVIRONMENT = true
127
+ window.history.replaceState({}, '', '/')
128
+ window.localStorage.clear()
129
+ formValues.clear()
130
+ touchedFields.clear()
131
+ setFieldValue.mockClear()
132
+ chainsConfig = undefined
133
+ swapOnly = false
134
+ fromChainConfig = undefined
135
+ availableChains = [chain(ETHEREUM), chain(ARBITRUM)]
136
+ fromIsAllNetworks = undefined
137
+ root = createRoot(document.createElement('div'))
138
+ })
139
+
140
+ afterEach(() => {
141
+ act(() => root.unmount())
142
+ })
143
+
144
+ describe('ChainOrderStoreProvider', () => {
145
+ it('starts on "All networks" when nothing names a chain', () => {
146
+ render()
147
+
148
+ expect(fromIsAllNetworks).toBe(true)
149
+ expect(fromChainWrites()).toHaveLength(0)
150
+ })
151
+
152
+ it('starts off "All networks" when a deep link named a chain', () => {
153
+ // How the widget learns this when the config carries nothing.
154
+ window.history.replaceState({}, '', `/?fromChain=${ARBITRUM}`)
155
+ formValues.set('fromChain', ARBITRUM)
156
+ render()
157
+
158
+ expect(fromIsAllNetworks).toBe(false)
159
+ expect(fromChainWrites()).toHaveLength(0)
160
+ })
161
+
162
+ it('starts off "All networks" when the config names a chain', () => {
163
+ fromChainConfig = POLYGON
164
+ render()
165
+
166
+ expect(fromIsAllNetworks).toBe(false)
167
+ })
168
+
169
+ it('does not put a chain back after the user chooses "All networks"', () => {
170
+ // jumper-frontend seeds its config from the URL, so a deep link arrives
171
+ // as a config chain as well as a form value and a query param.
172
+ fromChainConfig = ARBITRUM
173
+ formValues.set('fromChain', ARBITRUM)
174
+ window.history.replaceState({}, '', `/?fromChain=${ARBITRUM}`)
175
+ render()
176
+ expect(fromIsAllNetworks).toBe(false)
177
+
178
+ clickAllNetworks('from')
179
+ setFieldValue.mockClear()
180
+
181
+ // jumper-frontend rebuilds `chains` from form state, so clearing the
182
+ // field re-runs the effect on that same commit — while the config and the
183
+ // query string still name Arbitrum.
184
+ chainsConfig = { ...chainsConfig }
185
+ render()
186
+
187
+ expect(fromIsAllNetworks).toBe(true)
188
+ expect(fromChainWrites()).toHaveLength(0)
189
+ })
190
+
191
+ it('fills an empty chain when the "All networks" tab is withdrawn', () => {
192
+ render()
193
+ expect(fromChainWrites()).toHaveLength(0)
194
+
195
+ // An allow-list of one chain withdraws the tab, so this side needs a
196
+ // fallback.
197
+ chainsConfig = { from: { allow: [ARBITRUM] } }
198
+ setFieldValue.mockClear()
199
+ render()
200
+
201
+ expect(fromIsAllNetworks).toBe(false)
202
+ expect(fromChainWrites()).toHaveLength(1)
203
+ expect(fromChainWrites()[0][1]).toBe(ARBITRUM)
204
+ })
205
+ })
206
+
207
+ describe('ChainOrderStoreProvider in swap-only modes', () => {
208
+ beforeEach(() => {
209
+ swapOnly = true
210
+ })
211
+
212
+ const pickSource = (chainId: number) => {
213
+ window.history.replaceState({}, '', `/?fromChain=${chainId}`)
214
+ formValues.set('fromChain', chainId)
215
+ }
216
+
217
+ // The destination used to flip on every host rebuild until React threw #185.
218
+ it('does not alternate the destination while the source is on "All networks"', () => {
219
+ const destinations: unknown[] = []
220
+ render()
221
+ destinations.push(formValues.get('toChain'))
222
+ for (let rebuild = 0; rebuild < 4; rebuild++) {
223
+ rebuildHostConfig()
224
+ destinations.push(formValues.get('toChain'))
225
+ }
226
+
227
+ expect(fromIsAllNetworks).toBe(true)
228
+ expect(destinations).toEqual(Array(5).fill(ETHEREUM))
229
+ expect(toChainWrites()).toHaveLength(1)
230
+ })
231
+
232
+ it('fills an empty destination from the source, not from the chain order', () => {
233
+ pickSource(ARBITRUM)
234
+ render()
235
+
236
+ expect(formValues.get('toChain')).toBe(ARBITRUM)
237
+ })
238
+
239
+ it('moves a stale destination onto the source', () => {
240
+ pickSource(ARBITRUM)
241
+ formValues.set('toChain', ETHEREUM)
242
+ render()
243
+
244
+ expect(formValues.get('toChain')).toBe(ARBITRUM)
245
+ })
246
+
247
+ it('stops writing the destination once it matches the source', () => {
248
+ pickSource(ARBITRUM)
249
+ render()
250
+ expect(formValues.get('toChain')).toBe(ARBITRUM)
251
+ setFieldValue.mockClear()
252
+
253
+ rebuildHostConfig()
254
+ rebuildHostConfig()
255
+
256
+ expect(toChainWrites()).toHaveLength(0)
257
+ })
258
+
259
+ it('keeps the destination when the source goes back to "All networks"', () => {
260
+ pickSource(ARBITRUM)
261
+ render()
262
+ expect(formValues.get('toChain')).toBe(ARBITRUM)
263
+ clickAllNetworks('from')
264
+ setFieldValue.mockClear()
265
+
266
+ rebuildHostConfig()
267
+ rebuildHostConfig()
268
+
269
+ expect(formValues.get('toChain')).toBe(ARBITRUM)
270
+ expect(toChainWrites()).toHaveLength(0)
271
+ })
272
+
273
+ it('treats a zero source as no source', () => {
274
+ formValues.set('fromChain', 0)
275
+ render()
276
+ rebuildHostConfig()
277
+
278
+ expect(formValues.get('toChain')).toBe(ETHEREUM)
279
+ expect(toChainWrites()).toHaveLength(1)
280
+ })
281
+
282
+ // The list can shrink under a destination, e.g. when the integrator narrows `chains`.
283
+ it('replaces a destination the chain list does not offer while there is no source', () => {
284
+ swapOnly = false
285
+ availableChains = [chain(ETHEREUM), chain(ARBITRUM), chain(SOLANA)]
286
+ formValues.set('toChain', SOLANA)
287
+ render()
288
+ expect(formValues.get('toChain')).toBe(SOLANA)
289
+
290
+ swapOnly = true
291
+ availableChains = [chain(ETHEREUM), chain(ARBITRUM)]
292
+ render()
293
+ rebuildHostConfig()
294
+
295
+ expect(formValues.get('toChain')).toBe(ETHEREUM)
296
+ expect(toChainWrites()).toHaveLength(1)
297
+ })
298
+ })
@@ -30,7 +30,7 @@ export function ChainOrderStoreProvider({
30
30
  } = useWidgetConfig()
31
31
  const storeRef = useRef<ChainOrderStore>(null)
32
32
  const { chains } = useChains()
33
- const { setFieldValue, getFieldValues } = useFieldActions()
33
+ const { setFieldValue, getFieldValues, isTouched } = useFieldActions()
34
34
  const swapOnly = useSwapOnly()
35
35
  const { variant } = useWidgetConfig()
36
36
  const { externalChainTypes, useExternalWalletProvidersOnly } =
@@ -85,15 +85,30 @@ export function ChainOrderStoreProvider({
85
85
  key === 'from' ? urlValues.fromChain : urlValues.toChain
86
86
  const configChainValue =
87
87
  key === 'from' ? fromChainConfig : toChainConfig
88
+
89
+ // A touched, empty chain field was cleared deliberately — "All
90
+ // networks". It has to outrank the two values above, because both
91
+ // still name the chain the page was opened on at that moment: the
92
+ // query string is rewritten a commit later, and an integrator that
93
+ // seeds its config from the URL never rewrites it.
94
+ const [chainValue] = getFieldValues(`${key}Chain`)
95
+ const clearedForAllNetworks = !chainValue && isTouched(`${key}Chain`)
96
+
88
97
  const initialIsAllNetworks =
89
- showAllNetworks && !configChainValue && !urlChainValue
98
+ showAllNetworks &&
99
+ (clearedForAllNetworks || (!configChainValue && !urlChainValue))
90
100
  storeRef.current?.getState().setIsAllNetworks(initialIsAllNetworks, key)
91
101
  storeRef.current?.getState().setShowAllNetworks(showAllNetworks, key)
92
102
 
93
- // If swap only, set the to chain to the from chain
103
+ // Copying an empty source would alternate with the fallback below.
94
104
  if (isSwapTo) {
95
105
  const [fromChainValue] = getFieldValues('fromChain')
96
- setFieldValue('toChain', fromChainValue)
106
+ if (fromChainValue) {
107
+ if (fromChainValue !== chainValue) {
108
+ setFieldValue('toChain', fromChainValue)
109
+ }
110
+ return
111
+ }
97
112
  }
98
113
 
99
114
  // When "All Networks" is active, don't auto-select a chain from the
@@ -104,8 +119,11 @@ export function ChainOrderStoreProvider({
104
119
  return
105
120
  }
106
121
 
107
- const [chainValue] = getFieldValues(`${key}Chain`)
108
- if (chainValue) {
122
+ // With no source to follow, drop a destination the list no longer offers.
123
+ const keepsChain =
124
+ chainValue &&
125
+ (!isSwapTo || filteredChains.some((chain) => chain.id === chainValue))
126
+ if (keepsChain) {
109
127
  return
110
128
  }
111
129
 
@@ -130,6 +148,7 @@ export function ChainOrderStoreProvider({
130
148
  chainsConfig,
131
149
  externalChainTypes,
132
150
  getFieldValues,
151
+ isTouched,
133
152
  setFieldValue,
134
153
  useExternalWalletProvidersOnly,
135
154
  variant,
@@ -0,0 +1,39 @@
1
+ import { type Context, createContext, use, useRef } from 'react'
2
+ import { useShallow } from 'zustand/shallow'
3
+ import type { PersistStoreProviderProps } from '../types.js'
4
+ import { createRecentTokensStore } from './createRecentTokensStore.js'
5
+ import type { RecentTokensState, RecentTokensStore } from './types.js'
6
+
7
+ const RecentTokensStoreContext: Context<RecentTokensStore | null> =
8
+ createContext<RecentTokensStore | null>(null)
9
+
10
+ export const RecentTokensStoreProvider: React.FC<PersistStoreProviderProps> = ({
11
+ children,
12
+ ...props
13
+ }) => {
14
+ const storeRef = useRef<RecentTokensStore>(null)
15
+
16
+ if (!storeRef.current) {
17
+ storeRef.current = createRecentTokensStore(props)
18
+ }
19
+
20
+ return (
21
+ <RecentTokensStoreContext value={storeRef.current}>
22
+ {children}
23
+ </RecentTokensStoreContext>
24
+ )
25
+ }
26
+
27
+ export function useRecentTokensStore<T>(
28
+ selector: (store: RecentTokensState) => T
29
+ ): T {
30
+ const useStore = use(RecentTokensStoreContext)
31
+
32
+ if (!useStore) {
33
+ throw new Error(
34
+ `You forgot to wrap your component in <${RecentTokensStoreProvider.name}>.`
35
+ )
36
+ }
37
+
38
+ return useStore(useShallow(selector))
39
+ }
@@ -0,0 +1,107 @@
1
+ import type { StoreApi, UseBoundStore } from 'zustand'
2
+ import { create } from 'zustand'
3
+ import { persist } from 'zustand/middleware'
4
+ import type { PersistStoreProps } from '../types.js'
5
+ import type {
6
+ RecentToken,
7
+ RecentTokensProps,
8
+ RecentTokensState,
9
+ } from './types.js'
10
+ import { getTokenKey } from './utils.js'
11
+
12
+ const recentTokensLimit = 10
13
+
14
+ const isStoredRecentToken = (value: unknown): value is RecentToken => {
15
+ const token = value as Partial<RecentToken> | null
16
+ return (
17
+ typeof token === 'object' &&
18
+ token !== null &&
19
+ typeof token.chainId === 'number' &&
20
+ typeof token.address === 'string' &&
21
+ typeof token.symbol === 'string' &&
22
+ typeof token.name === 'string' &&
23
+ typeof token.decimals === 'number' &&
24
+ (token.logoURI === undefined || typeof token.logoURI === 'string') &&
25
+ (token.flagged === undefined || typeof token.flagged === 'boolean')
26
+ )
27
+ }
28
+
29
+ // The key lives on the integrator's origin, so its content is untrusted.
30
+ const sanitizeRecentTokens = (stored: unknown): RecentToken[] => {
31
+ if (!Array.isArray(stored)) {
32
+ return []
33
+ }
34
+ const seen = new Set<string>()
35
+ const recentTokens: RecentToken[] = []
36
+ for (const token of stored) {
37
+ if (!isStoredRecentToken(token)) {
38
+ continue
39
+ }
40
+ const key = getTokenKey(token.chainId, token.address)
41
+ if (!seen.has(key)) {
42
+ seen.add(key)
43
+ recentTokens.push(token)
44
+ }
45
+ }
46
+ return recentTokens.slice(0, recentTokensLimit)
47
+ }
48
+
49
+ export const createRecentTokensStore = ({
50
+ namePrefix,
51
+ }: PersistStoreProps): UseBoundStore<StoreApi<RecentTokensState>> =>
52
+ create<RecentTokensState>()(
53
+ persist(
54
+ (set, get) => ({
55
+ recentTokens: [],
56
+ addRecentToken: (token) => {
57
+ const key = getTokenKey(token.chainId, token.address)
58
+ set((state) => ({
59
+ recentTokens: [
60
+ token,
61
+ ...state.recentTokens.filter(
62
+ (t) => getTokenKey(t.chainId, t.address) !== key
63
+ ),
64
+ ].slice(0, recentTokensLimit),
65
+ }))
66
+ },
67
+ removeRecentToken: (chainId, address) => {
68
+ const key = getTokenKey(chainId, address)
69
+ set((state) => ({
70
+ recentTokens: state.recentTokens.filter(
71
+ (t) => getTokenKey(t.chainId, t.address) !== key
72
+ ),
73
+ }))
74
+ },
75
+ clearRecentTokens: (entries) =>
76
+ set((state) => {
77
+ const removed = new Set(
78
+ entries.map((e) => getTokenKey(e.chainId, e.address))
79
+ )
80
+ return {
81
+ recentTokens: state.recentTokens.filter(
82
+ (t) => !removed.has(getTokenKey(t.chainId, t.address))
83
+ ),
84
+ }
85
+ }),
86
+ isRecentToken: (chainId, address) => {
87
+ const key = getTokenKey(chainId, address)
88
+ return get().recentTokens.some(
89
+ (t) => getTokenKey(t.chainId, t.address) === key
90
+ )
91
+ },
92
+ }),
93
+ {
94
+ name: `${namePrefix || 'li.fi'}-recent-tokens`,
95
+ version: 0,
96
+ partialize: (state) => ({
97
+ recentTokens: state.recentTokens,
98
+ }),
99
+ merge: (persisted, current) => ({
100
+ ...current,
101
+ recentTokens: sanitizeRecentTokens(
102
+ (persisted as Partial<RecentTokensProps> | undefined)?.recentTokens
103
+ ),
104
+ }),
105
+ }
106
+ )
107
+ )
@@ -0,0 +1,37 @@
1
+ import type { StoreApi } from 'zustand'
2
+ import type { UseBoundStoreWithEqualityFn } from 'zustand/traditional'
3
+
4
+ export interface RecentToken {
5
+ chainId: number
6
+ /** The token's own casing. Compared case-insensitively with chainId. */
7
+ address: string
8
+ symbol: string
9
+ name: string
10
+ decimals: number
11
+ logoURI?: string
12
+ flagged?: boolean
13
+ }
14
+
15
+ /** Compared case-insensitively. */
16
+ export interface RecentTokenId {
17
+ chainId: number
18
+ address: string
19
+ }
20
+
21
+ export interface RecentTokensProps {
22
+ recentTokens: RecentToken[]
23
+ }
24
+
25
+ export interface RecentTokensActions {
26
+ addRecentToken: (token: RecentToken) => void
27
+ removeRecentToken: (chainId: number, address: string) => void
28
+ /** Removes exactly these entries, never one outside the band's scope. */
29
+ clearRecentTokens: (entries: RecentTokenId[]) => void
30
+ isRecentToken: (chainId: number, address: string) => boolean
31
+ }
32
+
33
+ export type RecentTokensState = RecentTokensProps & RecentTokensActions
34
+
35
+ export type RecentTokensStore = UseBoundStoreWithEqualityFn<
36
+ StoreApi<RecentTokensState>
37
+ >
@@ -0,0 +1,18 @@
1
+ import type { TokenAmount } from '../../types/token.js'
2
+ import type { RecentToken } from './types.js'
3
+
4
+ export const getTokenKey = (chainId: number, address: string): string =>
5
+ `${chainId}-${address.toLowerCase()}`
6
+
7
+ // No price, and no verdict but a flag: a stale "verified" would hide a warning.
8
+ export const toRecentToken = (token: TokenAmount): RecentToken => ({
9
+ // Plain-JS configs can pass a string chainId or null fields; hydration rejects both.
10
+ chainId: Number(token.chainId),
11
+ // Keep the casing: the form and the selected check compare it exactly.
12
+ address: token.address,
13
+ symbol: token.symbol,
14
+ name: token.name ?? '',
15
+ decimals: token.decimals,
16
+ logoURI: token.logoURI ?? undefined,
17
+ flagged: token.verificationStatus === 'flagged' || undefined,
18
+ })
@@ -8,6 +8,8 @@ export interface TokenFlags {
8
8
  featured?: boolean
9
9
  popular?: boolean
10
10
  pinned?: boolean
11
+ /** A copy promoted into the "Recent searches" band. */
12
+ recent?: boolean
11
13
  verified?: boolean
12
14
  native?: boolean
13
15
  /** Came from the main token list rather than from a search. */
@@ -156,6 +156,11 @@ export interface HiddenUIConfig {
156
156
  */
157
157
  lowAddressActivityConfirmation?: boolean
158
158
  poweredBy?: boolean
159
+ /**
160
+ * Hide the "Recent searches" section in the token select view.
161
+ * @default false
162
+ */
163
+ recentSearches?: boolean
159
164
  reverseTokensButton?: boolean
160
165
  routeCardPriceImpact?: boolean
161
166
  routeTokenDescription?: boolean
@@ -0,0 +1,21 @@
1
+ import type { ChainType } from '@lifi/sdk'
2
+
3
+ /**
4
+ * True when a token search query is a contract address rather than free text.
5
+ * `getChainTypeFromTokenAddress` asks the configured providers' own
6
+ * `isTokenAddress` (see `useChainTypeFromAddress`), so it covers the token
7
+ * identifier of every ecosystem. Two consequences follow. `bitcoin` stays a
8
+ * name search, because the Bitcoin provider implements no token check. And an
9
+ * ecosystem is not detected unless the host configures its provider from
10
+ * `@lifi/sdk` 4.7.0 or later, even when its chains are listed.
11
+ */
12
+ export const isAddressQuery = (
13
+ query: string | undefined,
14
+ getChainTypeFromTokenAddress: (address: string) => ChainType | undefined
15
+ ): boolean => {
16
+ const value = query?.trim()
17
+ if (!value) {
18
+ return false
19
+ }
20
+ return getChainTypeFromTokenAddress(value) !== undefined
21
+ }
@@ -5,3 +5,27 @@ export const isCustomReceiverUnsupported = (
5
5
  fromChainType?: ChainType,
6
6
  toChainType?: ChainType
7
7
  ): boolean => fromChainType === ChainType.STL && toChainType === ChainType.STL
8
+
9
+ export interface CustomReceiverBlockedArgs {
10
+ fromChainType?: ChainType
11
+ toChainType?: ChainType
12
+ toAddress?: string
13
+ signerAddress?: string
14
+ receiverRequired?: boolean
15
+ }
16
+
17
+ export const isCustomReceiverBlocked = ({
18
+ fromChainType,
19
+ toChainType,
20
+ toAddress,
21
+ signerAddress,
22
+ receiverRequired,
23
+ }: CustomReceiverBlockedArgs): boolean => {
24
+ if (!isCustomReceiverUnsupported(fromChainType, toChainType)) {
25
+ return false
26
+ }
27
+ if (!toAddress) {
28
+ return Boolean(receiverRequired)
29
+ }
30
+ return toAddress.toLowerCase() !== signerAddress?.toLowerCase()
31
+ }