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/dist/trails.js ADDED
@@ -0,0 +1,1031 @@
1
+ import { useMutation, useQuery } from "@tanstack/react-query";
2
+ import { Address } from "ox";
3
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
+ import { createPublicClient, createWalletClient, custom, http, isAddress, isAddressEqual, zeroAddress, } from "viem";
5
+ import { useEstimateGas, useSendTransaction, useSwitchChain, useWaitForTransactionReceipt, } from "wagmi";
6
+ import { useAPIClient } from "./apiClient.js";
7
+ import { attemptSwitchChain } from "./chainSwitch.js";
8
+ import { getChainInfo } from "./chains.js";
9
+ import { TRAILS_CCTP_SAPIENT_SIGNER_ADDRESS, TRAILS_LIFI_SAPIENT_SIGNER_ADDRESS, TRAILS_RELAY_SAPIENT_SIGNER_ADDRESS, DEFAULT_USE_V3_RELAYERS, } from "./constants.js";
10
+ import { getERC20TransferData } from "./encoders.js";
11
+ import { calculateIntentAddress, calculateOriginAndDestinationIntentAddresses, getIntentCallsPayloads as getIntentCallsPayloadsFromIntents, } from "./intents.js";
12
+ import { useMetaTxnsMonitor } from "./metaTxnMonitor.js";
13
+ import { findPreconditionAddresses } from "./preconditions.js";
14
+ import { getBackupRelayer, useRelayers } from "./relayer.js";
15
+ const RETRY_WINDOW_MS = 10_000;
16
+ export function useTrails(config) {
17
+ const { account, disableAutoExecute = false, env, useV3Relayers = DEFAULT_USE_V3_RELAYERS, sequenceProjectAccessKey, } = config;
18
+ const apiClient = useAPIClient({ projectAccessKey: sequenceProjectAccessKey });
19
+ const [isAutoExecute, setIsAutoExecute] = useState(!disableAutoExecute);
20
+ const [hasAutoExecuted, setHasAutoExecuted] = useState(false);
21
+ // Track timestamps of when each meta-transaction was last sent
22
+ const [sentMetaTxns, setSentMetaTxns] = useState({});
23
+ // State declarations
24
+ const [metaTxns, setMetaTxns] = useState(null);
25
+ const [intentCallsPayloads, setIntentCallsPayloads] = useState(null);
26
+ const [intentPreconditions, setIntentPreconditions] = useState(null);
27
+ const [trailsFee, setTrailsFee] = useState(null);
28
+ const [txnHash, setTxnHash] = useState();
29
+ const [committedOriginIntentAddress, setCommittedOriginIntentAddress] = useState(null);
30
+ const [committedDestinationIntentAddress, setCommittedDestinationIntentAddress,] = useState(null);
31
+ const [originIntentAddress, setOriginIntentAddress] = useState(null);
32
+ const [destinationIntentAddress, setDestinationIntentAddress] = useState(null);
33
+ // const [preconditionStatuses, setPreconditionStatuses] = useState<boolean[]>([])
34
+ const [originCallParams, setOriginCallParams] = useState(null);
35
+ const [operationHashes, setOperationHashes] = useState({});
36
+ const [isTransactionInProgress, setIsTransactionInProgress] = useState(false);
37
+ const [isChainSwitchRequired, setIsChainSwitchRequired] = useState(false);
38
+ const { switchChain, isPending: isSwitchingChain, error: switchChainError, } = useSwitchChain();
39
+ const sendOriginTxn = useSendTransaction();
40
+ const [isEstimatingGas, setIsEstimatingGas] = useState(false);
41
+ const [originCallStatus, setOriginCallStatus] = useState(null);
42
+ const [originBlockTimestamp, setOriginBlockTimestamp] = useState(null);
43
+ const [metaTxnBlockTimestamps, setMetaTxnBlockTimestamps] = useState({});
44
+ const [verificationStatus, setVerificationStatus] = useState(null);
45
+ const { getRelayer } = useRelayers({
46
+ env,
47
+ useV3Relayers,
48
+ });
49
+ // Add gas estimation hook with proper types
50
+ const { data: estimatedGas, isError: isEstimateError, error: estimateError, } = useEstimateGas(originCallParams?.to && originCallParams?.chainId && !originCallParams.error
51
+ ? {
52
+ to: originCallParams.to || undefined,
53
+ data: originCallParams.data || undefined,
54
+ value: originCallParams.value || undefined,
55
+ chainId: originCallParams.chainId || undefined,
56
+ }
57
+ : undefined);
58
+ const commitIntentConfigMutation = useMutation({
59
+ mutationFn: async (args) => {
60
+ console.log("[useTrails] commitIntentConfigMutation started with args:", args);
61
+ if (!apiClient) {
62
+ console.error("[useTrails] API client not available");
63
+ throw new Error("API client not available");
64
+ }
65
+ if (!args.quoteProvider) {
66
+ console.error("[useTrails] quoteProvider is required");
67
+ throw new Error("quoteProvider is required");
68
+ }
69
+ try {
70
+ console.log("[useTrails] Calculating intent address...");
71
+ console.log("[useTrails] Main signer:", args.mainSignerAddress);
72
+ console.log("[useTrails] Calls:", args.calls);
73
+ const originChainId = createIntentMutation.variables?.originChainId;
74
+ const destinationChainId = createIntentMutation.variables?.destinationChainId;
75
+ if (!originChainId || !destinationChainId) {
76
+ console.error("[useTrails] Could not determine origin/destination chain IDs for verification.");
77
+ throw new Error("Could not determine origin/destination chain IDs for verification.");
78
+ }
79
+ const { originIntentAddress, destinationIntentAddress } = calculateOriginAndDestinationIntentAddresses(args.mainSignerAddress, args.calls);
80
+ const { originAddress: originPreconditionAddress, destinationAddress: destinationPreconditionAddress, } = findPreconditionAddresses(args.preconditions, originChainId, destinationChainId);
81
+ console.log("[useTrails] Verification addresses:", {
82
+ calculatedOrigin: originIntentAddress.toString(),
83
+ calculatedDestination: destinationIntentAddress.toString(),
84
+ preconditionOrigin: originPreconditionAddress,
85
+ preconditionDestination: destinationPreconditionAddress,
86
+ });
87
+ const isOriginVerified = !!originPreconditionAddress &&
88
+ isAddressEqual(Address.from(originPreconditionAddress), originIntentAddress);
89
+ // For single chain, destination address may not be in preconditions,
90
+ // but the destination intent address should be the zero address.
91
+ const isDestinationVerified = (destinationPreconditionAddress &&
92
+ isAddressEqual(Address.from(destinationPreconditionAddress), destinationIntentAddress)) ||
93
+ (!destinationPreconditionAddress &&
94
+ originChainId === destinationChainId &&
95
+ isAddressEqual(destinationIntentAddress, zeroAddress));
96
+ const isVerified = isOriginVerified && isDestinationVerified;
97
+ setVerificationStatus({
98
+ success: isVerified,
99
+ receivedOriginAddress: originPreconditionAddress,
100
+ receivedDestinationAddress: destinationPreconditionAddress,
101
+ calculatedOriginAddress: originIntentAddress.toString(),
102
+ calculatedDestinationAddress: destinationIntentAddress.toString(),
103
+ });
104
+ if (!isVerified) {
105
+ console.error("[useTrails] Address verification failed.", {
106
+ isOriginVerified,
107
+ isDestinationVerified,
108
+ });
109
+ throw new Error(`Address verification failed. Origin verified: ${isOriginVerified}, Destination verified: ${isDestinationVerified}`);
110
+ }
111
+ // Commit the intent config
112
+ console.log("[useTrails] Committing intent config to API...");
113
+ const response = await apiClient.commitIntentConfig({
114
+ originIntentAddress: originIntentAddress.toString(),
115
+ destinationIntentAddress: destinationIntentAddress.toString(),
116
+ mainSigner: args.mainSignerAddress,
117
+ calls: args.calls,
118
+ preconditions: args.preconditions,
119
+ addressOverrides: {
120
+ trailsLiFiSapientSignerAddress: TRAILS_LIFI_SAPIENT_SIGNER_ADDRESS,
121
+ trailsRelaySapientSignerAddress: TRAILS_RELAY_SAPIENT_SIGNER_ADDRESS,
122
+ trailsCCTPV2SapientSignerAddress: TRAILS_CCTP_SAPIENT_SIGNER_ADDRESS,
123
+ ...args.addressOverrides,
124
+ },
125
+ });
126
+ console.log("[useTrails] API Commit Response:", response);
127
+ return {
128
+ originIntentAddress: originIntentAddress.toString(),
129
+ destinationIntentAddress: destinationIntentAddress.toString(),
130
+ response,
131
+ };
132
+ }
133
+ catch (error) {
134
+ console.error("[useTrails] Error during commit intent mutation:", error);
135
+ throw error;
136
+ }
137
+ },
138
+ onSuccess: (data) => {
139
+ console.log("[useTrails] Intent config committed successfully. Data:", data);
140
+ console.log("[useTrails] Setting committedOriginIntentAddress:", data.originIntentAddress);
141
+ setCommittedOriginIntentAddress(data.originIntentAddress);
142
+ setCommittedDestinationIntentAddress(data.destinationIntentAddress);
143
+ },
144
+ onError: (error) => {
145
+ console.error("[useTrails] Failed to commit intent config:", error);
146
+ setCommittedOriginIntentAddress(null);
147
+ setCommittedDestinationIntentAddress(null);
148
+ },
149
+ });
150
+ // New Query to fetch committed intent config
151
+ const { data: committedIntentConfig, isLoading: isLoadingCommittedConfig, error: committedConfigError, } = useQuery({
152
+ queryKey: ["getIntentConfig", committedOriginIntentAddress],
153
+ queryFn: async () => {
154
+ if (!apiClient || !committedOriginIntentAddress) {
155
+ throw new Error("API client or committed intent address not available");
156
+ }
157
+ console.log("Fetching intent config for address:", committedOriginIntentAddress);
158
+ return await apiClient.getIntentConfig({
159
+ intentAddress: committedOriginIntentAddress,
160
+ });
161
+ },
162
+ enabled: !!committedOriginIntentAddress &&
163
+ !!apiClient &&
164
+ commitIntentConfigMutation.isSuccess,
165
+ staleTime: 1000 * 60 * 5, // 5 minutes
166
+ retry: 1,
167
+ });
168
+ async function getIntentCallsPayloads(args) {
169
+ return getIntentCallsPayloadsFromIntents(apiClient, {
170
+ ...args,
171
+ addressOverrides: {
172
+ trailsLiFiSapientSignerAddress: TRAILS_LIFI_SAPIENT_SIGNER_ADDRESS,
173
+ trailsRelaySapientSignerAddress: TRAILS_RELAY_SAPIENT_SIGNER_ADDRESS,
174
+ trailsCCTPV2SapientSignerAddress: TRAILS_CCTP_SAPIENT_SIGNER_ADDRESS,
175
+ ...args.addressOverrides,
176
+ },
177
+ });
178
+ }
179
+ // TODO: Add type for args
180
+ const createIntentMutation = useMutation({
181
+ mutationFn: async (args) => {
182
+ if (args.originChainId === args.destinationChainId &&
183
+ isAddressEqual(Address.from(args.originTokenAddress), Address.from(args.destinationTokenAddress))) {
184
+ throw new Error("The same token cannot be used as both the source and destination token.");
185
+ }
186
+ if (!account.address) {
187
+ throw new Error("Missing selected token or account address");
188
+ }
189
+ // Reset commit state when generating a new intent
190
+ setCommittedOriginIntentAddress(null);
191
+ setCommittedDestinationIntentAddress(null);
192
+ setVerificationStatus(null);
193
+ setTrailsFee(null);
194
+ setMetaTxns(null);
195
+ setIntentCallsPayloads(null);
196
+ setIntentPreconditions(null);
197
+ setOriginIntentAddress(null);
198
+ setDestinationIntentAddress(null);
199
+ const data = await getIntentCallsPayloads(args);
200
+ setMetaTxns(data.metaTxns);
201
+ setIntentCallsPayloads(data.calls);
202
+ setIntentPreconditions(data.preconditions);
203
+ setTrailsFee(data.trailsFee);
204
+ setOriginIntentAddress(data.originIntentAddress);
205
+ setDestinationIntentAddress(data.destinationIntentAddress);
206
+ setCommittedOriginIntentAddress(null);
207
+ setCommittedDestinationIntentAddress(null);
208
+ setVerificationStatus(null);
209
+ return data;
210
+ },
211
+ onSuccess: (data) => {
212
+ console.log("Intent Config Success:", data);
213
+ setTrailsFee(data.trailsFee || null);
214
+ setOriginIntentAddress(data.originIntentAddress);
215
+ setDestinationIntentAddress(data.destinationIntentAddress);
216
+ if (data?.calls &&
217
+ data.calls.length > 0 &&
218
+ data.preconditions &&
219
+ data.preconditions.length > 0 &&
220
+ data.metaTxns &&
221
+ data.metaTxns.length > 0) {
222
+ setIntentCallsPayloads(data.calls);
223
+ setIntentPreconditions(data.preconditions);
224
+ setMetaTxns(data.metaTxns);
225
+ }
226
+ else {
227
+ console.warn("API returned success but no operations found.");
228
+ setIntentCallsPayloads(null);
229
+ setIntentPreconditions(null);
230
+ setMetaTxns(null);
231
+ }
232
+ },
233
+ onError: (error) => {
234
+ console.error("Intent Config Error:", error);
235
+ setIntentCallsPayloads(null);
236
+ setIntentPreconditions(null);
237
+ setMetaTxns(null);
238
+ setTrailsFee(null);
239
+ setOriginIntentAddress(null);
240
+ setDestinationIntentAddress(null);
241
+ },
242
+ });
243
+ function callIntentCallsPayload(args) {
244
+ createIntentMutation.mutate(args);
245
+ }
246
+ const clearIntent = useCallback(() => {
247
+ console.log("[Trails] Clearing intent state");
248
+ setIntentCallsPayloads(null);
249
+ setIntentPreconditions(null);
250
+ setMetaTxns(null);
251
+ setTrailsFee(null);
252
+ setCommittedOriginIntentAddress(null);
253
+ setCommittedDestinationIntentAddress(null);
254
+ setVerificationStatus(null);
255
+ setOriginIntentAddress(null);
256
+ setDestinationIntentAddress(null);
257
+ setOperationHashes({});
258
+ setHasAutoExecuted(false);
259
+ setMetaTxnBlockTimestamps({});
260
+ }, []); // Empty deps array since these setters are stable
261
+ const updateOriginCallStatus = useCallback((hash, status, gasUsed, effectiveGasPrice, revertReason) => {
262
+ setOriginCallStatus({
263
+ txnHash: hash,
264
+ status: status === "success"
265
+ ? "Success"
266
+ : status === "reverted"
267
+ ? "Failed"
268
+ : status === "sending"
269
+ ? "Sending..."
270
+ : "Pending",
271
+ revertReason: status === "reverted"
272
+ ? revertReason || "Transaction reverted"
273
+ : undefined,
274
+ gasUsed: gasUsed ? Number(gasUsed) : undefined,
275
+ effectiveGasPrice: effectiveGasPrice?.toString(),
276
+ });
277
+ }, []);
278
+ const sendOriginTransaction = async () => {
279
+ console.log("Sending origin transaction...");
280
+ console.log(isTransactionInProgress, originCallParams, originCallParams?.error, originCallParams?.to, originCallParams?.data, originCallParams?.value, originCallParams?.chainId);
281
+ if (isTransactionInProgress || // Prevent duplicate transactions
282
+ !originCallParams ||
283
+ originCallParams.error ||
284
+ !originCallParams.to ||
285
+ originCallParams.data === null ||
286
+ originCallParams.value === null ||
287
+ originCallParams.chainId === null) {
288
+ console.error("Origin call parameters not available or invalid:", originCallParams);
289
+ updateOriginCallStatus(undefined, "reverted", undefined, undefined, "Origin call parameters not ready");
290
+ return;
291
+ }
292
+ // Check if we need to switch chains
293
+ if (account.chainId !== originCallParams.chainId) {
294
+ setIsChainSwitchRequired(true);
295
+ updateOriginCallStatus(undefined, "pending", undefined, undefined, `Switching to chain ${originCallParams.chainId}...`);
296
+ const walletClient = createWalletClient({
297
+ chain: getChainInfo(originCallParams.chainId),
298
+ transport: custom((await account.connector.getProvider())), // TODO: Add proper type
299
+ });
300
+ try {
301
+ await attemptSwitchChain({
302
+ walletClient,
303
+ desiredChainId: originCallParams.chainId,
304
+ });
305
+ setIsChainSwitchRequired(false);
306
+ }
307
+ catch (error) {
308
+ console.error("Chain switch failed:", error);
309
+ if (error instanceof Error && error.message.includes("User rejected")) {
310
+ setIsAutoExecute(false);
311
+ }
312
+ updateOriginCallStatus(undefined, "reverted", undefined, undefined, error instanceof Error
313
+ ? error.message
314
+ : "Unknown error switching chain");
315
+ setIsChainSwitchRequired(false);
316
+ return; // Stop execution on switch failure
317
+ }
318
+ }
319
+ // Ensure only one transaction is sent at a time
320
+ if (!isTransactionInProgress) {
321
+ setIsTransactionInProgress(true); // Mark transaction as in progress
322
+ setTxnHash(undefined);
323
+ updateOriginCallStatus(undefined, "sending");
324
+ if (!estimatedGas && !isEstimateError) {
325
+ setIsEstimatingGas(true);
326
+ return; // Wait for gas estimation
327
+ }
328
+ if (isEstimateError) {
329
+ console.error("Gas estimation failed:", estimateError);
330
+ updateOriginCallStatus(undefined, "reverted", undefined, undefined, `Gas estimation failed: ${estimateError?.message}`);
331
+ setIsTransactionInProgress(false);
332
+ return;
333
+ }
334
+ // Add 20% buffer to estimated gas
335
+ const gasLimit = estimatedGas
336
+ ? BigInt(Math.floor(Number(estimatedGas) * 1.2))
337
+ : undefined;
338
+ sendOriginTxn.sendTransaction({
339
+ to: originCallParams.to,
340
+ data: originCallParams.data,
341
+ value: originCallParams.value,
342
+ chainId: originCallParams.chainId,
343
+ gas: gasLimit,
344
+ }, {
345
+ onSuccess: (hash) => {
346
+ console.log("Transaction sent, hash:", hash);
347
+ setTxnHash(hash);
348
+ setIsTransactionInProgress(false); // Reset transaction state
349
+ },
350
+ onError: (error) => {
351
+ console.error("Transaction failed:", error);
352
+ if (error instanceof Error &&
353
+ (error.message.includes("User rejected") ||
354
+ error.message.includes("user rejected"))) {
355
+ setIsAutoExecute(false);
356
+ }
357
+ updateOriginCallStatus(undefined, "reverted", undefined, undefined, error instanceof Error ? error.message : "Unknown error");
358
+ setIsTransactionInProgress(false);
359
+ },
360
+ });
361
+ }
362
+ else {
363
+ console.warn("Transaction already in progress. Skipping duplicate request.");
364
+ }
365
+ };
366
+ // Remove the chain change effect that might be resetting state
367
+ useEffect(() => {
368
+ if (switchChainError) {
369
+ console.error("Chain switch error:", switchChainError);
370
+ updateOriginCallStatus(undefined, "reverted", undefined, undefined, `Chain switch failed: ${switchChainError.message || "Unknown error"}`);
371
+ setIsChainSwitchRequired(false);
372
+ }
373
+ }, [switchChainError, updateOriginCallStatus]);
374
+ // Reset gas estimation state when parameters change
375
+ useEffect(() => {
376
+ setIsEstimatingGas(false);
377
+ }, []);
378
+ // Only update chain switch required state when needed
379
+ useEffect(() => {
380
+ if (originCallParams?.chainId &&
381
+ account.chainId === originCallParams.chainId) {
382
+ console.log("No chain switch required");
383
+ setIsChainSwitchRequired(false);
384
+ }
385
+ }, [account.chainId, originCallParams?.chainId]);
386
+ // Effect to handle chain switching
387
+ useEffect(() => {
388
+ if (originCallParams?.chainId &&
389
+ account.chainId !== originCallParams.chainId) {
390
+ async function check() {
391
+ try {
392
+ const chainId = originCallParams.chainId;
393
+ const walletClient = createWalletClient({
394
+ chain: getChainInfo(chainId),
395
+ transport: custom((await account.connector.getProvider())), // TODO: Add proper type
396
+ });
397
+ await attemptSwitchChain({
398
+ walletClient,
399
+ desiredChainId: chainId,
400
+ });
401
+ }
402
+ catch (error) {
403
+ console.error("Chain switch failed:", error);
404
+ }
405
+ }
406
+ check().catch(console.error);
407
+ }
408
+ }, [account, originCallParams]);
409
+ // Hook to wait for transaction receipt
410
+ const { data: receipt, isLoading: isWaitingForReceipt, isSuccess: receiptIsSuccess, isError: receiptIsError, error: receiptError, } = useWaitForTransactionReceipt({
411
+ hash: txnHash,
412
+ confirmations: 1,
413
+ query: {
414
+ enabled: !!txnHash,
415
+ },
416
+ });
417
+ // Modify the effect that watches for transaction status
418
+ // biome-ignore lint/correctness/useExhaustiveDependencies: False positive
419
+ useEffect(() => {
420
+ if (!txnHash) {
421
+ // Only reset these when txnHash is cleared
422
+ if (originCallStatus?.txnHash) {
423
+ setOriginCallStatus(null);
424
+ }
425
+ setOriginBlockTimestamp(null);
426
+ if (Object.keys(sentMetaTxns).length > 0) {
427
+ setSentMetaTxns({});
428
+ }
429
+ return;
430
+ }
431
+ if (originCallStatus?.txnHash === txnHash &&
432
+ (originCallStatus?.status === "Success" ||
433
+ originCallStatus?.status === "Failed") &&
434
+ !isWaitingForReceipt) {
435
+ return;
436
+ }
437
+ if (isWaitingForReceipt) {
438
+ setOriginCallStatus((prevStatus) => ({
439
+ ...(prevStatus?.txnHash === txnHash
440
+ ? prevStatus
441
+ : {
442
+ gasUsed: undefined,
443
+ effectiveGasPrice: undefined,
444
+ revertReason: undefined,
445
+ }),
446
+ txnHash,
447
+ status: "Pending",
448
+ }));
449
+ return;
450
+ }
451
+ if (receiptIsSuccess && receipt) {
452
+ const newStatus = receipt.status === "success" ? "Success" : "Failed";
453
+ setOriginCallStatus({
454
+ txnHash: receipt.transactionHash,
455
+ status: newStatus,
456
+ gasUsed: receipt.gasUsed ? Number(receipt.gasUsed) : undefined,
457
+ effectiveGasPrice: receipt.effectiveGasPrice?.toString(),
458
+ revertReason: receipt.status === "reverted"
459
+ ? receiptError?.message ||
460
+ "Transaction reverted by receipt"
461
+ : undefined,
462
+ });
463
+ if (newStatus === "Success" && receipt.blockNumber) {
464
+ const fetchTimestamp = async () => {
465
+ try {
466
+ if (!originCallParams?.chainId) {
467
+ console.error("[Trails] Origin chainId not available for fetching origin block timestamp.");
468
+ setOriginBlockTimestamp(null);
469
+ return;
470
+ }
471
+ const chainConfig = getChainInfo(originCallParams.chainId);
472
+ const client = createPublicClient({
473
+ chain: chainConfig,
474
+ transport: http(),
475
+ });
476
+ const block = await client.getBlock({
477
+ blockNumber: BigInt(receipt.blockNumber),
478
+ });
479
+ setOriginBlockTimestamp(Number(block.timestamp));
480
+ }
481
+ catch (error) {
482
+ console.error("[Trails] Error fetching origin block timestamp:", error);
483
+ setOriginBlockTimestamp(null);
484
+ }
485
+ };
486
+ fetchTimestamp();
487
+ }
488
+ else if (newStatus !== "Success") {
489
+ setOriginBlockTimestamp(null);
490
+ }
491
+ if (newStatus === "Success" &&
492
+ metaTxns &&
493
+ metaTxns.length > 0 &&
494
+ isAutoExecute &&
495
+ !metaTxns.some((tx) => sentMetaTxns[`${tx.chainId}-${tx.id}`])) {
496
+ console.log("Origin transaction successful, auto-sending all meta transactions...");
497
+ sendMetaTxnMutation.mutate({ selectedId: null });
498
+ }
499
+ }
500
+ else if (receiptIsError) {
501
+ setOriginCallStatus({
502
+ txnHash,
503
+ status: "Failed",
504
+ revertReason: receiptError?.message ||
505
+ "Failed to get receipt",
506
+ gasUsed: undefined,
507
+ effectiveGasPrice: undefined,
508
+ });
509
+ setOriginBlockTimestamp(null);
510
+ }
511
+ }, [
512
+ txnHash,
513
+ isWaitingForReceipt,
514
+ receiptIsSuccess,
515
+ receiptIsError,
516
+ receipt,
517
+ receiptError,
518
+ metaTxns,
519
+ sentMetaTxns,
520
+ isAutoExecute,
521
+ originCallParams?.chainId,
522
+ originCallStatus?.status,
523
+ originCallStatus?.txnHash,
524
+ ]);
525
+ // Modify the auto-execute effect
526
+ useEffect(() => {
527
+ const shouldAutoSend = isAutoExecute &&
528
+ commitIntentConfigMutation.isSuccess &&
529
+ originCallParams?.chainId &&
530
+ account.chainId === originCallParams.chainId &&
531
+ !originCallParams.error &&
532
+ originCallParams.to &&
533
+ originCallParams.data !== null &&
534
+ originCallParams.value !== null &&
535
+ !sendOriginTxn.isPending &&
536
+ !isWaitingForReceipt &&
537
+ !txnHash &&
538
+ !isChainSwitchRequired &&
539
+ !originCallStatus &&
540
+ !hasAutoExecuted;
541
+ if (shouldAutoSend) {
542
+ console.log("Auto-executing transaction: All conditions met.");
543
+ setHasAutoExecuted(true);
544
+ // Set initial status
545
+ setOriginCallStatus({
546
+ status: "Sending...",
547
+ });
548
+ sendOriginTxn.sendTransaction({
549
+ to: originCallParams.to,
550
+ data: originCallParams.data,
551
+ value: originCallParams.value,
552
+ chainId: originCallParams.chainId,
553
+ }, {
554
+ onSuccess: (hash) => {
555
+ console.log("Auto-executed transaction sent, hash:", hash);
556
+ setTxnHash(hash);
557
+ },
558
+ onError: (error) => {
559
+ console.error("Auto-executed transaction failed:", error);
560
+ if (error instanceof Error &&
561
+ (error.message.includes("User rejected") ||
562
+ error.message.includes("user rejected"))) {
563
+ setIsAutoExecute(false);
564
+ }
565
+ setOriginCallStatus({
566
+ status: "Failed",
567
+ revertReason: error instanceof Error ? error.message : "Unknown error",
568
+ });
569
+ setHasAutoExecuted(false);
570
+ },
571
+ });
572
+ }
573
+ }, [
574
+ isAutoExecute,
575
+ commitIntentConfigMutation.isSuccess,
576
+ originCallParams,
577
+ account.chainId,
578
+ sendOriginTxn.isPending,
579
+ isWaitingForReceipt,
580
+ txnHash,
581
+ isChainSwitchRequired,
582
+ originCallStatus,
583
+ hasAutoExecuted,
584
+ sendOriginTxn,
585
+ ]);
586
+ // Effect to auto-commit when intent calls payloads are ready
587
+ useEffect(() => {
588
+ if (isAutoExecute &&
589
+ intentCallsPayloads &&
590
+ intentPreconditions &&
591
+ trailsFee &&
592
+ account.address &&
593
+ originIntentAddress &&
594
+ !commitIntentConfigMutation.isPending &&
595
+ !commitIntentConfigMutation.isSuccess) {
596
+ console.log("Auto-committing intent configuration...");
597
+ commitIntentConfigMutation.mutate({
598
+ mainSignerAddress: account.address,
599
+ calls: intentCallsPayloads,
600
+ preconditions: intentPreconditions,
601
+ quoteProvider: trailsFee.quoteProvider,
602
+ addressOverrides: createIntentMutation.variables?.addressOverrides,
603
+ });
604
+ }
605
+ }, [
606
+ isAutoExecute,
607
+ intentCallsPayloads,
608
+ intentPreconditions,
609
+ trailsFee,
610
+ account.address,
611
+ originIntentAddress,
612
+ commitIntentConfigMutation,
613
+ createIntentMutation.variables,
614
+ ]);
615
+ // Update the sendMetaTxn mutation
616
+ const sendMetaTxnMutation = useMutation({
617
+ mutationFn: async ({ selectedId }) => {
618
+ if (!intentCallsPayloads ||
619
+ !intentPreconditions ||
620
+ !metaTxns ||
621
+ !account.address) {
622
+ throw new Error("Missing required data for meta-transaction");
623
+ }
624
+ if (!trailsFee?.quoteProvider) {
625
+ throw new Error("quoteProvider is required");
626
+ }
627
+ const intentAddress = calculateIntentAddress(account.address, intentCallsPayloads);
628
+ // If no specific ID is selected, send all meta transactions
629
+ const txnsToSend = selectedId
630
+ ? [metaTxns.find((tx) => tx.id === selectedId)]
631
+ : metaTxns;
632
+ if (!txnsToSend || (selectedId && !txnsToSend[0])) {
633
+ throw new Error("Meta transaction not found");
634
+ }
635
+ const results = [];
636
+ for (const metaTxn of txnsToSend) {
637
+ if (!metaTxn)
638
+ continue;
639
+ const operationKey = `${metaTxn.chainId}-${metaTxn.id}`;
640
+ const lastSentTime = sentMetaTxns[operationKey];
641
+ const now = Date.now();
642
+ if (lastSentTime && now - lastSentTime < RETRY_WINDOW_MS) {
643
+ const timeLeft = Math.ceil((RETRY_WINDOW_MS - (now - lastSentTime)) / 1000);
644
+ console.log(`Meta transaction for ${operationKey} was sent recently. Wait ${timeLeft}s before retry`);
645
+ continue;
646
+ }
647
+ try {
648
+ const chainId = parseInt(metaTxn.chainId);
649
+ if (Number.isNaN(chainId) || chainId <= 0) {
650
+ throw new Error(`Invalid chainId for meta transaction: ${chainId}`);
651
+ }
652
+ const chainRelayer = getRelayer(chainId);
653
+ if (!chainRelayer) {
654
+ throw new Error(`No relayer found for chainId: ${chainId}`);
655
+ }
656
+ const relevantPreconditions = intentPreconditions.filter((p) => p.chainId && parseInt(p.chainId) === chainId);
657
+ console.log(`Relaying meta transaction ${operationKey} to intent ${intentAddress} via relayer:`, chainRelayer);
658
+ const { opHash } = await chainRelayer.sendMetaTxn(metaTxn.walletAddress, metaTxn.contract, metaTxn.input, BigInt(metaTxn.chainId), undefined, relevantPreconditions);
659
+ const useBackupRelayer = false; // Disable backup relayer for now
660
+ if (useBackupRelayer) {
661
+ try {
662
+ // Fire and forget send tx to backup relayer
663
+ const backupRelayer = getBackupRelayer(chainId);
664
+ backupRelayer
665
+ ?.sendMetaTxn(metaTxn.walletAddress, metaTxn.contract, metaTxn.input, BigInt(metaTxn.chainId), undefined, relevantPreconditions)
666
+ .then(() => { })
667
+ .catch(() => { });
668
+ }
669
+ catch { }
670
+ }
671
+ results.push({
672
+ operationKey,
673
+ opHash,
674
+ success: true,
675
+ });
676
+ }
677
+ catch (error) {
678
+ results.push({
679
+ operationKey,
680
+ error: error instanceof Error ? error.message : "Unknown error",
681
+ success: false,
682
+ });
683
+ }
684
+ }
685
+ return results;
686
+ },
687
+ onSuccess: (results) => {
688
+ // Update states based on results
689
+ results.forEach(({ operationKey, opHash, success }) => {
690
+ if (success && opHash) {
691
+ setSentMetaTxns((prev) => ({
692
+ ...prev,
693
+ [operationKey]: Date.now(),
694
+ }));
695
+ setOperationHashes((prev) => ({
696
+ ...prev,
697
+ [operationKey]: opHash,
698
+ }));
699
+ }
700
+ });
701
+ },
702
+ onError: (error) => {
703
+ console.error("Error in meta-transaction process:", error);
704
+ },
705
+ retry: 5, // Allow up to 2 retries
706
+ retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000), // Exponential backoff
707
+ });
708
+ const [tokenAddress, setTokenAddress] = useState(null);
709
+ const [originChainId, setOriginChainId] = useState(null);
710
+ useEffect(() => {
711
+ if (!originIntentAddress ||
712
+ !intentCallsPayloads?.[0]?.chainId ||
713
+ !tokenAddress ||
714
+ !originChainId ||
715
+ !intentPreconditions ||
716
+ !account.address) {
717
+ setOriginCallParams(null);
718
+ return;
719
+ }
720
+ try {
721
+ const intentAddressString = originIntentAddress;
722
+ if (!intentAddressString || !isAddress(intentAddressString)) {
723
+ setOriginCallParams(null);
724
+ return;
725
+ }
726
+ let calcTo;
727
+ let calcData = "0x";
728
+ let calcValue = 0n;
729
+ const recipientAddress = intentAddressString;
730
+ const isNative = tokenAddress === zeroAddress;
731
+ if (isNative) {
732
+ const nativePrecondition = intentPreconditions.find((p) => (p.type === "transfer-native" || p.type === "native-balance") &&
733
+ p.chainId === originChainId.toString());
734
+ const nativeMinAmount = nativePrecondition?.data?.minAmount?.toString() ??
735
+ nativePrecondition?.data?.min?.toString();
736
+ if (nativeMinAmount === undefined) {
737
+ throw new Error("Could not find native precondition (transfer-native or native-balance) or min amount");
738
+ }
739
+ calcValue = BigInt(nativeMinAmount);
740
+ calcTo = recipientAddress;
741
+ }
742
+ else {
743
+ const erc20Precondition = intentPreconditions.find((p) => p.type === "erc20-balance" &&
744
+ p.chainId === originChainId.toString() &&
745
+ p.data?.token &&
746
+ isAddressEqual(Address.from(p.data.token), Address.from(tokenAddress)));
747
+ const erc20MinAmount = erc20Precondition?.data?.min?.toString();
748
+ if (erc20MinAmount === undefined) {
749
+ throw new Error("Could not find ERC20 balance precondition or min amount");
750
+ }
751
+ calcData = getERC20TransferData({
752
+ recipient: recipientAddress,
753
+ amount: BigInt(erc20MinAmount),
754
+ });
755
+ calcTo = tokenAddress;
756
+ }
757
+ setOriginCallParams({
758
+ to: calcTo,
759
+ data: calcData,
760
+ value: calcValue,
761
+ chainId: originChainId,
762
+ error: undefined,
763
+ });
764
+ }
765
+ catch (error) {
766
+ console.error("[trails-sdk] Failed to calculate origin call params for UI:", error);
767
+ setOriginCallParams({
768
+ to: null,
769
+ data: null,
770
+ value: null,
771
+ chainId: null,
772
+ error: error instanceof Error ? error.message : "Unknown error",
773
+ });
774
+ }
775
+ }, [
776
+ intentCallsPayloads,
777
+ tokenAddress,
778
+ originChainId,
779
+ intentPreconditions,
780
+ account.address,
781
+ originIntentAddress,
782
+ ]);
783
+ // const checkPreconditionStatuses = useCallback(async () => {
784
+ // if (!intentPreconditions) return
785
+ // const statuses = await Promise.all(
786
+ // intentPreconditions.map(async (precondition) => {
787
+ // try {
788
+ // const chainIdString = precondition.chainId
789
+ // if (!chainIdString) {
790
+ // console.warn('[trails-sdk] Precondition missing chainId:', precondition)
791
+ // return false
792
+ // }
793
+ // const chainId = parseInt(chainIdString)
794
+ // if (isNaN(chainId) || chainId <= 0) {
795
+ // console.warn('[trails-sdk] Precondition has invalid chainId:', chainIdString, precondition)
796
+ // return false
797
+ // }
798
+ // const chainRelayer = getRelayer(chainId)
799
+ // if (!chainRelayer) {
800
+ // console.error(`[trails-sdk] No relayer found for chainId: ${chainId}`)
801
+ // return false
802
+ // }
803
+ // return await chainRelayer.checkPrecondition(precondition)
804
+ // } catch (error) {
805
+ // console.error('[trails-sdk] Error checking precondition:', error, 'Precondition:', precondition)
806
+ // return false
807
+ // }
808
+ // }),
809
+ // )
810
+ // setPreconditionStatuses(statuses)
811
+ // }, [intentPreconditions, getRelayer])
812
+ // useEffect(() => {
813
+ // // TODO: Remove this once we have a way to check precondition statuses
814
+ // if (false) {
815
+ // checkPreconditionStatuses()
816
+ // }
817
+ // }, [intentPreconditions, checkPreconditionStatuses])
818
+ // Add monitoring for each meta transaction
819
+ const metaTxnMonitorStatuses = useMetaTxnsMonitor(metaTxns, getRelayer);
820
+ // Create a stable dependency for the meta timestamp effect
821
+ const _stableMetaTxnStatusesKey = useMemo(() => {
822
+ if (!metaTxns || Object.keys(metaTxnMonitorStatuses).length === 0) {
823
+ return "no_statuses";
824
+ }
825
+ // Sort by a stable key (e.g., id) to ensure consistent order if metaTxns array order changes
826
+ // but content is the same, though metaTxns itself is a dependency, so this might be redundant if metaTxns order is stable.
827
+ const sortedTxnIds = metaTxns
828
+ .map((tx) => `${tx.chainId}-${tx.id}`)
829
+ .sort();
830
+ return sortedTxnIds
831
+ .map((key) => {
832
+ const statusObj = metaTxnMonitorStatuses[key];
833
+ return `${key}:${statusObj ? statusObj.status : "loading"}`;
834
+ })
835
+ .join(",");
836
+ }, [metaTxns, metaTxnMonitorStatuses]);
837
+ const processedTxns = useRef(new Set());
838
+ // Effect to fetch meta-transaction block timestamps
839
+ useEffect(() => {
840
+ console.log("[trails-sdk] Running meta-transaction block timestamp effect:", {
841
+ metaTxnsLength: metaTxns?.length,
842
+ monitorStatusesLength: Object.keys(metaTxnMonitorStatuses).length,
843
+ });
844
+ if (!metaTxns || metaTxns.length === 0) {
845
+ console.log("[trails-sdk] No meta transactions, clearing timestamps");
846
+ processedTxns.current.clear();
847
+ if (Object.keys(metaTxnBlockTimestamps).length > 0) {
848
+ setMetaTxnBlockTimestamps({});
849
+ }
850
+ return;
851
+ }
852
+ if (!Object.keys(metaTxnMonitorStatuses).length) {
853
+ console.log("[trails-sdk] No monitor statuses yet, waiting...");
854
+ return;
855
+ }
856
+ metaTxns.forEach(async (metaTxn) => {
857
+ const operationKey = `${metaTxn.chainId}-${metaTxn.id}`;
858
+ // Skip if already processed
859
+ if (processedTxns.current.has(operationKey)) {
860
+ console.log(`[trails-sdk] MetaTxn ${operationKey}: Already processed, skipping`);
861
+ return;
862
+ }
863
+ const monitorStatus = metaTxnMonitorStatuses[operationKey];
864
+ if (!monitorStatus || monitorStatus.status !== "confirmed") {
865
+ console.log(`[trails-sdk] MetaTxn ${operationKey}: Status not confirmed, skipping`);
866
+ return;
867
+ }
868
+ // Type assertion since we know it exists when status is "confirmed"
869
+ const transactionHash = monitorStatus.transactionHash;
870
+ if (!transactionHash) {
871
+ console.log(`[trails-sdk] MetaTxn ${operationKey}: No transaction hash, skipping`);
872
+ return;
873
+ }
874
+ console.log(`[trails-sdk] MetaTxn ${operationKey}: Processing transaction ${transactionHash}`);
875
+ processedTxns.current.add(operationKey);
876
+ try {
877
+ const chainId = parseInt(metaTxn.chainId);
878
+ if (Number.isNaN(chainId) || chainId <= 0) {
879
+ throw new Error(`Invalid chainId for meta transaction: ${metaTxn.chainId}`);
880
+ }
881
+ const chainConfig = getChainInfo(chainId);
882
+ const client = createPublicClient({
883
+ chain: chainConfig,
884
+ transport: http(),
885
+ });
886
+ const receipt = await client.getTransactionReceipt({
887
+ hash: transactionHash,
888
+ });
889
+ if (receipt && typeof receipt.blockNumber === "bigint") {
890
+ const block = await client.getBlock({
891
+ blockNumber: receipt.blockNumber,
892
+ });
893
+ console.log(`[trails-sdk] MetaTxn ${operationKey}: Got block timestamp ${block.timestamp}`);
894
+ setMetaTxnBlockTimestamps((prev) => ({
895
+ ...prev,
896
+ [operationKey]: {
897
+ timestamp: Number(block.timestamp),
898
+ error: undefined,
899
+ },
900
+ }));
901
+ }
902
+ else {
903
+ console.warn(`[trails-sdk] MetaTxn ${operationKey}: No block number in receipt`);
904
+ setMetaTxnBlockTimestamps((prev) => ({
905
+ ...prev,
906
+ [operationKey]: {
907
+ timestamp: null,
908
+ error: "Block number not found in receipt",
909
+ },
910
+ }));
911
+ }
912
+ }
913
+ catch (error) {
914
+ console.error(`[trails-sdk] MetaTxn ${operationKey}: Error:`, error);
915
+ setMetaTxnBlockTimestamps((prev) => ({
916
+ ...prev,
917
+ [operationKey]: {
918
+ timestamp: null,
919
+ error: error.message || "Failed to fetch receipt/timestamp",
920
+ },
921
+ }));
922
+ }
923
+ });
924
+ }, [metaTxns, metaTxnMonitorStatuses, metaTxnBlockTimestamps]);
925
+ const updateAutoExecute = (enabled) => {
926
+ setIsAutoExecute(enabled);
927
+ };
928
+ function createIntent(args) {
929
+ createIntentMutation.mutate({
930
+ ...args,
931
+ destinationSalt: Date.now().toString(),
932
+ });
933
+ }
934
+ function commitIntentConfig(args) {
935
+ console.log("[trails-sdk] commitIntentConfig", args);
936
+ commitIntentConfigMutation.mutate(args);
937
+ }
938
+ function updateOriginCallParams(args) {
939
+ if (!args) {
940
+ setOriginCallParams(null);
941
+ return;
942
+ }
943
+ const { originChainId, tokenAddress } = args;
944
+ setOriginChainId(originChainId);
945
+ setTokenAddress(tokenAddress);
946
+ }
947
+ function sendMetaTxn(selectedId) {
948
+ sendMetaTxnMutation.mutate({ selectedId });
949
+ }
950
+ const { chainId } = account;
951
+ const originChainIdFromParams = originCallParams?.chainId;
952
+ const createIntentPending = createIntentMutation.isPending;
953
+ const createIntentSuccess = createIntentMutation.isSuccess;
954
+ const createIntentError = createIntentMutation.error;
955
+ const createIntentArgs = createIntentMutation.variables;
956
+ const commitIntentConfigPending = commitIntentConfigMutation.isPending;
957
+ const commitIntentConfigSuccess = commitIntentConfigMutation.isSuccess;
958
+ const commitIntentConfigError = commitIntentConfigMutation.error;
959
+ const commitIntentConfigArgs = commitIntentConfigMutation.variables;
960
+ const sendMetaTxnPending = sendMetaTxnMutation.isPending;
961
+ const sendMetaTxnSuccess = sendMetaTxnMutation.isSuccess;
962
+ const sendMetaTxnError = sendMetaTxnMutation.error;
963
+ const sendMetaTxnArgs = sendMetaTxnMutation.variables;
964
+ return {
965
+ apiClient,
966
+ metaTxns,
967
+ intentCallsPayloads,
968
+ intentPreconditions,
969
+ trailsFee,
970
+ txnHash,
971
+ committedOriginIntentAddress,
972
+ committedDestinationIntentAddress,
973
+ verificationStatus,
974
+ getRelayer,
975
+ estimatedGas,
976
+ isEstimateError,
977
+ estimateError,
978
+ calculateIntentAddress,
979
+ calculateOriginAndDestinationIntentAddresses,
980
+ committedIntentConfig,
981
+ isLoadingCommittedConfig,
982
+ committedConfigError,
983
+ commitIntentConfig,
984
+ commitIntentConfigPending,
985
+ commitIntentConfigSuccess,
986
+ commitIntentConfigError,
987
+ commitIntentConfigArgs,
988
+ getIntentCallsPayloads,
989
+ operationHashes,
990
+ callIntentCallsPayload,
991
+ sendOriginTransaction,
992
+ switchChain,
993
+ isSwitchingChain,
994
+ switchChainError,
995
+ isTransactionInProgress,
996
+ isChainSwitchRequired: chainId !== originChainIdFromParams && !!originChainIdFromParams,
997
+ sendTransaction: sendOriginTxn.sendTransaction,
998
+ isSendingTransaction: sendOriginTxn.isPending,
999
+ originCallStatus,
1000
+ updateOriginCallStatus,
1001
+ isEstimatingGas,
1002
+ isAutoExecute,
1003
+ updateAutoExecute,
1004
+ receipt,
1005
+ isWaitingForReceipt,
1006
+ receiptIsSuccess,
1007
+ receiptIsError,
1008
+ receiptError,
1009
+ hasAutoExecuted,
1010
+ originCallSuccess: sendOriginTxn.isSuccess,
1011
+ sentMetaTxns,
1012
+ sendMetaTxn,
1013
+ sendMetaTxnPending,
1014
+ sendMetaTxnSuccess,
1015
+ sendMetaTxnError,
1016
+ sendMetaTxnArgs,
1017
+ clearIntent,
1018
+ metaTxnMonitorStatuses,
1019
+ createIntent,
1020
+ createIntentPending,
1021
+ createIntentSuccess,
1022
+ createIntentError,
1023
+ createIntentArgs,
1024
+ originCallParams,
1025
+ updateOriginCallParams,
1026
+ originBlockTimestamp,
1027
+ metaTxnBlockTimestamps,
1028
+ originIntentAddress,
1029
+ destinationIntentAddress,
1030
+ };
1031
+ }