0xtrails 0.0.1 → 0.0.2

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 (359) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +42 -0
  3. package/dist/abi.d.ts +37 -0
  4. package/dist/abi.d.ts.map +1 -0
  5. package/dist/abi.js +36 -0
  6. package/dist/apiClient.d.ts +9 -0
  7. package/dist/apiClient.d.ts.map +1 -0
  8. package/dist/apiClient.js +18 -0
  9. package/dist/buffer.d.ts +3 -0
  10. package/dist/buffer.d.ts.map +1 -0
  11. package/dist/buffer.js +8 -0
  12. package/dist/cctp.d.ts +84 -0
  13. package/dist/cctp.d.ts.map +1 -0
  14. package/dist/cctp.js +401 -0
  15. package/dist/chainSwitch.d.ts +7 -0
  16. package/dist/chainSwitch.d.ts.map +1 -0
  17. package/dist/chainSwitch.js +33 -0
  18. package/dist/chains.d.ts +13 -0
  19. package/dist/chains.d.ts.map +1 -0
  20. package/dist/chains.js +95 -0
  21. package/dist/constants.d.ts +11 -0
  22. package/dist/constants.d.ts.map +1 -0
  23. package/dist/constants.js +16 -0
  24. package/dist/encoders.d.ts +7 -0
  25. package/dist/encoders.d.ts.map +1 -0
  26. package/dist/encoders.js +8 -0
  27. package/dist/error.d.ts +2 -0
  28. package/dist/error.d.ts.map +1 -0
  29. package/dist/error.js +12 -0
  30. package/dist/explorer.d.ts +12 -0
  31. package/dist/explorer.d.ts.map +1 -0
  32. package/dist/explorer.js +18 -0
  33. package/dist/gasless.d.ts +116 -0
  34. package/dist/gasless.d.ts.map +1 -0
  35. package/dist/gasless.js +297 -0
  36. package/dist/index.d.ts +13 -0
  37. package/dist/index.d.ts.map +1 -0
  38. package/dist/index.js +9 -0
  39. package/dist/indexerClient.d.ts +9 -0
  40. package/dist/indexerClient.d.ts.map +1 -0
  41. package/dist/indexerClient.js +18 -0
  42. package/dist/intents.d.ts +83 -0
  43. package/dist/intents.d.ts.map +1 -0
  44. package/dist/intents.js +288 -0
  45. package/dist/metaTxnMonitor.d.ts +14 -0
  46. package/dist/metaTxnMonitor.d.ts.map +1 -0
  47. package/dist/metaTxnMonitor.js +121 -0
  48. package/dist/metaTxns.d.ts +6 -0
  49. package/dist/metaTxns.d.ts.map +1 -0
  50. package/dist/metaTxns.js +4 -0
  51. package/dist/paymasterSend.d.ts +90 -0
  52. package/dist/paymasterSend.d.ts.map +1 -0
  53. package/dist/paymasterSend.js +329 -0
  54. package/dist/preconditions.d.ts +11 -0
  55. package/dist/preconditions.d.ts.map +1 -0
  56. package/dist/preconditions.js +29 -0
  57. package/dist/prepareSend.d.ts +102 -0
  58. package/dist/prepareSend.d.ts.map +1 -0
  59. package/dist/prepareSend.js +1080 -0
  60. package/dist/prices.d.ts +18 -0
  61. package/dist/prices.d.ts.map +1 -0
  62. package/dist/prices.js +142 -0
  63. package/dist/queryParams.d.ts +9 -0
  64. package/dist/queryParams.d.ts.map +1 -0
  65. package/dist/queryParams.js +66 -0
  66. package/dist/relaySdk.d.ts +65 -0
  67. package/dist/relaySdk.d.ts.map +1 -0
  68. package/dist/relaySdk.js +314 -0
  69. package/dist/relayer.d.ts +23 -0
  70. package/dist/relayer.d.ts.map +1 -0
  71. package/dist/relayer.js +230 -0
  72. package/dist/sendUserOp.d.ts +140 -0
  73. package/dist/sendUserOp.d.ts.map +1 -0
  74. package/dist/sendUserOp.js +388 -0
  75. package/dist/sequenceWallet.d.ts +79 -0
  76. package/dist/sequenceWallet.d.ts.map +1 -0
  77. package/dist/sequenceWallet.js +374 -0
  78. package/dist/theme.d.ts +3 -0
  79. package/dist/theme.d.ts.map +1 -0
  80. package/dist/theme.js +1 -0
  81. package/dist/toSimpleSmartAccount.d.ts +95 -0
  82. package/dist/toSimpleSmartAccount.d.ts.map +1 -0
  83. package/dist/toSimpleSmartAccount.js +373 -0
  84. package/dist/tokenBalances.d.ts +118 -0
  85. package/dist/tokenBalances.d.ts.map +1 -0
  86. package/dist/tokenBalances.js +492 -0
  87. package/dist/tokens.d.ts +50 -0
  88. package/dist/tokens.d.ts.map +1 -0
  89. package/dist/tokens.js +356 -0
  90. package/dist/trails.d.ts +128 -0
  91. package/dist/trails.d.ts.map +1 -0
  92. package/dist/trails.js +1031 -0
  93. package/dist/umd/trails.min.js +12610 -0
  94. package/dist/umd/trails.min.js.map +1 -0
  95. package/dist/umd.d.ts +24 -0
  96. package/dist/umd.d.ts.map +1 -0
  97. package/dist/utils.d.ts +5 -0
  98. package/dist/utils.d.ts.map +1 -0
  99. package/dist/utils.js +9 -0
  100. package/dist/widget/ConstantsUtil-B-_-u8aQ.js +6 -0
  101. package/dist/widget/add-hVLs3ldJ.js +20 -0
  102. package/dist/widget/all-wallets-Cwxnx4BT.js +11 -0
  103. package/dist/widget/app-store-CAAVQjW0.js +22 -0
  104. package/dist/widget/apple-C3BSbglw.js +23 -0
  105. package/dist/widget/arrow-bottom-circle-BGU9MmsZ.js +16 -0
  106. package/dist/widget/arrow-bottom-hS_SA8Gp.js +13 -0
  107. package/dist/widget/arrow-left-CJZanWz7.js +13 -0
  108. package/dist/widget/arrow-right-C1qL8EMd.js +13 -0
  109. package/dist/widget/arrow-top-CbuCmbQs.js +13 -0
  110. package/dist/widget/bank-CXBEEGbb.js +19 -0
  111. package/dist/widget/bin-Dqzv3zCZ.js +9 -0
  112. package/dist/widget/bitcoin-4y3sovZp.js +18 -0
  113. package/dist/widget/browser-DyOl4_8m.js +1413 -0
  114. package/dist/widget/browser-t7Fh0sEU.js +19 -0
  115. package/dist/widget/card-Bo4CZkTs.js +19 -0
  116. package/dist/widget/ccip-BynehMIN.js +232 -0
  117. package/dist/widget/checkmark-DV6OKvnY.js +16 -0
  118. package/dist/widget/checkmark-bold-CAp1-IQ2.js +13 -0
  119. package/dist/widget/chevron-bottom-BjzsVzk9.js +13 -0
  120. package/dist/widget/chevron-left-CQZBDCiR.js +13 -0
  121. package/dist/widget/chevron-right-Dhg4zeZM.js +13 -0
  122. package/dist/widget/chevron-top-CDQmfJef.js +13 -0
  123. package/dist/widget/chrome-store-BNaC_b6w.js +66 -0
  124. package/dist/widget/circle-BC_GBj91.js +9 -0
  125. package/dist/widget/clock-BmF8-4a0.js +13 -0
  126. package/dist/widget/close-Bf61nZ8o.js +13 -0
  127. package/dist/widget/coinPlaceholder-7cZW2058.js +13 -0
  128. package/dist/widget/compass-CFC3yhnW.js +13 -0
  129. package/dist/widget/components/ChainImage.d.ts +8 -0
  130. package/dist/widget/components/ChainImage.d.ts.map +1 -0
  131. package/dist/widget/components/ChainImage.js +6 -0
  132. package/dist/widget/components/ConnectWallet.d.ts +18 -0
  133. package/dist/widget/components/ConnectWallet.d.ts.map +1 -0
  134. package/dist/widget/components/ConnectWallet.js +66 -0
  135. package/dist/widget/components/DebugScreensDropdown.d.ts +9 -0
  136. package/dist/widget/components/DebugScreensDropdown.d.ts.map +1 -0
  137. package/dist/widget/components/DebugScreensDropdown.js +40 -0
  138. package/dist/widget/components/FeeOptions.d.ts +17 -0
  139. package/dist/widget/components/FeeOptions.d.ts.map +1 -0
  140. package/dist/widget/components/FeeOptions.js +65 -0
  141. package/dist/widget/components/Footer.d.ts +9 -0
  142. package/dist/widget/components/Footer.d.ts.map +1 -0
  143. package/dist/widget/components/Footer.js +13 -0
  144. package/dist/widget/components/GreenCheckAnimation.d.ts +2 -0
  145. package/dist/widget/components/GreenCheckAnimation.d.ts.map +1 -0
  146. package/dist/widget/components/GreenCheckAnimation.js +74 -0
  147. package/dist/widget/components/Modal.d.ts +11 -0
  148. package/dist/widget/components/Modal.d.ts.map +1 -0
  149. package/dist/widget/components/Modal.js +36 -0
  150. package/dist/widget/components/Receipt.d.ts +13 -0
  151. package/dist/widget/components/Receipt.d.ts.map +1 -0
  152. package/dist/widget/components/Receipt.js +36 -0
  153. package/dist/widget/components/SendForm.d.ts +44 -0
  154. package/dist/widget/components/SendForm.d.ts.map +1 -0
  155. package/dist/widget/components/SendForm.js +177 -0
  156. package/dist/widget/components/TokenImage.d.ts +10 -0
  157. package/dist/widget/components/TokenImage.d.ts.map +1 -0
  158. package/dist/widget/components/TokenImage.js +8 -0
  159. package/dist/widget/components/TokenList.d.ts +16 -0
  160. package/dist/widget/components/TokenList.d.ts.map +1 -0
  161. package/dist/widget/components/TokenList.js +39 -0
  162. package/dist/widget/components/TransferPending.d.ts +11 -0
  163. package/dist/widget/components/TransferPending.d.ts.map +1 -0
  164. package/dist/widget/components/TransferPending.js +77 -0
  165. package/dist/widget/components/TransferPendingVertical.d.ts +18 -0
  166. package/dist/widget/components/TransferPendingVertical.d.ts.map +1 -0
  167. package/dist/widget/components/TransferPendingVertical.js +183 -0
  168. package/dist/widget/components/WalletConfirmation.d.ts +18 -0
  169. package/dist/widget/components/WalletConfirmation.d.ts.map +1 -0
  170. package/dist/widget/components/WalletConfirmation.js +22 -0
  171. package/dist/widget/config.d.ts +4 -0
  172. package/dist/widget/config.d.ts.map +1 -0
  173. package/dist/widget/config.js +3 -0
  174. package/dist/widget/copy-e0xXvKN0.js +20 -0
  175. package/dist/widget/cursor-CqM3v0xJ.js +8 -0
  176. package/dist/widget/cursor-transparent-CUQpdsCG.js +17 -0
  177. package/dist/widget/desktop-DUDGIRpM.js +14 -0
  178. package/dist/widget/disconnect-DUFST9QQ.js +13 -0
  179. package/dist/widget/discord-C1cj365Z.js +22 -0
  180. package/dist/widget/email-BHhmb_lX.js +703 -0
  181. package/dist/widget/embedded-wallet-CuuC4eah.js +467 -0
  182. package/dist/widget/ethereum-CfmBVfeB.js +15 -0
  183. package/dist/widget/etherscan-BSiynDhW.js +11 -0
  184. package/dist/widget/exclamation-triangle-DEiFNpHw.js +9 -0
  185. package/dist/widget/extension-mRmfCDxo.js +13 -0
  186. package/dist/widget/external-link-B4xMIVnW.js +13 -0
  187. package/dist/widget/facebook-CBAZStBR.js +31 -0
  188. package/dist/widget/farcaster-LHDEDf5S.js +17 -0
  189. package/dist/widget/filters-CBijuvFv.js +13 -0
  190. package/dist/widget/github-C3ILD420.js +23 -0
  191. package/dist/widget/google-CSj73POX.js +23 -0
  192. package/dist/widget/help-circle-2hdG5IdB.js +17 -0
  193. package/dist/widget/hooks/useAmountUsd.d.ts +13 -0
  194. package/dist/widget/hooks/useAmountUsd.d.ts.map +1 -0
  195. package/dist/widget/hooks/useAmountUsd.js +35 -0
  196. package/dist/widget/hooks/useSendForm.d.ts +125 -0
  197. package/dist/widget/hooks/useSendForm.d.ts.map +1 -0
  198. package/dist/widget/hooks/useSendForm.js +450 -0
  199. package/dist/widget/hooks/useTokenList.d.ts +52 -0
  200. package/dist/widget/hooks/useTokenList.d.ts.map +1 -0
  201. package/dist/widget/hooks/useTokenList.js +252 -0
  202. package/dist/widget/id-ByYSrwsd.js +17 -0
  203. package/dist/widget/if-defined-DRXJEhv7.js +752 -0
  204. package/dist/widget/image-C90L4Rf6.js +9 -0
  205. package/dist/widget/index-B3SlQ9v3.js +46 -0
  206. package/dist/widget/index-B8LPuLXQ.js +78 -0
  207. package/dist/widget/index-BDbworWA.js +171 -0
  208. package/dist/widget/index-BTlDgFSK.js +98 -0
  209. package/dist/widget/index-BUCcjXbd.js +306 -0
  210. package/dist/widget/index-BZ34edi2.js +1055 -0
  211. package/dist/widget/index-BiCU29wK.js +147 -0
  212. package/dist/widget/index-BlmqIKsY.js +266 -0
  213. package/dist/widget/index-BlviH5nG.js +55 -0
  214. package/dist/widget/index-Bwd5X3fS.js +8306 -0
  215. package/dist/widget/index-C5gmknHK.js +78 -0
  216. package/dist/widget/index-CD6dBcRj.js +76 -0
  217. package/dist/widget/index-CDlhy529.js +63 -0
  218. package/dist/widget/index-CHXa5ke-.js +59 -0
  219. package/dist/widget/index-CK94R-H7.js +22498 -0
  220. package/dist/widget/index-CQzo3m3x.js +182 -0
  221. package/dist/widget/index-CRT8cAwG.js +325 -0
  222. package/dist/widget/index-CY7Lt2Yu.js +310 -0
  223. package/dist/widget/index-CiKfAu1E.js +79 -0
  224. package/dist/widget/index-CkCu6rMi.js +258 -0
  225. package/dist/widget/index-CngLTu_R.js +517 -0
  226. package/dist/widget/index-CsMV8-em.js +2577 -0
  227. package/dist/widget/index-Cu2Wva8v.js +200 -0
  228. package/dist/widget/index-DKBxLTEF.js +240 -0
  229. package/dist/widget/index-DQEVT3dx.js +511 -0
  230. package/dist/widget/index-DU2HcCis.js +200 -0
  231. package/dist/widget/index-DutZGWNW.js +321 -0
  232. package/dist/widget/index-O0glArmc.js +182 -0
  233. package/dist/widget/index-O8FmRjKe.js +63719 -0
  234. package/dist/widget/index-RtKXrB6I.js +576 -0
  235. package/dist/widget/index-TIYtS0gE.js +88 -0
  236. package/dist/widget/index-dQNJvWHs.js +66 -0
  237. package/dist/widget/index-wmEwdsq7.js +909 -0
  238. package/dist/widget/index.d.ts +3 -0
  239. package/dist/widget/index.d.ts.map +1 -0
  240. package/dist/widget/index.js +2 -0
  241. package/dist/widget/info-DMPChDjV.js +8 -0
  242. package/dist/widget/info-circle-DAvS_7nY.js +17 -0
  243. package/dist/widget/lightbulb-DnZ9mNEs.js +8 -0
  244. package/dist/widget/lit-html-BRjl1r6K.js +243 -0
  245. package/dist/widget/mail-DpaVSOP8.js +13 -0
  246. package/dist/widget/mobile-CRvdyu7I.js +14 -0
  247. package/dist/widget/more-C5VqW9PR.js +16 -0
  248. package/dist/widget/network-placeholder-CZ0vApma.js +19 -0
  249. package/dist/widget/nftPlaceholder-7jjIK2bT.js +13 -0
  250. package/dist/widget/off-4mHjJLLX.js +9 -0
  251. package/dist/widget/onramp-Bc0ozVsw.js +929 -0
  252. package/dist/widget/play-store-Uocul8nC.js +37 -0
  253. package/dist/widget/plus-DrYF7siO.js +18 -0
  254. package/dist/widget/prepareSend-BQJmzM5B.js +54987 -0
  255. package/dist/widget/qr-code-DcnGMUB3.js +11 -0
  256. package/dist/widget/receive-fvIVd7R_.js +184 -0
  257. package/dist/widget/recycle-horizontal-DrDwXC4D.js +14 -0
  258. package/dist/widget/ref-CXNmEjML.js +41 -0
  259. package/dist/widget/refresh-OK9lIPLS.js +13 -0
  260. package/dist/widget/reown-logo-C-Qn7mS3.js +17 -0
  261. package/dist/widget/search-DZqv1oKg.js +13 -0
  262. package/dist/widget/send-CJlmI-xe.js +1039 -0
  263. package/dist/widget/send-otoEC8uU.js +20 -0
  264. package/dist/widget/socials-BJciurWF.js +599 -0
  265. package/dist/widget/solana-Bv5Hs_0T.js +18 -0
  266. package/dist/widget/swapHorizontal-BzOPGV37.js +13 -0
  267. package/dist/widget/swapHorizontalBold-axyHnSmj.js +13 -0
  268. package/dist/widget/swapHorizontalMedium-C6YOPfPz.js +21 -0
  269. package/dist/widget/swapHorizontalRoundedBold-yVcLbWNT.js +13 -0
  270. package/dist/widget/swapVertical-BDjxt9pE.js +13 -0
  271. package/dist/widget/swaps-DEWNj4kd.js +1637 -0
  272. package/dist/widget/telegram-BQJD7dlP.js +21 -0
  273. package/dist/widget/three-dots-DW9jmSMG.js +10 -0
  274. package/dist/widget/transactions-uCseGQQt.js +38 -0
  275. package/dist/widget/twitch-XugxDfOE.js +23 -0
  276. package/dist/widget/twitterIcon-DQVObQUL.js +11 -0
  277. package/dist/widget/types.d.ts +51 -0
  278. package/dist/widget/types.d.ts.map +1 -0
  279. package/dist/widget/types.js +1 -0
  280. package/dist/widget/verify-DpMYHxLf.js +13 -0
  281. package/dist/widget/verify-filled-KpEL6ZJ_.js +13 -0
  282. package/dist/widget/w3m-modal-C8e-6Kba.js +1047 -0
  283. package/dist/widget/wallet-D8ssEB0o.js +13 -0
  284. package/dist/widget/wallet-placeholder-HtAy21Wc.js +19 -0
  285. package/dist/widget/walletconnect-Bp_4XfrY.js +37 -0
  286. package/dist/widget/warning-circle-FgYS7P7n.js +17 -0
  287. package/dist/widget/widget/index.js +7 -0
  288. package/dist/widget/widget.d.ts +47 -0
  289. package/dist/widget/widget.d.ts.map +1 -0
  290. package/dist/widget/widget.js +932 -0
  291. package/dist/widget/x-DlZBoP9k.js +17 -0
  292. package/dist/widget/x-mark-Ba9pt-_h.js +8 -0
  293. package/package.json +102 -8
  294. package/src/abi.ts +38 -0
  295. package/src/apiClient.ts +32 -0
  296. package/src/buffer.ts +10 -0
  297. package/src/cctp.ts +579 -0
  298. package/src/chainSwitch.ts +55 -0
  299. package/src/chains.ts +124 -0
  300. package/src/constants.ts +26 -0
  301. package/src/encoders.ts +20 -0
  302. package/src/error.ts +15 -0
  303. package/src/explorer.ts +37 -0
  304. package/src/gasless.ts +545 -0
  305. package/src/index.ts +48 -0
  306. package/src/indexerClient.ts +36 -0
  307. package/src/intents.ts +537 -0
  308. package/src/metaTxnMonitor.ts +163 -0
  309. package/src/metaTxns.ts +21 -0
  310. package/src/paymasterSend.ts +503 -0
  311. package/src/preconditions.ts +52 -0
  312. package/src/prepareSend.ts +1849 -0
  313. package/src/prices.ts +186 -0
  314. package/src/queryParams.ts +80 -0
  315. package/src/relaySdk.ts +481 -0
  316. package/src/relayer.ts +255 -0
  317. package/src/sendUserOp.ts +570 -0
  318. package/src/sequenceWallet.ts +579 -0
  319. package/src/theme.ts +2 -0
  320. package/src/toSimpleSmartAccount.ts +567 -0
  321. package/src/tokenBalances.ts +760 -0
  322. package/src/tokens.ts +471 -0
  323. package/src/trails.ts +1591 -0
  324. package/src/types.d.ts +11 -0
  325. package/src/umd.tsx +49 -0
  326. package/src/utils.ts +16 -0
  327. package/src/vite-env.d.ts +4 -0
  328. package/src/widget/assets/MetaMask-icon-fox-with-margins.svg +31 -0
  329. package/src/widget/assets/MetaMask-icon-fox.svg +26 -0
  330. package/src/widget/assets/MetaMask-logo-black.svg +3 -0
  331. package/src/widget/assets/MetaMask-logo-white.svg +16 -0
  332. package/src/widget/assets/Privy_Brandmark_Black.svg +9 -0
  333. package/src/widget/assets/Privy_Brandmark_White.svg +9 -0
  334. package/src/widget/assets/Trails-logo-black.svg +11 -0
  335. package/src/widget/assets/Trails-logo-white.svg +11 -0
  336. package/src/widget/components/ChainImage.tsx +28 -0
  337. package/src/widget/components/ConnectWallet.tsx +206 -0
  338. package/src/widget/components/DebugScreensDropdown.tsx +88 -0
  339. package/src/widget/components/FeeOptions.tsx +199 -0
  340. package/src/widget/components/Footer.tsx +51 -0
  341. package/src/widget/components/GreenCheckAnimation.tsx +119 -0
  342. package/src/widget/components/Modal.tsx +97 -0
  343. package/src/widget/components/Receipt.tsx +237 -0
  344. package/src/widget/components/SendForm.tsx +695 -0
  345. package/src/widget/components/TokenImage.tsx +37 -0
  346. package/src/widget/components/TokenList.tsx +287 -0
  347. package/src/widget/components/TransferPending.tsx +204 -0
  348. package/src/widget/components/TransferPendingVertical.tsx +412 -0
  349. package/src/widget/components/WalletConfirmation.tsx +172 -0
  350. package/src/widget/config.ts +5 -0
  351. package/src/widget/hooks/useAmountUsd.ts +59 -0
  352. package/src/widget/hooks/useSendForm.ts +715 -0
  353. package/src/widget/hooks/useTokenList.ts +397 -0
  354. package/src/widget/index.css +2 -0
  355. package/src/widget/index.tsx +8 -0
  356. package/src/widget/types/svg.d.ts +8 -0
  357. package/src/widget/types.ts +59 -0
  358. package/src/widget/widget.tsx +1438 -0
  359. package/index.js +0 -1
package/src/tokens.ts ADDED
@@ -0,0 +1,471 @@
1
+ import { QueryClient, useQuery } from "@tanstack/react-query"
2
+ import { zeroAddress, erc20Abi } from "viem"
3
+ import * as chains from "viem/chains"
4
+ import { getChainInfo } from "./chains.js"
5
+ import { getRelaySupportedTokens } from "./relaySdk.js"
6
+ import { useReadContracts } from "wagmi"
7
+ import { useMemo } from "react"
8
+
9
+ export type SupportedToken = {
10
+ id: string
11
+ symbol: string
12
+ name: string
13
+ contractAddress: string
14
+ decimals: number
15
+ chainId: number
16
+ chainName: string
17
+ imageUrl: string
18
+ }
19
+
20
+ // LocalStorage cache utilities for token images
21
+ const TOKEN_IMAGE_CACHE_KEY = "trails-sdk:token-image-cache"
22
+ const TOKEN_IMAGE_CACHE_EXPIRY = 7 * 24 * 60 * 60 * 1000 // 7 days
23
+
24
+ interface TokenImageCacheEntry {
25
+ imageUrl: string
26
+ timestamp: number
27
+ found: boolean
28
+ }
29
+
30
+ function getTokenImageCache(): Record<string, TokenImageCacheEntry> {
31
+ if (typeof window === "undefined") return {}
32
+
33
+ try {
34
+ const cached = localStorage.getItem(TOKEN_IMAGE_CACHE_KEY)
35
+ if (!cached) return {}
36
+
37
+ const parsed = JSON.parse(cached) as Record<string, TokenImageCacheEntry>
38
+ const now = Date.now()
39
+
40
+ // Clean up expired entries
41
+ const validEntries = Object.entries(parsed).filter(([_, entry]) => {
42
+ return now - entry.timestamp < TOKEN_IMAGE_CACHE_EXPIRY
43
+ })
44
+
45
+ return Object.fromEntries(validEntries) as Record<
46
+ string,
47
+ TokenImageCacheEntry
48
+ >
49
+ } catch {
50
+ return {}
51
+ }
52
+ }
53
+
54
+ function setTokenImageCache(
55
+ key: string,
56
+ imageUrl: string,
57
+ found: boolean,
58
+ ): void {
59
+ if (typeof window === "undefined") return
60
+
61
+ try {
62
+ const cache = getTokenImageCache()
63
+ cache[key] = {
64
+ imageUrl,
65
+ timestamp: Date.now(),
66
+ found,
67
+ }
68
+ localStorage.setItem(TOKEN_IMAGE_CACHE_KEY, JSON.stringify(cache))
69
+ } catch (error) {
70
+ console.warn("[trails-sdk] Failed to cache token image:", error)
71
+ }
72
+ }
73
+
74
+ // Dedicated QueryClient for token image fetching
75
+ const tokenImageQueryClient = new QueryClient({
76
+ defaultOptions: {
77
+ queries: {
78
+ staleTime: 24 * 60 * 60 * 1000, // 24 hours
79
+ gcTime: 7 * 24 * 60 * 60 * 1000, // 7 days
80
+ retry: 2,
81
+ retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
82
+ refetchOnWindowFocus: false,
83
+ refetchOnReconnect: false,
84
+ },
85
+ },
86
+ })
87
+
88
+ // Priority tokens that should appear first in the list
89
+ const PRIORITY_TOKENS = [
90
+ "ETH",
91
+ "WETH",
92
+ "AVAX",
93
+ "WAVAX",
94
+ "xDAI",
95
+ "POL",
96
+ "USDC",
97
+ "USDT",
98
+ "DAI",
99
+ "MATIC",
100
+ "ARB",
101
+ "OP",
102
+ "BAT",
103
+ "WBTC",
104
+ "cbBTC",
105
+ "XAI",
106
+ ]
107
+
108
+ // Sort function for tokens with the specified priority order
109
+ function sortTokens(tokens: SupportedToken[]): SupportedToken[] {
110
+ return tokens.sort((a, b) => {
111
+ // 1. Priority tokens first (ETH, USDC, USDT, DAI)
112
+ const aPriority = PRIORITY_TOKENS.indexOf(a.symbol)
113
+ const bPriority = PRIORITY_TOKENS.indexOf(b.symbol)
114
+
115
+ // If both are priority tokens, sort by priority order
116
+ if (aPriority !== -1 && bPriority !== -1) {
117
+ return aPriority - bPriority
118
+ }
119
+
120
+ // If only one is a priority token, prioritize it
121
+ if (aPriority !== -1) return -1
122
+ if (bPriority !== -1) return 1
123
+
124
+ // 2. Tokens with imageUrl before those without
125
+ const aHasImage = !!a.imageUrl
126
+ const bHasImage = !!b.imageUrl
127
+
128
+ if (aHasImage && !bHasImage) return -1
129
+ if (!aHasImage && bHasImage) return 1
130
+
131
+ // 3. Alphabetical by symbol
132
+ const symbolComparison = a.symbol.localeCompare(b.symbol)
133
+ if (symbolComparison !== 0) {
134
+ return symbolComparison
135
+ }
136
+
137
+ // 4. If symbols are the same, sort by chainName
138
+ return a.chainName.localeCompare(b.chainName)
139
+ })
140
+ }
141
+
142
+ export async function getTokenImageUrlOrFallback(
143
+ chainId: number,
144
+ contractAddress: string,
145
+ ): Promise<string> {
146
+ const cacheKey = `${chainId}:${contractAddress}`
147
+ const imageUrl = getTokenImageUrl(chainId, contractAddress)
148
+
149
+ // Check localStorage cache first
150
+ const cache = getTokenImageCache()
151
+ const cachedEntry = cache[cacheKey]
152
+
153
+ if (cachedEntry) {
154
+ if (cachedEntry.found) {
155
+ return cachedEntry.imageUrl
156
+ } else {
157
+ return "" // Return empty string if we previously found no image
158
+ }
159
+ }
160
+
161
+ // Use QueryClient to fetch and cache the result
162
+ try {
163
+ const result = await tokenImageQueryClient.fetchQuery({
164
+ queryKey: ["tokenImage", chainId, contractAddress],
165
+ queryFn: async () => {
166
+ const response = await fetch(imageUrl, {
167
+ method: "HEAD", // Only fetch headers to check if image exists
168
+ cache: "no-cache", // Don't cache the fetch request itself
169
+ })
170
+ const found = response.ok
171
+ const resultUrl = found ? imageUrl : ""
172
+
173
+ // Cache the result in localStorage
174
+ setTokenImageCache(cacheKey, resultUrl, found)
175
+
176
+ return resultUrl
177
+ },
178
+ staleTime: 24 * 60 * 60 * 1000, // 24 hours
179
+ gcTime: 7 * 24 * 60 * 60 * 1000, // 7 days
180
+ })
181
+
182
+ return result
183
+ } catch (error) {
184
+ console.error("[trails-sdk] Error fetching token image:", error)
185
+ // Cache the failure to avoid repeated requests
186
+ setTokenImageCache(cacheKey, "", false)
187
+ return ""
188
+ }
189
+ }
190
+
191
+ export async function getSupportedTokens(): Promise<SupportedToken[]> {
192
+ const tokens = await getRelaySupportedTokens()
193
+ for (const token of tokens) {
194
+ if (!token.imageUrl) {
195
+ token.imageUrl = getTokenImageUrl(token.chainId, token.contractAddress)
196
+ }
197
+
198
+ getTokenImageUrlOrFallback(token.chainId, token.contractAddress)
199
+ .then((imageUrl) => {
200
+ token.imageUrl = imageUrl
201
+ })
202
+ .catch((error) => {
203
+ console.error("[trails-sdk] Error getting token image url:", error)
204
+ })
205
+ }
206
+
207
+ const uniqueTokens = tokens.filter(
208
+ (token, index, self) =>
209
+ index ===
210
+ self.findIndex(
211
+ (t) =>
212
+ t.chainId === token.chainId &&
213
+ t.contractAddress === token.contractAddress,
214
+ ),
215
+ )
216
+
217
+ // Sort tokens according to priority order
218
+ return sortTokens(uniqueTokens as SupportedToken[])
219
+ }
220
+
221
+ export function useSupportedTokens({ chainId }: { chainId?: number } = {}): {
222
+ supportedTokens: SupportedToken[]
223
+ isLoadingTokens: boolean
224
+ } {
225
+ const { data: supportedTokens = [], isLoading: isLoadingTokens } = useQuery({
226
+ queryKey: ["supportedTokens"],
227
+ queryFn: getSupportedTokens,
228
+ staleTime: 60 * 60 * 1000, // 1 hour - tokens rarely change
229
+ gcTime: 24 * 60 * 60 * 1000, // 24 hours - keep in cache for a full day
230
+ refetchOnWindowFocus: false, // Don't refetch when window regains focus
231
+ refetchOnReconnect: false, // Don't refetch on network reconnect
232
+ })
233
+
234
+ const filteredTokens = useMemo(() => {
235
+ if (!chainId) {
236
+ return supportedTokens
237
+ }
238
+ return supportedTokens.filter((token) => token.chainId === chainId)
239
+ }, [supportedTokens, chainId])
240
+
241
+ return {
242
+ supportedTokens: filteredTokens || [],
243
+ isLoadingTokens,
244
+ }
245
+ }
246
+
247
+ export async function getSourceTokenList(): Promise<string[]> {
248
+ const tokens = await getSupportedTokens()
249
+ return tokens.map((token) => token.symbol)
250
+ }
251
+
252
+ export function useSourceTokenList(): string[] {
253
+ const { data: list = [] } = useQuery({
254
+ queryKey: ["sourceTokenList"],
255
+ queryFn: getSourceTokenList,
256
+ staleTime: 1000 * 60 * 60, // 1 hour - token list rarely changes
257
+ gcTime: 1000 * 60 * 60 * 24, // 24 hours cache time
258
+ refetchOnWindowFocus: false,
259
+ refetchOnReconnect: false,
260
+ })
261
+ return list
262
+ }
263
+
264
+ const tokenNames: Record<string, string> = {
265
+ ETH: "Ethereum",
266
+ WETH: "Wrapped ETH",
267
+ USDC: "USDC",
268
+ USDT: "Tether",
269
+ DAI: "Dai Stablecoin",
270
+ OP: "Optimism",
271
+ ARB: "Arbitrum",
272
+ POL: "POL",
273
+ MATIC: "Matic Token",
274
+ BAT: "Basic Attention Token",
275
+ }
276
+
277
+ export const tokensToPrefix: Record<string, string> = {
278
+ USDC: "USDC",
279
+ ETH: "ETH",
280
+ POL: "POL",
281
+ }
282
+
283
+ export const tokenNamePrefixes: Record<string, string> = {
284
+ [chains.optimism.id]: "Optimistic",
285
+ [chains.arbitrum.id]: "Arbitrum",
286
+ [chains.polygon.id]: "Polygon",
287
+ }
288
+
289
+ export function getFormatttedTokenName(
290
+ currentName: string,
291
+ tokenSymbol: string,
292
+ chainId?: number,
293
+ ): string {
294
+ let name = tokenNames[tokenSymbol] || currentName || tokenSymbol
295
+ if (chainId) {
296
+ try {
297
+ const chainInfo = getChainInfo(chainId)
298
+ if (chainInfo) {
299
+ if (tokensToPrefix[tokenSymbol]) {
300
+ if (chainId !== chains.mainnet.id) {
301
+ name = `${chainInfo?.name} ${tokenSymbol}`
302
+ }
303
+ const prefix = tokenNamePrefixes[chainId]
304
+ if (prefix) {
305
+ name = `${prefix} ${tokenSymbol}`
306
+ }
307
+ }
308
+ }
309
+ } catch (e) {
310
+ console.error("[trails-sdk] Error getting chain info:", e)
311
+ }
312
+ }
313
+ return name
314
+ }
315
+
316
+ export async function getTokenAddress(chainId: number, tokenSymbol: string) {
317
+ const chainInfo = getChainInfo(chainId)
318
+ if (tokenSymbol === chainInfo?.nativeCurrency.symbol) {
319
+ return zeroAddress
320
+ }
321
+
322
+ const tokens = await getSupportedTokens()
323
+ const token = tokens.find(
324
+ (t) => t.symbol === tokenSymbol && t.chainId === chainId,
325
+ )
326
+ if (token?.contractAddress) {
327
+ return token.contractAddress
328
+ }
329
+
330
+ throw new Error(
331
+ `Unsupported token symbol: ${tokenSymbol} for chainId: ${chainId}`,
332
+ )
333
+ }
334
+
335
+ type UseTokenAddressProps = {
336
+ chainId?: number | null
337
+ tokenSymbol?: string | null
338
+ }
339
+
340
+ export function useTokenAddress({
341
+ chainId,
342
+ tokenSymbol,
343
+ }: UseTokenAddressProps) {
344
+ const { data: tokenAddress } = useQuery({
345
+ queryKey: ["tokenAddress", chainId, tokenSymbol],
346
+ queryFn: () =>
347
+ chainId && tokenSymbol ? getTokenAddress(chainId, tokenSymbol) : null,
348
+ enabled: !!chainId && !!tokenSymbol,
349
+ staleTime: 60 * 60 * 1000, // 1 hour - token addresses rarely change
350
+ gcTime: 24 * 60 * 60 * 1000, // 24 hours - keep in cache for a full day
351
+ refetchOnWindowFocus: false, // Don't refetch when window regains focus
352
+ refetchOnReconnect: false, // Don't refetch on network reconnect
353
+ })
354
+
355
+ return tokenAddress || null
356
+ }
357
+
358
+ export function getTokenImageUrl(chainId: number, contractAddress: string) {
359
+ const imageUrl = `https://assets.sequence.info/images/tokens/large/${chainId}/${contractAddress}.webp`
360
+ return imageUrl
361
+ }
362
+
363
+ // React hook for token image fetching with caching
364
+ export function useTokenImageUrl(
365
+ chainId: number | undefined,
366
+ contractAddress: string | undefined,
367
+ ) {
368
+ const {
369
+ data: imageUrl = "",
370
+ isLoading,
371
+ error,
372
+ } = useQuery({
373
+ queryKey: ["tokenImage", chainId, contractAddress],
374
+ queryFn: () => getTokenImageUrlOrFallback(chainId!, contractAddress!),
375
+ enabled: !!chainId && !!contractAddress,
376
+ staleTime: 24 * 60 * 60 * 1000, // 24 hours
377
+ gcTime: 7 * 24 * 60 * 60 * 1000, // 7 days
378
+ retry: 2,
379
+ retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
380
+ refetchOnWindowFocus: false,
381
+ refetchOnReconnect: false,
382
+ })
383
+
384
+ return {
385
+ imageUrl,
386
+ isLoading,
387
+ error,
388
+ hasImage: !!imageUrl,
389
+ }
390
+ }
391
+
392
+ export function useTokenList() {
393
+ const { supportedTokens: tokens, isLoadingTokens } = useSupportedTokens()
394
+
395
+ return {
396
+ tokens,
397
+ isLoadingTokens: isLoadingTokens,
398
+ }
399
+ }
400
+
401
+ export function useTokenInfo({
402
+ address,
403
+ chainId,
404
+ }: {
405
+ address: string
406
+ chainId?: number
407
+ }): {
408
+ tokenInfo: Partial<SupportedToken> | null
409
+ isLoading: boolean
410
+ error: Error | null
411
+ } {
412
+ const isAddress = address?.startsWith("0x") ?? false
413
+
414
+ if (!isAddress || !chainId) {
415
+ return {
416
+ tokenInfo: null,
417
+ isLoading: false,
418
+ error: null,
419
+ }
420
+ }
421
+
422
+ const contract = {
423
+ address: address as `0x${string}`,
424
+ abi: erc20Abi,
425
+ chainId,
426
+ } as const
427
+
428
+ const result = useReadContracts({
429
+ contracts: [
430
+ { ...contract, functionName: "name" },
431
+ { ...contract, functionName: "symbol" },
432
+ { ...contract, functionName: "decimals" },
433
+ ],
434
+ })
435
+
436
+ const error =
437
+ result?.error ?? result?.data?.find((r) => r.error)?.error ?? null
438
+
439
+ const [name, symbol, decimals] = result.data ?? []
440
+ const chainInfo = getChainInfo(chainId)
441
+
442
+ const tokenInfo = useMemo(() => {
443
+ if (!symbol?.result || !name?.result || decimals?.result == null) {
444
+ return null
445
+ }
446
+
447
+ return {
448
+ id: symbol.result,
449
+ name: name.result,
450
+ symbol: symbol.result,
451
+ decimals: decimals.result,
452
+ chainId,
453
+ contractAddress: address,
454
+ chainName: chainInfo?.name ?? "",
455
+ imageUrl: "",
456
+ }
457
+ }, [
458
+ address,
459
+ chainId,
460
+ chainInfo?.name,
461
+ name?.result,
462
+ symbol?.result,
463
+ decimals?.result,
464
+ ])
465
+
466
+ return {
467
+ tokenInfo,
468
+ isLoading: result.isLoading,
469
+ error: error,
470
+ }
471
+ }