@lifi/widget 4.6.0 → 4.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (394) hide show
  1. package/README.md +26 -3
  2. package/dist/esm/App.d.ts +1 -2
  3. package/dist/esm/App.d.ts.map +1 -1
  4. package/dist/esm/AppDrawer.d.ts +1 -2
  5. package/dist/esm/AppDrawer.d.ts.map +1 -1
  6. package/dist/esm/components/ActionRow/ActionRow.d.ts +1 -2
  7. package/dist/esm/components/ActionRow/ActionRow.d.ts.map +1 -1
  8. package/dist/esm/components/AmountInput/AmountInput.style.d.ts +2 -2
  9. package/dist/esm/components/AmountInput/AmountInput.style.d.ts.map +1 -1
  10. package/dist/esm/components/AmountInput/AmountInputEndAdornment.d.ts +1 -2
  11. package/dist/esm/components/AmountInput/AmountInputEndAdornment.d.ts.map +1 -1
  12. package/dist/esm/components/AmountInput/PriceFormHelperText.style.d.ts +1 -2
  13. package/dist/esm/components/AmountInput/PriceFormHelperText.style.d.ts.map +1 -1
  14. package/dist/esm/components/AmountInputCard/ReceiveAmountCard.js +6 -2
  15. package/dist/esm/components/AmountInputCard/ReceiveAmountCard.js.map +1 -1
  16. package/dist/esm/components/Avatar/Avatar.d.ts +1 -2
  17. package/dist/esm/components/Avatar/Avatar.d.ts.map +1 -1
  18. package/dist/esm/components/Avatar/TokenAvatar.d.ts +1 -2
  19. package/dist/esm/components/Avatar/TokenAvatar.d.ts.map +1 -1
  20. package/dist/esm/components/BaseTransactionButton/BaseTransactionButton.d.ts +1 -2
  21. package/dist/esm/components/BaseTransactionButton/BaseTransactionButton.d.ts.map +1 -1
  22. package/dist/esm/components/BaseTransactionButton/types.d.ts +1 -2
  23. package/dist/esm/components/BaseTransactionButton/types.d.ts.map +1 -1
  24. package/dist/esm/components/BottomSheet/BottomSheet.js +1 -0
  25. package/dist/esm/components/BottomSheet/BottomSheet.js.map +1 -1
  26. package/dist/esm/components/BottomSheet/types.d.ts +1 -2
  27. package/dist/esm/components/BottomSheet/types.d.ts.map +1 -1
  28. package/dist/esm/components/ButtonTertiary.d.ts +1 -2
  29. package/dist/esm/components/ButtonTertiary.d.ts.map +1 -1
  30. package/dist/esm/components/Card/CardIconButton.d.ts +1 -2
  31. package/dist/esm/components/Card/CardIconButton.d.ts.map +1 -1
  32. package/dist/esm/components/Card/CardTitle.d.ts +1 -2
  33. package/dist/esm/components/Card/CardTitle.d.ts.map +1 -1
  34. package/dist/esm/components/Card/InputCard.d.ts +1 -2
  35. package/dist/esm/components/Card/InputCard.d.ts.map +1 -1
  36. package/dist/esm/components/ChainSelect/ChainSelect.d.ts +1 -2
  37. package/dist/esm/components/ChainSelect/ChainSelect.d.ts.map +1 -1
  38. package/dist/esm/components/ChainSelect/ChainSelect.style.d.ts +1 -2
  39. package/dist/esm/components/ChainSelect/ChainSelect.style.d.ts.map +1 -1
  40. package/dist/esm/components/ContractComponent/ContractComponent.d.ts +1 -2
  41. package/dist/esm/components/ContractComponent/ContractComponent.d.ts.map +1 -1
  42. package/dist/esm/components/ContractComponent/ItemPrice.d.ts +2 -3
  43. package/dist/esm/components/ContractComponent/ItemPrice.d.ts.map +1 -1
  44. package/dist/esm/components/ContractComponent/NFT/NFT.d.ts +1 -2
  45. package/dist/esm/components/ContractComponent/NFT/NFT.d.ts.map +1 -1
  46. package/dist/esm/components/ContractComponent/NFT/NFTBase.d.ts +1 -2
  47. package/dist/esm/components/ContractComponent/NFT/NFTBase.d.ts.map +1 -1
  48. package/dist/esm/components/ContractComponent/NFT/types.d.ts +3 -4
  49. package/dist/esm/components/ContractComponent/NFT/types.d.ts.map +1 -1
  50. package/dist/esm/components/ExecutionStatusCard/ExecutionStatusCard.d.ts +1 -2
  51. package/dist/esm/components/ExecutionStatusCard/ExecutionStatusCard.d.ts.map +1 -1
  52. package/dist/esm/components/ExecutionStatusCard/StatusIcon.d.ts +1 -2
  53. package/dist/esm/components/ExecutionStatusCard/StatusIcon.d.ts.map +1 -1
  54. package/dist/esm/components/FeeBreakdownTooltip.d.ts +1 -2
  55. package/dist/esm/components/FeeBreakdownTooltip.d.ts.map +1 -1
  56. package/dist/esm/components/IconCircle/IconCircle.d.ts +1 -2
  57. package/dist/esm/components/IconCircle/IconCircle.d.ts.map +1 -1
  58. package/dist/esm/components/IconCircle/statusIcons.d.ts +1 -2
  59. package/dist/esm/components/IconCircle/statusIcons.d.ts.map +1 -1
  60. package/dist/esm/components/IconTypography.d.ts +1 -2
  61. package/dist/esm/components/IconTypography.d.ts.map +1 -1
  62. package/dist/esm/components/ListItem/ListItemButton.d.ts +1 -2
  63. package/dist/esm/components/ListItem/ListItemButton.d.ts.map +1 -1
  64. package/dist/esm/components/Messages/WarningMessages.d.ts +1 -2
  65. package/dist/esm/components/Messages/WarningMessages.d.ts.map +1 -1
  66. package/dist/esm/components/PageContainer.d.ts +2 -3
  67. package/dist/esm/components/PageContainer.d.ts.map +1 -1
  68. package/dist/esm/components/PoweredBy/PoweredBy.d.ts +1 -2
  69. package/dist/esm/components/PoweredBy/PoweredBy.d.ts.map +1 -1
  70. package/dist/esm/components/ProgressToNextUpdate.d.ts +1 -2
  71. package/dist/esm/components/ProgressToNextUpdate.d.ts.map +1 -1
  72. package/dist/esm/components/RouteCard/RouteCard.d.ts +1 -2
  73. package/dist/esm/components/RouteCard/RouteCard.d.ts.map +1 -1
  74. package/dist/esm/components/RouteCard/RouteCardSkeleton.d.ts +1 -2
  75. package/dist/esm/components/RouteCard/RouteCardSkeleton.d.ts.map +1 -1
  76. package/dist/esm/components/RouteCard/RouteDetails.d.ts +1 -2
  77. package/dist/esm/components/RouteCard/RouteDetails.d.ts.map +1 -1
  78. package/dist/esm/components/RouteCard/RouteDetails.style.d.ts +5 -6
  79. package/dist/esm/components/RouteCard/RouteDetails.style.d.ts.map +1 -1
  80. package/dist/esm/components/RouteCard/RouteNotFoundCard.d.ts +1 -2
  81. package/dist/esm/components/RouteCard/RouteNotFoundCard.d.ts.map +1 -1
  82. package/dist/esm/components/RouteCard/RouteTokens.d.ts +1 -2
  83. package/dist/esm/components/RouteCard/RouteTokens.d.ts.map +1 -1
  84. package/dist/esm/components/RouteCard/types.d.ts +2 -3
  85. package/dist/esm/components/RouteCard/types.d.ts.map +1 -1
  86. package/dist/esm/components/Search/SearchInput.d.ts +1 -2
  87. package/dist/esm/components/Search/SearchInput.d.ts.map +1 -1
  88. package/dist/esm/components/SendToWallet/SendToWalletButton.d.ts +1 -2
  89. package/dist/esm/components/SendToWallet/SendToWalletButton.d.ts.map +1 -1
  90. package/dist/esm/components/Skeleton/WidgetSkeleton.d.ts +1 -2
  91. package/dist/esm/components/Skeleton/WidgetSkeleton.d.ts.map +1 -1
  92. package/dist/esm/components/StepActions/SentToWalletRow.d.ts +1 -2
  93. package/dist/esm/components/StepActions/SentToWalletRow.d.ts.map +1 -1
  94. package/dist/esm/components/StepActions/StepActionRow.style.d.ts +1 -2
  95. package/dist/esm/components/StepActions/StepActionRow.style.d.ts.map +1 -1
  96. package/dist/esm/components/StepActions/StepActionsList.d.ts +1 -2
  97. package/dist/esm/components/StepActions/StepActionsList.d.ts.map +1 -1
  98. package/dist/esm/components/StepActions/executionRows.d.ts +2 -3
  99. package/dist/esm/components/StepActions/executionRows.d.ts.map +1 -1
  100. package/dist/esm/components/Token/Token.d.ts.map +1 -1
  101. package/dist/esm/components/Token/Token.js +2 -4
  102. package/dist/esm/components/Token/Token.js.map +1 -1
  103. package/dist/esm/components/TokenList/RecentTokensHeader.js +44 -0
  104. package/dist/esm/components/TokenList/RecentTokensHeader.js.map +1 -0
  105. package/dist/esm/components/TokenList/RecentTokensToggle.js +36 -0
  106. package/dist/esm/components/TokenList/RecentTokensToggle.js.map +1 -0
  107. package/dist/esm/components/TokenList/RemoveRecentTokenButton.js +32 -0
  108. package/dist/esm/components/TokenList/RemoveRecentTokenButton.js.map +1 -0
  109. package/dist/esm/components/TokenList/TokenList.js +55 -5
  110. package/dist/esm/components/TokenList/TokenList.js.map +1 -1
  111. package/dist/esm/components/TokenList/TokenListItem.js +35 -19
  112. package/dist/esm/components/TokenList/TokenListItem.js.map +1 -1
  113. package/dist/esm/components/TokenList/TokenNotFound.d.ts +1 -2
  114. package/dist/esm/components/TokenList/TokenNotFound.d.ts.map +1 -1
  115. package/dist/esm/components/TokenList/VirtualizedTokenList.d.ts +1 -2
  116. package/dist/esm/components/TokenList/VirtualizedTokenList.d.ts.map +1 -1
  117. package/dist/esm/components/TokenList/VirtualizedTokenList.js +55 -42
  118. package/dist/esm/components/TokenList/VirtualizedTokenList.js.map +1 -1
  119. package/dist/esm/components/TokenList/types.d.ts +9 -2
  120. package/dist/esm/components/TokenList/types.d.ts.map +1 -1
  121. package/dist/esm/components/TokenList/useTokenSelect.d.ts +1 -2
  122. package/dist/esm/components/TokenList/useTokenSelect.d.ts.map +1 -1
  123. package/dist/esm/components/TokenRate/TokenRate.d.ts +1 -2
  124. package/dist/esm/components/TokenRate/TokenRate.d.ts.map +1 -1
  125. package/dist/esm/components/TransactionCard/TransactionCard.style.d.ts +2 -3
  126. package/dist/esm/components/TransactionCard/TransactionCard.style.d.ts.map +1 -1
  127. package/dist/esm/config/constants.d.ts +1 -2
  128. package/dist/esm/config/constants.d.ts.map +1 -1
  129. package/dist/esm/config/version.d.ts +2 -3
  130. package/dist/esm/config/version.d.ts.map +1 -1
  131. package/dist/esm/config/version.js +1 -1
  132. package/dist/esm/config/version.js.map +1 -1
  133. package/dist/esm/hooks/useAddressActivity.d.ts +1 -2
  134. package/dist/esm/hooks/useAddressActivity.d.ts.map +1 -1
  135. package/dist/esm/hooks/useAddressValidation.d.ts +2 -3
  136. package/dist/esm/hooks/useAddressValidation.d.ts.map +1 -1
  137. package/dist/esm/hooks/useAvailableChains.d.ts +1 -2
  138. package/dist/esm/hooks/useAvailableChains.d.ts.map +1 -1
  139. package/dist/esm/hooks/useChain.d.ts +1 -2
  140. package/dist/esm/hooks/useChain.d.ts.map +1 -1
  141. package/dist/esm/hooks/useContactSupport.d.ts +1 -2
  142. package/dist/esm/hooks/useContactSupport.d.ts.map +1 -1
  143. package/dist/esm/hooks/useDebouncedWatch.d.ts +1 -2
  144. package/dist/esm/hooks/useDebouncedWatch.d.ts.map +1 -1
  145. package/dist/esm/hooks/useDisplayedTokens.js +6 -4
  146. package/dist/esm/hooks/useDisplayedTokens.js.map +1 -1
  147. package/dist/esm/hooks/useExplorer.d.ts +1 -2
  148. package/dist/esm/hooks/useExplorer.d.ts.map +1 -1
  149. package/dist/esm/hooks/useHeader.d.ts +1 -2
  150. package/dist/esm/hooks/useHeader.d.ts.map +1 -1
  151. package/dist/esm/hooks/useListHeight.d.ts +1 -2
  152. package/dist/esm/hooks/useListHeight.d.ts.map +1 -1
  153. package/dist/esm/hooks/useNavigateBack.d.ts +1 -2
  154. package/dist/esm/hooks/useNavigateBack.d.ts.map +1 -1
  155. package/dist/esm/hooks/useRecentTokens.js +46 -0
  156. package/dist/esm/hooks/useRecentTokens.js.map +1 -0
  157. package/dist/esm/hooks/useRouteExecution.d.ts +1 -2
  158. package/dist/esm/hooks/useRouteExecution.d.ts.map +1 -1
  159. package/dist/esm/hooks/useRouteExecutionMessage.d.ts +1 -2
  160. package/dist/esm/hooks/useRouteExecutionMessage.d.ts.map +1 -1
  161. package/dist/esm/hooks/useRoutes.d.ts +1 -2
  162. package/dist/esm/hooks/useRoutes.d.ts.map +1 -1
  163. package/dist/esm/hooks/useRoutes.js +7 -2
  164. package/dist/esm/hooks/useRoutes.js.map +1 -1
  165. package/dist/esm/hooks/useToAddressRequirements.d.ts +1 -2
  166. package/dist/esm/hooks/useToAddressRequirements.d.ts.map +1 -1
  167. package/dist/esm/hooks/useToAddressRequirements.js +8 -2
  168. package/dist/esm/hooks/useToAddressRequirements.js.map +1 -1
  169. package/dist/esm/hooks/useToken.d.ts +1 -2
  170. package/dist/esm/hooks/useToken.d.ts.map +1 -1
  171. package/dist/esm/hooks/useTokenAddressBalance.d.ts +1 -2
  172. package/dist/esm/hooks/useTokenAddressBalance.d.ts.map +1 -1
  173. package/dist/esm/hooks/useTokenBalances.d.ts +3 -2
  174. package/dist/esm/hooks/useTokenBalances.d.ts.map +1 -1
  175. package/dist/esm/hooks/useTokenBalances.js +15 -9
  176. package/dist/esm/hooks/useTokenBalances.js.map +1 -1
  177. package/dist/esm/hooks/useTokenList.d.ts +1 -2
  178. package/dist/esm/hooks/useTokenList.d.ts.map +1 -1
  179. package/dist/esm/hooks/useTokenSearch.js +3 -2
  180. package/dist/esm/hooks/useTokenSearch.js.map +1 -1
  181. package/dist/esm/hooks/useTokens.js +26 -15
  182. package/dist/esm/hooks/useTokens.js.map +1 -1
  183. package/dist/esm/hooks/useTools.d.ts +1 -2
  184. package/dist/esm/hooks/useTools.d.ts.map +1 -1
  185. package/dist/esm/hooks/useWidgetChains.d.ts +1 -2
  186. package/dist/esm/hooks/useWidgetChains.d.ts.map +1 -1
  187. package/dist/esm/hooks/useWidgetEvents.d.ts +3 -4
  188. package/dist/esm/hooks/useWidgetEvents.d.ts.map +1 -1
  189. package/dist/esm/i18n/bn.json +4 -0
  190. package/dist/esm/i18n/de.json +4 -0
  191. package/dist/esm/i18n/en.json +4 -0
  192. package/dist/esm/i18n/es.json +4 -0
  193. package/dist/esm/i18n/fr.json +4 -0
  194. package/dist/esm/i18n/hi.json +4 -0
  195. package/dist/esm/i18n/id.json +4 -0
  196. package/dist/esm/i18n/it.json +4 -0
  197. package/dist/esm/i18n/ja.json +4 -0
  198. package/dist/esm/i18n/ko.json +4 -0
  199. package/dist/esm/i18n/pl.json +4 -0
  200. package/dist/esm/i18n/pt.json +4 -0
  201. package/dist/esm/i18n/th.json +4 -0
  202. package/dist/esm/i18n/tr.json +4 -0
  203. package/dist/esm/i18n/uk.json +4 -0
  204. package/dist/esm/i18n/vi.json +4 -0
  205. package/dist/esm/i18n/zh.json +4 -0
  206. package/dist/esm/pages/MainPage/MainWarningMessages.d.ts +1 -2
  207. package/dist/esm/pages/MainPage/MainWarningMessages.d.ts.map +1 -1
  208. package/dist/esm/pages/SelectChainPage/SelectChainPage.d.ts +1 -2
  209. package/dist/esm/pages/SelectChainPage/SelectChainPage.d.ts.map +1 -1
  210. package/dist/esm/pages/SelectChainPage/types.d.ts +1 -2
  211. package/dist/esm/pages/SelectChainPage/types.d.ts.map +1 -1
  212. package/dist/esm/pages/SelectTokenPage/SearchTokenInput.d.ts +1 -2
  213. package/dist/esm/pages/SelectTokenPage/SearchTokenInput.d.ts.map +1 -1
  214. package/dist/esm/pages/TransactionDetailsPage/ContactSupportButton.d.ts +1 -2
  215. package/dist/esm/pages/TransactionDetailsPage/ContactSupportButton.d.ts.map +1 -1
  216. package/dist/esm/pages/TransactionPage/ConfirmToAddressSheet.d.ts +1 -2
  217. package/dist/esm/pages/TransactionPage/ConfirmToAddressSheet.d.ts.map +1 -1
  218. package/dist/esm/pages/TransactionPage/ExchangeRateBottomSheet.d.ts +2 -3
  219. package/dist/esm/pages/TransactionPage/ExchangeRateBottomSheet.d.ts.map +1 -1
  220. package/dist/esm/pages/TransactionPage/ExecutionDoneCard.d.ts +1 -2
  221. package/dist/esm/pages/TransactionPage/ExecutionDoneCard.d.ts.map +1 -1
  222. package/dist/esm/pages/TransactionPage/RouteTracker.d.ts +1 -2
  223. package/dist/esm/pages/TransactionPage/RouteTracker.d.ts.map +1 -1
  224. package/dist/esm/pages/TransactionPage/StartTransactionButton.d.ts +1 -2
  225. package/dist/esm/pages/TransactionPage/StartTransactionButton.d.ts.map +1 -1
  226. package/dist/esm/pages/TransactionPage/TokenValueBottomSheet.d.ts +1 -2
  227. package/dist/esm/pages/TransactionPage/TokenValueBottomSheet.d.ts.map +1 -1
  228. package/dist/esm/pages/TransactionPage/TokenValueBottomSheet.js +0 -1
  229. package/dist/esm/pages/TransactionPage/TokenValueBottomSheet.js.map +1 -1
  230. package/dist/esm/pages/TransactionPage/TokenVerificationBottomSheet.js +0 -1
  231. package/dist/esm/pages/TransactionPage/TokenVerificationBottomSheet.js.map +1 -1
  232. package/dist/esm/pages/TransactionPage/TransactionDoneButtons.d.ts +1 -2
  233. package/dist/esm/pages/TransactionPage/TransactionDoneButtons.d.ts.map +1 -1
  234. package/dist/esm/pages/TransactionPage/TransactionFailedButtons.js +38 -12
  235. package/dist/esm/pages/TransactionPage/TransactionFailedButtons.js.map +1 -1
  236. package/dist/esm/pages/TransactionPage/TransactionReview.js +22 -21
  237. package/dist/esm/pages/TransactionPage/TransactionReview.js.map +1 -1
  238. package/dist/esm/pages/TransactionPage/types.d.ts +1 -2
  239. package/dist/esm/pages/TransactionPage/types.d.ts.map +1 -1
  240. package/dist/esm/pages/TransactionPage/utils.d.ts +2 -3
  241. package/dist/esm/pages/TransactionPage/utils.d.ts.map +1 -1
  242. package/dist/esm/pages/TransactionPage/utils.js +23 -1
  243. package/dist/esm/pages/TransactionPage/utils.js.map +1 -1
  244. package/dist/esm/providers/I18nProvider/I18nProvider.d.ts +1 -2
  245. package/dist/esm/providers/I18nProvider/I18nProvider.d.ts.map +1 -1
  246. package/dist/esm/providers/I18nProvider/types.d.ts +4 -5
  247. package/dist/esm/providers/I18nProvider/types.d.ts.map +1 -1
  248. package/dist/esm/providers/QueryClientProvider.d.ts +1 -2
  249. package/dist/esm/providers/QueryClientProvider.d.ts.map +1 -1
  250. package/dist/esm/providers/SDKClientProvider.d.ts +2 -3
  251. package/dist/esm/providers/SDKClientProvider.d.ts.map +1 -1
  252. package/dist/esm/providers/WalletProvider/WalletProvider.d.ts +1 -2
  253. package/dist/esm/providers/WalletProvider/WalletProvider.d.ts.map +1 -1
  254. package/dist/esm/providers/WidgetProvider/WidgetProvider.d.ts +2 -3
  255. package/dist/esm/providers/WidgetProvider/WidgetProvider.d.ts.map +1 -1
  256. package/dist/esm/providers/WidgetProvider/types.d.ts +2 -3
  257. package/dist/esm/providers/WidgetProvider/types.d.ts.map +1 -1
  258. package/dist/esm/stores/StoreProvider.d.ts +1 -2
  259. package/dist/esm/stores/StoreProvider.d.ts.map +1 -1
  260. package/dist/esm/stores/StoreProvider.js +10 -6
  261. package/dist/esm/stores/StoreProvider.js.map +1 -1
  262. package/dist/esm/stores/bookmarks/BookmarkStore.d.ts +1 -2
  263. package/dist/esm/stores/bookmarks/BookmarkStore.d.ts.map +1 -1
  264. package/dist/esm/stores/bookmarks/types.d.ts +5 -6
  265. package/dist/esm/stores/bookmarks/types.d.ts.map +1 -1
  266. package/dist/esm/stores/chains/ChainOrderStore.d.ts +1 -2
  267. package/dist/esm/stores/chains/ChainOrderStore.d.ts.map +1 -1
  268. package/dist/esm/stores/chains/ChainOrderStore.js +11 -5
  269. package/dist/esm/stores/chains/ChainOrderStore.js.map +1 -1
  270. package/dist/esm/stores/chains/types.d.ts +1 -2
  271. package/dist/esm/stores/chains/types.d.ts.map +1 -1
  272. package/dist/esm/stores/form/types.d.ts +20 -21
  273. package/dist/esm/stores/form/types.d.ts.map +1 -1
  274. package/dist/esm/stores/form/useFieldActions.d.ts +1 -2
  275. package/dist/esm/stores/form/useFieldActions.d.ts.map +1 -1
  276. package/dist/esm/stores/form/useFieldValues.d.ts +1 -2
  277. package/dist/esm/stores/form/useFieldValues.d.ts.map +1 -1
  278. package/dist/esm/stores/header/types.d.ts +1 -2
  279. package/dist/esm/stores/header/types.d.ts.map +1 -1
  280. package/dist/esm/stores/header/useHeaderStore.d.ts +2 -3
  281. package/dist/esm/stores/header/useHeaderStore.d.ts.map +1 -1
  282. package/dist/esm/stores/inputMode/useInputModeStore.d.ts +1 -2
  283. package/dist/esm/stores/inputMode/useInputModeStore.d.ts.map +1 -1
  284. package/dist/esm/stores/recentTokens/RecentTokensStore.js +23 -0
  285. package/dist/esm/stores/recentTokens/RecentTokensStore.js.map +1 -0
  286. package/dist/esm/stores/recentTokens/createRecentTokensStore.js +54 -0
  287. package/dist/esm/stores/recentTokens/createRecentTokensStore.js.map +1 -0
  288. package/dist/esm/stores/recentTokens/utils.js +15 -0
  289. package/dist/esm/stores/recentTokens/utils.js.map +1 -0
  290. package/dist/esm/stores/routes/RouteExecutionStore.d.ts +2 -3
  291. package/dist/esm/stores/routes/RouteExecutionStore.d.ts.map +1 -1
  292. package/dist/esm/stores/routes/types.d.ts +3 -4
  293. package/dist/esm/stores/routes/types.d.ts.map +1 -1
  294. package/dist/esm/stores/routes/utils.d.ts +4 -5
  295. package/dist/esm/stores/routes/utils.d.ts.map +1 -1
  296. package/dist/esm/stores/settings/SettingsStore.d.ts +2 -3
  297. package/dist/esm/stores/settings/SettingsStore.d.ts.map +1 -1
  298. package/dist/esm/stores/settings/types.d.ts +2 -3
  299. package/dist/esm/stores/settings/types.d.ts.map +1 -1
  300. package/dist/esm/stores/types.d.ts +2 -3
  301. package/dist/esm/stores/types.d.ts.map +1 -1
  302. package/dist/esm/themes/azureLight.d.ts +1 -2
  303. package/dist/esm/themes/azureLight.d.ts.map +1 -1
  304. package/dist/esm/themes/jumper.d.ts +1 -2
  305. package/dist/esm/themes/jumper.d.ts.map +1 -1
  306. package/dist/esm/themes/palettes.d.ts +3 -4
  307. package/dist/esm/themes/palettes.d.ts.map +1 -1
  308. package/dist/esm/themes/types.d.ts +1 -0
  309. package/dist/esm/themes/watermelonLight.d.ts +1 -2
  310. package/dist/esm/themes/watermelonLight.d.ts.map +1 -1
  311. package/dist/esm/themes/windows95.d.ts +1 -2
  312. package/dist/esm/themes/windows95.d.ts.map +1 -1
  313. package/dist/esm/types/events.d.ts +13 -14
  314. package/dist/esm/types/events.d.ts.map +1 -1
  315. package/dist/esm/types/token.d.ts +4 -2
  316. package/dist/esm/types/token.d.ts.map +1 -1
  317. package/dist/esm/types/widget.d.ts +55 -51
  318. package/dist/esm/types/widget.d.ts.map +1 -1
  319. package/dist/esm/types/widget.js.map +1 -1
  320. package/dist/esm/utils/address.js +19 -0
  321. package/dist/esm/utils/address.js.map +1 -0
  322. package/dist/esm/utils/compactNumberFormatter.d.ts +1 -2
  323. package/dist/esm/utils/compactNumberFormatter.d.ts.map +1 -1
  324. package/dist/esm/utils/converters.d.ts +1 -2
  325. package/dist/esm/utils/converters.d.ts.map +1 -1
  326. package/dist/esm/utils/currencyExtendedFormatter.d.ts +1 -2
  327. package/dist/esm/utils/currencyExtendedFormatter.d.ts.map +1 -1
  328. package/dist/esm/utils/customReceiver.js +6 -1
  329. package/dist/esm/utils/customReceiver.js.map +1 -1
  330. package/dist/esm/utils/elements.d.ts +2 -3
  331. package/dist/esm/utils/elements.d.ts.map +1 -1
  332. package/dist/esm/utils/enum.d.ts +1 -2
  333. package/dist/esm/utils/enum.d.ts.map +1 -1
  334. package/dist/esm/utils/fees.d.ts +2 -3
  335. package/dist/esm/utils/fees.d.ts.map +1 -1
  336. package/dist/esm/utils/format.d.ts +8 -9
  337. package/dist/esm/utils/format.d.ts.map +1 -1
  338. package/dist/esm/utils/getPriceImpact.d.ts +1 -2
  339. package/dist/esm/utils/getPriceImpact.d.ts.map +1 -1
  340. package/dist/esm/utils/input.d.ts +1 -2
  341. package/dist/esm/utils/input.d.ts.map +1 -1
  342. package/dist/esm/utils/navigationRoutes.d.ts +2 -3
  343. package/dist/esm/utils/navigationRoutes.d.ts.map +1 -1
  344. package/dist/esm/utils/percentFormatter.d.ts +1 -2
  345. package/dist/esm/utils/percentFormatter.d.ts.map +1 -1
  346. package/dist/esm/utils/recentTokens.js +97 -0
  347. package/dist/esm/utils/recentTokens.js.map +1 -0
  348. package/dist/esm/utils/token.js +30 -4
  349. package/dist/esm/utils/token.js.map +1 -1
  350. package/dist/esm/utils/tokenList.js +18 -6
  351. package/dist/esm/utils/tokenList.js.map +1 -1
  352. package/dist/esm/utils/tokenListBands.js +85 -0
  353. package/dist/esm/utils/tokenListBands.js.map +1 -0
  354. package/dist/esm/utils/wallet.d.ts +1 -2
  355. package/dist/esm/utils/wallet.d.ts.map +1 -1
  356. package/package.json +9 -9
  357. package/src/components/AmountInputCard/ReceiveAmountCard.tsx +16 -3
  358. package/src/components/BottomSheet/BottomSheet.tsx +3 -0
  359. package/src/components/Token/Token.tsx +2 -1
  360. package/src/components/TokenList/RecentTokensHeader.tsx +52 -0
  361. package/src/components/TokenList/RecentTokensToggle.tsx +45 -0
  362. package/src/components/TokenList/RemoveRecentTokenButton.tsx +38 -0
  363. package/src/components/TokenList/TokenList.tsx +92 -3
  364. package/src/components/TokenList/TokenListItem.tsx +15 -0
  365. package/src/components/TokenList/VirtualizedTokenList.tsx +84 -125
  366. package/src/components/TokenList/types.ts +9 -0
  367. package/src/config/version.ts +1 -1
  368. package/src/hooks/useDisplayedTokens.ts +17 -3
  369. package/src/hooks/useRecentTokens.ts +88 -0
  370. package/src/hooks/useRoutes.ts +8 -5
  371. package/src/hooks/useToAddressRequirements.ts +11 -5
  372. package/src/hooks/useTokenBalances.ts +34 -31
  373. package/src/hooks/useTokenSearch.ts +5 -14
  374. package/src/hooks/useTokens.ts +69 -27
  375. package/src/pages/TransactionPage/TokenValueBottomSheet.tsx +1 -1
  376. package/src/pages/TransactionPage/TokenVerificationBottomSheet.tsx +1 -1
  377. package/src/pages/TransactionPage/TransactionFailedButtons.tsx +62 -26
  378. package/src/pages/TransactionPage/TransactionReview.tsx +36 -37
  379. package/src/pages/TransactionPage/utils.ts +78 -0
  380. package/src/stores/StoreProvider.tsx +10 -7
  381. package/src/stores/chains/ChainOrderStore.test.tsx +298 -0
  382. package/src/stores/chains/ChainOrderStore.tsx +25 -6
  383. package/src/stores/recentTokens/RecentTokensStore.tsx +39 -0
  384. package/src/stores/recentTokens/createRecentTokensStore.ts +107 -0
  385. package/src/stores/recentTokens/types.ts +37 -0
  386. package/src/stores/recentTokens/utils.ts +18 -0
  387. package/src/types/token.ts +2 -0
  388. package/src/types/widget.ts +5 -0
  389. package/src/utils/address.ts +21 -0
  390. package/src/utils/customReceiver.ts +24 -0
  391. package/src/utils/recentTokens.ts +187 -0
  392. package/src/utils/token.ts +69 -10
  393. package/src/utils/tokenList.ts +41 -23
  394. package/src/utils/tokenListBands.ts +181 -0
@@ -0,0 +1,187 @@
1
+ import type { FormType } from '../stores/form/types.js'
2
+ import type {
3
+ RecentToken,
4
+ RecentTokenId,
5
+ } from '../stores/recentTokens/types.js'
6
+ import { getTokenKey } from '../stores/recentTokens/utils.js'
7
+ import type { TokenAmount } from '../types/token.js'
8
+ import type { AllowDenySets, WidgetTokens } from '../types/widget.js'
9
+ import { isFormItemAllowed } from './item.js'
10
+ import { getChainTokenAllowSets } from './token.js'
11
+
12
+ export const collapsedRecentCount = 4
13
+
14
+ export interface ResolveRecentRowsParams {
15
+ recentTokens: RecentToken[]
16
+ availableChainIds: Set<number>
17
+ configTokens: WidgetTokens | undefined
18
+ formType: FormType
19
+ selectedChainId?: number
20
+ isAllNetworks?: boolean
21
+ nativeHoisted: boolean
22
+ disabled: boolean
23
+ }
24
+
25
+ export interface RecentRows {
26
+ /** Unsliced; empty when the band is inactive. */
27
+ rows: TokenAmount[]
28
+ /** Everything Clear removes, including toggled-away and displaced rows. */
29
+ bandEntries: RecentTokenId[]
30
+ recentStartIndex: number
31
+ }
32
+
33
+ export interface RecentTokensResult {
34
+ tokens: TokenAmount[]
35
+ recentCount: number
36
+ totalRecentCount: number
37
+ }
38
+
39
+ // Whitelisted fields; a flag is the one verdict kept, as stale it only over-warns.
40
+ const toSnapshotRow = (recent: RecentToken): TokenAmount =>
41
+ ({
42
+ chainId: recent.chainId,
43
+ address: recent.address,
44
+ symbol: recent.symbol,
45
+ name: recent.name,
46
+ decimals: recent.decimals,
47
+ logoURI: recent.logoURI,
48
+ priceUSD: '',
49
+ verificationStatus: recent.flagged ? 'flagged' : undefined,
50
+ }) as TokenAmount
51
+
52
+ const isFeaturedCopy = (token: TokenAmount): boolean =>
53
+ !!token.featured && !token.amount
54
+
55
+ export const resolveRecentRows = (
56
+ tokens: TokenAmount[],
57
+ {
58
+ recentTokens,
59
+ availableChainIds,
60
+ configTokens,
61
+ formType,
62
+ selectedChainId,
63
+ isAllNetworks,
64
+ nativeHoisted,
65
+ disabled,
66
+ }: ResolveRecentRowsParams
67
+ ): RecentRows => {
68
+ let recentStartIndex = nativeHoisted ? 1 : 0
69
+ while (tokens[recentStartIndex]?.pinned) {
70
+ recentStartIndex++
71
+ }
72
+
73
+ const inactive: RecentRows = { rows: [], bandEntries: [], recentStartIndex }
74
+
75
+ if (disabled || !recentTokens.length || !tokens.length) {
76
+ return inactive
77
+ }
78
+
79
+ const candidates = recentTokens.filter(
80
+ (recent) =>
81
+ (isAllNetworks || recent.chainId === selectedChainId) &&
82
+ availableChainIds.has(recent.chainId)
83
+ )
84
+
85
+ if (!candidates.length) {
86
+ return inactive
87
+ }
88
+
89
+ const wanted = new Set(
90
+ candidates.map((recent) => getTokenKey(recent.chainId, recent.address))
91
+ )
92
+ // All-networks holds tens of thousands of rows: test the chain before the key.
93
+ const wantedChains = new Set(candidates.map((recent) => recent.chainId))
94
+ const fresh = new Map<string, TokenAmount>()
95
+ // A featured config copy has no balance and sits above the row with one.
96
+ let awaitingBalance = 0
97
+ for (const token of tokens) {
98
+ if (!wantedChains.has(token.chainId)) {
99
+ continue
100
+ }
101
+ const key = getTokenKey(token.chainId, token.address)
102
+ if (!wanted.has(key)) {
103
+ continue
104
+ }
105
+ const found = fresh.get(key)
106
+ if (!found) {
107
+ fresh.set(key, token)
108
+ if (isFeaturedCopy(token)) {
109
+ awaitingBalance++
110
+ }
111
+ } else if (token.amount && isFeaturedCopy(found)) {
112
+ fresh.set(key, {
113
+ ...found,
114
+ amount: token.amount,
115
+ priceUSD: token.priceUSD || found.priceUSD,
116
+ })
117
+ awaitingBalance--
118
+ }
119
+ if (fresh.size === wanted.size && !awaitingBalance) {
120
+ break
121
+ }
122
+ }
123
+
124
+ const allowedByChain = new Map<number, AllowDenySets | undefined>()
125
+ const allowedFor = (chainId: number) => {
126
+ if (!allowedByChain.has(chainId)) {
127
+ allowedByChain.set(
128
+ chainId,
129
+ getChainTokenAllowSets(configTokens, chainId, formType)
130
+ )
131
+ }
132
+ return allowedByChain.get(chainId)
133
+ }
134
+
135
+ const hoistedKey = nativeHoisted
136
+ ? getTokenKey(tokens[0].chainId, tokens[0].address)
137
+ : undefined
138
+
139
+ const rows: TokenAmount[] = []
140
+ const bandEntries: RecentTokenId[] = []
141
+ for (const recent of candidates) {
142
+ const key = getTokenKey(recent.chainId, recent.address)
143
+ const resolved = fresh.get(key) ?? toSnapshotRow(recent)
144
+ if (
145
+ !isFormItemAllowed(resolved, allowedFor(recent.chainId), formType, (t) =>
146
+ t.address.toLowerCase()
147
+ )
148
+ ) {
149
+ // Denied on this side only, so Clear must leave it for the other side.
150
+ continue
151
+ }
152
+ // Clear owns displaced entries too, or they reappear once unpinned.
153
+ bandEntries.push({ chainId: recent.chainId, address: recent.address })
154
+ if (key === hoistedKey || resolved.pinned) {
155
+ continue
156
+ }
157
+ rows.push({ ...resolved, recent: true })
158
+ }
159
+
160
+ if (!rows.length) {
161
+ return inactive
162
+ }
163
+
164
+ return { rows, bandEntries, recentStartIndex }
165
+ }
166
+
167
+ export const spliceRecentRows = (
168
+ tokens: TokenAmount[],
169
+ { rows, recentStartIndex }: RecentRows,
170
+ expanded: boolean
171
+ ): RecentTokensResult => {
172
+ if (!rows.length) {
173
+ return { tokens, recentCount: 0, totalRecentCount: 0 }
174
+ }
175
+
176
+ const visible = expanded ? rows : rows.slice(0, collapsedRecentCount)
177
+
178
+ return {
179
+ tokens: [
180
+ ...tokens.slice(0, recentStartIndex),
181
+ ...visible,
182
+ ...tokens.slice(recentStartIndex),
183
+ ],
184
+ recentCount: visible.length,
185
+ totalRecentCount: rows.length,
186
+ }
187
+ }
@@ -5,13 +5,35 @@ import type {
5
5
  StaticToken,
6
6
  Token,
7
7
  TokenAmount,
8
+ TokenExtended,
8
9
  } from '@lifi/sdk'
9
10
  import { ChainId } from '@lifi/sdk'
10
11
  import type { FormType } from '../stores/form/types.js'
11
12
  import type { TokensByChain, TokenWithFlags } from '../types/token.js'
12
- import type { WidgetChains, WidgetTokens } from '../types/widget.js'
13
+ import type {
14
+ AllowDenySets,
15
+ WidgetChains,
16
+ WidgetTokens,
17
+ } from '../types/widget.js'
13
18
  import { getConfigItemSets, isFormItemAllowed } from './item.js'
14
19
 
20
+ // Number(): plain-JS integrators may pass chainId as a string.
21
+ export const getChainTokenAllowSets = (
22
+ configTokens: WidgetTokens | undefined,
23
+ chainId: number,
24
+ formType?: FormType
25
+ ): AllowDenySets | undefined =>
26
+ getConfigItemSets(
27
+ configTokens,
28
+ (tokens: BaseToken[]) =>
29
+ new Set(
30
+ tokens
31
+ .filter((t) => Number(t.chainId) === chainId)
32
+ .map((t) => t.address.toLowerCase())
33
+ ),
34
+ formType
35
+ )
36
+
15
37
  /**
16
38
  * Builds per-chain sets of lowercase token addresses from the
17
39
  * `tokens.verified` config allowlist.
@@ -71,9 +93,39 @@ export const mergeVerifiedWithSearchTokens = (
71
93
  return result
72
94
  }
73
95
 
96
+ /**
97
+ * `priceUSD` is a required string, so an unknown price arrives as `'0'` or `''`
98
+ * rather than as an absent key — the same reading as in `useLimitMarketRate`. A
99
+ * known price must never lose to one, or a route that cannot price its output
100
+ * blanks a value the cache already knew.
101
+ */
102
+ export const knownPriceUSD = (fresher: string, cached: string): string =>
103
+ Number(fresher) ? fresher : cached
104
+
105
+ /**
106
+ * Merges a cached token into a route token for display. The route wins on every
107
+ * field it carries; the cache only fills gaps. Letting the cache win instead
108
+ * swaps the quoted price for a stale feed price.
109
+ */
110
+ export const mergeFallbackToken = (
111
+ routeToken: TokenAmount,
112
+ cachedToken: TokenExtended | undefined
113
+ ): TokenAmount =>
114
+ cachedToken
115
+ ? {
116
+ ...cachedToken,
117
+ ...routeToken,
118
+ priceUSD: knownPriceUSD(routeToken.priceUSD, cachedToken.priceUSD),
119
+ logoURI: routeToken.logoURI || cachedToken.logoURI,
120
+ }
121
+ : routeToken
122
+
74
123
  /**
75
124
  * Updates a token in the cache by chainId and address.
76
125
  * Returns a new cache object with the token updated, or the original if not found.
126
+ * Addresses are compared case-insensitively: the token list and the route can
127
+ * spell the same address differently. The incoming token only overwrites what it
128
+ * actually knows, so an unpriced or logo-less route cannot blank the cache.
77
129
  */
78
130
  export const updateTokenInCache = (
79
131
  data: TokensByChain | undefined,
@@ -86,14 +138,26 @@ export const updateTokenInCache = (
86
138
  if (!chainTokens) {
87
139
  return data
88
140
  }
89
- const index = chainTokens.findIndex((t) => t.address === token.address)
141
+ const address = token.address.toLowerCase()
142
+ const index = chainTokens.findIndex(
143
+ (t) => t.address.toLowerCase() === address
144
+ )
90
145
  if (index < 0) {
91
146
  return data
92
147
  }
93
148
  return {
94
149
  ...data,
95
150
  [token.chainId]: chainTokens.map((t, i) =>
96
- i === index ? { ...t, ...token } : t
151
+ i === index
152
+ ? {
153
+ ...t,
154
+ ...token,
155
+ // Keep the cached address: other readers hold it as the identity.
156
+ address: t.address,
157
+ priceUSD: knownPriceUSD(token.priceUSD, t.priceUSD),
158
+ logoURI: token.logoURI || t.logoURI,
159
+ }
160
+ : t
97
161
  ),
98
162
  }
99
163
  }
@@ -140,14 +204,9 @@ export const filterAllowedTokens = (
140
204
  chainIncludedTokens.map((t) => t.address.toLowerCase())
141
205
  )
142
206
 
143
- const allowedAddresses = getConfigItemSets(
207
+ const allowedAddresses = getChainTokenAllowSets(
144
208
  configTokens,
145
- (tokens: BaseToken[]) =>
146
- new Set(
147
- tokens
148
- .filter((t) => Number(t.chainId) === chainId)
149
- .map((t) => t.address.toLowerCase())
150
- ),
209
+ chainId,
151
210
  formType
152
211
  )
153
212
 
@@ -256,25 +256,49 @@ const processedTypedTokens = (
256
256
  }
257
257
  }
258
258
 
259
- export const isSearchMatch = (
260
- token: TokenExtended | TokenAmountExtended,
261
- search?: string
262
- ): boolean => {
263
- if (!search) {
264
- return true
259
+ /**
260
+ * Builds the filter for one search, so the query is normalized once instead of
261
+ * once per token: an all-networks list holds tens of thousands of rows, where
262
+ * normalizing per row costs more than the matching itself. Binding the mode
263
+ * here also keeps every call site of one search on the same rule.
264
+ *
265
+ * An address search matches the token at that address only. Impersonators
266
+ * embed a real address in their name or symbol, so a substring match on
267
+ * those fields would surface them for the very query meant to avoid them.
268
+ */
269
+ export const createSearchMatcher = (
270
+ search: string | undefined,
271
+ isAddressSearch: boolean
272
+ ): ((token: TokenExtended | TokenAmountExtended) => boolean) => {
273
+ const searchTerm = search?.trim().toLowerCase()
274
+
275
+ if (!searchTerm) {
276
+ return () => true
265
277
  }
266
278
 
267
- const searchLowerCase = search.toLowerCase()
268
- return (
269
- token.name?.toLowerCase().includes(searchLowerCase) ||
270
- token.symbol
271
- ?.replaceAll('₮', 'T')
272
- .toLowerCase()
273
- .includes(searchLowerCase) ||
274
- token.address?.toLowerCase().includes(searchLowerCase)
275
- )
279
+ if (isAddressSearch) {
280
+ return (token) => token.address?.toLowerCase() === searchTerm
281
+ }
282
+
283
+ return (token) =>
284
+ Boolean(
285
+ token.name?.toLowerCase().includes(searchTerm) ||
286
+ token.symbol?.replaceAll('₮', 'T').toLowerCase().includes(searchTerm) ||
287
+ token.address?.toLowerCase().includes(searchTerm)
288
+ )
276
289
  }
277
290
 
291
+ export const isHoistableNative = (
292
+ token: TokenAmount | undefined,
293
+ selectedChainId?: number
294
+ ): boolean =>
295
+ !!token?.native &&
296
+ !token.pinned &&
297
+ !token.featured &&
298
+ !token.popular &&
299
+ !token.verified &&
300
+ (selectedChainId === undefined || token.chainId === selectedChainId)
301
+
278
302
  /**
279
303
  * Moves the chain's native token to the top of its own chain's list, unless an
280
304
  * external list already places it. Returns the array unchanged when nothing
@@ -287,14 +311,8 @@ export const hoistNativeToken = (
287
311
  if (!selectedChainId) {
288
312
  return tokens
289
313
  }
290
- const index = tokens.findIndex(
291
- (token) =>
292
- token.native &&
293
- token.chainId === selectedChainId &&
294
- !token.featured &&
295
- !token.popular &&
296
- !token.pinned &&
297
- !token.verified
314
+ const index = tokens.findIndex((token) =>
315
+ isHoistableNative(token, selectedChainId)
298
316
  )
299
317
  // -1 is no native token, 0 is already first
300
318
  if (index < 1) {
@@ -0,0 +1,181 @@
1
+ import type { TokenAmount } from '../types/token.js'
2
+
3
+ const bandHeaderAtListStart = 24
4
+ const bandHeaderAfterBand = 32
5
+ const toggleRowHeight = 26
6
+
7
+ type BandLabelKey =
8
+ | 'main.pinnedTokens'
9
+ | 'main.featuredTokens'
10
+ | 'main.myTokens'
11
+ | 'main.popularTokens'
12
+ | 'main.allTokens'
13
+
14
+ export type BandLabel =
15
+ | { kind: 'text'; key: BandLabelKey; atListStart: boolean }
16
+ | { kind: 'recent'; atListStart: boolean }
17
+
18
+ export interface BandResolverOptions {
19
+ showCategories: boolean
20
+ showPinnedTokens: boolean
21
+ nativeHoisted: boolean
22
+ /** Ignored while `recentCount` is 0. */
23
+ recentStartIndex: number
24
+ recentCount: number
25
+ showRecentToggle: boolean
26
+ }
27
+
28
+ export interface BandResolver {
29
+ /** Called for every index by the virtualizer, so it allocates nothing. */
30
+ getRowExtraHeight: (index: number) => number
31
+ /** Called for visible rows only, so the label may be resolved lazily. */
32
+ getRowBandLabel: (index: number) => BandLabel | undefined
33
+ /** The row the toggle hangs below. Shares one definition with the height. */
34
+ isToggleRow: (index: number) => boolean
35
+ }
36
+
37
+ // Pinned and recent rows sit above the categories; transitions ignore both.
38
+ const isPromoted = (token: TokenAmount | undefined): boolean =>
39
+ !!token?.pinned || !!token?.recent
40
+
41
+ export const createBandResolver = (
42
+ tokens: TokenAmount[],
43
+ {
44
+ showCategories,
45
+ showPinnedTokens,
46
+ nativeHoisted,
47
+ recentStartIndex,
48
+ recentCount,
49
+ showRecentToggle,
50
+ }: BandResolverOptions
51
+ ): BandResolver => {
52
+ const listStartIndex = nativeHoisted ? 1 : 0
53
+ const recentEndIndex = recentStartIndex + recentCount - 1
54
+
55
+ const isFirstPinned = (index: number) =>
56
+ !!tokens[index]?.pinned && index === listStartIndex
57
+ const isFirstRecent = (index: number) =>
58
+ recentCount > 0 && index === recentStartIndex && !!tokens[index]?.recent
59
+ const isLastRecent = (index: number) =>
60
+ recentCount > 0 && index === recentEndIndex && !!tokens[index]?.recent
61
+ // The hoisted native sits above every band, so the list-start row follows none.
62
+ const previousOf = (index: number) =>
63
+ index === listStartIndex ? undefined : tokens[index - 1]
64
+ const isTransitionFromPromoted = (index: number) =>
65
+ isPromoted(tokens[index - 1]) && !isPromoted(tokens[index])
66
+ // Only the top row has no card above it; below the hoisted native one does.
67
+ const isTopRow = (index: number) => index === 0
68
+ const headerHeight = (index: number) =>
69
+ isTopRow(index) ? bandHeaderAtListStart : bandHeaderAfterBand
70
+
71
+ const getRowExtraHeight = (index: number): number => {
72
+ const current = tokens[index]
73
+ const previous = previousOf(index)
74
+ let extra = 0
75
+
76
+ if (showPinnedTokens && isFirstPinned(index)) {
77
+ extra += headerHeight(index)
78
+ }
79
+
80
+ if (isFirstRecent(index)) {
81
+ extra += headerHeight(index)
82
+ }
83
+
84
+ if (showRecentToggle && isLastRecent(index)) {
85
+ extra += toggleRowHeight
86
+ }
87
+
88
+ // The recent band needs its own term: all-networks has no categories.
89
+ if (
90
+ (showPinnedTokens || showCategories || recentCount > 0) &&
91
+ isTransitionFromPromoted(index)
92
+ ) {
93
+ extra += bandHeaderAfterBand
94
+ }
95
+
96
+ if (!showCategories) {
97
+ return extra
98
+ }
99
+
100
+ if (current?.featured && !isPromoted(current) && index === listStartIndex) {
101
+ extra += headerHeight(index)
102
+ }
103
+
104
+ const isNotPromoted = !isPromoted(current) && !isPromoted(previous)
105
+ if (
106
+ index !== listStartIndex &&
107
+ isNotPromoted &&
108
+ ((previous?.amount && !current?.amount) ||
109
+ (previous?.featured && !current?.featured) ||
110
+ (previous?.popular && !current?.popular))
111
+ ) {
112
+ extra += bandHeaderAfterBand
113
+ }
114
+
115
+ return extra
116
+ }
117
+
118
+ const getRowBandLabel = (index: number): BandLabel | undefined => {
119
+ const current = tokens[index]
120
+ const previous = previousOf(index)
121
+ const isListStart = index === listStartIndex
122
+ const notPromoted = !isPromoted(current)
123
+ const fromPromoted = isPromoted(previous) && notPromoted
124
+
125
+ if (showPinnedTokens && isFirstPinned(index)) {
126
+ return {
127
+ kind: 'text',
128
+ key: 'main.pinnedTokens',
129
+ atListStart: isTopRow(index),
130
+ }
131
+ }
132
+ if (isFirstRecent(index)) {
133
+ return { kind: 'recent', atListStart: isTopRow(index) }
134
+ }
135
+ if (
136
+ (showPinnedTokens || recentCount > 0) &&
137
+ !showCategories &&
138
+ fromPromoted
139
+ ) {
140
+ return { kind: 'text', key: 'main.allTokens', atListStart: false }
141
+ }
142
+ if (!showCategories) {
143
+ return undefined
144
+ }
145
+
146
+ const fromFeatured =
147
+ !!previous?.featured && !current?.featured && notPromoted
148
+ const fromMyTokens = !!previous?.amount && !current?.amount && notPromoted
149
+ const fromPopular = !!previous?.popular && !current?.popular && notPromoted
150
+
151
+ if (
152
+ (fromPromoted && current?.featured) ||
153
+ (current?.featured && notPromoted && isListStart)
154
+ ) {
155
+ return {
156
+ kind: 'text',
157
+ key: 'main.featuredTokens',
158
+ atListStart: isTopRow(index),
159
+ }
160
+ }
161
+ if ((fromFeatured || fromPromoted) && current?.amount && notPromoted) {
162
+ return { kind: 'text', key: 'main.myTokens', atListStart: false }
163
+ }
164
+ if (
165
+ (fromFeatured || fromMyTokens || fromPromoted) &&
166
+ current?.popular &&
167
+ notPromoted
168
+ ) {
169
+ return { kind: 'text', key: 'main.popularTokens', atListStart: false }
170
+ }
171
+ if (fromMyTokens || fromFeatured || fromPromoted || fromPopular) {
172
+ return { kind: 'text', key: 'main.allTokens', atListStart: false }
173
+ }
174
+ return undefined
175
+ }
176
+
177
+ const isToggleRow = (index: number): boolean =>
178
+ showRecentToggle && isLastRecent(index)
179
+
180
+ return { getRowExtraHeight, getRowBandLabel, isToggleRow }
181
+ }