0xtrails 0.6.6 → 0.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 (365) hide show
  1. package/dist/aave.d.ts +10 -2
  2. package/dist/aave.d.ts.map +1 -1
  3. package/dist/analytics.d.ts +26 -0
  4. package/dist/analytics.d.ts.map +1 -1
  5. package/dist/{ccip-CbJrlK-L.js → ccip-fConRNoG.js} +21 -21
  6. package/dist/chains.d.ts +23 -8
  7. package/dist/chains.d.ts.map +1 -1
  8. package/dist/constants.d.ts +5 -5
  9. package/dist/constants.d.ts.map +1 -1
  10. package/dist/customTokens.d.ts +12 -0
  11. package/dist/customTokens.d.ts.map +1 -0
  12. package/dist/decoders.d.ts +2 -2
  13. package/dist/decoders.d.ts.map +1 -1
  14. package/dist/error.d.ts.map +1 -1
  15. package/dist/fees.d.ts +37 -2
  16. package/dist/fees.d.ts.map +1 -1
  17. package/dist/gasless.d.ts +15 -36
  18. package/dist/gasless.d.ts.map +1 -1
  19. package/dist/{index-w7_dK4c5.js → index-BbajxCG_.js} +59269 -77146
  20. package/dist/index.d.ts +8 -6
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +828 -359
  23. package/dist/indexerClient.d.ts.map +1 -1
  24. package/dist/intentReceiptMonitor.d.ts +1 -1
  25. package/dist/intentReceiptMonitor.d.ts.map +1 -1
  26. package/dist/intentReceiptPoller.d.ts +1 -1
  27. package/dist/intentReceiptPoller.d.ts.map +1 -1
  28. package/dist/intents.d.ts +3 -2
  29. package/dist/intents.d.ts.map +1 -1
  30. package/dist/mode.d.ts +1 -1
  31. package/dist/mode.d.ts.map +1 -1
  32. package/dist/mutations.d.ts +2 -2
  33. package/dist/mutations.d.ts.map +1 -1
  34. package/dist/prepareSend.d.ts +2 -2
  35. package/dist/prepareSend.d.ts.map +1 -1
  36. package/dist/prices.d.ts +1 -1
  37. package/dist/prices.d.ts.map +1 -1
  38. package/dist/sequenceWallet.d.ts +2 -2
  39. package/dist/sequenceWallet.d.ts.map +1 -1
  40. package/dist/time.d.ts +6 -0
  41. package/dist/time.d.ts.map +1 -1
  42. package/dist/tokenBalances.d.ts +40 -25
  43. package/dist/tokenBalances.d.ts.map +1 -1
  44. package/dist/tokens.d.ts +54 -14
  45. package/dist/tokens.d.ts.map +1 -1
  46. package/dist/trailsClient.d.ts +1 -1
  47. package/dist/trailsClient.d.ts.map +1 -1
  48. package/dist/trailsRouter.d.ts +2 -1
  49. package/dist/trailsRouter.d.ts.map +1 -1
  50. package/dist/transactionIntent/deposits/depositOrchestrator.d.ts +3 -2
  51. package/dist/transactionIntent/deposits/depositOrchestrator.d.ts.map +1 -1
  52. package/dist/transactionIntent/deposits/gaslessDeposit.d.ts +2 -1
  53. package/dist/transactionIntent/deposits/gaslessDeposit.d.ts.map +1 -1
  54. package/dist/transactionIntent/execution/transactionState.d.ts +1 -1
  55. package/dist/transactionIntent/execution/transactionState.d.ts.map +1 -1
  56. package/dist/transactionIntent/handlers/crossChain.d.ts +5 -3
  57. package/dist/transactionIntent/handlers/crossChain.d.ts.map +1 -1
  58. package/dist/transactionIntent/handlers/sameChainSameToken.d.ts +5 -3
  59. package/dist/transactionIntent/handlers/sameChainSameToken.d.ts.map +1 -1
  60. package/dist/transactionIntent/quote/feeExtractors.d.ts +1 -6
  61. package/dist/transactionIntent/quote/feeExtractors.d.ts.map +1 -1
  62. package/dist/transactionIntent/quote/normalizeQuote.d.ts +4 -2
  63. package/dist/transactionIntent/quote/normalizeQuote.d.ts.map +1 -1
  64. package/dist/transactionIntent/quote/quoteHelpers.d.ts +1 -1
  65. package/dist/transactionIntent/quote/quoteHelpers.d.ts.map +1 -1
  66. package/dist/transactionIntent/types.d.ts +28 -5
  67. package/dist/transactionIntent/types.d.ts.map +1 -1
  68. package/dist/transactionIntent/utils/testnetHelpers.d.ts +0 -1
  69. package/dist/transactionIntent/utils/testnetHelpers.d.ts.map +1 -1
  70. package/dist/transactionIntent/validators.d.ts +0 -2
  71. package/dist/transactionIntent/validators.d.ts.map +1 -1
  72. package/dist/transactions.d.ts +2 -2
  73. package/dist/transactions.d.ts.map +1 -1
  74. package/dist/utils.d.ts +7 -0
  75. package/dist/utils.d.ts.map +1 -1
  76. package/dist/wallets.d.ts +1 -0
  77. package/dist/wallets.d.ts.map +1 -1
  78. package/dist/widget/components/AccountIntentTransactionHistory.d.ts.map +1 -1
  79. package/dist/widget/components/AddressWalletIcon.d.ts +6 -0
  80. package/dist/widget/components/AddressWalletIcon.d.ts.map +1 -0
  81. package/dist/widget/components/ChainFilterDropdown.d.ts +2 -6
  82. package/dist/widget/components/ChainFilterDropdown.d.ts.map +1 -1
  83. package/dist/widget/components/ChainImage.d.ts.map +1 -1
  84. package/dist/widget/components/ChainList.d.ts +0 -5
  85. package/dist/widget/components/ChainList.d.ts.map +1 -1
  86. package/dist/widget/components/ClassicSwap.d.ts +6 -6
  87. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  88. package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
  89. package/dist/widget/components/ConnectedWallets.d.ts.map +1 -1
  90. package/dist/widget/components/DebugMenu.d.ts +1 -1
  91. package/dist/widget/components/DebugMenu.d.ts.map +1 -1
  92. package/dist/widget/components/DebugScreensList.d.ts.map +1 -1
  93. package/dist/widget/components/DepositTracker.d.ts.map +1 -1
  94. package/dist/widget/components/Earn.d.ts +5 -5
  95. package/dist/widget/components/Earn.d.ts.map +1 -1
  96. package/dist/widget/components/FeeOption.d.ts +1 -1
  97. package/dist/widget/components/FeeOption.d.ts.map +1 -1
  98. package/dist/widget/components/FeeOptions.d.ts +2 -2
  99. package/dist/widget/components/FeeOptions.d.ts.map +1 -1
  100. package/dist/widget/components/Footer.d.ts +1 -1
  101. package/dist/widget/components/Footer.d.ts.map +1 -1
  102. package/dist/widget/components/Fund.d.ts +5 -5
  103. package/dist/widget/components/Fund.d.ts.map +1 -1
  104. package/dist/widget/components/FundMethods.d.ts +0 -1
  105. package/dist/widget/components/FundMethods.d.ts.map +1 -1
  106. package/dist/widget/components/FundSwap.d.ts +6 -6
  107. package/dist/widget/components/FundSwap.d.ts.map +1 -1
  108. package/dist/widget/components/HookModalContent.d.ts +8 -0
  109. package/dist/widget/components/HookModalContent.d.ts.map +1 -0
  110. package/dist/widget/components/OriginSelectionAmount.d.ts +11 -0
  111. package/dist/widget/components/OriginSelectionAmount.d.ts.map +1 -0
  112. package/dist/widget/components/Pay.d.ts +5 -5
  113. package/dist/widget/components/Pay.d.ts.map +1 -1
  114. package/dist/widget/components/PoolDeposit.d.ts +5 -5
  115. package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
  116. package/dist/widget/components/PoolWithdraw.d.ts +3 -3
  117. package/dist/widget/components/PoolWithdraw.d.ts.map +1 -1
  118. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  119. package/dist/widget/components/Receipt.d.ts +2 -1
  120. package/dist/widget/components/Receipt.d.ts.map +1 -1
  121. package/dist/widget/components/RecentTokens.d.ts +4 -4
  122. package/dist/widget/components/RecentTokens.d.ts.map +1 -1
  123. package/dist/widget/components/RecipientSelectorButton.d.ts.map +1 -1
  124. package/dist/widget/components/ShadowPortal.d.ts +6 -0
  125. package/dist/widget/components/ShadowPortal.d.ts.map +1 -0
  126. package/dist/widget/components/Swap.d.ts +6 -6
  127. package/dist/widget/components/Swap.d.ts.map +1 -1
  128. package/dist/widget/components/ThemeProvider.d.ts +1 -1
  129. package/dist/widget/components/ThemeProvider.d.ts.map +1 -1
  130. package/dist/widget/components/TokenList.d.ts +3 -4
  131. package/dist/widget/components/TokenList.d.ts.map +1 -1
  132. package/dist/widget/components/TokenSelector.d.ts +3 -4
  133. package/dist/widget/components/TokenSelector.d.ts.map +1 -1
  134. package/dist/widget/components/Tooltip.d.ts +6 -1
  135. package/dist/widget/components/Tooltip.d.ts.map +1 -1
  136. package/dist/widget/components/TrailsHookModal.d.ts +10 -0
  137. package/dist/widget/components/TrailsHookModal.d.ts.map +1 -0
  138. package/dist/widget/components/WaasFeeOptions.d.ts +3 -0
  139. package/dist/widget/components/WaasFeeOptions.d.ts.map +1 -1
  140. package/dist/widget/components/WalletConfirmation.d.ts.map +1 -1
  141. package/dist/widget/components/WalletConnect.d.ts.map +1 -1
  142. package/dist/widget/components/WidgetProviders.d.ts +14 -0
  143. package/dist/widget/components/WidgetProviders.d.ts.map +1 -0
  144. package/dist/widget/css/compiled.css +1 -1
  145. package/dist/widget/hooks/useAddressWalletIcon.d.ts +10 -0
  146. package/dist/widget/hooks/useAddressWalletIcon.d.ts.map +1 -0
  147. package/dist/widget/hooks/useBalanceVisible.d.ts +1 -1
  148. package/dist/widget/hooks/useBalanceVisible.d.ts.map +1 -1
  149. package/dist/widget/hooks/useChainFilter.d.ts +1 -1
  150. package/dist/widget/hooks/useChainFilter.d.ts.map +1 -1
  151. package/dist/widget/hooks/useCheckout.d.ts +13 -1
  152. package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
  153. package/dist/widget/hooks/useConnector.d.ts +4 -0
  154. package/dist/widget/hooks/useConnector.d.ts.map +1 -0
  155. package/dist/widget/hooks/useCurrentScreen.d.ts +1 -1
  156. package/dist/widget/hooks/useCurrentScreen.d.ts.map +1 -1
  157. package/dist/widget/hooks/useCustomTokenFetch.d.ts +19 -0
  158. package/dist/widget/hooks/useCustomTokenFetch.d.ts.map +1 -0
  159. package/dist/widget/hooks/useCustomTokenSearch.d.ts +20 -0
  160. package/dist/widget/hooks/useCustomTokenSearch.d.ts.map +1 -0
  161. package/dist/widget/hooks/useDebounce.d.ts +10 -0
  162. package/dist/widget/hooks/useDebounce.d.ts.map +1 -0
  163. package/dist/widget/hooks/useDebugScreens.d.ts +7 -2
  164. package/dist/widget/hooks/useDebugScreens.d.ts.map +1 -1
  165. package/dist/widget/hooks/useDefaultTokenSelection.d.ts +3 -19
  166. package/dist/widget/hooks/useDefaultTokenSelection.d.ts.map +1 -1
  167. package/dist/widget/hooks/useDestinationSelectedToken.d.ts +1 -14
  168. package/dist/widget/hooks/useDestinationSelectedToken.d.ts.map +1 -1
  169. package/dist/widget/hooks/useEarnPool.d.ts +1 -1
  170. package/dist/widget/hooks/useEarnPool.d.ts.map +1 -1
  171. package/dist/widget/hooks/useIntentTransactionHistory.d.ts.map +1 -1
  172. package/dist/widget/hooks/useIsMobile.d.ts +5 -0
  173. package/dist/widget/hooks/useIsMobile.d.ts.map +1 -0
  174. package/dist/widget/hooks/useMode.d.ts +2 -2
  175. package/dist/widget/hooks/useMode.d.ts.map +1 -1
  176. package/dist/widget/hooks/useOriginSelectedToken.d.ts +2 -15
  177. package/dist/widget/hooks/useOriginSelectedToken.d.ts.map +1 -1
  178. package/dist/widget/hooks/usePayMessage.d.ts.map +1 -1
  179. package/dist/widget/hooks/usePriceImpactWarning.d.ts +1 -1
  180. package/dist/widget/hooks/usePriceImpactWarning.d.ts.map +1 -1
  181. package/dist/widget/hooks/useQuote.d.ts +173 -4
  182. package/dist/widget/hooks/useQuote.d.ts.map +1 -1
  183. package/dist/widget/hooks/useRecentTokens.d.ts +3 -3
  184. package/dist/widget/hooks/useRecentTokens.d.ts.map +1 -1
  185. package/dist/widget/hooks/useRecipients.d.ts +1 -1
  186. package/dist/widget/hooks/useRecipients.d.ts.map +1 -1
  187. package/dist/widget/hooks/useSelectedFeeOption.d.ts +2 -2
  188. package/dist/widget/hooks/useSelectedFeeOption.d.ts.map +1 -1
  189. package/dist/widget/hooks/useSelectedFundMethod.d.ts +1 -1
  190. package/dist/widget/hooks/useSelectedFundMethod.d.ts.map +1 -1
  191. package/dist/widget/hooks/useSelectedRecipient.d.ts +1 -1
  192. package/dist/widget/hooks/useSelectedRecipient.d.ts.map +1 -1
  193. package/dist/widget/hooks/useSendForm.d.ts +9 -31
  194. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  195. package/dist/widget/hooks/useSwapAmount.d.ts +1 -1
  196. package/dist/widget/hooks/useSwapAmount.d.ts.map +1 -1
  197. package/dist/widget/hooks/useTheme.d.ts +1 -1
  198. package/dist/widget/hooks/useTheme.d.ts.map +1 -1
  199. package/dist/widget/hooks/useTokenList.d.ts +7 -31
  200. package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
  201. package/dist/widget/hooks/useTrailsSendTransaction.d.ts +83 -0
  202. package/dist/widget/hooks/useTrailsSendTransaction.d.ts.map +1 -0
  203. package/dist/widget/hooks/useWalletConnectUri.d.ts +11 -0
  204. package/dist/widget/hooks/useWalletConnectUri.d.ts.map +1 -0
  205. package/dist/widget/hooks/useWidgetProps.d.ts +5 -0
  206. package/dist/widget/hooks/useWidgetProps.d.ts.map +1 -1
  207. package/dist/widget/index.d.ts +2 -0
  208. package/dist/widget/index.d.ts.map +1 -1
  209. package/dist/widget/index.js +8 -5
  210. package/dist/widget/providers/TrailsModalProvider.d.ts +65 -0
  211. package/dist/widget/providers/TrailsModalProvider.d.ts.map +1 -0
  212. package/dist/widget/providers/TrailsProvider.d.ts +1 -1
  213. package/dist/widget/providers/TrailsProvider.d.ts.map +1 -1
  214. package/dist/widget/types.d.ts +11 -18
  215. package/dist/widget/types.d.ts.map +1 -1
  216. package/dist/widget/widget.d.ts +20 -11
  217. package/dist/widget/widget.d.ts.map +1 -1
  218. package/package.json +45 -49
  219. package/src/aave.ts +387 -138
  220. package/src/analytics.ts +208 -2
  221. package/src/chains.ts +65 -64
  222. package/src/constants.ts +18 -14
  223. package/src/customTokens.ts +151 -0
  224. package/src/decoders.ts +4 -7
  225. package/src/error.ts +7 -3
  226. package/src/fees.ts +239 -125
  227. package/src/gasless.ts +54 -108
  228. package/src/index.ts +29 -9
  229. package/src/indexerClient.ts +2 -0
  230. package/src/intentReceiptMonitor.ts +1 -4
  231. package/src/intentReceiptPoller.ts +2 -2
  232. package/src/intents.ts +16 -5
  233. package/src/mode.ts +1 -1
  234. package/src/mutations.ts +7 -3
  235. package/src/prepareSend.ts +19 -14
  236. package/src/prices.ts +1 -1
  237. package/src/sequenceWallet.ts +2 -2
  238. package/src/time.ts +28 -0
  239. package/src/tokenBalances.ts +348 -153
  240. package/src/tokens.ts +393 -142
  241. package/src/trailsClient.ts +1 -1
  242. package/src/trailsRouter.ts +4 -5
  243. package/src/transactionIntent/deposits/depositOrchestrator.ts +6 -2
  244. package/src/transactionIntent/deposits/gaslessDeposit.ts +13 -7
  245. package/src/transactionIntent/deposits/standardDeposit.ts +1 -1
  246. package/src/transactionIntent/execution/transactionState.ts +1 -1
  247. package/src/transactionIntent/handlers/crossChain.ts +75 -37
  248. package/src/transactionIntent/handlers/sameChainSameToken.ts +66 -20
  249. package/src/transactionIntent/quote/feeExtractors.ts +1 -29
  250. package/src/transactionIntent/quote/normalizeQuote.ts +99 -7
  251. package/src/transactionIntent/quote/quoteHelpers.ts +1 -1
  252. package/src/transactionIntent/types.ts +31 -6
  253. package/src/transactionIntent/utils/testnetHelpers.ts +0 -5
  254. package/src/transactionIntent/validators.ts +0 -30
  255. package/src/transactions.ts +3 -3
  256. package/src/utils.ts +18 -0
  257. package/src/wallets.ts +32 -10
  258. package/src/widget/compiled.css +1 -1
  259. package/src/widget/components/AccountIntentTransactionHistory.tsx +2 -1
  260. package/src/widget/components/AccountIntentTransactionHistoryButton.tsx +2 -2
  261. package/src/widget/components/AddressWalletIcon.tsx +29 -0
  262. package/src/widget/components/ChainFilterDropdown.tsx +2 -8
  263. package/src/widget/components/ChainImage.tsx +8 -5
  264. package/src/widget/components/ChainList.tsx +6 -8
  265. package/src/widget/components/ClassicSwap.tsx +93 -85
  266. package/src/widget/components/ConnectWallet.tsx +1 -2
  267. package/src/widget/components/ConnectedWallets.tsx +17 -4
  268. package/src/widget/components/DebugMenu.tsx +2 -2
  269. package/src/widget/components/DebugScreensList.tsx +0 -1
  270. package/src/widget/components/DepositTracker.tsx +20 -34
  271. package/src/widget/components/Earn.tsx +7 -6
  272. package/src/widget/components/FeeOption.tsx +4 -4
  273. package/src/widget/components/FeeOptions.tsx +19 -39
  274. package/src/widget/components/Footer.tsx +1 -1
  275. package/src/widget/components/Fund.tsx +23 -119
  276. package/src/widget/components/FundMethods.tsx +9 -6
  277. package/src/widget/components/FundSwap.tsx +6 -5
  278. package/src/widget/components/FundingMethodSelectorButton.tsx +2 -2
  279. package/src/widget/components/HookModalContent.tsx +306 -0
  280. package/src/widget/components/Modal.tsx +1 -1
  281. package/src/widget/components/OriginSelectionAmount.tsx +135 -0
  282. package/src/widget/components/Pay.tsx +66 -124
  283. package/src/widget/components/PoolDeposit.tsx +11 -55
  284. package/src/widget/components/PoolWithdraw.tsx +3 -3
  285. package/src/widget/components/QuoteDetails.tsx +473 -728
  286. package/src/widget/components/Receipt.tsx +74 -7
  287. package/src/widget/components/RecentTokens.tsx +8 -8
  288. package/src/widget/components/RecipientSelectorButton.tsx +4 -2
  289. package/src/widget/components/ScreenHeader.tsx +2 -2
  290. package/src/widget/components/SearchInputField.tsx +1 -1
  291. package/src/widget/components/ShadowPortal.tsx +58 -0
  292. package/src/widget/components/Swap.tsx +6 -5
  293. package/src/widget/components/ThemeProvider.tsx +1 -1
  294. package/src/widget/components/TokenList.tsx +3 -4
  295. package/src/widget/components/TokenSelector.tsx +211 -80
  296. package/src/widget/components/Tooltip.tsx +18 -7
  297. package/src/widget/components/TrailsHookModal.tsx +118 -0
  298. package/src/widget/components/WaasFeeOptions.tsx +333 -138
  299. package/src/widget/components/WalletConfirmation.tsx +7 -2
  300. package/src/widget/components/WalletConnect.tsx +197 -235
  301. package/src/widget/components/WidgetProviders.tsx +75 -0
  302. package/src/widget/hooks/useAddressWalletIcon.ts +53 -0
  303. package/src/widget/hooks/useBalanceVisible.tsx +1 -1
  304. package/src/widget/hooks/useChainFilter.tsx +1 -1
  305. package/src/widget/hooks/useCheckout.ts +13 -1
  306. package/src/widget/hooks/useConnector.tsx +18 -0
  307. package/src/widget/hooks/useCurrentScreen.tsx +3 -3
  308. package/src/widget/hooks/useCustomTokenFetch.tsx +72 -0
  309. package/src/widget/hooks/useCustomTokenSearch.tsx +402 -0
  310. package/src/widget/hooks/useDebounce.ts +25 -0
  311. package/src/widget/hooks/useDebugScreens.ts +26 -10
  312. package/src/widget/hooks/useDefaultTokenSelection.tsx +99 -143
  313. package/src/widget/hooks/useDestinationSelectedToken.tsx +1 -14
  314. package/src/widget/hooks/useEarnPool.tsx +1 -1
  315. package/src/widget/hooks/useIntentTransactionHistory.ts +20 -11
  316. package/src/widget/hooks/useIsMobile.tsx +50 -0
  317. package/src/widget/hooks/useMode.ts +2 -3
  318. package/src/widget/hooks/useOriginSelectedToken.tsx +2 -15
  319. package/src/widget/hooks/usePayMessage.tsx +31 -11
  320. package/src/widget/hooks/usePriceImpactWarning.ts +1 -1
  321. package/src/widget/hooks/useQuote.ts +189 -6
  322. package/src/widget/hooks/useRecentTokens.ts +6 -6
  323. package/src/widget/hooks/useRecipients.ts +1 -1
  324. package/src/widget/hooks/useSelectedFeeOption.tsx +2 -2
  325. package/src/widget/hooks/useSelectedFundMethod.tsx +1 -1
  326. package/src/widget/hooks/useSelectedRecipient.tsx +1 -1
  327. package/src/widget/hooks/useSendForm.ts +328 -152
  328. package/src/widget/hooks/useSwapAmount.tsx +1 -1
  329. package/src/widget/hooks/useTheme.tsx +1 -1
  330. package/src/widget/hooks/useTokenList.ts +672 -400
  331. package/src/widget/hooks/useTrailsSendTransaction.ts +949 -0
  332. package/src/widget/hooks/useWalletConnectUri.tsx +228 -0
  333. package/src/widget/hooks/useWidgetProps.tsx +3 -1
  334. package/src/widget/index.tsx +12 -0
  335. package/src/widget/providers/TrailsModalProvider.tsx +195 -0
  336. package/src/widget/providers/TrailsProvider.tsx +9 -3
  337. package/src/widget/types.ts +12 -20
  338. package/src/widget/widget.tsx +598 -385
  339. package/dist/cctp.d.ts +0 -3
  340. package/dist/cctp.d.ts.map +0 -1
  341. package/dist/lifi.d.ts +0 -4
  342. package/dist/lifi.d.ts.map +0 -1
  343. package/dist/meshconnect.d.ts +0 -171
  344. package/dist/meshconnect.d.ts.map +0 -1
  345. package/dist/relaySdk.d.ts +0 -87
  346. package/dist/relaySdk.d.ts.map +0 -1
  347. package/dist/widget/components/MeshConnectExchanges.d.ts +0 -7
  348. package/dist/widget/components/MeshConnectExchanges.d.ts.map +0 -1
  349. package/dist/widget/components/MeshConnectFlow.d.ts +0 -13
  350. package/dist/widget/components/MeshConnectFlow.d.ts.map +0 -1
  351. package/dist/widget/components/MeshConnectIframe.d.ts +0 -15
  352. package/dist/widget/components/MeshConnectIframe.d.ts.map +0 -1
  353. package/dist/widget/components/Receive.d.ts +0 -12
  354. package/dist/widget/components/Receive.d.ts.map +0 -1
  355. package/dist/widget/hooks/useSelectedMeshExchange.d.ts +0 -14
  356. package/dist/widget/hooks/useSelectedMeshExchange.d.ts.map +0 -1
  357. package/src/cctp.ts +0 -54
  358. package/src/lifi.ts +0 -108
  359. package/src/meshconnect.ts +0 -531
  360. package/src/relaySdk.ts +0 -703
  361. package/src/widget/components/MeshConnectExchanges.tsx +0 -290
  362. package/src/widget/components/MeshConnectFlow.tsx +0 -90
  363. package/src/widget/components/MeshConnectIframe.tsx +0 -500
  364. package/src/widget/components/Receive.tsx +0 -175
  365. package/src/widget/hooks/useSelectedMeshExchange.tsx +0 -46
@@ -1,29 +1,26 @@
1
- import { ResourceStatus } from "@0xsequence/indexer"
2
1
  import { Address } from "ox"
3
2
  import { useEffect, useMemo, useState } from "react"
4
- import { isAddressEqual, zeroAddress } from "viem"
3
+ import { isAddress, isAddressEqual, zeroAddress } from "viem"
5
4
  import { useAccount } from "wagmi"
5
+ import { useQuery } from "@tanstack/react-query"
6
6
  import { getChainInfo, useSupportedChains } from "../../chains.js"
7
- import type {
8
- TokenBalanceExtended,
9
- TokenBalanceWithPrice,
10
- NativeTokenBalanceWithPrice,
11
- } from "../../tokenBalances.js"
7
+ import type { Token } from "../../tokens.js"
8
+ import { isNativeToken as isNativeTokenUtil } from "../../utils.js"
12
9
  import {
13
- formatRawAmount,
14
10
  useAccountTotalBalanceUsd,
15
11
  useHasSufficientBalanceUsd,
16
12
  useTokenBalances,
13
+ formatPriceFields,
17
14
  } from "../../tokenBalances.js"
18
- import {
19
- getFormatttedTokenName,
20
- useSupportedTokens,
21
- useGetTokenImageUrl,
22
- } from "../../tokens.js"
15
+ import { useSupportedTokens, useGetTokenImageUrl } from "../../tokens.js"
23
16
  import { useIndexerGatewayClient } from "../../indexerClient.js"
24
17
  import { useTrailsClient } from "../../trailsClient.js"
25
18
  import { useTokenPrices } from "../../prices.js"
26
19
  import { logger } from "../../logger.js"
20
+ import { useTrails } from "../providers/TrailsProvider.js"
21
+ import { useCustomTokenFetch } from "./useCustomTokenFetch.js"
22
+ import { useChainFilter } from "./useChainFilter.js"
23
+ import { useDebounce } from "./useDebounce.js"
27
24
 
28
25
  // Standard tokens for QR code and exchange modes
29
26
  const STANDARD_TOKENS = [
@@ -36,6 +33,79 @@ const STANDARD_TOKENS = [
36
33
  "WETH",
37
34
  ] as const
38
35
 
36
+ // Type for API token info from getTokenList endpoint
37
+ type ApiTokenInfo = {
38
+ chainId: number
39
+ address: string
40
+ name: string
41
+ symbol: string
42
+ decimals: number
43
+ supportsBridging?: boolean
44
+ logoUri?: string
45
+ }
46
+
47
+ // Helper function to validate and clamp decimals (1-18)
48
+ function validateDecimals(decimals: number): number {
49
+ return Math.max(1, Math.min(18, Math.floor(decimals || 18)))
50
+ }
51
+
52
+ // Helper function to check if token is native
53
+ function isNativeToken(token: Token | ApiTokenInfo): boolean {
54
+ // Token has isNativeToken property
55
+ if ("isNativeToken" in token && typeof token.isNativeToken === "boolean") {
56
+ return token.isNativeToken
57
+ }
58
+ // ApiTokenInfo uses "address" field (intermediate type before conversion to Token)
59
+ if ("address" in token) {
60
+ return isNativeTokenUtil(token.address)
61
+ }
62
+ return false
63
+ }
64
+
65
+ // Helper function to get token key for mapping
66
+ function getTokenKey(chainId: number, contractAddress: string): string {
67
+ return `${chainId}:${contractAddress.toLowerCase()}`
68
+ }
69
+
70
+ // Helper function to convert API token info to Token
71
+ function convertApiTokenToToken(
72
+ tokenInfo: ApiTokenInfo,
73
+ getTokenImageUrl: (params: {
74
+ chainId: number
75
+ contractAddress: string
76
+ symbol: string
77
+ }) => string,
78
+ ): Token {
79
+ const chainInfo = getChainInfo(tokenInfo.chainId)
80
+ const isNative = isNativeToken(tokenInfo)
81
+ const validatedDecimals = validateDecimals(tokenInfo.decimals)
82
+
83
+ return {
84
+ symbol: isNative
85
+ ? chainInfo?.nativeCurrency.symbol || "ETH"
86
+ : tokenInfo.symbol,
87
+ name: isNative
88
+ ? chainInfo?.nativeCurrency.name || "Native Token"
89
+ : tokenInfo.name,
90
+ decimals: isNative
91
+ ? chainInfo?.nativeCurrency.decimals || 18
92
+ : validatedDecimals,
93
+ contractAddress: isNative ? zeroAddress : tokenInfo.address,
94
+ chainId: tokenInfo.chainId,
95
+ chainName: chainInfo?.name || `Chain ${tokenInfo.chainId}`,
96
+ imageUrl:
97
+ tokenInfo.logoUri ||
98
+ getTokenImageUrl({
99
+ chainId: tokenInfo.chainId,
100
+ contractAddress: isNative ? zeroAddress : tokenInfo.address,
101
+ symbol: isNative
102
+ ? chainInfo?.nativeCurrency.symbol || "ETH"
103
+ : tokenInfo.symbol,
104
+ }),
105
+ isNativeToken: isNative,
106
+ }
107
+ }
108
+
39
109
  // Helper function to calculate search relevance score
40
110
  function calculateSearchRelevanceScore(
41
111
  tokenSymbol: string,
@@ -74,35 +144,6 @@ function calculateSearchRelevanceScore(
74
144
  return score
75
145
  }
76
146
 
77
- export interface Token {
78
- id: number
79
- name: string
80
- symbol: string
81
- balance: string
82
- imageUrl: string
83
- chainId: number
84
- contractAddress: string
85
- balanceUsdFormatted: string
86
- tokenPriceUsd: number
87
- contractInfo?: {
88
- decimals: number
89
- symbol: string
90
- name: string
91
- }
92
- }
93
-
94
- export type TokenFormatted = Token &
95
- TokenBalanceExtended & {
96
- balanceFormatted: string
97
- tokenPriceUsd: number
98
- balanceUsdFormatted: string
99
- isNative: boolean
100
- tokenName: string
101
- priceUsd: number
102
- isSufficientBalance: boolean
103
- chainName: string
104
- }
105
-
106
147
  export type UseTokenListProps = {
107
148
  onContinue: (selectedToken: Token) => void
108
149
  targetAmountUsd?: number | null
@@ -114,14 +155,16 @@ export type UseTokenListProps = {
114
155
  export type UseTokenListReturn = {
115
156
  selectedToken: Token | null
116
157
  searchQuery: string
117
- handleTokenSelect: (token: TokenFormatted) => void
118
- filteredTokens: TokenBalanceExtended[]
158
+ handleTokenSelect: (token: Token) => void
159
+ filteredTokens: Token[]
119
160
  isLoadingSortedTokens: boolean
161
+ isLoadingSearchTokens?: boolean
162
+ isSearching: boolean // True when search is in progress (typing or fetching)
120
163
  balanceError: Error | null
121
164
  showContinueButton: boolean
122
- isTokenSelected: (token: TokenBalanceExtended) => boolean
165
+ isTokenSelected: (token: Token) => boolean
123
166
  setSearchQuery: (query: string) => void
124
- filteredTokensFormatted: TokenFormatted[]
167
+ filteredTokensFormatted: Token[]
125
168
  totalBalanceUsd: number
126
169
  totalBalanceUsdFormatted: string
127
170
  isLoadingTotalBalanceUsd: boolean
@@ -152,6 +195,145 @@ export function useTokenList({
152
195
  const { supportedTokens, isLoadingTokens: isLoadingSupportedTokens } =
153
196
  useSupportedTokens()
154
197
 
198
+ // Get trails config for API calls
199
+ const trailsConfig = useTrails()
200
+
201
+ // Get supported chains for getTokenList API
202
+ const { supportedChains } = useSupportedChains()
203
+
204
+ // Get chain filter for destination token list
205
+ const { selectedChainId: filterByChainId } = useChainFilter()
206
+
207
+ // Only use API search for exchange/qr-code modes or when allSupportedTokens is true
208
+ // (where we show all tokens, not just user balances)
209
+ // For other modes, we only filter user's token balances locally
210
+ const shouldUseApiSearch =
211
+ fundMethod === "exchange" || fundMethod === "qr-code" || allSupportedTokens
212
+
213
+ // Memoize chain IDs to avoid recalculating
214
+ const apiSearchChainIds = useMemo(
215
+ () => supportedChains.map((chain) => chain.id),
216
+ [supportedChains],
217
+ )
218
+
219
+ // Check if search query is an address
220
+ const trimmedSearchQuery = useMemo(() => searchQuery.trim(), [searchQuery])
221
+ // Debounce the search query to avoid excessive API calls (300ms delay)
222
+ const debouncedSearchQuery = useDebounce(trimmedSearchQuery, 300)
223
+ const isSearchQueryAddress = useMemo(
224
+ () => (debouncedSearchQuery ? isAddress(debouncedSearchQuery) : false),
225
+ [debouncedSearchQuery],
226
+ )
227
+
228
+ // Track if user is actively typing (search query changed but debounce hasn't fired yet)
229
+ const isSearchPending = trimmedSearchQuery !== debouncedSearchQuery
230
+
231
+ // Call getTokenList API when there's a search query
232
+ const { data: searchTokensResult, isLoading: isLoadingSearchTokens } =
233
+ useQuery({
234
+ queryKey: [
235
+ "getTokenList",
236
+ debouncedSearchQuery,
237
+ apiSearchChainIds,
238
+ trailsConfig.trailsApiUrl,
239
+ trailsConfig.trailsApiKey,
240
+ ],
241
+ queryFn: async () => {
242
+ if (
243
+ !debouncedSearchQuery ||
244
+ !trailsClient ||
245
+ !trailsConfig.trailsApiKey
246
+ ) {
247
+ return { tokens: [] }
248
+ }
249
+
250
+ try {
251
+ const response = await trailsClient.getTokenList({
252
+ searchQuery: debouncedSearchQuery,
253
+ chainIds: apiSearchChainIds,
254
+ includeAllListed: true,
255
+ })
256
+ return response
257
+ } catch (error) {
258
+ logger.console.warn("[trails-sdk] Error searching tokens:", error)
259
+ return { tokens: [] }
260
+ }
261
+ },
262
+ enabled:
263
+ shouldUseApiSearch &&
264
+ debouncedSearchQuery.length > 0 &&
265
+ !!trailsClient &&
266
+ !!trailsConfig.trailsApiKey,
267
+ staleTime: 30 * 1000, // 30 seconds
268
+ gcTime: 5 * 60 * 1000, // 5 minutes
269
+ })
270
+
271
+ // Fetch custom token info if searching by address and getTokenList returned no results
272
+ const hasNoSearchResults = searchTokensResult?.tokens?.length === 0
273
+ const shouldFetchCustomToken =
274
+ shouldUseApiSearch &&
275
+ isSearchQueryAddress &&
276
+ hasNoSearchResults &&
277
+ !isLoadingSearchTokens &&
278
+ trimmedSearchQuery.length > 0
279
+ const customTokenChainId = supportedChains[0]?.id ?? null
280
+
281
+ const { tokenInfo: customTokenInfo, isLoading: isLoadingCustomToken } =
282
+ useCustomTokenFetch({
283
+ address: shouldFetchCustomToken ? trimmedSearchQuery : null,
284
+ chainId: customTokenChainId,
285
+ enabled: shouldFetchCustomToken,
286
+ })
287
+
288
+ // Fetch tokens from getTokenList when a chain is selected (for destination token list only)
289
+ // This fetches all available tokens from that chain, not just filtering existing tokens
290
+ const {
291
+ data: chainFilteredTokenList,
292
+ isLoading: isLoadingChainFilteredTokens,
293
+ } = useQuery({
294
+ queryKey: [
295
+ "getTokenList",
296
+ filterByChainId,
297
+ trailsConfig.trailsApiUrl,
298
+ trailsConfig.trailsApiKey,
299
+ ],
300
+ queryFn: async () => {
301
+ if (
302
+ !filterByChainId ||
303
+ !trailsClient ||
304
+ !trailsConfig.trailsApiKey ||
305
+ !shouldUseApiSearch
306
+ ) {
307
+ return { tokens: [] }
308
+ }
309
+
310
+ try {
311
+ // Fetch tokens from the selected chain with limit of 100 for single chain
312
+ const response = await trailsClient.getTokenList({
313
+ chainIds: [filterByChainId],
314
+ includeAllListed: true,
315
+ limit: 100,
316
+ })
317
+ return response
318
+ } catch (error) {
319
+ logger.console.warn(
320
+ "[trails-sdk] Error fetching tokens for chain filter:",
321
+ error,
322
+ )
323
+ return { tokens: [] }
324
+ }
325
+ },
326
+ enabled:
327
+ shouldUseApiSearch &&
328
+ filterByChainId !== null &&
329
+ !!trailsClient &&
330
+ !!trailsConfig.trailsApiKey,
331
+ staleTime: 60 * 60 * 1000, // 1 hour - tokens rarely change
332
+ gcTime: 24 * 60 * 60 * 1000, // 24 hours
333
+ refetchOnWindowFocus: false,
334
+ refetchOnReconnect: false,
335
+ })
336
+
155
337
  // Fetch prices for supported tokens when allSupportedTokens is true OR for qr-code/exchange modes
156
338
  const supportedTokensForPricing = useMemo(() => {
157
339
  if (fundMethod === "qr-code" || fundMethod === "exchange") {
@@ -187,11 +369,16 @@ export function useTokenList({
187
369
  // Determine loading state based on fund method and allSupportedTokens
188
370
  const isLoadingTokens =
189
371
  fundMethod === "qr-code" || fundMethod === "exchange"
190
- ? isLoadingSupportedTokens || isLoadingSupportedTokenPrices
372
+ ? isLoadingSupportedTokens ||
373
+ isLoadingSupportedTokenPrices ||
374
+ isLoadingSearchTokens ||
375
+ isLoadingCustomToken ||
376
+ isLoadingChainFilteredTokens
191
377
  : allSupportedTokens
192
378
  ? isLoadingSortedTokens ||
193
379
  isLoadingSupportedTokens ||
194
- isLoadingSupportedTokenPrices
380
+ isLoadingSupportedTokenPrices ||
381
+ isLoadingChainFilteredTokens
195
382
  : isLoadingSortedTokens
196
383
 
197
384
  const {
@@ -213,90 +400,194 @@ export function useTokenList({
213
400
  return new Set(supportedToChains.map((c) => c.id))
214
401
  }, [supportedToChains])
215
402
 
216
- const sortedTokens = useMemo<Array<TokenBalanceExtended>>(() => {
403
+ // Map chain-filtered token list results to Token format
404
+ // Used when a chain is selected for destination token list
405
+ const chainFilteredApiTokens = useMemo(() => {
406
+ if (
407
+ !shouldUseApiSearch ||
408
+ !chainFilteredTokenList?.tokens?.length ||
409
+ filterByChainId === null
410
+ ) {
411
+ return []
412
+ }
413
+
414
+ return chainFilteredTokenList.tokens
415
+ .filter((tokenInfo: ApiTokenInfo) => {
416
+ // Validate decimals - must be between 1 and 18
417
+ const decimals = tokenInfo.decimals
418
+ if (!Number.isInteger(decimals) || decimals < 1 || decimals > 18) {
419
+ logger.console.warn(
420
+ "[trails-sdk] Skipping token with invalid decimals:",
421
+ {
422
+ symbol: tokenInfo.symbol,
423
+ address: tokenInfo.address,
424
+ decimals: tokenInfo.decimals,
425
+ },
426
+ )
427
+ return false
428
+ }
429
+ return true
430
+ })
431
+ .map((tokenInfo: ApiTokenInfo) =>
432
+ convertApiTokenToToken(tokenInfo, getTokenImageUrl),
433
+ )
434
+ }, [
435
+ chainFilteredTokenList,
436
+ getTokenImageUrl,
437
+ shouldUseApiSearch,
438
+ filterByChainId,
439
+ ])
440
+
441
+ const sortedTokens = useMemo<Array<Token>>(() => {
217
442
  // If fundMethod is "qr-code" or "exchange", use filtered supported tokens
218
443
  if (fundMethod === "qr-code" || fundMethod === "exchange") {
444
+ // If chain filter is active and we have chain-filtered tokens, use those
445
+ if (
446
+ shouldUseApiSearch &&
447
+ filterByChainId !== null &&
448
+ chainFilteredApiTokens.length > 0
449
+ ) {
450
+ // Merge chain-filtered tokens with local tokens (prefer local tokens with balances)
451
+ const localTokensMap = new Map<string, Token>()
452
+ for (const tokenItem of allSortedTokens) {
453
+ const contractAddress = tokenItem.contractAddress || zeroAddress
454
+ const key = getTokenKey(tokenItem.chainId || 0, contractAddress)
455
+ localTokensMap.set(key, tokenItem)
456
+ }
457
+
458
+ const mergedTokens: Token[] = chainFilteredApiTokens.map(
459
+ (apiToken: Token) => {
460
+ const contractAddress = apiToken.contractAddress || zeroAddress
461
+ const key = getTokenKey(apiToken.chainId || 0, contractAddress)
462
+ const localToken = localTokensMap.get(key)
463
+
464
+ // If local token exists and has balance, use it; otherwise use API token
465
+ return localToken && (localToken.balanceUsd || 0) > 0
466
+ ? localToken
467
+ : apiToken
468
+ },
469
+ )
470
+
471
+ // Add any local tokens that weren't in API results
472
+ const apiTokenKeys = new Set(
473
+ mergedTokens.map((token) =>
474
+ getTokenKey(
475
+ token.chainId || 0,
476
+ token.contractAddress || zeroAddress,
477
+ ),
478
+ ),
479
+ )
480
+ for (const localToken of allSortedTokens) {
481
+ const key = getTokenKey(
482
+ localToken.chainId || 0,
483
+ localToken.contractAddress || zeroAddress,
484
+ )
485
+ if (
486
+ !apiTokenKeys.has(key) &&
487
+ (localToken.chainId || 0) === filterByChainId
488
+ ) {
489
+ mergedTokens.push(localToken)
490
+ }
491
+ }
492
+
493
+ return mergedTokens
494
+ }
495
+
219
496
  // Filter to only show specific tokens for QR code and exchange modes
220
497
  const filteredTokens = supportedTokens.filter((token: any) => {
221
498
  const symbol = token.symbol.toUpperCase()
222
499
  return STANDARD_TOKENS.includes(symbol as any)
223
500
  })
224
501
 
225
- // Convert SupportedToken to TokenBalanceExtended format and add price data
226
- const tokensWithPrices = filteredTokens.map((token: any) => {
227
- // Find price data for this token
228
- const priceData = supportedTokenPrices?.find(
229
- (p: any) =>
230
- p.token.tokenAddress?.toLowerCase() ===
231
- (token.contractAddress || zeroAddress).toLowerCase() &&
232
- p.token.chainId === token.chainId,
233
- )
502
+ // Convert SupportedToken to Token format and add price data
503
+ const tokensWithPrices: Token[] = filteredTokens.map(
504
+ (token: Token): Token => {
505
+ // Find price data for this token
506
+ const priceData = supportedTokenPrices?.find(
507
+ (p: any) =>
508
+ p.token.tokenAddress?.toLowerCase() ===
509
+ (token.contractAddress || zeroAddress).toLowerCase() &&
510
+ p.token.chainId === token.chainId,
511
+ )
234
512
 
235
- // Check if it's a native token (like ETH)
236
- if (
237
- token.contractAddress === "0x0000000000000000000000000000000000000000"
238
- ) {
239
- // Native token format
240
- return {
241
- chainId: token.chainId,
242
- balance: "0", // No balance info for QR code and exchange modes
243
- balanceUsd: 0,
244
- balanceUsdFormatted: "0",
245
- price:
246
- priceData?.priceUsd !== undefined
247
- ? { value: priceData.priceUsd, currency: "USD" }
248
- : { value: 0, currency: "USD" },
249
- price24hVol: 0, // Volume data not available in new API
250
- imageUrl: token.imageUrl,
251
- symbol: token.symbol,
252
- isSufficientBalance: true, // Always true for QR code and exchange modes
253
- accountAddress: address as Address.Address,
254
- } as NativeTokenBalanceWithPrice
255
- } else {
256
- // ERC20 token format
513
+ const priceUsd = priceData?.priceUsd
514
+
515
+ // Format price fields if price exists
516
+ const priceFields = priceUsd ? formatPriceFields(priceUsd) : undefined
517
+
518
+ // Return Token with price data
257
519
  return {
258
- chainId: token.chainId,
259
- contractAddress: token.contractAddress,
260
- balance: "0", // No balance info for QR code and exchange modes
261
- balanceUsd: 0,
262
- balanceUsdFormatted: "0",
263
- price:
264
- priceData?.priceUsd !== undefined
265
- ? { value: priceData.priceUsd, currency: "USD" }
266
- : { value: 0, currency: "USD" },
267
- price24hVol: 0, // Volume data not available in new API
268
- imageUrl: token.imageUrl,
269
- contractInfo: {
270
- decimals: token.decimals,
271
- symbol: token.symbol,
272
- name: token.name,
273
- },
520
+ ...token,
521
+ // No balance info for QR code and exchange modes
522
+ ...(priceFields && {
523
+ priceUsd: priceFields.priceUsd,
524
+ priceUsdFormatted: priceFields.priceUsdFormatted,
525
+ priceUsdDisplay: priceFields.priceUsdDisplay,
526
+ }),
274
527
  isSufficientBalance: true, // Always true for QR code and exchange modes
275
- // Add required properties for TokenBalanceWithPrice
276
- contractType: "ERC20",
277
- accountAddress: address as Address.Address,
278
- blockHash: "",
279
- blockNumber: 0,
280
- logIndex: 0,
281
- transactionHash: "",
282
- transactionIndex: 0,
283
- uniqueCollectibles: "0",
284
- isSummary: false,
285
- } as any
286
- }
528
+ }
529
+ },
530
+ )
531
+
532
+ // Sort by price (highest first) since volume is not available
533
+ return tokensWithPrices.sort((a, b) => {
534
+ return (b.priceUsd || 0) - (a.priceUsd || 0)
287
535
  })
536
+ }
537
+
538
+ // If allSupportedTokens is true and chain filter is active, use chain-filtered tokens
539
+ if (
540
+ allSupportedTokens &&
541
+ shouldUseApiSearch &&
542
+ filterByChainId !== null &&
543
+ chainFilteredApiTokens.length > 0
544
+ ) {
545
+ // Merge chain-filtered tokens with local tokens (prefer local tokens with balances)
546
+ const localTokensMap = new Map<string, Token>()
547
+ for (const tokenItem of allSortedTokens) {
548
+ const contractAddress = tokenItem.contractAddress || zeroAddress
549
+ const key = getTokenKey(tokenItem.chainId || 0, contractAddress)
550
+ localTokensMap.set(key, tokenItem)
551
+ }
552
+
553
+ const mergedTokens: Token[] = chainFilteredApiTokens.map(
554
+ (apiToken: Token) => {
555
+ const contractAddress = apiToken.contractAddress || zeroAddress
556
+ const key = getTokenKey(apiToken.chainId || 0, contractAddress)
557
+ const localToken = localTokensMap.get(key)
558
+
559
+ // If local token exists and has balance, use it; otherwise use API token
560
+ return localToken && (localToken.balanceUsd || 0) > 0
561
+ ? localToken
562
+ : apiToken
563
+ },
564
+ )
288
565
 
289
- // Sort by 24h volume (highest first)
290
- return tokensWithPrices.sort((a: any, b: any) => {
291
- const aVolume = Number(a?.price24hVol || 0)
292
- const bVolume = Number(b?.price24hVol || 0)
293
- return bVolume - aVolume
294
- }) as unknown as TokenBalanceExtended[]
566
+ // Add any local tokens that weren't in API results
567
+ const apiTokenKeys = new Set(
568
+ mergedTokens.map((token) =>
569
+ getTokenKey(token.chainId || 0, token.contractAddress || zeroAddress),
570
+ ),
571
+ )
572
+ for (const localToken of allSortedTokens) {
573
+ const key = getTokenKey(
574
+ localToken.chainId || 0,
575
+ localToken.contractAddress || zeroAddress,
576
+ )
577
+ if (
578
+ !apiTokenKeys.has(key) &&
579
+ (localToken.chainId || 0) === filterByChainId
580
+ ) {
581
+ mergedTokens.push(localToken)
582
+ }
583
+ }
584
+
585
+ return mergedTokens
295
586
  }
296
587
 
297
- // Default behavior: use account-specific tokens
298
- return allSortedTokens.filter((token: TokenBalanceExtended) => {
299
- if (!supportedChainIds.has(token.chainId)) {
588
+ // Default behavior: use account-specific tokens (already Token[])
589
+ return allSortedTokens.filter((token: Token) => {
590
+ if (!supportedChainIds.has(token.chainId || 0)) {
300
591
  return false
301
592
  }
302
593
  return true
@@ -306,8 +597,11 @@ export function useTokenList({
306
597
  supportedChainIds,
307
598
  fundMethod,
308
599
  supportedTokens,
309
- address,
310
600
  supportedTokenPrices,
601
+ shouldUseApiSearch,
602
+ filterByChainId,
603
+ chainFilteredApiTokens,
604
+ allSupportedTokens,
311
605
  ])
312
606
 
313
607
  useEffect(() => {
@@ -316,17 +610,14 @@ export function useTokenList({
316
610
  }
317
611
  }, [balanceError, onError])
318
612
 
319
- const handleTokenSelect = (token: TokenFormatted) => {
320
- const isNative =
321
- !("contractAddress" in token) || token.contractAddress === zeroAddress
322
- const chainInfo = getChainInfo(token.chainId)
323
- const imageUrl = token.imageUrl
324
- const decimals = isNative ? 18 : token.contractInfo?.decimals
613
+ const handleTokenSelect = (token: Token) => {
614
+ const isNative = token.isNativeToken ?? false
615
+ const decimals = token.decimals
325
616
  if (!decimals) {
326
617
  logger.console.warn(
327
618
  "[trails-sdk] [useTokenList] Missing decimals for token:",
328
619
  {
329
- token: token.contractInfo,
620
+ token: token,
330
621
  chainId: token.chainId,
331
622
  isNative,
332
623
  },
@@ -335,71 +626,32 @@ export function useTokenList({
335
626
  return
336
627
  }
337
628
 
338
- let formattedToken: Token
339
- if (isNative) {
340
- formattedToken = {
341
- id: (token as TokenBalanceExtended).chainId,
342
- name: chainInfo?.nativeCurrency.name || "Native Token",
343
- symbol: chainInfo?.nativeCurrency.symbol || "ETH",
344
- balance: (token as TokenBalanceExtended).balance,
345
- imageUrl,
346
- chainId: (token as TokenBalanceExtended).chainId,
347
- contractAddress: zeroAddress,
348
- balanceUsdFormatted:
349
- (token as TokenBalanceExtended).balanceUsdFormatted ?? "",
350
- tokenPriceUsd: (token as TokenBalanceExtended).price?.value ?? 0,
351
- contractInfo: {
352
- decimals,
353
- symbol: chainInfo?.nativeCurrency.symbol || "ETH",
354
- name: chainInfo?.nativeCurrency.name || "Native Token",
355
- },
356
- }
357
- } else {
358
- formattedToken = {
359
- id: token.chainId,
360
- name: token.contractInfo?.name || "Unknown Token",
361
- symbol: token.contractInfo?.symbol || "???",
362
- balance: token.balance,
363
- imageUrl,
364
- chainId: token.chainId,
365
- contractAddress: token.contractAddress,
366
- contractInfo: {
367
- ...token.contractInfo,
368
- name: token.contractInfo?.name ?? "Unknown Token",
369
- symbol: token.contractInfo?.symbol ?? "???",
370
- decimals,
371
- },
372
- balanceUsdFormatted: token.balanceUsdFormatted ?? "",
373
- tokenPriceUsd: token.price?.value ?? 0,
374
- }
375
- }
376
-
377
- setSelectedToken(formattedToken)
629
+ setSelectedToken(token)
378
630
  const canContinue =
379
631
  (!targetAmountUsd || (targetAmountUsd && hasSufficientBalanceUsd)) &&
380
- token.isSufficientBalance
632
+ (token.isSufficientBalance ?? true)
381
633
 
382
634
  if (!canContinue) {
383
635
  logger.console.warn("[trails-sdk] Cannot continue with token selection", {
384
- token: formattedToken,
636
+ token: token,
385
637
  targetAmountUsd,
386
638
  hasSufficientBalanceUsd,
387
639
  isSufficientBalance: token.isSufficientBalance,
388
640
  })
389
641
  }
390
642
 
391
- onContinue(formattedToken)
643
+ onContinue(token)
392
644
  }
393
645
 
394
- const isTokenSelected = (token: TokenBalanceExtended): boolean => {
646
+ const isTokenSelected = (token: Token): boolean => {
395
647
  if (!selectedToken) return false
396
648
 
397
- const isNative =
398
- !("contractAddress" in token) || token.contractAddress === zeroAddress
649
+ const isNative = token.isNativeToken ?? false
650
+ const selectedIsNative = selectedToken.isNativeToken ?? false
399
651
  return (
400
- selectedToken.chainId === token.chainId &&
652
+ (selectedToken.chainId || 0) === (token.chainId || 0) &&
401
653
  (isNative
402
- ? selectedToken.contractAddress === zeroAddress
654
+ ? selectedIsNative
403
655
  : isAddressEqual(
404
656
  Address.from(selectedToken.contractAddress),
405
657
  Address.from(token.contractAddress),
@@ -407,105 +659,182 @@ export function useTokenList({
407
659
  )
408
660
  }
409
661
 
662
+ // Map API search results to Token format
663
+ // Only used for exchange/qr-code modes
664
+ const apiSearchTokens = useMemo(() => {
665
+ if (!shouldUseApiSearch || !searchTokensResult?.tokens?.length) {
666
+ return []
667
+ }
668
+
669
+ return searchTokensResult.tokens
670
+ .filter((tokenInfo: ApiTokenInfo) => {
671
+ // Validate decimals - must be between 1 and 18 (formatRawAmount requires 1-18)
672
+ const decimals = tokenInfo.decimals
673
+ if (!Number.isInteger(decimals) || decimals < 1 || decimals > 18) {
674
+ logger.console.warn(
675
+ "[trails-sdk] Skipping token with invalid decimals:",
676
+ {
677
+ symbol: tokenInfo.symbol,
678
+ address: tokenInfo.address,
679
+ decimals: tokenInfo.decimals,
680
+ },
681
+ )
682
+ return false
683
+ }
684
+ return true
685
+ })
686
+ .map((tokenInfo: ApiTokenInfo) =>
687
+ convertApiTokenToToken(tokenInfo, getTokenImageUrl),
688
+ )
689
+ }, [searchTokensResult, getTokenImageUrl, shouldUseApiSearch])
690
+
691
+ // Convert custom token info to Token format
692
+ const customTokenAsTokenBalance = useMemo(() => {
693
+ if (!customTokenInfo || !customTokenChainId) return null
694
+
695
+ const chainInfo = getChainInfo(customTokenChainId)
696
+ const validatedDecimals = validateDecimals(customTokenInfo.decimals)
697
+
698
+ return {
699
+ symbol: customTokenInfo.symbol,
700
+ name: customTokenInfo.name,
701
+ decimals: validatedDecimals,
702
+ contractAddress: customTokenInfo.contractAddress,
703
+ chainId: customTokenChainId,
704
+ chainName: chainInfo?.name || `Chain ${customTokenChainId}`,
705
+ imageUrl: getTokenImageUrl({
706
+ chainId: customTokenChainId,
707
+ contractAddress: customTokenInfo.contractAddress,
708
+ symbol: customTokenInfo.symbol,
709
+ }),
710
+ isCustomToken: true,
711
+ } as Token
712
+ }, [customTokenInfo, customTokenChainId, getTokenImageUrl])
713
+
410
714
  const filteredTokens = useMemo(() => {
411
- if (!searchQuery.trim()) {
412
- return sortedTokens
715
+ // If searching by address and getTokenList returned no results, return custom token if available
716
+ if (
717
+ shouldUseApiSearch &&
718
+ isSearchQueryAddress &&
719
+ hasNoSearchResults &&
720
+ customTokenAsTokenBalance
721
+ ) {
722
+ return [customTokenAsTokenBalance]
413
723
  }
414
724
 
415
- const query = searchQuery.trim().toLowerCase()
416
- const queryParts = query.split(/\s+/).filter((part) => part.length > 0)
725
+ // sortedTokens is already Token[] from useTokenBalances
726
+ // Only use API search results for exchange/qr-code modes
727
+ if (
728
+ shouldUseApiSearch &&
729
+ trimmedSearchQuery &&
730
+ apiSearchTokens.length > 0
731
+ ) {
732
+ // Create a map of local tokens by chainId and contractAddress for quick lookup
733
+ const localTokensMap = new Map<string, Token>()
734
+ for (const tokenItem of sortedTokens) {
735
+ const contractAddress = tokenItem.contractAddress || zeroAddress
736
+ const key = getTokenKey(tokenItem.chainId || 0, contractAddress)
737
+ localTokensMap.set(key, tokenItem)
738
+ }
417
739
 
418
- const matchingTokens = sortedTokens.filter(
419
- (token: TokenBalanceExtended) => {
420
- const isNative =
421
- !("contractAddress" in token) || token.contractAddress === zeroAddress
422
- const chainInfo = getChainInfo(token.chainId)
423
- const chainName = chainInfo?.name || ""
424
- const chainNameLower = chainName.toLowerCase()
740
+ // Merge API results with local tokens (prefer local tokens with balances)
741
+ const mergedTokens: Token[] = apiSearchTokens.map((apiToken: Token) => {
742
+ const contractAddress = apiToken.contractAddress || zeroAddress
743
+ const key = getTokenKey(apiToken.chainId || 0, contractAddress)
744
+ const localToken = localTokensMap.get(key)
425
745
 
426
- if (isNative) {
427
- const nativeSymbol = chainInfo?.nativeCurrency.symbol || "ETH"
428
- const nativeName = chainInfo?.nativeCurrency.name || "Native Token"
429
- const nativeSymbolLower = nativeSymbol.toLowerCase()
430
- const nativeNameLower = nativeName.toLowerCase()
746
+ // If local token exists and has balance, use it; otherwise use API token
747
+ return localToken && (localToken.balanceUsd || 0) > 0
748
+ ? localToken
749
+ : apiToken
750
+ })
431
751
 
432
- // If multiple query parts, check if they match chain + token combination
433
- if (queryParts.length > 1) {
434
- const matchesChain = queryParts.some((part) =>
435
- chainNameLower.includes(part),
436
- )
437
- const matchesToken = queryParts.some(
438
- (part) =>
439
- nativeSymbolLower.includes(part) ||
440
- nativeNameLower.includes(part),
441
- )
442
- return matchesChain && matchesToken
443
- }
752
+ // Add any local tokens that weren't in API results but match the search
753
+ const apiTokenKeys = new Set(
754
+ mergedTokens.map((token) => {
755
+ const contractAddress = token.contractAddress || zeroAddress
756
+ return getTokenKey(token.chainId || 0, contractAddress)
757
+ }),
758
+ )
444
759
 
445
- // Single query part - match against any field
446
- return queryParts.some(
447
- (part) =>
448
- nativeSymbolLower.includes(part) ||
449
- nativeNameLower.includes(part) ||
450
- chainNameLower.includes(part),
451
- )
760
+ const queryLower = trimmedSearchQuery.toLowerCase()
761
+ for (const tokenItem of sortedTokens) {
762
+ const contractAddress = tokenItem.contractAddress || zeroAddress
763
+ const key = getTokenKey(tokenItem.chainId || 0, contractAddress)
764
+
765
+ if (!apiTokenKeys.has(key)) {
766
+ // Check if token matches search query locally
767
+ const tokenSymbol = (tokenItem.symbol || "").toLowerCase()
768
+ const tokenName = (tokenItem.name || "").toLowerCase()
769
+
770
+ if (
771
+ tokenSymbol.includes(queryLower) ||
772
+ tokenName.includes(queryLower) ||
773
+ contractAddress.toLowerCase().includes(queryLower)
774
+ ) {
775
+ mergedTokens.push(tokenItem)
776
+ }
452
777
  }
778
+ }
453
779
 
454
- const tokenSymbol = token.contractInfo?.symbol || "???"
455
- const tokenName = token.contractInfo?.name || "Unknown Token"
456
- const contractAddress = token.contractAddress || ""
457
- const tokenSymbolLower = tokenSymbol.toLowerCase()
458
- const tokenNameLower = tokenName.toLowerCase()
459
- const contractAddressLower = contractAddress.toLowerCase()
460
-
461
- // If multiple query parts, check if they match chain + token combination
462
- if (queryParts.length > 1) {
463
- const matchesChain = queryParts.some((part) =>
464
- chainNameLower.includes(part),
465
- )
466
- const matchesToken = queryParts.some(
467
- (part) =>
468
- tokenSymbolLower.includes(part) ||
469
- tokenNameLower.includes(part) ||
470
- contractAddressLower.includes(part),
471
- )
472
- return matchesChain && matchesToken
473
- }
780
+ return mergedTokens
781
+ }
782
+
783
+ // Fallback to local filtering if no API results or not using API search
784
+ if (!trimmedSearchQuery) {
785
+ return sortedTokens
786
+ }
787
+
788
+ const queryLower = trimmedSearchQuery.toLowerCase()
789
+ const queryParts = queryLower.split(/\s+/).filter((part) => part.length > 0)
790
+
791
+ const matchingTokens = sortedTokens.filter((token: Token) => {
792
+ const chainInfo = getChainInfo(token.chainId || 0)
793
+ const chainName = chainInfo?.name || ""
794
+ const chainNameLower = chainName.toLowerCase()
474
795
 
475
- // Single query part - match against any field
476
- return queryParts.some(
796
+ const tokenSymbol = (token.symbol || "???").toLowerCase()
797
+ const tokenName = (token.name || "Unknown Token").toLowerCase()
798
+ const contractAddress = (
799
+ token.contractAddress || zeroAddress
800
+ ).toLowerCase()
801
+
802
+ // If multiple query parts, check if they match chain + token combination
803
+ if (queryParts.length > 1) {
804
+ const matchesChain = queryParts.some((part) =>
805
+ chainNameLower.includes(part),
806
+ )
807
+ const matchesToken = queryParts.some(
477
808
  (part) =>
478
- tokenSymbolLower.includes(part) ||
479
- tokenNameLower.includes(part) ||
480
- chainNameLower.includes(part) ||
481
- contractAddressLower.includes(part),
809
+ tokenSymbol.includes(part) ||
810
+ tokenName.includes(part) ||
811
+ contractAddress.includes(part),
482
812
  )
483
- },
484
- )
813
+ return matchesChain && matchesToken
814
+ }
485
815
 
486
- // Sort by relevance for balance tokens too
487
- return matchingTokens.sort((a, b) => {
488
- const isANative = !("contractAddress" in a)
489
- const isBNative = !("contractAddress" in b)
816
+ // Single query part - match against any field
817
+ return queryParts.some(
818
+ (part) =>
819
+ tokenSymbol.includes(part) ||
820
+ tokenName.includes(part) ||
821
+ chainNameLower.includes(part) ||
822
+ contractAddress.includes(part),
823
+ )
824
+ })
490
825
 
491
- const aChainInfo = getChainInfo(a.chainId)
492
- const bChainInfo = getChainInfo(b.chainId)
826
+ // Sort by relevance for balance tokens too
827
+ return matchingTokens.sort((a: Token, b: Token) => {
828
+ const aChainInfo = getChainInfo(a.chainId || 0)
829
+ const bChainInfo = getChainInfo(b.chainId || 0)
493
830
  const aChainName = aChainInfo?.name?.toLowerCase() || ""
494
831
  const bChainName = bChainInfo?.name?.toLowerCase() || ""
495
832
 
496
- const aTokenSymbol = isANative
497
- ? (aChainInfo?.nativeCurrency.symbol || "ETH").toLowerCase()
498
- : (a.contractInfo?.symbol || "").toLowerCase()
499
- const bTokenSymbol = isBNative
500
- ? (bChainInfo?.nativeCurrency.symbol || "ETH").toLowerCase()
501
- : (b.contractInfo?.symbol || "").toLowerCase()
833
+ const aTokenSymbol = (a.symbol || "???").toLowerCase()
834
+ const bTokenSymbol = (b.symbol || "???").toLowerCase()
502
835
 
503
- const aTokenName = isANative
504
- ? (aChainInfo?.nativeCurrency.name || "Native Token").toLowerCase()
505
- : (a.contractInfo?.name || "").toLowerCase()
506
- const bTokenName = isBNative
507
- ? (bChainInfo?.nativeCurrency.name || "Native Token").toLowerCase()
508
- : (b.contractInfo?.name || "").toLowerCase()
836
+ const aTokenName = (a.name || "Unknown Token").toLowerCase()
837
+ const bTokenName = (b.name || "Unknown Token").toLowerCase()
509
838
 
510
839
  // Calculate relevance scores using helper function
511
840
  const aScore = calculateSearchRelevanceScore(
@@ -523,118 +852,63 @@ export function useTokenList({
523
852
 
524
853
  return bScore - aScore // Higher score first
525
854
  })
526
- }, [sortedTokens, searchQuery])
855
+ }, [
856
+ sortedTokens,
857
+ trimmedSearchQuery,
858
+ apiSearchTokens,
859
+ shouldUseApiSearch,
860
+ isSearchQueryAddress,
861
+ hasNoSearchResults,
862
+ customTokenAsTokenBalance,
863
+ ])
527
864
 
528
865
  const filteredTokensFormatted = useMemo(() => {
529
- // Get base formatted tokens
866
+ // Get base formatted tokens - filteredTokens is already Token[]
530
867
  const baseFormattedTokens = filteredTokens
531
- .map((token: TokenBalanceExtended): TokenFormatted | null => {
532
- const isNative =
533
- !("contractAddress" in token) || token.contractAddress === zeroAddress
534
- const chainInfo = getChainInfo(token.chainId)
535
- const nativeSymbol = chainInfo?.nativeCurrency.symbol || "ETH"
536
- const tokenSymbol = isNative
537
- ? nativeSymbol
538
- : token.contractInfo?.symbol || "???"
539
- const contractAddress = isNative ? zeroAddress : token.contractAddress
540
- let imageContractAddress = contractAddress
541
- if (tokenSymbol === "WETH") {
542
- imageContractAddress = zeroAddress
543
- }
544
- const fallbackImageUrl =
545
- (token as TokenBalanceWithPrice).contractInfo?.logoURI || ""
546
- const imageUrl = getTokenImageUrl({
547
- chainId: token.chainId,
548
- contractAddress: imageContractAddress,
549
- symbol: tokenSymbol,
550
- fallbackImageUrl: fallbackImageUrl || undefined,
551
- })
552
- const currentTokenName =
553
- (token as TokenBalanceWithPrice).contractInfo?.name || ""
554
- const tokenName = getFormatttedTokenName(
555
- currentTokenName,
556
- tokenSymbol,
557
- token.chainId,
558
- )
559
- const decimals = isNative ? 18 : token.contractInfo?.decimals
868
+ .map((token: Token): Token | null => {
869
+ const isNative = token.isNativeToken ?? false
870
+ const chainInfo = getChainInfo(token.chainId || 0)
871
+ const decimals = token.decimals
560
872
  if (!decimals) {
561
873
  logger.console.warn(
562
874
  "[trails-sdk] Missing decimals for token, skipping:",
563
875
  {
564
- token: isNative ? token : token.contractInfo,
876
+ token: token,
565
877
  chainId: token.chainId,
566
878
  isNative,
567
879
  },
568
880
  )
569
881
  return null // Return null to filter out tokens without decimals
570
882
  }
571
- const formattedBalance = formatRawAmount(token.balance, decimals)
572
- const priceUsd = Number(token.price?.value) ?? 0
573
- const balanceUsdFormatted = token.balanceUsdFormatted ?? ""
574
883
 
575
- let isSufficientBalance = true
884
+ let isSufficientBalance = token.isSufficientBalance ?? true
576
885
  if (targetAmountUsd) {
577
886
  isSufficientBalance = (token.balanceUsd ?? 0) >= targetAmountUsd
578
887
  }
579
- const chainName = chainInfo?.name || ""
888
+ const chainName = token.chainName || chainInfo?.name || ""
580
889
 
890
+ // Format price fields if not already formatted
891
+ const priceFields = token.priceUsdFormatted
892
+ ? undefined
893
+ : token.priceUsd
894
+ ? formatPriceFields(token.priceUsd)
895
+ : undefined
896
+
897
+ // Return Token with all formatting fields set
581
898
  return {
582
899
  ...token,
583
- id: token.chainId,
584
- contractInfo: {
585
- ...(token as TokenBalanceWithPrice).contractInfo,
586
- chainId: Number(token.chainId),
587
- source: (token as TokenBalanceWithPrice).contractInfo?.source || "",
588
- type: (token as TokenBalanceWithPrice).contractInfo?.type || "",
589
- logoURI:
590
- (token as TokenBalanceWithPrice).contractInfo?.logoURI || "",
591
- deployed:
592
- (token as TokenBalanceWithPrice).contractInfo?.deployed || false,
593
- bytecodeHash:
594
- (token as TokenBalanceWithPrice).contractInfo?.bytecodeHash || "",
595
- updatedAt:
596
- (token as TokenBalanceWithPrice).contractInfo?.updatedAt || "",
597
- queuedAt:
598
- (token as TokenBalanceWithPrice).contractInfo?.queuedAt || "",
599
- extensions: (token as TokenBalanceWithPrice).contractInfo
600
- ?.extensions || {
601
- link: "",
602
- description: "",
603
- categories: [],
604
- ogImage: "",
605
- ogName: "",
606
- originChainId: 0,
607
- originAddress: "",
608
- blacklist: false,
609
- verified: false,
610
- featureIndex: 0,
611
- verifiedBy: "",
612
- featured: false,
613
- },
614
- status:
615
- (token as TokenBalanceWithPrice).contractInfo?.status ||
616
- ResourceStatus.NOT_AVAILABLE,
617
- address: contractAddress,
618
- name: tokenName,
619
- symbol: tokenSymbol,
620
- decimals: decimals,
621
- },
622
- name: tokenName,
623
- symbol: tokenSymbol,
624
- balanceFormatted: formattedBalance,
625
- imageUrl,
626
- chainId: token.chainId,
627
- contractAddress: contractAddress,
628
- balanceUsdFormatted,
629
- tokenPriceUsd: priceUsd,
630
- isNative: isNative,
631
- tokenName: tokenName,
632
- priceUsd: priceUsd,
900
+ balanceDisplay: token.balanceDisplay || token.balanceFormatted,
901
+ priceUsdFormatted:
902
+ token.priceUsdFormatted || priceFields?.priceUsdFormatted,
903
+ priceUsdDisplay:
904
+ token.priceUsdDisplay || priceFields?.priceUsdDisplay,
905
+ balanceUsdDisplay:
906
+ token.balanceUsdDisplay || token.balanceUsdFormatted,
633
907
  isSufficientBalance,
634
908
  chainName,
635
909
  }
636
910
  })
637
- .filter(Boolean) as TokenFormatted[] // Filter out null values for tokens without decimals
911
+ .filter(Boolean) as Token[] // Filter out null values for tokens without decimals
638
912
 
639
913
  // If allSupportedTokens is true, combine with supported tokens
640
914
  if (allSupportedTokens) {
@@ -646,19 +920,15 @@ export function useTokenList({
646
920
  )
647
921
 
648
922
  // Add supported tokens that don't exist in the base list
649
- const additionalSupportedTokens = supportedTokens
923
+ const additionalSupportedTokens: Token[] = supportedTokens
650
924
  .filter((supportedToken) => {
651
- const key = `${supportedToken.chainId}-${supportedToken.contractAddress.toLowerCase()}`
925
+ const key = `${supportedToken.chainId || 0}-${supportedToken.contractAddress.toLowerCase()}`
652
926
  return !existingTokenKeys.has(key)
653
927
  })
654
- .map((supportedToken) => {
655
- const chainInfo = getChainInfo(supportedToken.chainId)
656
- const chainName = chainInfo?.name || `Chain ${supportedToken.chainId}`
657
- const tokenName = getFormatttedTokenName(
658
- supportedToken.name,
659
- supportedToken.symbol,
660
- supportedToken.chainId,
661
- )
928
+ .map((supportedToken): Token => {
929
+ const chainInfo = getChainInfo(supportedToken.chainId || 0)
930
+ const chainName =
931
+ chainInfo?.name || `Chain ${supportedToken.chainId || 0}`
662
932
 
663
933
  // Find price data for this supported token
664
934
  const priceData = supportedTokenPrices?.find(
@@ -668,30 +938,30 @@ export function useTokenList({
668
938
  p.token.chainId === supportedToken.chainId,
669
939
  )
670
940
 
941
+ // Format price fields if price exists
942
+ const priceFields = priceData?.priceUsd
943
+ ? formatPriceFields(priceData.priceUsd)
944
+ : undefined
945
+
671
946
  return {
672
- ...supportedToken,
673
- tokenName: tokenName,
947
+ symbol: supportedToken.symbol,
948
+ name: supportedToken.name,
949
+ decimals: supportedToken.decimals,
950
+ contractAddress: supportedToken.contractAddress,
951
+ chainId: supportedToken.chainId,
674
952
  chainName: chainName,
675
- contractInfo: {
676
- decimals: supportedToken.decimals,
677
- symbol: supportedToken.symbol,
678
- name: supportedToken.name,
679
- },
680
- // Add minimal required properties to make it work
681
- balance: "",
682
- balanceFormatted: "",
683
- balanceUsdFormatted: "",
684
- priceUsd: priceData?.priceUsd || 0,
685
- price24hVol: 0, // Volume data not available in new API
953
+ imageUrl: supportedToken.imageUrl || "",
954
+ ...(priceFields && {
955
+ priceUsd: priceFields.priceUsd,
956
+ priceUsdFormatted: priceFields.priceUsdFormatted,
957
+ priceUsdDisplay: priceFields.priceUsdDisplay,
958
+ }),
686
959
  isSufficientBalance: true,
687
- // Use any type to bypass strict type checking for now
688
- } as any
960
+ }
689
961
  })
690
- .sort((a: any, b: any) => {
691
- // Sort supported tokens by 24h volume (highest first)
692
- const aVolume = Number(a?.price24hVol || 0)
693
- const bVolume = Number(b?.price24hVol || 0)
694
- return bVolume - aVolume
962
+ .sort((a, b) => {
963
+ // Sort supported tokens by price (highest first) since volume is not available
964
+ return (b.priceUsd || 0) - (a.priceUsd || 0)
695
965
  })
696
966
 
697
967
  // Combine lists with base tokens taking precedence
@@ -705,37 +975,35 @@ export function useTokenList({
705
975
  const query = searchQuery.toLowerCase().trim()
706
976
  const queryParts = query.split(/\s+/).filter((part) => part.length > 0)
707
977
 
708
- const filteredTokens = combinedTokens.filter(
709
- (token: TokenFormatted) => {
710
- const chainName = token.chainName?.toLowerCase() || ""
711
- const tokenSymbol = token.symbol?.toLowerCase() || ""
712
- const tokenName = token.name?.toLowerCase() || ""
713
- const contractAddress = token.contractAddress?.toLowerCase() || ""
714
-
715
- // If multiple query parts, check if they match chain + token combination
716
- if (queryParts.length > 1) {
717
- const matchesChain = queryParts.some((part) =>
718
- chainName.includes(part),
719
- )
720
- const matchesToken = queryParts.some(
721
- (part) =>
722
- tokenSymbol.includes(part) ||
723
- tokenName.includes(part) ||
724
- contractAddress.includes(part),
725
- )
726
- return matchesChain && matchesToken
727
- }
728
-
729
- // Single query part - match against any field
730
- return queryParts.some(
978
+ const filteredTokens = combinedTokens.filter((token: Token) => {
979
+ const chainName = token.chainName?.toLowerCase() || ""
980
+ const tokenSymbol = token.symbol?.toLowerCase() || ""
981
+ const tokenName = token.name?.toLowerCase() || ""
982
+ const contractAddress = token.contractAddress?.toLowerCase() || ""
983
+
984
+ // If multiple query parts, check if they match chain + token combination
985
+ if (queryParts.length > 1) {
986
+ const matchesChain = queryParts.some((part) =>
987
+ chainName.includes(part),
988
+ )
989
+ const matchesToken = queryParts.some(
731
990
  (part) =>
732
991
  tokenSymbol.includes(part) ||
733
992
  tokenName.includes(part) ||
734
- chainName.includes(part) ||
735
993
  contractAddress.includes(part),
736
994
  )
737
- },
738
- )
995
+ return matchesChain && matchesToken
996
+ }
997
+
998
+ // Single query part - match against any field
999
+ return queryParts.some(
1000
+ (part) =>
1001
+ tokenSymbol.includes(part) ||
1002
+ tokenName.includes(part) ||
1003
+ chainName.includes(part) ||
1004
+ contractAddress.includes(part),
1005
+ )
1006
+ })
739
1007
 
740
1008
  // Sort filtered tokens by relevance
741
1009
  return filteredTokens.sort((a, b) => {
@@ -783,7 +1051,6 @@ export function useTokenList({
783
1051
  supportedTokens,
784
1052
  supportedTokenPrices,
785
1053
  searchQuery,
786
- getTokenImageUrl,
787
1054
  ])
788
1055
 
789
1056
  const showInsufficientBalance = useMemo(() => {
@@ -805,12 +1072,17 @@ export function useTokenList({
805
1072
  totalBalanceUsd,
806
1073
  ])
807
1074
 
1075
+ // isSearching: true when user is typing (debounce pending) or API is fetching
1076
+ const isSearching = isSearchPending || isLoadingSearchTokens
1077
+
808
1078
  return {
809
1079
  selectedToken,
810
1080
  searchQuery,
811
1081
  handleTokenSelect,
812
1082
  filteredTokens,
813
- isLoadingSortedTokens,
1083
+ isLoadingSortedTokens: isLoadingSortedTokens || isLoadingSearchTokens,
1084
+ isLoadingSearchTokens,
1085
+ isSearching,
814
1086
  balanceError,
815
1087
  showContinueButton,
816
1088
  isTokenSelected,