@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
@@ -1,12 +1,12 @@
1
1
  //#region src/utils/format.d.ts
2
- declare const usdDecimals = 2;
2
+ export declare const usdDecimals = 2;
3
3
  /**
4
4
  * Format token amount to at least 6 decimals.
5
5
  * @param amount amount to format.
6
6
  * @returns formatted amount.
7
7
  */
8
- declare function formatTokenAmount(amount: bigint | undefined, decimals: number): string;
9
- declare function formatSlippage(slippage?: string, defaultValue?: string, returnInitial?: boolean): string;
8
+ export declare function formatTokenAmount(amount: bigint | undefined, decimals: number): string;
9
+ export declare function formatSlippage(slippage?: string, defaultValue?: string, returnInitial?: boolean): string;
10
10
  /**
11
11
  * Formats a user input amount string, normalizing it and optionally limiting decimal places.
12
12
  * @param amount - The amount string to format (e.g., '123.45', '1,23', '0..')
@@ -16,14 +16,13 @@ declare function formatSlippage(slippage?: string, defaultValue?: string, return
16
16
  * format on blur (removes leading/trailing zeros, removes negative signs).
17
17
  * @returns The formatted amount string, or empty string for invalid input.
18
18
  */
19
- declare function formatInputAmount(amount: string, decimals?: number | null, returnInitial?: boolean): string;
20
- declare function formatTokenPrice(amount?: string | bigint, price?: string, decimals?: number): number;
19
+ export declare function formatInputAmount(amount: string, decimals?: number | null, returnInitial?: boolean): string;
20
+ export declare function formatTokenPrice(amount?: string | bigint, price?: string, decimals?: number): number;
21
21
  /**
22
22
  * Convert price value to token amount
23
23
  */
24
- declare function priceToTokenAmount(priceValue: string, priceUSD?: string): string;
25
- declare function formatDuration(seconds: number, locale: string, unitDisplay?: Intl.NumberFormatOptions["unitDisplay"]): string;
26
- declare function wrapLongWords(text: string): string;
24
+ export declare function priceToTokenAmount(priceValue: string, priceUSD?: string): string;
25
+ export declare function formatDuration(seconds: number, locale: string, unitDisplay?: Intl.NumberFormatOptions["unitDisplay"]): string;
26
+ export declare function wrapLongWords(text: string): string;
27
27
  //#endregion
28
- export { formatDuration, formatInputAmount, formatSlippage, formatTokenAmount, formatTokenPrice, priceToTokenAmount, usdDecimals, wrapLongWords };
29
28
  //# sourceMappingURL=format.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"format.d.ts","names":[],"sources":["../../../src/utils/format.ts"],"mappings":";cAEa;;;;;;iBAOG,kBACd,4BACA;iBAWc,eACd,mBACA,uBACA;;;;;;;;;;iBAiCc,kBACd,gBACA,0BACA;iBAqDc,iBACd,0BACA,gBACA;;;;iBA+Bc,mBACd,oBACA;iBA2Bc,eACd,iBACA,gBACA,cAAa,KAAK;iBAaJ,cAAc"}
1
+ {"version":3,"file":"format.d.ts","names":[],"sources":["../../../src/utils/format.ts"],"mappings":";qBAEa;;;;;;wBAOG,kBACd,4BACA;wBAWc,eACd,mBACA,uBACA;;;;;;;;;;wBAiCc,kBACd,gBACA,0BACA;wBAqDc,iBACd,0BACA,gBACA;;;;wBA+Bc,mBACd,oBACA;wBA2Bc,eACd,iBACA,gBACA,cAAa,KAAK;wBAaJ,cAAc"}
@@ -6,7 +6,6 @@ interface GetPriceImpactProps {
6
6
  fromAmount?: bigint;
7
7
  toAmount?: bigint;
8
8
  }
9
- declare const getPriceImpact: ({ fromToken, toToken, fromAmount, toAmount }: GetPriceImpactProps) => number;
9
+ export declare const getPriceImpact: ({ fromToken, toToken, fromAmount, toAmount }: GetPriceImpactProps) => number;
10
10
  //#endregion
11
- export { getPriceImpact };
12
11
  //# sourceMappingURL=getPriceImpact.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"getPriceImpact.d.ts","names":[],"sources":["../../../src/utils/getPriceImpact.ts"],"mappings":";;UAGU;EACR,WAAW;EACX,SAAS;EACT;EACA;;cAGW,mBACX,WACA,SACA,YACA,YACC"}
1
+ {"version":3,"file":"getPriceImpact.d.ts","names":[],"sources":["../../../src/utils/getPriceImpact.ts"],"mappings":";;UAGU;EACR,WAAW;EACX,SAAS;EACT;EACA;;qBAGW,mBACX,WACA,SACA,YACA,YACC"}
@@ -1,5 +1,4 @@
1
1
  //#region src/utils/input.d.ts
2
- declare const fitInputText: (maxFontSize: number, minFontSize: number, element: HTMLElement) => void;
2
+ export declare const fitInputText: (maxFontSize: number, minFontSize: number, element: HTMLElement) => void;
3
3
  //#endregion
4
- export { fitInputText };
5
4
  //# sourceMappingURL=input.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"input.d.ts","names":[],"sources":["../../../src/utils/input.ts"],"mappings":";cAAa,eACX,qBACA,qBACA,SAAS"}
1
+ {"version":3,"file":"input.d.ts","names":[],"sources":["../../../src/utils/input.ts"],"mappings":";qBAAa,eACX,qBACA,qBACA,SAAS"}
@@ -1,5 +1,5 @@
1
1
  //#region src/utils/navigationRoutes.d.ts
2
- declare const navigationRoutes: {
2
+ export declare const navigationRoutes: {
3
3
  home: string;
4
4
  activities: string;
5
5
  bridges: string;
@@ -23,7 +23,6 @@ declare const navigationRoutes: {
23
23
  configuredWallets: string;
24
24
  };
25
25
  type NavigationRouteTypeKeys = keyof typeof navigationRoutes;
26
- type NavigationRouteType = (typeof navigationRoutes)[NavigationRouteTypeKeys];
26
+ export type NavigationRouteType = (typeof navigationRoutes)[NavigationRouteTypeKeys];
27
27
  //#endregion
28
- export { NavigationRouteType, navigationRoutes };
29
28
  //# sourceMappingURL=navigationRoutes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"navigationRoutes.d.ts","names":[],"sources":["../../../src/utils/navigationRoutes.ts"],"mappings":";cAAa;EACX;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;KAiDG,uCAAuC;KAEhC,8BACF,kBAAkB"}
1
+ {"version":3,"file":"navigationRoutes.d.ts","names":[],"sources":["../../../src/utils/navigationRoutes.ts"],"mappings":";qBAAa;EACX;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;KAiDG,uCAAuC;YAEhC,8BACF,kBAAkB"}
@@ -5,7 +5,6 @@
5
5
  * +0.007% -> <0.01%
6
6
  * -0.003% -> <0.01%
7
7
  */
8
- declare const percentFormatter: (lng: string | undefined, options: any) => ((value: any) => string);
8
+ export declare const percentFormatter: (lng: string | undefined, options: any) => ((value: any) => string);
9
9
  //#endregion
10
- export { percentFormatter };
11
10
  //# sourceMappingURL=percentFormatter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"percentFormatter.d.ts","names":[],"sources":["../../../src/utils/percentFormatter.ts"],"mappings":";;;;;;;cAMa,mBACX,yBACA,mBACG"}
1
+ {"version":3,"file":"percentFormatter.d.ts","names":[],"sources":["../../../src/utils/percentFormatter.ts"],"mappings":";;;;;;;qBAMa,mBACX,yBACA,mBACG"}
@@ -0,0 +1,97 @@
1
+ import { isFormItemAllowed } from "./item.js";
2
+ import { getChainTokenAllowSets } from "./token.js";
3
+ import { getTokenKey } from "../stores/recentTokens/utils.js";
4
+ const toSnapshotRow = (recent) => ({
5
+ chainId: recent.chainId,
6
+ address: recent.address,
7
+ symbol: recent.symbol,
8
+ name: recent.name,
9
+ decimals: recent.decimals,
10
+ logoURI: recent.logoURI,
11
+ priceUSD: "",
12
+ verificationStatus: recent.flagged ? "flagged" : void 0
13
+ });
14
+ const isFeaturedCopy = (token) => !!token.featured && !token.amount;
15
+ const resolveRecentRows = (tokens, { recentTokens, availableChainIds, configTokens, formType, selectedChainId, isAllNetworks, nativeHoisted, disabled }) => {
16
+ let recentStartIndex = nativeHoisted ? 1 : 0;
17
+ while (tokens[recentStartIndex]?.pinned) recentStartIndex++;
18
+ const inactive = {
19
+ rows: [],
20
+ bandEntries: [],
21
+ recentStartIndex
22
+ };
23
+ if (disabled || !recentTokens.length || !tokens.length) return inactive;
24
+ const candidates = recentTokens.filter((recent) => (isAllNetworks || recent.chainId === selectedChainId) && availableChainIds.has(recent.chainId));
25
+ if (!candidates.length) return inactive;
26
+ const wanted = new Set(candidates.map((recent) => getTokenKey(recent.chainId, recent.address)));
27
+ const wantedChains = new Set(candidates.map((recent) => recent.chainId));
28
+ const fresh = /* @__PURE__ */ new Map();
29
+ let awaitingBalance = 0;
30
+ for (const token of tokens) {
31
+ if (!wantedChains.has(token.chainId)) continue;
32
+ const key = getTokenKey(token.chainId, token.address);
33
+ if (!wanted.has(key)) continue;
34
+ const found = fresh.get(key);
35
+ if (!found) {
36
+ fresh.set(key, token);
37
+ if (isFeaturedCopy(token)) awaitingBalance++;
38
+ } else if (token.amount && isFeaturedCopy(found)) {
39
+ fresh.set(key, {
40
+ ...found,
41
+ amount: token.amount,
42
+ priceUSD: token.priceUSD || found.priceUSD
43
+ });
44
+ awaitingBalance--;
45
+ }
46
+ if (fresh.size === wanted.size && !awaitingBalance) break;
47
+ }
48
+ const allowedByChain = /* @__PURE__ */ new Map();
49
+ const allowedFor = (chainId) => {
50
+ if (!allowedByChain.has(chainId)) allowedByChain.set(chainId, getChainTokenAllowSets(configTokens, chainId, formType));
51
+ return allowedByChain.get(chainId);
52
+ };
53
+ const hoistedKey = nativeHoisted ? getTokenKey(tokens[0].chainId, tokens[0].address) : void 0;
54
+ const rows = [];
55
+ const bandEntries = [];
56
+ for (const recent of candidates) {
57
+ const key = getTokenKey(recent.chainId, recent.address);
58
+ const resolved = fresh.get(key) ?? toSnapshotRow(recent);
59
+ if (!isFormItemAllowed(resolved, allowedFor(recent.chainId), formType, (t) => t.address.toLowerCase())) continue;
60
+ bandEntries.push({
61
+ chainId: recent.chainId,
62
+ address: recent.address
63
+ });
64
+ if (key === hoistedKey || resolved.pinned) continue;
65
+ rows.push({
66
+ ...resolved,
67
+ recent: true
68
+ });
69
+ }
70
+ if (!rows.length) return inactive;
71
+ return {
72
+ rows,
73
+ bandEntries,
74
+ recentStartIndex
75
+ };
76
+ };
77
+ const spliceRecentRows = (tokens, { rows, recentStartIndex }, expanded) => {
78
+ if (!rows.length) return {
79
+ tokens,
80
+ recentCount: 0,
81
+ totalRecentCount: 0
82
+ };
83
+ const visible = expanded ? rows : rows.slice(0, 4);
84
+ return {
85
+ tokens: [
86
+ ...tokens.slice(0, recentStartIndex),
87
+ ...visible,
88
+ ...tokens.slice(recentStartIndex)
89
+ ],
90
+ recentCount: visible.length,
91
+ totalRecentCount: rows.length
92
+ };
93
+ };
94
+ //#endregion
95
+ export { resolveRecentRows, spliceRecentRows };
96
+
97
+ //# sourceMappingURL=recentTokens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recentTokens.js","names":[],"sources":["../../../src/utils/recentTokens.ts"],"sourcesContent":["import type { FormType } from '../stores/form/types.js'\nimport type {\n RecentToken,\n RecentTokenId,\n} from '../stores/recentTokens/types.js'\nimport { getTokenKey } from '../stores/recentTokens/utils.js'\nimport type { TokenAmount } from '../types/token.js'\nimport type { AllowDenySets, WidgetTokens } from '../types/widget.js'\nimport { isFormItemAllowed } from './item.js'\nimport { getChainTokenAllowSets } from './token.js'\n\nexport const collapsedRecentCount = 4\n\nexport interface ResolveRecentRowsParams {\n recentTokens: RecentToken[]\n availableChainIds: Set<number>\n configTokens: WidgetTokens | undefined\n formType: FormType\n selectedChainId?: number\n isAllNetworks?: boolean\n nativeHoisted: boolean\n disabled: boolean\n}\n\nexport interface RecentRows {\n /** Unsliced; empty when the band is inactive. */\n rows: TokenAmount[]\n /** Everything Clear removes, including toggled-away and displaced rows. */\n bandEntries: RecentTokenId[]\n recentStartIndex: number\n}\n\nexport interface RecentTokensResult {\n tokens: TokenAmount[]\n recentCount: number\n totalRecentCount: number\n}\n\n// Whitelisted fields; a flag is the one verdict kept, as stale it only over-warns.\nconst toSnapshotRow = (recent: RecentToken): TokenAmount =>\n ({\n chainId: recent.chainId,\n address: recent.address,\n symbol: recent.symbol,\n name: recent.name,\n decimals: recent.decimals,\n logoURI: recent.logoURI,\n priceUSD: '',\n verificationStatus: recent.flagged ? 'flagged' : undefined,\n }) as TokenAmount\n\nconst isFeaturedCopy = (token: TokenAmount): boolean =>\n !!token.featured && !token.amount\n\nexport const resolveRecentRows = (\n tokens: TokenAmount[],\n {\n recentTokens,\n availableChainIds,\n configTokens,\n formType,\n selectedChainId,\n isAllNetworks,\n nativeHoisted,\n disabled,\n }: ResolveRecentRowsParams\n): RecentRows => {\n let recentStartIndex = nativeHoisted ? 1 : 0\n while (tokens[recentStartIndex]?.pinned) {\n recentStartIndex++\n }\n\n const inactive: RecentRows = { rows: [], bandEntries: [], recentStartIndex }\n\n if (disabled || !recentTokens.length || !tokens.length) {\n return inactive\n }\n\n const candidates = recentTokens.filter(\n (recent) =>\n (isAllNetworks || recent.chainId === selectedChainId) &&\n availableChainIds.has(recent.chainId)\n )\n\n if (!candidates.length) {\n return inactive\n }\n\n const wanted = new Set(\n candidates.map((recent) => getTokenKey(recent.chainId, recent.address))\n )\n // All-networks holds tens of thousands of rows: test the chain before the key.\n const wantedChains = new Set(candidates.map((recent) => recent.chainId))\n const fresh = new Map<string, TokenAmount>()\n // A featured config copy has no balance and sits above the row with one.\n let awaitingBalance = 0\n for (const token of tokens) {\n if (!wantedChains.has(token.chainId)) {\n continue\n }\n const key = getTokenKey(token.chainId, token.address)\n if (!wanted.has(key)) {\n continue\n }\n const found = fresh.get(key)\n if (!found) {\n fresh.set(key, token)\n if (isFeaturedCopy(token)) {\n awaitingBalance++\n }\n } else if (token.amount && isFeaturedCopy(found)) {\n fresh.set(key, {\n ...found,\n amount: token.amount,\n priceUSD: token.priceUSD || found.priceUSD,\n })\n awaitingBalance--\n }\n if (fresh.size === wanted.size && !awaitingBalance) {\n break\n }\n }\n\n const allowedByChain = new Map<number, AllowDenySets | undefined>()\n const allowedFor = (chainId: number) => {\n if (!allowedByChain.has(chainId)) {\n allowedByChain.set(\n chainId,\n getChainTokenAllowSets(configTokens, chainId, formType)\n )\n }\n return allowedByChain.get(chainId)\n }\n\n const hoistedKey = nativeHoisted\n ? getTokenKey(tokens[0].chainId, tokens[0].address)\n : undefined\n\n const rows: TokenAmount[] = []\n const bandEntries: RecentTokenId[] = []\n for (const recent of candidates) {\n const key = getTokenKey(recent.chainId, recent.address)\n const resolved = fresh.get(key) ?? toSnapshotRow(recent)\n if (\n !isFormItemAllowed(resolved, allowedFor(recent.chainId), formType, (t) =>\n t.address.toLowerCase()\n )\n ) {\n // Denied on this side only, so Clear must leave it for the other side.\n continue\n }\n // Clear owns displaced entries too, or they reappear once unpinned.\n bandEntries.push({ chainId: recent.chainId, address: recent.address })\n if (key === hoistedKey || resolved.pinned) {\n continue\n }\n rows.push({ ...resolved, recent: true })\n }\n\n if (!rows.length) {\n return inactive\n }\n\n return { rows, bandEntries, recentStartIndex }\n}\n\nexport const spliceRecentRows = (\n tokens: TokenAmount[],\n { rows, recentStartIndex }: RecentRows,\n expanded: boolean\n): RecentTokensResult => {\n if (!rows.length) {\n return { tokens, recentCount: 0, totalRecentCount: 0 }\n }\n\n const visible = expanded ? rows : rows.slice(0, collapsedRecentCount)\n\n return {\n tokens: [\n ...tokens.slice(0, recentStartIndex),\n ...visible,\n ...tokens.slice(recentStartIndex),\n ],\n recentCount: visible.length,\n totalRecentCount: rows.length,\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { getConfigItemSets, isFormItemAllowed } from "./item.js";
2
2
  import { ChainId } from "@lifi/sdk";
3
3
  //#region src/utils/token.ts
4
+ const getChainTokenAllowSets = (configTokens, chainId, formType) => getConfigItemSets(configTokens, (tokens) => new Set(tokens.filter((t) => Number(t.chainId) === chainId).map((t) => t.address.toLowerCase())), formType);
4
5
  /**
5
6
  * Builds per-chain sets of lowercase token addresses from the
6
7
  * `tokens.verified` config allowlist.
@@ -37,20 +38,45 @@ const mergeVerifiedWithSearchTokens = (verifiedTokens, searchTokens) => {
37
38
  return result;
38
39
  };
39
40
  /**
41
+ * `priceUSD` is a required string, so an unknown price arrives as `'0'` or `''`
42
+ * rather than as an absent key — the same reading as in `useLimitMarketRate`. A
43
+ * known price must never lose to one, or a route that cannot price its output
44
+ * blanks a value the cache already knew.
45
+ */
46
+ const knownPriceUSD = (fresher, cached) => Number(fresher) ? fresher : cached;
47
+ /**
48
+ * Merges a cached token into a route token for display. The route wins on every
49
+ * field it carries; the cache only fills gaps. Letting the cache win instead
50
+ * swaps the quoted price for a stale feed price.
51
+ */
52
+ const mergeFallbackToken = (routeToken, cachedToken) => cachedToken ? {
53
+ ...cachedToken,
54
+ ...routeToken,
55
+ priceUSD: knownPriceUSD(routeToken.priceUSD, cachedToken.priceUSD),
56
+ logoURI: routeToken.logoURI || cachedToken.logoURI
57
+ } : routeToken;
58
+ /**
40
59
  * Updates a token in the cache by chainId and address.
41
60
  * Returns a new cache object with the token updated, or the original if not found.
61
+ * Addresses are compared case-insensitively: the token list and the route can
62
+ * spell the same address differently. The incoming token only overwrites what it
63
+ * actually knows, so an unpriced or logo-less route cannot blank the cache.
42
64
  */
43
65
  const updateTokenInCache = (data, token) => {
44
66
  if (!data) return data;
45
67
  const chainTokens = data[token.chainId];
46
68
  if (!chainTokens) return data;
47
- const index = chainTokens.findIndex((t) => t.address === token.address);
69
+ const address = token.address.toLowerCase();
70
+ const index = chainTokens.findIndex((t) => t.address.toLowerCase() === address);
48
71
  if (index < 0) return data;
49
72
  return {
50
73
  ...data,
51
74
  [token.chainId]: chainTokens.map((t, i) => i === index ? {
52
75
  ...t,
53
- ...token
76
+ ...token,
77
+ address: t.address,
78
+ priceUSD: knownPriceUSD(token.priceUSD, t.priceUSD),
79
+ logoURI: token.logoURI || t.logoURI
54
80
  } : t)
55
81
  };
56
82
  };
@@ -65,7 +91,7 @@ const filterAllowedTokens = (dataTokens, configTokens, chainsConfig, formType, n
65
91
  for (const chainId of allowedChainIds) {
66
92
  const chainIncludedTokens = includedTokens.filter((t) => Number(t.chainId) === chainId);
67
93
  const includedAddresses = new Set(chainIncludedTokens.map((t) => t.address.toLowerCase()));
68
- const allowedAddresses = getConfigItemSets(configTokens, (tokens) => new Set(tokens.filter((t) => Number(t.chainId) === chainId).map((t) => t.address.toLowerCase())), formType);
94
+ const allowedAddresses = getChainTokenAllowSets(configTokens, chainId, formType);
69
95
  const verifiedAddresses = verifiedTokensSets?.get(chainId);
70
96
  const nativeAddress = nativeTokenAddresses?.get(chainId)?.toLowerCase();
71
97
  const chainTokens = [...dataTokens[chainId] ?? [], ...chainIncludedTokens];
@@ -106,6 +132,6 @@ const getTokenVerificationProvider = (token) => {
106
132
  const chainIdsWithoutNativeToken = /* @__PURE__ */ new Set([ChainId.HPL, ChainId.LTR]);
107
133
  const getNativeTokenAddresses = (chains) => new Map(chains?.filter((chain) => !chainIdsWithoutNativeToken.has(chain.id)).map((chain) => [chain.id, chain.nativeToken.address]));
108
134
  //#endregion
109
- export { filterAllowedTokens, getExecutionToToken, getNativeTokenAddresses, getTokenVerificationProvider, getVerifiedTokensSets, mergeVerifiedWithSearchTokens, updateTokenInCache };
135
+ export { filterAllowedTokens, getChainTokenAllowSets, getExecutionToToken, getNativeTokenAddresses, getTokenVerificationProvider, getVerifiedTokensSets, knownPriceUSD, mergeFallbackToken, mergeVerifiedWithSearchTokens, updateTokenInCache };
110
136
 
111
137
  //# sourceMappingURL=token.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"token.js","names":[],"sources":["../../../src/utils/token.ts"],"sourcesContent":["import type {\n BaseToken,\n ExtendedChain,\n RouteExtended,\n StaticToken,\n Token,\n TokenAmount,\n} from '@lifi/sdk'\nimport { ChainId } from '@lifi/sdk'\nimport type { FormType } from '../stores/form/types.js'\nimport type { TokensByChain, TokenWithFlags } from '../types/token.js'\nimport type { WidgetChains, WidgetTokens } from '../types/widget.js'\nimport { getConfigItemSets, isFormItemAllowed } from './item.js'\n\n/**\n * Builds per-chain sets of lowercase token addresses from the\n * `tokens.verified` config allowlist.\n */\nexport const getVerifiedTokensSets = (\n configTokens?: WidgetTokens\n): Map<number, Set<string>> | undefined => {\n if (!configTokens?.verified?.length) {\n return undefined\n }\n const sets = new Map<number, Set<string>>()\n for (const token of configTokens.verified) {\n const chainId = Number(token.chainId)\n let addresses = sets.get(chainId)\n if (!addresses) {\n addresses = new Set()\n sets.set(chainId, addresses)\n }\n addresses.add(token.address.toLowerCase())\n }\n return sets\n}\n\n/**\n * Merges verified tokens with search tokens.\n * Verified tokens take priority - search tokens are only added if they don't already exist.\n */\nexport const mergeVerifiedWithSearchTokens = (\n verifiedTokens?: TokensByChain,\n searchTokens?: TokensByChain\n): TokensByChain | undefined => {\n if (!verifiedTokens) {\n return searchTokens\n }\n if (!searchTokens) {\n return verifiedTokens\n }\n\n const result = { ...verifiedTokens }\n\n for (const [chainId, tokens] of Object.entries(searchTokens)) {\n const chainIdNum = Number(chainId)\n const existingTokens = result[chainIdNum] || []\n const existingAddresses = new Set(\n existingTokens.map((t) => t.address.toLowerCase())\n )\n\n const newTokens = tokens.filter(\n (t) => !existingAddresses.has(t.address.toLowerCase())\n )\n\n if (newTokens.length) {\n result[chainIdNum] = [...existingTokens, ...newTokens]\n }\n }\n\n return result\n}\n\n/**\n * Updates a token in the cache by chainId and address.\n * Returns a new cache object with the token updated, or the original if not found.\n */\nexport const updateTokenInCache = (\n data: TokensByChain | undefined,\n token: Token\n): TokensByChain | undefined => {\n if (!data) {\n return data\n }\n const chainTokens = data[token.chainId]\n if (!chainTokens) {\n return data\n }\n const index = chainTokens.findIndex((t) => t.address === token.address)\n if (index < 0) {\n return data\n }\n return {\n ...data,\n [token.chainId]: chainTokens.map((t, i) =>\n i === index ? { ...t, ...token } : t\n ),\n }\n}\n\nexport const filterAllowedTokens = (\n dataTokens: TokensByChain | undefined,\n configTokens?: WidgetTokens,\n chainsConfig?: WidgetChains,\n formType?: FormType,\n nativeTokenAddresses?: Map<number, string>\n): TokensByChain | undefined => {\n if (!dataTokens) {\n return\n }\n\n const includedTokens = configTokens?.include || []\n const allChainIds = Array.from(\n new Set([\n ...includedTokens.map((t) => t.chainId),\n ...Object.keys(dataTokens),\n ])\n ).map((chainId) => Number(chainId))\n\n const configChainIdsSet = getConfigItemSets(\n chainsConfig,\n (chainIds: number[]) => new Set(chainIds),\n formType\n )\n\n const allowedChainIds = configChainIdsSet\n ? allChainIds.filter((chainId) =>\n isFormItemAllowed(chainId, configChainIdsSet, formType)\n )\n : allChainIds\n\n const verifiedTokensSets = getVerifiedTokensSets(configTokens)\n\n const allowedTokensByChain: TokensByChain = {}\n for (const chainId of allowedChainIds) {\n const chainIncludedTokens = includedTokens.filter(\n (t) => Number(t.chainId) === chainId\n )\n const includedAddresses = new Set(\n chainIncludedTokens.map((t) => t.address.toLowerCase())\n )\n\n const allowedAddresses = getConfigItemSets(\n configTokens,\n (tokens: BaseToken[]) =>\n new Set(\n tokens\n .filter((t) => Number(t.chainId) === chainId)\n .map((t) => t.address.toLowerCase())\n ),\n formType\n )\n\n const verifiedAddresses = verifiedTokensSets?.get(chainId)\n const nativeAddress = nativeTokenAddresses?.get(chainId)?.toLowerCase()\n\n const chainTokens: TokenWithFlags[] = [\n ...(dataTokens[chainId] ?? []),\n ...chainIncludedTokens,\n ]\n\n const filtered: TokenWithFlags[] = []\n const seenAddresses = new Set<string>()\n for (const token of chainTokens) {\n const address = token.address.toLowerCase()\n // Data tokens come first and win over include duplicates,\n // keeping their extended data\n if (seenAddresses.has(address)) {\n continue\n }\n seenAddresses.add(address)\n if (\n !isFormItemAllowed(token, allowedAddresses, formType, (t) =>\n t.address.toLowerCase()\n )\n ) {\n continue\n }\n // Include and verified-allowlist tokens are explicitly set by the\n // integrator, mark them as verified. Flags are only ever set to true,\n // so a token that earns none keeps its cached object.\n const markVerified =\n !token.verified &&\n (includedAddresses.has(address) || verifiedAddresses?.has(address))\n const markNative = !!nativeAddress && nativeAddress === address\n if (markVerified || markNative) {\n filtered.push({\n ...token,\n ...(markVerified ? { verified: true } : {}),\n ...(markNative ? { native: true } : {}),\n })\n } else {\n filtered.push(token)\n }\n }\n\n allowedTokensByChain[chainId] = filtered\n }\n\n return allowedTokensByChain\n}\n\nexport const getExecutionToToken = (route: RouteExtended): TokenAmount => ({\n ...(route.steps.at(-1)?.execution?.toToken ?? route.toToken),\n amount: BigInt(\n route.steps.at(-1)?.execution?.toAmount ??\n route.steps.at(-1)?.estimate.toAmount ??\n route.toAmount\n ),\n})\n\n/** Capitalized name of the first provider that verified the token. */\nexport const getTokenVerificationProvider = (\n token: Pick<StaticToken, 'verificationStatusBreakdown'>\n): string | undefined => {\n const provider = token.verificationStatusBreakdown?.find(\n (entry) => entry.result === 'verified'\n )?.provider\n return provider\n ? `${provider.charAt(0).toUpperCase()}${provider.slice(1)}`\n : undefined\n}\n\n/**\n * Hyperliquid declares Arbitrum USDC as its native token, Lighter declares\n * Ethereum USDC. Neither is a gas token you hold there. A chain-local\n * stablecoin gas token, as on Arc or Stable, is genuine and stays.\n */\nconst chainIdsWithoutNativeToken = new Set([ChainId.HPL, ChainId.LTR])\n\nexport const getNativeTokenAddresses = (\n chains?: ExtendedChain[]\n): Map<number, string> =>\n new Map(\n chains\n ?.filter((chain) => !chainIdsWithoutNativeToken.has(chain.id))\n .map((chain) => [chain.id, chain.nativeToken.address])\n )\n"],"mappings":"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"}
1
+ {"version":3,"file":"token.js","names":[],"sources":["../../../src/utils/token.ts"],"sourcesContent":["import type {\n BaseToken,\n ExtendedChain,\n RouteExtended,\n StaticToken,\n Token,\n TokenAmount,\n TokenExtended,\n} from '@lifi/sdk'\nimport { ChainId } from '@lifi/sdk'\nimport type { FormType } from '../stores/form/types.js'\nimport type { TokensByChain, TokenWithFlags } from '../types/token.js'\nimport type {\n AllowDenySets,\n WidgetChains,\n WidgetTokens,\n} from '../types/widget.js'\nimport { getConfigItemSets, isFormItemAllowed } from './item.js'\n\n// Number(): plain-JS integrators may pass chainId as a string.\nexport const getChainTokenAllowSets = (\n configTokens: WidgetTokens | undefined,\n chainId: number,\n formType?: FormType\n): AllowDenySets | undefined =>\n getConfigItemSets(\n configTokens,\n (tokens: BaseToken[]) =>\n new Set(\n tokens\n .filter((t) => Number(t.chainId) === chainId)\n .map((t) => t.address.toLowerCase())\n ),\n formType\n )\n\n/**\n * Builds per-chain sets of lowercase token addresses from the\n * `tokens.verified` config allowlist.\n */\nexport const getVerifiedTokensSets = (\n configTokens?: WidgetTokens\n): Map<number, Set<string>> | undefined => {\n if (!configTokens?.verified?.length) {\n return undefined\n }\n const sets = new Map<number, Set<string>>()\n for (const token of configTokens.verified) {\n const chainId = Number(token.chainId)\n let addresses = sets.get(chainId)\n if (!addresses) {\n addresses = new Set()\n sets.set(chainId, addresses)\n }\n addresses.add(token.address.toLowerCase())\n }\n return sets\n}\n\n/**\n * Merges verified tokens with search tokens.\n * Verified tokens take priority - search tokens are only added if they don't already exist.\n */\nexport const mergeVerifiedWithSearchTokens = (\n verifiedTokens?: TokensByChain,\n searchTokens?: TokensByChain\n): TokensByChain | undefined => {\n if (!verifiedTokens) {\n return searchTokens\n }\n if (!searchTokens) {\n return verifiedTokens\n }\n\n const result = { ...verifiedTokens }\n\n for (const [chainId, tokens] of Object.entries(searchTokens)) {\n const chainIdNum = Number(chainId)\n const existingTokens = result[chainIdNum] || []\n const existingAddresses = new Set(\n existingTokens.map((t) => t.address.toLowerCase())\n )\n\n const newTokens = tokens.filter(\n (t) => !existingAddresses.has(t.address.toLowerCase())\n )\n\n if (newTokens.length) {\n result[chainIdNum] = [...existingTokens, ...newTokens]\n }\n }\n\n return result\n}\n\n/**\n * `priceUSD` is a required string, so an unknown price arrives as `'0'` or `''`\n * rather than as an absent key — the same reading as in `useLimitMarketRate`. A\n * known price must never lose to one, or a route that cannot price its output\n * blanks a value the cache already knew.\n */\nexport const knownPriceUSD = (fresher: string, cached: string): string =>\n Number(fresher) ? fresher : cached\n\n/**\n * Merges a cached token into a route token for display. The route wins on every\n * field it carries; the cache only fills gaps. Letting the cache win instead\n * swaps the quoted price for a stale feed price.\n */\nexport const mergeFallbackToken = (\n routeToken: TokenAmount,\n cachedToken: TokenExtended | undefined\n): TokenAmount =>\n cachedToken\n ? {\n ...cachedToken,\n ...routeToken,\n priceUSD: knownPriceUSD(routeToken.priceUSD, cachedToken.priceUSD),\n logoURI: routeToken.logoURI || cachedToken.logoURI,\n }\n : routeToken\n\n/**\n * Updates a token in the cache by chainId and address.\n * Returns a new cache object with the token updated, or the original if not found.\n * Addresses are compared case-insensitively: the token list and the route can\n * spell the same address differently. The incoming token only overwrites what it\n * actually knows, so an unpriced or logo-less route cannot blank the cache.\n */\nexport const updateTokenInCache = (\n data: TokensByChain | undefined,\n token: Token\n): TokensByChain | undefined => {\n if (!data) {\n return data\n }\n const chainTokens = data[token.chainId]\n if (!chainTokens) {\n return data\n }\n const address = token.address.toLowerCase()\n const index = chainTokens.findIndex(\n (t) => t.address.toLowerCase() === address\n )\n if (index < 0) {\n return data\n }\n return {\n ...data,\n [token.chainId]: chainTokens.map((t, i) =>\n i === index\n ? {\n ...t,\n ...token,\n // Keep the cached address: other readers hold it as the identity.\n address: t.address,\n priceUSD: knownPriceUSD(token.priceUSD, t.priceUSD),\n logoURI: token.logoURI || t.logoURI,\n }\n : t\n ),\n }\n}\n\nexport const filterAllowedTokens = (\n dataTokens: TokensByChain | undefined,\n configTokens?: WidgetTokens,\n chainsConfig?: WidgetChains,\n formType?: FormType,\n nativeTokenAddresses?: Map<number, string>\n): TokensByChain | undefined => {\n if (!dataTokens) {\n return\n }\n\n const includedTokens = configTokens?.include || []\n const allChainIds = Array.from(\n new Set([\n ...includedTokens.map((t) => t.chainId),\n ...Object.keys(dataTokens),\n ])\n ).map((chainId) => Number(chainId))\n\n const configChainIdsSet = getConfigItemSets(\n chainsConfig,\n (chainIds: number[]) => new Set(chainIds),\n formType\n )\n\n const allowedChainIds = configChainIdsSet\n ? allChainIds.filter((chainId) =>\n isFormItemAllowed(chainId, configChainIdsSet, formType)\n )\n : allChainIds\n\n const verifiedTokensSets = getVerifiedTokensSets(configTokens)\n\n const allowedTokensByChain: TokensByChain = {}\n for (const chainId of allowedChainIds) {\n const chainIncludedTokens = includedTokens.filter(\n (t) => Number(t.chainId) === chainId\n )\n const includedAddresses = new Set(\n chainIncludedTokens.map((t) => t.address.toLowerCase())\n )\n\n const allowedAddresses = getChainTokenAllowSets(\n configTokens,\n chainId,\n formType\n )\n\n const verifiedAddresses = verifiedTokensSets?.get(chainId)\n const nativeAddress = nativeTokenAddresses?.get(chainId)?.toLowerCase()\n\n const chainTokens: TokenWithFlags[] = [\n ...(dataTokens[chainId] ?? []),\n ...chainIncludedTokens,\n ]\n\n const filtered: TokenWithFlags[] = []\n const seenAddresses = new Set<string>()\n for (const token of chainTokens) {\n const address = token.address.toLowerCase()\n // Data tokens come first and win over include duplicates,\n // keeping their extended data\n if (seenAddresses.has(address)) {\n continue\n }\n seenAddresses.add(address)\n if (\n !isFormItemAllowed(token, allowedAddresses, formType, (t) =>\n t.address.toLowerCase()\n )\n ) {\n continue\n }\n // Include and verified-allowlist tokens are explicitly set by the\n // integrator, mark them as verified. Flags are only ever set to true,\n // so a token that earns none keeps its cached object.\n const markVerified =\n !token.verified &&\n (includedAddresses.has(address) || verifiedAddresses?.has(address))\n const markNative = !!nativeAddress && nativeAddress === address\n if (markVerified || markNative) {\n filtered.push({\n ...token,\n ...(markVerified ? { verified: true } : {}),\n ...(markNative ? { native: true } : {}),\n })\n } else {\n filtered.push(token)\n }\n }\n\n allowedTokensByChain[chainId] = filtered\n }\n\n return allowedTokensByChain\n}\n\nexport const getExecutionToToken = (route: RouteExtended): TokenAmount => ({\n ...(route.steps.at(-1)?.execution?.toToken ?? route.toToken),\n amount: BigInt(\n route.steps.at(-1)?.execution?.toAmount ??\n route.steps.at(-1)?.estimate.toAmount ??\n route.toAmount\n ),\n})\n\n/** Capitalized name of the first provider that verified the token. */\nexport const getTokenVerificationProvider = (\n token: Pick<StaticToken, 'verificationStatusBreakdown'>\n): string | undefined => {\n const provider = token.verificationStatusBreakdown?.find(\n (entry) => entry.result === 'verified'\n )?.provider\n return provider\n ? `${provider.charAt(0).toUpperCase()}${provider.slice(1)}`\n : undefined\n}\n\n/**\n * Hyperliquid declares Arbitrum USDC as its native token, Lighter declares\n * Ethereum USDC. Neither is a gas token you hold there. A chain-local\n * stablecoin gas token, as on Arc or Stable, is genuine and stays.\n */\nconst chainIdsWithoutNativeToken = new Set([ChainId.HPL, ChainId.LTR])\n\nexport const getNativeTokenAddresses = (\n chains?: ExtendedChain[]\n): Map<number, string> =>\n new Map(\n chains\n ?.filter((chain) => !chainIdsWithoutNativeToken.has(chain.id))\n .map((chain) => [chain.id, chain.nativeToken.address])\n )\n"],"mappings":"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"}
@@ -135,11 +135,23 @@ const processedTypedTokens = (tokens, tokensWithBalances, selectedChainId, confi
135
135
  withPinnedTokens: !!sortedPinnedTokens.length
136
136
  };
137
137
  };
138
- const isSearchMatch = (token, search) => {
139
- if (!search) return true;
140
- const searchLowerCase = search.toLowerCase();
141
- return token.name?.toLowerCase().includes(searchLowerCase) || token.symbol?.replaceAll("₮", "T").toLowerCase().includes(searchLowerCase) || token.address?.toLowerCase().includes(searchLowerCase);
138
+ /**
139
+ * Builds the filter for one search, so the query is normalized once instead of
140
+ * once per token: an all-networks list holds tens of thousands of rows, where
141
+ * normalizing per row costs more than the matching itself. Binding the mode
142
+ * here also keeps every call site of one search on the same rule.
143
+ *
144
+ * An address search matches the token at that address only. Impersonators
145
+ * embed a real address in their name or symbol, so a substring match on
146
+ * those fields would surface them for the very query meant to avoid them.
147
+ */
148
+ const createSearchMatcher = (search, isAddressSearch) => {
149
+ const searchTerm = search?.trim().toLowerCase();
150
+ if (!searchTerm) return () => true;
151
+ if (isAddressSearch) return (token) => token.address?.toLowerCase() === searchTerm;
152
+ return (token) => Boolean(token.name?.toLowerCase().includes(searchTerm) || token.symbol?.replaceAll("₮", "T").toLowerCase().includes(searchTerm) || token.address?.toLowerCase().includes(searchTerm));
142
153
  };
154
+ const isHoistableNative = (token, selectedChainId) => !!token?.native && !token.pinned && !token.featured && !token.popular && !token.verified && (selectedChainId === void 0 || token.chainId === selectedChainId);
143
155
  /**
144
156
  * Moves the chain's native token to the top of its own chain's list, unless an
145
157
  * external list already places it. Returns the array unchanged when nothing
@@ -147,7 +159,7 @@ const isSearchMatch = (token, search) => {
147
159
  */
148
160
  const hoistNativeToken = (tokens, selectedChainId) => {
149
161
  if (!selectedChainId) return tokens;
150
- const index = tokens.findIndex((token) => token.native && token.chainId === selectedChainId && !token.featured && !token.popular && !token.pinned && !token.verified);
162
+ const index = tokens.findIndex((token) => isHoistableNative(token, selectedChainId));
151
163
  if (index < 1) return tokens;
152
164
  return [
153
165
  tokens[index],
@@ -156,6 +168,6 @@ const hoistNativeToken = (tokens, selectedChainId) => {
156
168
  ];
157
169
  };
158
170
  //#endregion
159
- export { hoistNativeToken, isSearchMatch, processTokenList };
171
+ export { createSearchMatcher, hoistNativeToken, isHoistableNative, processTokenList };
160
172
 
161
173
  //# sourceMappingURL=tokenList.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tokenList.js","names":[],"sources":["../../../src/utils/tokenList.ts"],"sourcesContent":["import { formatUnits, type TokenExtended } from '@lifi/sdk'\nimport type { TokenAmount, TokenAmountExtended } from '../types/token.js'\nimport type { WidgetTokens } from '../types/widget.js'\n\nconst sortByBalances = (a: TokenAmount, b: TokenAmount) =>\n Number.parseFloat(formatUnits(b.amount ?? 0n, b.decimals)) *\n Number.parseFloat(b.priceUSD ?? '0') -\n Number.parseFloat(formatUnits(a.amount ?? 0n, a.decimals)) *\n Number.parseFloat(a.priceUSD ?? '0')\n\nconst sortByVolume = (a: TokenExtended, b: TokenExtended) =>\n (b.volumeUSD24H ?? 0) - (a.volumeUSD24H ?? 0)\n\nexport const processTokenList = (\n withoutBalances: boolean,\n noCategories: boolean,\n configTokens?: WidgetTokens,\n selectedChainId?: number,\n tokens?: TokenExtended[],\n tokensWithBalances?: TokenAmount[],\n isPinnedToken?: (chainId: number, tokenAddress: string) => boolean\n): {\n processedTokens: TokenAmount[]\n withCategories: boolean\n withPinnedTokens: boolean\n} => {\n if (withoutBalances) {\n if (noCategories) {\n const sortedTokens = [...(tokens ?? [])].sort(sortByVolume)\n // Separate pinned tokens\n if (isPinnedToken) {\n const pinned: TokenAmount[] = []\n const notPinned: TokenAmount[] = []\n for (const token of sortedTokens) {\n if (isPinnedToken(token.chainId, token.address)) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinned.push(pinnedToken)\n } else {\n notPinned.push(token)\n }\n }\n return {\n processedTokens: [...pinned, ...notPinned],\n withCategories: false,\n withPinnedTokens: !!pinned.length,\n }\n }\n return {\n processedTokens: sortedTokens,\n withCategories: false,\n withPinnedTokens: false,\n }\n } else {\n return processedTypedTokens(\n tokens ?? [],\n [],\n selectedChainId,\n configTokens,\n isPinnedToken\n )\n }\n }\n\n const sortedTokensWithBalances = [...(tokensWithBalances ?? [])].sort(\n sortByBalances\n )\n\n const tokensWithBalancesSet = new Set(\n sortedTokensWithBalances.map(\n (token) => `${token.chainId}-${token.address.toLowerCase()}`\n )\n )\n const tokensWithoutBalances =\n tokens\n ?.filter((token) => {\n const tokenKey = `${token.chainId}-${token.address.toLowerCase()}`\n return !tokensWithBalancesSet.has(tokenKey)\n })\n .sort(sortByVolume) ?? []\n\n if (noCategories) {\n // Separate pinned tokens\n if (isPinnedToken) {\n const pinnedWithBalances: TokenAmount[] = []\n const notPinnedWithBalances: TokenAmount[] = []\n const pinnedWithoutBalances: TokenAmount[] = []\n const notPinnedWithoutBalances: TokenAmount[] = []\n\n for (const token of sortedTokensWithBalances) {\n if (isPinnedToken(token.chainId, token.address)) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinnedWithBalances.push(pinnedToken)\n } else {\n notPinnedWithBalances.push(token)\n }\n }\n\n for (const token of tokensWithoutBalances) {\n if (isPinnedToken(token.chainId, token.address)) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinnedWithoutBalances.push(pinnedToken)\n } else {\n notPinnedWithoutBalances.push(token)\n }\n }\n\n return {\n processedTokens: [\n ...pinnedWithBalances,\n ...pinnedWithoutBalances,\n ...notPinnedWithBalances,\n ...notPinnedWithoutBalances,\n ],\n withCategories: false,\n withPinnedTokens:\n !!pinnedWithBalances.length || !!pinnedWithoutBalances.length,\n }\n }\n return {\n processedTokens: [...sortedTokensWithBalances, ...tokensWithoutBalances],\n withCategories: false,\n withPinnedTokens: false,\n }\n } else {\n return processedTypedTokens(\n tokensWithoutBalances,\n sortedTokensWithBalances,\n selectedChainId,\n configTokens,\n isPinnedToken\n )\n }\n}\n\n// NB: only for non-all-networks\nconst processedTypedTokens = (\n tokens: TokenAmount[],\n tokensWithBalances: TokenAmount[],\n selectedChainId?: number,\n configTokens?: WidgetTokens,\n isPinnedToken?: (chainId: number, tokenAddress: string) => boolean\n) => {\n const filteredTokensMap = new Map(\n tokens.map((token) => [token.address, token])\n )\n\n const pinnedTokens: TokenAmount[] = []\n const featuredTokensFromConfig: TokenAmount[] = []\n const popularTokensFromConfig: TokenAmount[] = []\n\n ;(['popular', 'featured'] as const).forEach((tokenType) => {\n const typedTokens = configTokens?.[tokenType]?.filter(\n (token) => token.chainId === selectedChainId\n )\n\n typedTokens?.forEach((token) => {\n const tokenAmount = { ...token } as TokenAmount\n tokenAmount[tokenType] = true\n // Config tokens are explicitly set by integrator, mark as verified\n tokenAmount.verified = true\n\n const match = filteredTokensMap.get(token.address)\n if (match?.priceUSD) {\n tokenAmount.priceUSD = match.priceUSD\n }\n if (!token.logoURI && match?.logoURI) {\n tokenAmount.logoURI = match.logoURI\n }\n\n if (tokenType === 'popular') {\n popularTokensFromConfig.push(tokenAmount)\n } else {\n featuredTokensFromConfig.push(tokenAmount)\n }\n\n // Additionally add to pinned tokens if it is pinned\n const isPinned =\n isPinnedToken && selectedChainId\n ? isPinnedToken(selectedChainId, token.address)\n : false\n if (isPinned) {\n const pinnedToken = { ...tokenAmount, pinned: true } as TokenAmount\n pinnedTokens.push(pinnedToken)\n }\n })\n })\n\n // Filter out config-added tokens from main list\n const configTokenAddresses = new Set(\n [...popularTokensFromConfig, ...featuredTokensFromConfig].map((t) =>\n t.address.toLowerCase()\n )\n )\n\n const remainingTokens = tokens.filter(\n (token) => !configTokenAddresses.has(token.address.toLowerCase())\n )\n\n const otherTokens: TokenAmount[] = []\n\n // Separate pinned tokens and categorize remaining tokens\n for (const token of remainingTokens) {\n const isPinned =\n isPinnedToken && selectedChainId\n ? isPinnedToken(selectedChainId, token.address)\n : false\n\n if (isPinned) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinnedTokens.push(pinnedToken)\n } else if (token.featured) {\n featuredTokensFromConfig.push(token)\n } else if (token.popular) {\n popularTokensFromConfig.push(token)\n } else {\n otherTokens.push(token)\n }\n }\n\n // Also check tokens with balances for pinned status\n const pinnedTokensWithBalances: TokenAmount[] = []\n const nonPinnedTokensWithBalances: TokenAmount[] = []\n\n if (isPinnedToken && selectedChainId) {\n for (const token of tokensWithBalances) {\n if (isPinnedToken(selectedChainId, token.address)) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinnedTokensWithBalances.push(pinnedToken)\n } else {\n nonPinnedTokensWithBalances.push(token)\n }\n }\n } else {\n nonPinnedTokensWithBalances.push(...tokensWithBalances)\n }\n\n const sortedPinnedTokens = [\n ...pinnedTokens,\n ...pinnedTokensWithBalances,\n ].sort(sortByVolume)\n const sortedFeaturedTokens = [...featuredTokensFromConfig].sort(sortByVolume)\n const sortedPopularTokens = [...popularTokensFromConfig].sort(sortByVolume)\n const sortedOtherTokens = [...otherTokens].sort(sortByVolume)\n\n return {\n processedTokens: [\n ...sortedPinnedTokens,\n ...sortedFeaturedTokens,\n ...nonPinnedTokensWithBalances,\n ...sortedPopularTokens,\n ...sortedOtherTokens,\n ],\n withCategories:\n !!featuredTokensFromConfig.length || !!popularTokensFromConfig.length,\n withPinnedTokens: !!sortedPinnedTokens.length,\n }\n}\n\nexport const isSearchMatch = (\n token: TokenExtended | TokenAmountExtended,\n search?: string\n): boolean => {\n if (!search) {\n return true\n }\n\n const searchLowerCase = search.toLowerCase()\n return (\n token.name?.toLowerCase().includes(searchLowerCase) ||\n token.symbol\n ?.replaceAll('₮', 'T')\n .toLowerCase()\n .includes(searchLowerCase) ||\n token.address?.toLowerCase().includes(searchLowerCase)\n )\n}\n\n/**\n * Moves the chain's native token to the top of its own chain's list, unless an\n * external list already places it. Returns the array unchanged when nothing\n * moves, so a memo downstream keeps its reference.\n */\nexport const hoistNativeToken = (\n tokens: TokenAmount[],\n selectedChainId?: number\n): TokenAmount[] => {\n if (!selectedChainId) {\n return tokens\n }\n const index = tokens.findIndex(\n (token) =>\n token.native &&\n token.chainId === selectedChainId &&\n !token.featured &&\n !token.popular &&\n !token.pinned &&\n !token.verified\n )\n // -1 is no native token, 0 is already first\n if (index < 1) {\n return tokens\n }\n return [tokens[index], ...tokens.slice(0, index), ...tokens.slice(index + 1)]\n}\n"],"mappings":";;AAIA,MAAM,kBAAkB,GAAgB,MACtC,OAAO,WAAW,YAAY,EAAE,UAAU,IAAI,EAAE,QAAQ,CAAC,IACvD,OAAO,WAAW,EAAE,YAAY,GAAG,IACrC,OAAO,WAAW,YAAY,EAAE,UAAU,IAAI,EAAE,QAAQ,CAAC,IACvD,OAAO,WAAW,EAAE,YAAY,GAAG;AAEvC,MAAM,gBAAgB,GAAkB,OACrC,EAAE,gBAAgB,MAAM,EAAE,gBAAgB;AAE7C,MAAa,oBACX,iBACA,cACA,cACA,iBACA,QACA,oBACA,kBAKG;CACH,IAAI,iBAAiB;EACnB,IAAI,cAAc;GAChB,MAAM,eAAe,CAAC,GAAI,UAAU,CAAC,CAAE,CAAC,CAAC,KAAK,YAAY;GAE1D,IAAI,eAAe;IACjB,MAAM,SAAwB,CAAC;IAC/B,MAAM,YAA2B,CAAC;IAClC,KAAK,MAAM,SAAS,cAClB,IAAI,cAAc,MAAM,SAAS,MAAM,OAAO,GAAG;KAC/C,MAAM,cAAc;MAAE,GAAG;MAAO,QAAQ;KAAK;KAC7C,OAAO,KAAK,WAAW;IACzB,OACE,UAAU,KAAK,KAAK;IAGxB,OAAO;KACL,iBAAiB,CAAC,GAAG,QAAQ,GAAG,SAAS;KACzC,gBAAgB;KAChB,kBAAkB,CAAC,CAAC,OAAO;IAC7B;GACF;GACA,OAAO;IACL,iBAAiB;IACjB,gBAAgB;IAChB,kBAAkB;GACpB;EACF,OACE,OAAO,qBACL,UAAU,CAAC,GACX,CAAC,GACD,iBACA,cACA,aACF;CAEJ;CAEA,MAAM,2BAA2B,CAAC,GAAI,sBAAsB,CAAC,CAAE,CAAC,CAAC,KAC/D,cACF;CAEA,MAAM,wBAAwB,IAAI,IAChC,yBAAyB,KACtB,UAAU,GAAG,MAAM,QAAQ,GAAG,MAAM,QAAQ,YAAY,GAC3D,CACF;CACA,MAAM,wBACJ,QACI,QAAQ,UAAU;EAClB,MAAM,WAAW,GAAG,MAAM,QAAQ,GAAG,MAAM,QAAQ,YAAY;EAC/D,OAAO,CAAC,sBAAsB,IAAI,QAAQ;CAC5C,CAAC,CAAC,CACD,KAAK,YAAY,KAAK,CAAC;CAE5B,IAAI,cAAc;EAEhB,IAAI,eAAe;GACjB,MAAM,qBAAoC,CAAC;GAC3C,MAAM,wBAAuC,CAAC;GAC9C,MAAM,wBAAuC,CAAC;GAC9C,MAAM,2BAA0C,CAAC;GAEjD,KAAK,MAAM,SAAS,0BAClB,IAAI,cAAc,MAAM,SAAS,MAAM,OAAO,GAAG;IAC/C,MAAM,cAAc;KAAE,GAAG;KAAO,QAAQ;IAAK;IAC7C,mBAAmB,KAAK,WAAW;GACrC,OACE,sBAAsB,KAAK,KAAK;GAIpC,KAAK,MAAM,SAAS,uBAClB,IAAI,cAAc,MAAM,SAAS,MAAM,OAAO,GAAG;IAC/C,MAAM,cAAc;KAAE,GAAG;KAAO,QAAQ;IAAK;IAC7C,sBAAsB,KAAK,WAAW;GACxC,OACE,yBAAyB,KAAK,KAAK;GAIvC,OAAO;IACL,iBAAiB;KACf,GAAG;KACH,GAAG;KACH,GAAG;KACH,GAAG;IACL;IACA,gBAAgB;IAChB,kBACE,CAAC,CAAC,mBAAmB,UAAU,CAAC,CAAC,sBAAsB;GAC3D;EACF;EACA,OAAO;GACL,iBAAiB,CAAC,GAAG,0BAA0B,GAAG,qBAAqB;GACvE,gBAAgB;GAChB,kBAAkB;EACpB;CACF,OACE,OAAO,qBACL,uBACA,0BACA,iBACA,cACA,aACF;AAEJ;AAGA,MAAM,wBACJ,QACA,oBACA,iBACA,cACA,kBACG;CACH,MAAM,oBAAoB,IAAI,IAC5B,OAAO,KAAK,UAAU,CAAC,MAAM,SAAS,KAAK,CAAC,CAC9C;CAEA,MAAM,eAA8B,CAAC;CACrC,MAAM,2BAA0C,CAAC;CACjD,MAAM,0BAAyC,CAAC;CAE/C,CAAE,WAAW,UAAU,CAAC,CAAW,SAAS,cAAc;EAKzD,CAJoB,eAAe,UAAU,EAAE,QAC5C,UAAU,MAAM,YAAY,eAC/B,EAAA,EAEa,SAAS,UAAU;GAC9B,MAAM,cAAc,EAAE,GAAG,MAAM;GAC/B,YAAY,aAAa;GAEzB,YAAY,WAAW;GAEvB,MAAM,QAAQ,kBAAkB,IAAI,MAAM,OAAO;GACjD,IAAI,OAAO,UACT,YAAY,WAAW,MAAM;GAE/B,IAAI,CAAC,MAAM,WAAW,OAAO,SAC3B,YAAY,UAAU,MAAM;GAG9B,IAAI,cAAc,WAChB,wBAAwB,KAAK,WAAW;QAExC,yBAAyB,KAAK,WAAW;GAQ3C,IAHE,iBAAiB,kBACb,cAAc,iBAAiB,MAAM,OAAO,IAC5C,OACQ;IACZ,MAAM,cAAc;KAAE,GAAG;KAAa,QAAQ;IAAK;IACnD,aAAa,KAAK,WAAW;GAC/B;EACF,CAAC;CACH,CAAC;CAGD,MAAM,uBAAuB,IAAI,IAC/B,CAAC,GAAG,yBAAyB,GAAG,wBAAwB,CAAC,CAAC,KAAK,MAC7D,EAAE,QAAQ,YAAY,CACxB,CACF;CAEA,MAAM,kBAAkB,OAAO,QAC5B,UAAU,CAAC,qBAAqB,IAAI,MAAM,QAAQ,YAAY,CAAC,CAClE;CAEA,MAAM,cAA6B,CAAC;CAGpC,KAAK,MAAM,SAAS,iBAMlB,IAJE,iBAAiB,kBACb,cAAc,iBAAiB,MAAM,OAAO,IAC5C,OAEQ;EACZ,MAAM,cAAc;GAAE,GAAG;GAAO,QAAQ;EAAK;EAC7C,aAAa,KAAK,WAAW;CAC/B,OAAO,IAAI,MAAM,UACf,yBAAyB,KAAK,KAAK;MAC9B,IAAI,MAAM,SACf,wBAAwB,KAAK,KAAK;MAElC,YAAY,KAAK,KAAK;CAK1B,MAAM,2BAA0C,CAAC;CACjD,MAAM,8BAA6C,CAAC;CAEpD,IAAI,iBAAiB,iBACnB,KAAK,MAAM,SAAS,oBAClB,IAAI,cAAc,iBAAiB,MAAM,OAAO,GAAG;EACjD,MAAM,cAAc;GAAE,GAAG;GAAO,QAAQ;EAAK;EAC7C,yBAAyB,KAAK,WAAW;CAC3C,OACE,4BAA4B,KAAK,KAAK;MAI1C,4BAA4B,KAAK,GAAG,kBAAkB;CAGxD,MAAM,qBAAqB,CACzB,GAAG,cACH,GAAG,wBACL,CAAC,CAAC,KAAK,YAAY;CACnB,MAAM,uBAAuB,CAAC,GAAG,wBAAwB,CAAC,CAAC,KAAK,YAAY;CAC5E,MAAM,sBAAsB,CAAC,GAAG,uBAAuB,CAAC,CAAC,KAAK,YAAY;CAC1E,MAAM,oBAAoB,CAAC,GAAG,WAAW,CAAC,CAAC,KAAK,YAAY;CAE5D,OAAO;EACL,iBAAiB;GACf,GAAG;GACH,GAAG;GACH,GAAG;GACH,GAAG;GACH,GAAG;EACL;EACA,gBACE,CAAC,CAAC,yBAAyB,UAAU,CAAC,CAAC,wBAAwB;EACjE,kBAAkB,CAAC,CAAC,mBAAmB;CACzC;AACF;AAEA,MAAa,iBACX,OACA,WACY;CACZ,IAAI,CAAC,QACH,OAAO;CAGT,MAAM,kBAAkB,OAAO,YAAY;CAC3C,OACE,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,eAAe,KAClD,MAAM,QACF,WAAW,KAAK,GAAG,CAAC,CACrB,YAAY,CAAC,CACb,SAAS,eAAe,KAC3B,MAAM,SAAS,YAAY,CAAC,CAAC,SAAS,eAAe;AAEzD;;;;;;AAOA,MAAa,oBACX,QACA,oBACkB;CAClB,IAAI,CAAC,iBACH,OAAO;CAET,MAAM,QAAQ,OAAO,WAClB,UACC,MAAM,UACN,MAAM,YAAY,mBAClB,CAAC,MAAM,YACP,CAAC,MAAM,WACP,CAAC,MAAM,UACP,CAAC,MAAM,QACX;CAEA,IAAI,QAAQ,GACV,OAAO;CAET,OAAO;EAAC,OAAO;EAAQ,GAAG,OAAO,MAAM,GAAG,KAAK;EAAG,GAAG,OAAO,MAAM,QAAQ,CAAC;CAAC;AAC9E"}
1
+ {"version":3,"file":"tokenList.js","names":[],"sources":["../../../src/utils/tokenList.ts"],"sourcesContent":["import { formatUnits, type TokenExtended } from '@lifi/sdk'\nimport type { TokenAmount, TokenAmountExtended } from '../types/token.js'\nimport type { WidgetTokens } from '../types/widget.js'\n\nconst sortByBalances = (a: TokenAmount, b: TokenAmount) =>\n Number.parseFloat(formatUnits(b.amount ?? 0n, b.decimals)) *\n Number.parseFloat(b.priceUSD ?? '0') -\n Number.parseFloat(formatUnits(a.amount ?? 0n, a.decimals)) *\n Number.parseFloat(a.priceUSD ?? '0')\n\nconst sortByVolume = (a: TokenExtended, b: TokenExtended) =>\n (b.volumeUSD24H ?? 0) - (a.volumeUSD24H ?? 0)\n\nexport const processTokenList = (\n withoutBalances: boolean,\n noCategories: boolean,\n configTokens?: WidgetTokens,\n selectedChainId?: number,\n tokens?: TokenExtended[],\n tokensWithBalances?: TokenAmount[],\n isPinnedToken?: (chainId: number, tokenAddress: string) => boolean\n): {\n processedTokens: TokenAmount[]\n withCategories: boolean\n withPinnedTokens: boolean\n} => {\n if (withoutBalances) {\n if (noCategories) {\n const sortedTokens = [...(tokens ?? [])].sort(sortByVolume)\n // Separate pinned tokens\n if (isPinnedToken) {\n const pinned: TokenAmount[] = []\n const notPinned: TokenAmount[] = []\n for (const token of sortedTokens) {\n if (isPinnedToken(token.chainId, token.address)) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinned.push(pinnedToken)\n } else {\n notPinned.push(token)\n }\n }\n return {\n processedTokens: [...pinned, ...notPinned],\n withCategories: false,\n withPinnedTokens: !!pinned.length,\n }\n }\n return {\n processedTokens: sortedTokens,\n withCategories: false,\n withPinnedTokens: false,\n }\n } else {\n return processedTypedTokens(\n tokens ?? [],\n [],\n selectedChainId,\n configTokens,\n isPinnedToken\n )\n }\n }\n\n const sortedTokensWithBalances = [...(tokensWithBalances ?? [])].sort(\n sortByBalances\n )\n\n const tokensWithBalancesSet = new Set(\n sortedTokensWithBalances.map(\n (token) => `${token.chainId}-${token.address.toLowerCase()}`\n )\n )\n const tokensWithoutBalances =\n tokens\n ?.filter((token) => {\n const tokenKey = `${token.chainId}-${token.address.toLowerCase()}`\n return !tokensWithBalancesSet.has(tokenKey)\n })\n .sort(sortByVolume) ?? []\n\n if (noCategories) {\n // Separate pinned tokens\n if (isPinnedToken) {\n const pinnedWithBalances: TokenAmount[] = []\n const notPinnedWithBalances: TokenAmount[] = []\n const pinnedWithoutBalances: TokenAmount[] = []\n const notPinnedWithoutBalances: TokenAmount[] = []\n\n for (const token of sortedTokensWithBalances) {\n if (isPinnedToken(token.chainId, token.address)) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinnedWithBalances.push(pinnedToken)\n } else {\n notPinnedWithBalances.push(token)\n }\n }\n\n for (const token of tokensWithoutBalances) {\n if (isPinnedToken(token.chainId, token.address)) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinnedWithoutBalances.push(pinnedToken)\n } else {\n notPinnedWithoutBalances.push(token)\n }\n }\n\n return {\n processedTokens: [\n ...pinnedWithBalances,\n ...pinnedWithoutBalances,\n ...notPinnedWithBalances,\n ...notPinnedWithoutBalances,\n ],\n withCategories: false,\n withPinnedTokens:\n !!pinnedWithBalances.length || !!pinnedWithoutBalances.length,\n }\n }\n return {\n processedTokens: [...sortedTokensWithBalances, ...tokensWithoutBalances],\n withCategories: false,\n withPinnedTokens: false,\n }\n } else {\n return processedTypedTokens(\n tokensWithoutBalances,\n sortedTokensWithBalances,\n selectedChainId,\n configTokens,\n isPinnedToken\n )\n }\n}\n\n// NB: only for non-all-networks\nconst processedTypedTokens = (\n tokens: TokenAmount[],\n tokensWithBalances: TokenAmount[],\n selectedChainId?: number,\n configTokens?: WidgetTokens,\n isPinnedToken?: (chainId: number, tokenAddress: string) => boolean\n) => {\n const filteredTokensMap = new Map(\n tokens.map((token) => [token.address, token])\n )\n\n const pinnedTokens: TokenAmount[] = []\n const featuredTokensFromConfig: TokenAmount[] = []\n const popularTokensFromConfig: TokenAmount[] = []\n\n ;(['popular', 'featured'] as const).forEach((tokenType) => {\n const typedTokens = configTokens?.[tokenType]?.filter(\n (token) => token.chainId === selectedChainId\n )\n\n typedTokens?.forEach((token) => {\n const tokenAmount = { ...token } as TokenAmount\n tokenAmount[tokenType] = true\n // Config tokens are explicitly set by integrator, mark as verified\n tokenAmount.verified = true\n\n const match = filteredTokensMap.get(token.address)\n if (match?.priceUSD) {\n tokenAmount.priceUSD = match.priceUSD\n }\n if (!token.logoURI && match?.logoURI) {\n tokenAmount.logoURI = match.logoURI\n }\n\n if (tokenType === 'popular') {\n popularTokensFromConfig.push(tokenAmount)\n } else {\n featuredTokensFromConfig.push(tokenAmount)\n }\n\n // Additionally add to pinned tokens if it is pinned\n const isPinned =\n isPinnedToken && selectedChainId\n ? isPinnedToken(selectedChainId, token.address)\n : false\n if (isPinned) {\n const pinnedToken = { ...tokenAmount, pinned: true } as TokenAmount\n pinnedTokens.push(pinnedToken)\n }\n })\n })\n\n // Filter out config-added tokens from main list\n const configTokenAddresses = new Set(\n [...popularTokensFromConfig, ...featuredTokensFromConfig].map((t) =>\n t.address.toLowerCase()\n )\n )\n\n const remainingTokens = tokens.filter(\n (token) => !configTokenAddresses.has(token.address.toLowerCase())\n )\n\n const otherTokens: TokenAmount[] = []\n\n // Separate pinned tokens and categorize remaining tokens\n for (const token of remainingTokens) {\n const isPinned =\n isPinnedToken && selectedChainId\n ? isPinnedToken(selectedChainId, token.address)\n : false\n\n if (isPinned) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinnedTokens.push(pinnedToken)\n } else if (token.featured) {\n featuredTokensFromConfig.push(token)\n } else if (token.popular) {\n popularTokensFromConfig.push(token)\n } else {\n otherTokens.push(token)\n }\n }\n\n // Also check tokens with balances for pinned status\n const pinnedTokensWithBalances: TokenAmount[] = []\n const nonPinnedTokensWithBalances: TokenAmount[] = []\n\n if (isPinnedToken && selectedChainId) {\n for (const token of tokensWithBalances) {\n if (isPinnedToken(selectedChainId, token.address)) {\n const pinnedToken = { ...token, pinned: true } as TokenAmount\n pinnedTokensWithBalances.push(pinnedToken)\n } else {\n nonPinnedTokensWithBalances.push(token)\n }\n }\n } else {\n nonPinnedTokensWithBalances.push(...tokensWithBalances)\n }\n\n const sortedPinnedTokens = [\n ...pinnedTokens,\n ...pinnedTokensWithBalances,\n ].sort(sortByVolume)\n const sortedFeaturedTokens = [...featuredTokensFromConfig].sort(sortByVolume)\n const sortedPopularTokens = [...popularTokensFromConfig].sort(sortByVolume)\n const sortedOtherTokens = [...otherTokens].sort(sortByVolume)\n\n return {\n processedTokens: [\n ...sortedPinnedTokens,\n ...sortedFeaturedTokens,\n ...nonPinnedTokensWithBalances,\n ...sortedPopularTokens,\n ...sortedOtherTokens,\n ],\n withCategories:\n !!featuredTokensFromConfig.length || !!popularTokensFromConfig.length,\n withPinnedTokens: !!sortedPinnedTokens.length,\n }\n}\n\n/**\n * Builds the filter for one search, so the query is normalized once instead of\n * once per token: an all-networks list holds tens of thousands of rows, where\n * normalizing per row costs more than the matching itself. Binding the mode\n * here also keeps every call site of one search on the same rule.\n *\n * An address search matches the token at that address only. Impersonators\n * embed a real address in their name or symbol, so a substring match on\n * those fields would surface them for the very query meant to avoid them.\n */\nexport const createSearchMatcher = (\n search: string | undefined,\n isAddressSearch: boolean\n): ((token: TokenExtended | TokenAmountExtended) => boolean) => {\n const searchTerm = search?.trim().toLowerCase()\n\n if (!searchTerm) {\n return () => true\n }\n\n if (isAddressSearch) {\n return (token) => token.address?.toLowerCase() === searchTerm\n }\n\n return (token) =>\n Boolean(\n token.name?.toLowerCase().includes(searchTerm) ||\n token.symbol?.replaceAll('₮', 'T').toLowerCase().includes(searchTerm) ||\n token.address?.toLowerCase().includes(searchTerm)\n )\n}\n\nexport const isHoistableNative = (\n token: TokenAmount | undefined,\n selectedChainId?: number\n): boolean =>\n !!token?.native &&\n !token.pinned &&\n !token.featured &&\n !token.popular &&\n !token.verified &&\n (selectedChainId === undefined || token.chainId === selectedChainId)\n\n/**\n * Moves the chain's native token to the top of its own chain's list, unless an\n * external list already places it. Returns the array unchanged when nothing\n * moves, so a memo downstream keeps its reference.\n */\nexport const hoistNativeToken = (\n tokens: TokenAmount[],\n selectedChainId?: number\n): TokenAmount[] => {\n if (!selectedChainId) {\n return tokens\n }\n const index = tokens.findIndex((token) =>\n isHoistableNative(token, selectedChainId)\n )\n // -1 is no native token, 0 is already first\n if (index < 1) {\n return tokens\n }\n return [tokens[index], ...tokens.slice(0, index), ...tokens.slice(index + 1)]\n}\n"],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ //#region src/utils/tokenListBands.ts
2
+ const bandHeaderAtListStart = 24;
3
+ const bandHeaderAfterBand = 32;
4
+ const toggleRowHeight = 26;
5
+ const isPromoted = (token) => !!token?.pinned || !!token?.recent;
6
+ const createBandResolver = (tokens, { showCategories, showPinnedTokens, nativeHoisted, recentStartIndex, recentCount, showRecentToggle }) => {
7
+ const listStartIndex = nativeHoisted ? 1 : 0;
8
+ const recentEndIndex = recentStartIndex + recentCount - 1;
9
+ const isFirstPinned = (index) => !!tokens[index]?.pinned && index === listStartIndex;
10
+ const isFirstRecent = (index) => recentCount > 0 && index === recentStartIndex && !!tokens[index]?.recent;
11
+ const isLastRecent = (index) => recentCount > 0 && index === recentEndIndex && !!tokens[index]?.recent;
12
+ const previousOf = (index) => index === listStartIndex ? void 0 : tokens[index - 1];
13
+ const isTransitionFromPromoted = (index) => isPromoted(tokens[index - 1]) && !isPromoted(tokens[index]);
14
+ const isTopRow = (index) => index === 0;
15
+ const headerHeight = (index) => isTopRow(index) ? bandHeaderAtListStart : bandHeaderAfterBand;
16
+ const getRowExtraHeight = (index) => {
17
+ const current = tokens[index];
18
+ const previous = previousOf(index);
19
+ let extra = 0;
20
+ if (showPinnedTokens && isFirstPinned(index)) extra += headerHeight(index);
21
+ if (isFirstRecent(index)) extra += headerHeight(index);
22
+ if (showRecentToggle && isLastRecent(index)) extra += toggleRowHeight;
23
+ if ((showPinnedTokens || showCategories || recentCount > 0) && isTransitionFromPromoted(index)) extra += bandHeaderAfterBand;
24
+ if (!showCategories) return extra;
25
+ if (current?.featured && !isPromoted(current) && index === listStartIndex) extra += headerHeight(index);
26
+ const isNotPromoted = !isPromoted(current) && !isPromoted(previous);
27
+ if (index !== listStartIndex && isNotPromoted && (previous?.amount && !current?.amount || previous?.featured && !current?.featured || previous?.popular && !current?.popular)) extra += bandHeaderAfterBand;
28
+ return extra;
29
+ };
30
+ const getRowBandLabel = (index) => {
31
+ const current = tokens[index];
32
+ const previous = previousOf(index);
33
+ const isListStart = index === listStartIndex;
34
+ const notPromoted = !isPromoted(current);
35
+ const fromPromoted = isPromoted(previous) && notPromoted;
36
+ if (showPinnedTokens && isFirstPinned(index)) return {
37
+ kind: "text",
38
+ key: "main.pinnedTokens",
39
+ atListStart: isTopRow(index)
40
+ };
41
+ if (isFirstRecent(index)) return {
42
+ kind: "recent",
43
+ atListStart: isTopRow(index)
44
+ };
45
+ if ((showPinnedTokens || recentCount > 0) && !showCategories && fromPromoted) return {
46
+ kind: "text",
47
+ key: "main.allTokens",
48
+ atListStart: false
49
+ };
50
+ if (!showCategories) return;
51
+ const fromFeatured = !!previous?.featured && !current?.featured && notPromoted;
52
+ const fromMyTokens = !!previous?.amount && !current?.amount && notPromoted;
53
+ const fromPopular = !!previous?.popular && !current?.popular && notPromoted;
54
+ if (fromPromoted && current?.featured || current?.featured && notPromoted && isListStart) return {
55
+ kind: "text",
56
+ key: "main.featuredTokens",
57
+ atListStart: isTopRow(index)
58
+ };
59
+ if ((fromFeatured || fromPromoted) && current?.amount && notPromoted) return {
60
+ kind: "text",
61
+ key: "main.myTokens",
62
+ atListStart: false
63
+ };
64
+ if ((fromFeatured || fromMyTokens || fromPromoted) && current?.popular && notPromoted) return {
65
+ kind: "text",
66
+ key: "main.popularTokens",
67
+ atListStart: false
68
+ };
69
+ if (fromMyTokens || fromFeatured || fromPromoted || fromPopular) return {
70
+ kind: "text",
71
+ key: "main.allTokens",
72
+ atListStart: false
73
+ };
74
+ };
75
+ const isToggleRow = (index) => showRecentToggle && isLastRecent(index);
76
+ return {
77
+ getRowExtraHeight,
78
+ getRowBandLabel,
79
+ isToggleRow
80
+ };
81
+ };
82
+ //#endregion
83
+ export { createBandResolver };
84
+
85
+ //# sourceMappingURL=tokenListBands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokenListBands.js","names":[],"sources":["../../../src/utils/tokenListBands.ts"],"sourcesContent":["import type { TokenAmount } from '../types/token.js'\n\nconst bandHeaderAtListStart = 24\nconst bandHeaderAfterBand = 32\nconst toggleRowHeight = 26\n\ntype BandLabelKey =\n | 'main.pinnedTokens'\n | 'main.featuredTokens'\n | 'main.myTokens'\n | 'main.popularTokens'\n | 'main.allTokens'\n\nexport type BandLabel =\n | { kind: 'text'; key: BandLabelKey; atListStart: boolean }\n | { kind: 'recent'; atListStart: boolean }\n\nexport interface BandResolverOptions {\n showCategories: boolean\n showPinnedTokens: boolean\n nativeHoisted: boolean\n /** Ignored while `recentCount` is 0. */\n recentStartIndex: number\n recentCount: number\n showRecentToggle: boolean\n}\n\nexport interface BandResolver {\n /** Called for every index by the virtualizer, so it allocates nothing. */\n getRowExtraHeight: (index: number) => number\n /** Called for visible rows only, so the label may be resolved lazily. */\n getRowBandLabel: (index: number) => BandLabel | undefined\n /** The row the toggle hangs below. Shares one definition with the height. */\n isToggleRow: (index: number) => boolean\n}\n\n// Pinned and recent rows sit above the categories; transitions ignore both.\nconst isPromoted = (token: TokenAmount | undefined): boolean =>\n !!token?.pinned || !!token?.recent\n\nexport const createBandResolver = (\n tokens: TokenAmount[],\n {\n showCategories,\n showPinnedTokens,\n nativeHoisted,\n recentStartIndex,\n recentCount,\n showRecentToggle,\n }: BandResolverOptions\n): BandResolver => {\n const listStartIndex = nativeHoisted ? 1 : 0\n const recentEndIndex = recentStartIndex + recentCount - 1\n\n const isFirstPinned = (index: number) =>\n !!tokens[index]?.pinned && index === listStartIndex\n const isFirstRecent = (index: number) =>\n recentCount > 0 && index === recentStartIndex && !!tokens[index]?.recent\n const isLastRecent = (index: number) =>\n recentCount > 0 && index === recentEndIndex && !!tokens[index]?.recent\n // The hoisted native sits above every band, so the list-start row follows none.\n const previousOf = (index: number) =>\n index === listStartIndex ? undefined : tokens[index - 1]\n const isTransitionFromPromoted = (index: number) =>\n isPromoted(tokens[index - 1]) && !isPromoted(tokens[index])\n // Only the top row has no card above it; below the hoisted native one does.\n const isTopRow = (index: number) => index === 0\n const headerHeight = (index: number) =>\n isTopRow(index) ? bandHeaderAtListStart : bandHeaderAfterBand\n\n const getRowExtraHeight = (index: number): number => {\n const current = tokens[index]\n const previous = previousOf(index)\n let extra = 0\n\n if (showPinnedTokens && isFirstPinned(index)) {\n extra += headerHeight(index)\n }\n\n if (isFirstRecent(index)) {\n extra += headerHeight(index)\n }\n\n if (showRecentToggle && isLastRecent(index)) {\n extra += toggleRowHeight\n }\n\n // The recent band needs its own term: all-networks has no categories.\n if (\n (showPinnedTokens || showCategories || recentCount > 0) &&\n isTransitionFromPromoted(index)\n ) {\n extra += bandHeaderAfterBand\n }\n\n if (!showCategories) {\n return extra\n }\n\n if (current?.featured && !isPromoted(current) && index === listStartIndex) {\n extra += headerHeight(index)\n }\n\n const isNotPromoted = !isPromoted(current) && !isPromoted(previous)\n if (\n index !== listStartIndex &&\n isNotPromoted &&\n ((previous?.amount && !current?.amount) ||\n (previous?.featured && !current?.featured) ||\n (previous?.popular && !current?.popular))\n ) {\n extra += bandHeaderAfterBand\n }\n\n return extra\n }\n\n const getRowBandLabel = (index: number): BandLabel | undefined => {\n const current = tokens[index]\n const previous = previousOf(index)\n const isListStart = index === listStartIndex\n const notPromoted = !isPromoted(current)\n const fromPromoted = isPromoted(previous) && notPromoted\n\n if (showPinnedTokens && isFirstPinned(index)) {\n return {\n kind: 'text',\n key: 'main.pinnedTokens',\n atListStart: isTopRow(index),\n }\n }\n if (isFirstRecent(index)) {\n return { kind: 'recent', atListStart: isTopRow(index) }\n }\n if (\n (showPinnedTokens || recentCount > 0) &&\n !showCategories &&\n fromPromoted\n ) {\n return { kind: 'text', key: 'main.allTokens', atListStart: false }\n }\n if (!showCategories) {\n return undefined\n }\n\n const fromFeatured =\n !!previous?.featured && !current?.featured && notPromoted\n const fromMyTokens = !!previous?.amount && !current?.amount && notPromoted\n const fromPopular = !!previous?.popular && !current?.popular && notPromoted\n\n if (\n (fromPromoted && current?.featured) ||\n (current?.featured && notPromoted && isListStart)\n ) {\n return {\n kind: 'text',\n key: 'main.featuredTokens',\n atListStart: isTopRow(index),\n }\n }\n if ((fromFeatured || fromPromoted) && current?.amount && notPromoted) {\n return { kind: 'text', key: 'main.myTokens', atListStart: false }\n }\n if (\n (fromFeatured || fromMyTokens || fromPromoted) &&\n current?.popular &&\n notPromoted\n ) {\n return { kind: 'text', key: 'main.popularTokens', atListStart: false }\n }\n if (fromMyTokens || fromFeatured || fromPromoted || fromPopular) {\n return { kind: 'text', key: 'main.allTokens', atListStart: false }\n }\n return undefined\n }\n\n const isToggleRow = (index: number): boolean =>\n showRecentToggle && isLastRecent(index)\n\n return { getRowExtraHeight, getRowBandLabel, isToggleRow }\n}\n"],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ //#region src/utils/url.d.ts
2
+ export declare const isHttpUrl: (url: string | undefined) => url is string;
3
+ export declare const openInNewTab: (url: string | undefined) => void;
4
+ //#endregion
5
+ //# sourceMappingURL=url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.d.ts","names":[],"sources":["../../../src/utils/url.ts"],"mappings":";qBAAa,YAAa,4BAA0B;qBAYvC,eAAgB"}
@@ -0,0 +1,17 @@
1
+ //#region src/utils/url.ts
2
+ const isHttpUrl = (url) => {
3
+ if (!url) return false;
4
+ try {
5
+ const { protocol } = new URL(url);
6
+ return protocol === "https:" || protocol === "http:";
7
+ } catch {
8
+ return false;
9
+ }
10
+ };
11
+ const openInNewTab = (url) => {
12
+ if (isHttpUrl(url)) window.open(url, "_blank", "noopener,noreferrer");
13
+ };
14
+ //#endregion
15
+ export { isHttpUrl, openInNewTab };
16
+
17
+ //# sourceMappingURL=url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.js","names":[],"sources":["../../../src/utils/url.ts"],"sourcesContent":["export const isHttpUrl = (url: string | undefined): url is string => {\n if (!url) {\n return false\n }\n try {\n const { protocol } = new URL(url)\n return protocol === 'https:' || protocol === 'http:'\n } catch {\n return false\n }\n}\n\nexport const openInNewTab = (url: string | undefined): void => {\n if (isHttpUrl(url)) {\n window.open(url, '_blank', 'noopener,noreferrer')\n }\n}\n"],"mappings":";AAAA,MAAa,aAAa,QAA2C;CACnE,IAAI,CAAC,KACH,OAAO;CAET,IAAI;EACF,MAAM,EAAE,aAAa,IAAI,IAAI,GAAG;EAChC,OAAO,aAAa,YAAY,aAAa;CAC/C,QAAQ;EACN,OAAO;CACT;AACF;AAEA,MAAa,gBAAgB,QAAkC;CAC7D,IAAI,UAAU,GAAG,GACf,OAAO,KAAK,KAAK,UAAU,qBAAqB;AAEpD"}
@@ -1,5 +1,4 @@
1
1
  //#region src/utils/wallet.d.ts
2
- declare const shortenAddress: (address?: string) => string | null;
2
+ export declare const shortenAddress: (address?: string) => string | null;
3
3
  //#endregion
4
- export { shortenAddress };
5
4
  //# sourceMappingURL=wallet.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"wallet.d.ts","names":[],"sources":["../../../src/utils/wallet.ts"],"mappings":";cAAa,iBAAkB"}
1
+ {"version":3,"file":"wallet.d.ts","names":[],"sources":["../../../src/utils/wallet.ts"],"mappings":";qBAAa,iBAAkB"}