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
@@ -0,0 +1,1849 @@
1
+ import type {
2
+ GetIntentCallsPayloadsArgs,
3
+ IntentPrecondition,
4
+ SequenceAPIClient,
5
+ } from "@0xsequence/trails-api"
6
+ import type { MetaTxnReceipt } from "@0xsequence/trails-relayer"
7
+ import type { Relayer } from "@0xsequence/wallet-core"
8
+ import type { Payload } from "@0xsequence/wallet-primitives"
9
+ import { useQuery } from "@tanstack/react-query"
10
+ import type {
11
+ Account,
12
+ Chain,
13
+ PublicClient,
14
+ TransactionReceipt,
15
+ WalletClient,
16
+ } from "viem"
17
+ import {
18
+ createPublicClient,
19
+ createWalletClient,
20
+ decodeFunctionData,
21
+ erc20Abi,
22
+ formatUnits,
23
+ http,
24
+ parseUnits,
25
+ zeroAddress,
26
+ } from "viem"
27
+ import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"
28
+ import { useAPIClient } from "./apiClient.js"
29
+ import {
30
+ cctpTransfer,
31
+ getIsUsdcAddress,
32
+ getMessageTransmitter,
33
+ getMintUSDCData,
34
+ } from "./cctp.js"
35
+ import { attemptSwitchChain } from "./chainSwitch.js"
36
+ import { getChainInfo, getTestnetChainInfo } from "./chains.js"
37
+ import { intentEntrypoints, DEFAULT_USE_V3_RELAYERS } from "./constants.js"
38
+ import { getERC20TransferData } from "./encoders.js"
39
+ import { getExplorerUrl } from "./explorer.js"
40
+ import {
41
+ getDepositToIntentCalls,
42
+ getPermitCalls,
43
+ getPermitSignature,
44
+ } from "./gasless.js"
45
+ import { useIndexerGatewayClient } from "./indexerClient.js"
46
+ import {
47
+ commitIntentConfig,
48
+ getIntentCallsPayloads as getIntentCallsPayloadsFromIntents,
49
+ sendOriginTransaction,
50
+ } from "./intents.js"
51
+ import type { MetaTxn } from "./metaTxnMonitor.js"
52
+ import { getMetaTxStatus } from "./metaTxnMonitor.js"
53
+ import { relayerSendMetaTx } from "./metaTxns.js"
54
+ import {
55
+ getDelegatorSmartAccount,
56
+ getPaymasterGaslessTransaction,
57
+ sendPaymasterGaslessTransaction,
58
+ } from "./paymasterSend.js"
59
+ import { findFirstPreconditionForChainId } from "./preconditions.js"
60
+ import { useTokenPrice } from "./prices.js"
61
+ import { getQueryParam } from "./queryParams.js"
62
+ import type { RelayerEnvConfig } from "./relayer.js"
63
+ import { useRelayers } from "./relayer.js"
64
+ import {
65
+ executeSimpleRelayTransaction,
66
+ getRelaySDKQuote,
67
+ getTxHashFromRelayResult,
68
+ } from "./relaySdk.js"
69
+ import {
70
+ getFeeOptions,
71
+ sequenceSendTransaction,
72
+ simpleCreateSequenceWallet,
73
+ } from "./sequenceWallet.js"
74
+ import { useAccountTokenBalance } from "./tokenBalances.js"
75
+ import { useSupportedTokens } from "./tokens.js"
76
+ import { requestWithTimeout } from "./utils.js"
77
+
78
+ type TransactionStateStatus = "pending" | "failed" | "confirmed"
79
+
80
+ export type TransactionState = {
81
+ transactionHash: string
82
+ explorerUrl: string
83
+ chainId: number
84
+ state: TransactionStateStatus
85
+ label: string
86
+ }
87
+
88
+ export type SendOptions = {
89
+ account: Account
90
+ originTokenAddress: string
91
+ originChainId: number
92
+ originTokenAmount: string
93
+ destinationChainId: number
94
+ recipient: string
95
+ destinationTokenAddress: string
96
+ destinationTokenAmount: string
97
+ destinationTokenSymbol: string
98
+ sequenceProjectAccessKey: string
99
+ fee: string
100
+ client?: WalletClient
101
+ dryMode: boolean
102
+ apiClient: SequenceAPIClient
103
+ originRelayer: Relayer.Standard.Rpc.RpcRelayer
104
+ destinationRelayer: Relayer.Standard.Rpc.RpcRelayer
105
+ destinationCalldata?: string
106
+ onTransactionStateChange: (transactionStates: TransactionState[]) => void
107
+ sourceTokenPriceUsd?: number | null
108
+ destinationTokenPriceUsd?: number | null
109
+ sourceTokenDecimals: number
110
+ destinationTokenDecimals: number
111
+ paymasterUrl?: string
112
+ gasless?: boolean
113
+ relayerConfig: RelayerEnvConfig
114
+ }
115
+
116
+ export type PrepareSendReturn = {
117
+ intentAddress?: string
118
+ originSendAmount: string
119
+ send: (onOriginSend?: () => void) => Promise<SendReturn>
120
+ }
121
+
122
+ export type SendReturn = {
123
+ originUserTxReceipt: TransactionReceipt | null
124
+ originMetaTxnReceipt: MetaTxnReceipt | null
125
+ destinationMetaTxnReceipt: MetaTxnReceipt | null
126
+ }
127
+
128
+ export function getIsToSameChain(
129
+ originChainId: number,
130
+ destinationChainId: number,
131
+ ): boolean {
132
+ return originChainId?.toString() === destinationChainId?.toString()
133
+ }
134
+
135
+ export function getIsToSameToken(
136
+ originTokenAddress: string,
137
+ destinationTokenAddress: string,
138
+ ): boolean {
139
+ return (
140
+ originTokenAddress?.toLowerCase() === destinationTokenAddress?.toLowerCase()
141
+ )
142
+ }
143
+
144
+ export function getIsToSameChainAndToken(
145
+ originChainId: number,
146
+ originTokenAddress: string,
147
+ destinationChainId: number,
148
+ destinationTokenAddress: string,
149
+ ): boolean {
150
+ return (
151
+ getIsToSameChain(originChainId, destinationChainId) &&
152
+ getIsToSameToken(originTokenAddress, destinationTokenAddress)
153
+ )
154
+ }
155
+
156
+ export function getUseCctp(
157
+ originTokenAddress: string,
158
+ destinationTokenAddress: string,
159
+ originChainId: number,
160
+ destinationChainId: number,
161
+ ) {
162
+ return (
163
+ getIsUsdcAddress(originTokenAddress, originChainId) &&
164
+ getIsUsdcAddress(destinationTokenAddress, destinationChainId)
165
+ )
166
+ }
167
+
168
+ function isTestnetDebugMode(): boolean {
169
+ return getQueryParam("testnet") === "true"
170
+ }
171
+
172
+ function getTestnetOriginTokenAddress(): string {
173
+ return "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
174
+ }
175
+
176
+ function getIntentArgs(
177
+ mainSignerAddress: string,
178
+ originChainId: number,
179
+ originTokenAddress: string,
180
+ originTokenAmount: string,
181
+ destinationChainId: number,
182
+ destinationTokenAddress: string,
183
+ destinationTokenAmount: string,
184
+ destinationTokenSymbol: string,
185
+ recipient: string,
186
+ destinationCalldata?: string,
187
+ ): GetIntentCallsPayloadsArgs {
188
+ const _destinationCalldata =
189
+ destinationCalldata ||
190
+ (destinationTokenAddress === zeroAddress
191
+ ? "0x"
192
+ : getERC20TransferData({
193
+ recipient,
194
+ amount: BigInt(destinationTokenAmount),
195
+ }))
196
+ const _destinationToAddress = destinationCalldata
197
+ ? recipient
198
+ : destinationTokenAddress === zeroAddress
199
+ ? recipient
200
+ : destinationTokenAddress
201
+ const _destinationCallValue =
202
+ destinationTokenAddress === zeroAddress ? destinationTokenAmount : "0"
203
+
204
+ const intentArgs = {
205
+ userAddress: mainSignerAddress,
206
+ originChainId,
207
+ originTokenAddress,
208
+ originTokenAmount:
209
+ originTokenAddress === destinationTokenAddress
210
+ ? destinationTokenAmount
211
+ : originTokenAmount, // max amount
212
+ destinationChainId,
213
+ destinationToAddress: _destinationToAddress,
214
+ destinationTokenAddress: destinationTokenAddress,
215
+ destinationTokenAmount: destinationTokenAmount,
216
+ destinationTokenSymbol: destinationTokenSymbol,
217
+ destinationCallData: _destinationCalldata,
218
+ destinationCallValue: _destinationCallValue,
219
+ }
220
+
221
+ return intentArgs
222
+ }
223
+
224
+ export async function prepareSend(
225
+ options: SendOptions,
226
+ ): Promise<PrepareSendReturn> {
227
+ const {
228
+ account,
229
+ originTokenAddress,
230
+ originChainId,
231
+ originTokenAmount, // account balance
232
+ destinationChainId,
233
+ recipient,
234
+ destinationTokenAddress,
235
+ destinationTokenAmount,
236
+ destinationTokenSymbol,
237
+ fee,
238
+ client: walletClient,
239
+ dryMode = false,
240
+ apiClient,
241
+ originRelayer,
242
+ destinationRelayer,
243
+ destinationCalldata,
244
+ onTransactionStateChange,
245
+ sourceTokenPriceUsd,
246
+ destinationTokenPriceUsd,
247
+ sourceTokenDecimals,
248
+ destinationTokenDecimals,
249
+ paymasterUrl,
250
+ gasless = false,
251
+ relayerConfig,
252
+ sequenceProjectAccessKey,
253
+ } = options
254
+
255
+ if (!walletClient) {
256
+ throw new Error("Wallet client not provided")
257
+ }
258
+
259
+ const chain = getChainInfo(originChainId)
260
+ if (!chain) {
261
+ throw new Error(`Chain ${originChainId} not found`)
262
+ }
263
+ const isToSameChain = getIsToSameChain(originChainId, destinationChainId)
264
+ const isToSameToken = getIsToSameToken(
265
+ originTokenAddress,
266
+ destinationTokenAddress,
267
+ )
268
+
269
+ console.log("[trails-sdk] isToSameChain", isToSameChain)
270
+ console.log("[trails-sdk] isToSameToken", isToSameToken)
271
+
272
+ const publicClient = createPublicClient({
273
+ chain,
274
+ transport: http(),
275
+ })
276
+
277
+ const mainSignerAddress = account.address
278
+ const transactionStates: TransactionState[] = []
279
+
280
+ // origin tx
281
+ transactionStates.push({
282
+ transactionHash: "",
283
+ explorerUrl: "",
284
+ chainId: originChainId,
285
+ state: "pending",
286
+ label:
287
+ isToSameChain && isToSameToken
288
+ ? "Execute"
289
+ : isToSameChain && !isToSameToken
290
+ ? "Swap"
291
+ : "Transfer",
292
+ })
293
+
294
+ if (!isToSameChain) {
295
+ // swap + bridge tx
296
+ transactionStates.push({
297
+ transactionHash: "",
298
+ explorerUrl: "",
299
+ chainId: originChainId,
300
+ state: "pending",
301
+ label: isToSameToken ? "Bridge" : "Swap & Bridge",
302
+ })
303
+
304
+ // destination tx
305
+ transactionStates.push({
306
+ transactionHash: "",
307
+ explorerUrl: "",
308
+ chainId: destinationChainId,
309
+ state: "pending",
310
+ label: "Execute",
311
+ })
312
+ }
313
+
314
+ if (isToSameChain && !isToSameToken) {
315
+ return await sendHandlerForSameChainDifferentToken({
316
+ originTokenAddress,
317
+ destinationTokenAmount,
318
+ destinationTokenAddress,
319
+ destinationCalldata,
320
+ recipient,
321
+ originChainId,
322
+ walletClient,
323
+ publicClient,
324
+ account,
325
+ })
326
+ }
327
+
328
+ if (isToSameToken && isToSameChain) {
329
+ return await sendHandlerForSameChainSameToken({
330
+ originTokenAddress,
331
+ destinationTokenAmount,
332
+ destinationCalldata,
333
+ recipient,
334
+ originChainId,
335
+ walletClient,
336
+ publicClient,
337
+ onTransactionStateChange,
338
+ dryMode,
339
+ account,
340
+ chain,
341
+ })
342
+ }
343
+
344
+ return await sendHandlerForDifferentChainDifferentToken({
345
+ mainSignerAddress,
346
+ originChainId,
347
+ originTokenAddress,
348
+ originTokenAmount,
349
+ destinationChainId,
350
+ destinationTokenAddress,
351
+ destinationTokenAmount,
352
+ destinationTokenSymbol,
353
+ recipient,
354
+ destinationCalldata,
355
+ apiClient,
356
+ sourceTokenPriceUsd,
357
+ destinationTokenPriceUsd,
358
+ sourceTokenDecimals,
359
+ destinationTokenDecimals,
360
+ gasless,
361
+ paymasterUrl,
362
+ originRelayer,
363
+ destinationRelayer,
364
+ sequenceProjectAccessKey,
365
+ relayerConfig,
366
+ walletClient,
367
+ publicClient,
368
+ chain,
369
+ account,
370
+ fee,
371
+ dryMode,
372
+ onTransactionStateChange,
373
+ transactionStates,
374
+ })
375
+ }
376
+
377
+ async function sendHandlerForDifferentChainDifferentToken({
378
+ mainSignerAddress,
379
+ originChainId,
380
+ originTokenAddress,
381
+ originTokenAmount,
382
+ destinationChainId,
383
+ destinationTokenAddress,
384
+ destinationTokenAmount,
385
+ destinationTokenSymbol,
386
+ recipient,
387
+ destinationCalldata,
388
+ apiClient,
389
+ sourceTokenPriceUsd,
390
+ destinationTokenPriceUsd,
391
+ sourceTokenDecimals,
392
+ destinationTokenDecimals,
393
+ gasless,
394
+ paymasterUrl,
395
+ originRelayer,
396
+ destinationRelayer,
397
+ sequenceProjectAccessKey,
398
+ relayerConfig,
399
+ walletClient,
400
+ publicClient,
401
+ chain,
402
+ account,
403
+ fee,
404
+ dryMode,
405
+ onTransactionStateChange,
406
+ transactionStates,
407
+ }: {
408
+ mainSignerAddress: string
409
+ originChainId: number
410
+ originTokenAddress: string
411
+ originTokenAmount: string
412
+ destinationChainId: number
413
+ destinationTokenAddress: string
414
+ destinationTokenAmount: string
415
+ destinationTokenSymbol: string
416
+ recipient: string
417
+ destinationCalldata?: string
418
+ apiClient: SequenceAPIClient
419
+ sourceTokenPriceUsd?: number | null
420
+ destinationTokenPriceUsd?: number | null
421
+ sourceTokenDecimals: number
422
+ destinationTokenDecimals: number
423
+ gasless: boolean
424
+ paymasterUrl?: string
425
+ originRelayer: Relayer.Standard.Rpc.RpcRelayer
426
+ destinationRelayer: Relayer.Standard.Rpc.RpcRelayer
427
+ sequenceProjectAccessKey: string
428
+ relayerConfig: RelayerEnvConfig
429
+ walletClient: WalletClient
430
+ publicClient: PublicClient
431
+ chain: Chain
432
+ account: Account
433
+ fee: string
434
+ dryMode: boolean
435
+ onTransactionStateChange: (transactionStates: TransactionState[]) => void
436
+ transactionStates: TransactionState[]
437
+ }): Promise<PrepareSendReturn> {
438
+ const testnet = isTestnetDebugMode()
439
+ const useCctp = getUseCctp(
440
+ originTokenAddress,
441
+ destinationTokenAddress,
442
+ originChainId,
443
+ destinationChainId,
444
+ )
445
+ if (useCctp && testnet) {
446
+ const amount = destinationTokenAmount
447
+
448
+ return {
449
+ intentAddress: "",
450
+ originSendAmount: amount,
451
+ send: async (onOriginSend?: () => void): Promise<SendReturn> => {
452
+ const originChain = testnet ? getTestnetChainInfo(chain)! : chain
453
+ const destinationChain = testnet
454
+ ? getTestnetChainInfo(destinationChainId)!
455
+ : getChainInfo(destinationChainId)
456
+
457
+ if (!originChain || !destinationChain) {
458
+ throw new Error("Invalid chain")
459
+ }
460
+
461
+ console.log("[trails-sdk] originChain", originChain)
462
+ console.log("[trails-sdk] destinationChain", destinationChain)
463
+
464
+ const originPublicClient = createPublicClient({
465
+ chain: originChain,
466
+ transport: http(),
467
+ })
468
+
469
+ const { txHash, attestation } = await cctpTransfer({
470
+ walletClient,
471
+ originChain,
472
+ destinationChain,
473
+ amount: BigInt(destinationTokenAmount),
474
+ })
475
+
476
+ if (!attestation) {
477
+ throw new Error("Failed to retrieve attestation")
478
+ }
479
+
480
+ if (onOriginSend) {
481
+ onOriginSend()
482
+ }
483
+
484
+ console.log("[trails-sdk] waiting for tx", txHash)
485
+
486
+ const receipt = await originPublicClient.waitForTransactionReceipt({
487
+ hash: txHash,
488
+ })
489
+
490
+ console.log("[trails-sdk] tx receipt", receipt)
491
+
492
+ transactionStates[0] = getTransactionStateFromReceipt(
493
+ receipt,
494
+ originChain.id,
495
+ "Transfer",
496
+ )
497
+ transactionStates[1] = getTransactionStateFromReceipt(
498
+ receipt,
499
+ originChain.id,
500
+ "Bridge",
501
+ )
502
+
503
+ onTransactionStateChange(transactionStates)
504
+
505
+ const tokenMessenger = getMessageTransmitter(destinationChain.id)!
506
+
507
+ console.log("[trails-sdk] tokenMessenger", tokenMessenger)
508
+ const calls = [
509
+ await getMintUSDCData({
510
+ tokenMessenger,
511
+ attestation,
512
+ }),
513
+ ]
514
+
515
+ console.log("[trails-sdk] calls", calls)
516
+ const delegatorPrivateKey = generatePrivateKey()
517
+ const delegatorAccount = privateKeyToAccount(delegatorPrivateKey)
518
+ const delegatorClient = createWalletClient({
519
+ account: delegatorAccount,
520
+ chain: destinationChain,
521
+ transport: http(),
522
+ })
523
+ const destinationPublicClient = createPublicClient({
524
+ chain: destinationChain,
525
+ transport: http(),
526
+ })
527
+ console.log("[trails-sdk] delegatorClient", delegatorClient)
528
+
529
+ const sequenceWalletAddress = await simpleCreateSequenceWallet(
530
+ delegatorAccount as any,
531
+ relayerConfig,
532
+ sequenceProjectAccessKey,
533
+ )
534
+ console.log("[trails-sdk] sequenceWalletAddress", sequenceWalletAddress)
535
+ const sequenceTxHash = await sequenceSendTransaction(
536
+ sequenceWalletAddress,
537
+ delegatorClient,
538
+ destinationPublicClient,
539
+ calls,
540
+ destinationChain,
541
+ relayerConfig,
542
+ sequenceProjectAccessKey,
543
+ )
544
+
545
+ const destinationReceipt =
546
+ await destinationPublicClient.waitForTransactionReceipt({
547
+ hash: sequenceTxHash as `0x${string}`,
548
+ })
549
+
550
+ console.log("[trails-sdk] destinationReceipt", destinationReceipt)
551
+
552
+ transactionStates[2] = getTransactionStateFromReceipt(
553
+ destinationReceipt,
554
+ destinationChain.id,
555
+ "Receive",
556
+ )
557
+ onTransactionStateChange(transactionStates)
558
+
559
+ return {
560
+ originUserTxReceipt: receipt,
561
+ originMetaTxnReceipt: null,
562
+ destinationMetaTxnReceipt: null,
563
+ }
564
+ },
565
+ }
566
+ }
567
+
568
+ const intentArgs = getIntentArgs(
569
+ mainSignerAddress,
570
+ originChainId,
571
+ originTokenAddress,
572
+ originTokenAmount,
573
+ destinationChainId,
574
+ destinationTokenAddress,
575
+ destinationTokenAmount,
576
+ destinationTokenSymbol,
577
+ recipient,
578
+ destinationCalldata,
579
+ )
580
+ console.log("[trails-sdk] Creating intent with args:", intentArgs)
581
+ const intent = await getIntentCallsPayloadsFromIntents(apiClient, intentArgs)
582
+ console.log("[trails-sdk] Got intent:", intent)
583
+
584
+ if (!intent) {
585
+ throw new Error("Invalid intent")
586
+ }
587
+
588
+ if (!intent.preconditions?.length || !intent.calls?.length) {
589
+ throw new Error("Invalid intent")
590
+ }
591
+
592
+ const intentAddress = intent.originIntentAddress
593
+ console.log("[trails-sdk] intent address:", intentAddress.toString())
594
+
595
+ await commitIntentConfig(
596
+ apiClient,
597
+ mainSignerAddress,
598
+ intent.calls,
599
+ intent.preconditions,
600
+ )
601
+
602
+ console.log("[trails-sdk] Committed intent config")
603
+
604
+ const firstPrecondition = findFirstPreconditionForChainId(
605
+ intent.preconditions,
606
+ originChainId,
607
+ )
608
+
609
+ if (!firstPrecondition) {
610
+ throw new Error("No precondition found for origin chain")
611
+ }
612
+
613
+ const firstPreconditionMin = firstPrecondition?.data?.min?.toString()
614
+ const depositAmount = firstPreconditionMin
615
+
616
+ const hasEnoughBalance = await checkAccountBalance({
617
+ account,
618
+ tokenAddress: originTokenAddress,
619
+ depositAmount,
620
+ publicClient,
621
+ })
622
+
623
+ if (!hasEnoughBalance) {
624
+ throw new Error("Account does not have enough balance for deposit")
625
+ }
626
+
627
+ return {
628
+ intentAddress,
629
+ originSendAmount: depositAmount,
630
+ send: async (onOriginSend?: () => void): Promise<SendReturn> => {
631
+ console.log("[trails-sdk] sending origin transaction")
632
+
633
+ const needsNativeFee = getNeedsLifiNativeFee({
634
+ originTokenAddress,
635
+ destinationTokenAmount,
636
+ destinationTokenDecimals,
637
+ sourceTokenDecimals,
638
+ sourceTokenPriceUsd: sourceTokenPriceUsd ?? null,
639
+ destinationTokenPriceUsd: destinationTokenPriceUsd ?? null,
640
+ depositAmount,
641
+ })
642
+
643
+ console.log("[trails-sdk] needsNativeFee", needsNativeFee)
644
+ console.log("[trails-sdk] sourceTokenPriceUsd", sourceTokenPriceUsd)
645
+ console.log(
646
+ "[trails-sdk] destinationTokenPriceUsd",
647
+ destinationTokenPriceUsd,
648
+ )
649
+ console.log("[trails-sdk] sourceTokenDecimals", sourceTokenDecimals)
650
+ console.log(
651
+ "[trails-sdk] destinationTokenDecimals",
652
+ destinationTokenDecimals,
653
+ )
654
+
655
+ // const originCallParams = {
656
+ // to:
657
+ // originTokenAddress === zeroAddress
658
+ // ? firstPreconditionAddress
659
+ // : originTokenAddress,
660
+ // data:
661
+ // originTokenAddress === zeroAddress
662
+ // ? "0x"
663
+ // : getERC20TransferData({
664
+ // recipient: firstPreconditionAddress,
665
+ // amount: BigInt(firstPreconditionMin) + BigInt(fee),
666
+ // }),
667
+ // value:
668
+ // originTokenAddress === zeroAddress
669
+ // ? BigInt(firstPreconditionMin) + BigInt(fee)
670
+ // : "0",
671
+ // chainId: originChainId,
672
+ // chain,
673
+ // }
674
+
675
+ let originUserTxReceipt: TransactionReceipt | null = null
676
+ let originMetaTxnReceipt: MetaTxnReceipt | null = null
677
+ let destinationMetaTxnReceipt: MetaTxnReceipt | null = null
678
+
679
+ const testnet = isTestnetDebugMode()
680
+ const testnetOriginTokenAddress = getTestnetOriginTokenAddress()
681
+ const chainToUse = testnet ? getTestnetChainInfo(chain)! : chain
682
+
683
+ console.log("[trails-sdk] testnet", testnet)
684
+
685
+ originUserTxReceipt = await attemptUserDepositTx({
686
+ originTokenAddress: testnet
687
+ ? testnetOriginTokenAddress
688
+ : originTokenAddress,
689
+ gasless,
690
+ paymasterUrl,
691
+ chain: chainToUse,
692
+ account,
693
+ relayerConfig,
694
+ sequenceProjectAccessKey,
695
+ originRelayer,
696
+ firstPreconditionMin,
697
+ intentAddress,
698
+ onOriginSend,
699
+ publicClient,
700
+ walletClient,
701
+ destinationTokenDecimals,
702
+ sourceTokenDecimals,
703
+ fee,
704
+ dryMode,
705
+ sourceTokenPriceUsd: sourceTokenPriceUsd ?? null,
706
+ destinationTokenPriceUsd: destinationTokenPriceUsd ?? null,
707
+ destinationTokenAmount,
708
+ })
709
+
710
+ if (!originUserTxReceipt) {
711
+ throw new Error("Failed to send origin transaction")
712
+ }
713
+
714
+ transactionStates[0] = getTransactionStateFromReceipt(
715
+ originUserTxReceipt,
716
+ originChainId,
717
+ "Transfer",
718
+ )
719
+ onTransactionStateChange(transactionStates)
720
+
721
+ if (intent.metaTxns[0] && intent.preconditions[0]) {
722
+ originMetaTxnReceipt = await sendMetaTxAndWaitForReceipt({
723
+ metaTx: intent.metaTxns[0] as MetaTxn,
724
+ relayer: originRelayer,
725
+ precondition: intent.preconditions[0] as IntentPrecondition,
726
+ })
727
+
728
+ if (originMetaTxnReceipt) {
729
+ transactionStates[1] = getTransactionStateFromReceipt(
730
+ originMetaTxnReceipt,
731
+ originChainId,
732
+ "Swap & Bridge",
733
+ )
734
+ onTransactionStateChange(transactionStates)
735
+ }
736
+ }
737
+
738
+ if (intent.metaTxns[1] && intent.preconditions[1]) {
739
+ destinationMetaTxnReceipt = await sendMetaTxAndWaitForReceipt({
740
+ metaTx: intent.metaTxns[1] as MetaTxn,
741
+ relayer: destinationRelayer,
742
+ precondition: intent.preconditions[1] as IntentPrecondition,
743
+ })
744
+
745
+ if (destinationMetaTxnReceipt) {
746
+ transactionStates[2] = getTransactionStateFromReceipt(
747
+ destinationMetaTxnReceipt,
748
+ destinationChainId,
749
+ "Execute",
750
+ )
751
+ onTransactionStateChange(transactionStates)
752
+ }
753
+ }
754
+
755
+ return {
756
+ originUserTxReceipt,
757
+ originMetaTxnReceipt,
758
+ destinationMetaTxnReceipt,
759
+ }
760
+ },
761
+ }
762
+ }
763
+
764
+ async function sendHandlerForSameChainSameToken({
765
+ originTokenAddress,
766
+ destinationTokenAmount,
767
+ destinationCalldata,
768
+ recipient,
769
+ originChainId,
770
+ walletClient,
771
+ publicClient,
772
+ onTransactionStateChange,
773
+ dryMode,
774
+ account,
775
+ chain,
776
+ }: {
777
+ originTokenAddress: string
778
+ destinationTokenAmount: string
779
+ destinationCalldata?: string
780
+ recipient: string
781
+ originChainId: number
782
+ walletClient: WalletClient
783
+ publicClient: PublicClient
784
+ onTransactionStateChange: (transactionStates: TransactionState[]) => void
785
+ dryMode: boolean
786
+ account: Account
787
+ chain: Chain
788
+ }): Promise<PrepareSendReturn> {
789
+ console.log("[trails-sdk] isToSameToken && isToSameChain")
790
+
791
+ const hasEnoughBalance = await checkAccountBalance({
792
+ account,
793
+ tokenAddress: originTokenAddress,
794
+ depositAmount: destinationTokenAmount,
795
+ publicClient,
796
+ })
797
+
798
+ if (!hasEnoughBalance) {
799
+ throw new Error("Account does not have enough balance for deposit")
800
+ }
801
+
802
+ return {
803
+ originSendAmount: destinationTokenAmount,
804
+ send: async (onOriginSend?: () => void): Promise<SendReturn> => {
805
+ const originCallParams = {
806
+ to: destinationCalldata
807
+ ? recipient
808
+ : originTokenAddress === zeroAddress
809
+ ? recipient
810
+ : originTokenAddress,
811
+ data:
812
+ destinationCalldata ||
813
+ (originTokenAddress === zeroAddress
814
+ ? "0x"
815
+ : getERC20TransferData({
816
+ recipient,
817
+ amount: BigInt(destinationTokenAmount),
818
+ })),
819
+ value:
820
+ originTokenAddress === zeroAddress
821
+ ? BigInt(destinationTokenAmount)
822
+ : "0",
823
+ chainId: originChainId,
824
+ chain,
825
+ }
826
+
827
+ console.log("[trails-sdk] origin call params", originCallParams)
828
+
829
+ let originUserTxReceipt: TransactionReceipt | null = null
830
+ const originMetaTxnReceipt: MetaTxnReceipt | null = null
831
+ const destinationMetaTxnReceipt: MetaTxnReceipt | null = null
832
+
833
+ await attemptSwitchChain({
834
+ walletClient,
835
+ desiredChainId: originChainId,
836
+ })
837
+ if (!dryMode) {
838
+ onTransactionStateChange([
839
+ {
840
+ transactionHash: "",
841
+ explorerUrl: "",
842
+ chainId: originChainId,
843
+ state: "pending",
844
+ label: "Execute",
845
+ },
846
+ ])
847
+ console.log("[trails-sdk] origin call params", originCallParams)
848
+ const txHash = await sendOriginTransaction(
849
+ account,
850
+ walletClient,
851
+ originCallParams as any,
852
+ ) // TODO: Add proper type
853
+
854
+ console.log("[trails-sdk] origin tx", txHash)
855
+
856
+ if (onOriginSend) {
857
+ onOriginSend()
858
+ }
859
+
860
+ // Wait for transaction receipt
861
+ const receipt = await publicClient.waitForTransactionReceipt({
862
+ hash: txHash,
863
+ })
864
+ console.log("[trails-sdk] receipt", receipt)
865
+ originUserTxReceipt = receipt
866
+
867
+ onTransactionStateChange([
868
+ getTransactionStateFromReceipt(
869
+ originUserTxReceipt,
870
+ originChainId,
871
+ "Swap",
872
+ ),
873
+ ])
874
+ }
875
+
876
+ return {
877
+ originUserTxReceipt,
878
+ originMetaTxnReceipt,
879
+ destinationMetaTxnReceipt,
880
+ }
881
+ },
882
+ }
883
+ }
884
+
885
+ async function sendHandlerForSameChainDifferentToken({
886
+ originTokenAddress,
887
+ destinationTokenAmount,
888
+ destinationTokenAddress,
889
+ destinationCalldata,
890
+ recipient,
891
+ originChainId,
892
+ walletClient,
893
+ publicClient,
894
+ account,
895
+ }: {
896
+ originTokenAddress: string
897
+ destinationTokenAmount: string
898
+ destinationTokenAddress: string
899
+ destinationCalldata?: string
900
+ recipient: string
901
+ originChainId: number
902
+ walletClient: WalletClient
903
+ publicClient: PublicClient
904
+ account: Account
905
+ }): Promise<PrepareSendReturn> {
906
+ const destinationTxs = []
907
+ if (destinationCalldata) {
908
+ destinationTxs.push({
909
+ to: recipient,
910
+ value:
911
+ destinationTokenAddress === zeroAddress ? destinationTokenAmount : "0",
912
+ data: destinationCalldata,
913
+ })
914
+ }
915
+
916
+ const quote = await getRelaySDKQuote({
917
+ wallet: walletClient,
918
+ chainId: originChainId,
919
+ amount: destinationTokenAmount,
920
+ currency: originTokenAddress,
921
+ toCurrency: destinationTokenAddress,
922
+ txs: destinationTxs,
923
+ })
924
+
925
+ console.log("[trails-sdk] relaysdk quote", quote)
926
+ let depositAmount = "0"
927
+
928
+ try {
929
+ depositAmount = quote.steps?.[0]?.items?.[0]?.data?.value
930
+ if (originTokenAddress !== zeroAddress) {
931
+ const decoded = decodeFunctionData({
932
+ abi: erc20Abi,
933
+ data: quote.steps?.[0]?.items?.[0]?.data?.data,
934
+ })
935
+ if (decoded.functionName === "approve") {
936
+ depositAmount = decoded.args[1].toString()
937
+ }
938
+ if (decoded.functionName === "transfer") {
939
+ depositAmount = decoded.args[1].toString()
940
+ }
941
+ }
942
+ } catch (error) {
943
+ console.error("[trails-sdk] Error decoding function data:", error)
944
+ }
945
+
946
+ const hasEnoughBalance = await checkAccountBalance({
947
+ account,
948
+ tokenAddress: originTokenAddress,
949
+ depositAmount,
950
+ publicClient,
951
+ })
952
+
953
+ if (!hasEnoughBalance) {
954
+ throw new Error("Account does not have enough balance for deposit")
955
+ }
956
+
957
+ return {
958
+ originSendAmount: depositAmount,
959
+ send: async (onOriginSend?: () => void): Promise<SendReturn> => {
960
+ await attemptSwitchChain({
961
+ walletClient,
962
+ desiredChainId: originChainId,
963
+ })
964
+
965
+ const result = await executeSimpleRelayTransaction(quote, walletClient)
966
+ console.log("[trails-sdk] relaysdk result", result)
967
+
968
+ const txHash = getTxHashFromRelayResult(result)
969
+
970
+ if (onOriginSend) {
971
+ onOriginSend()
972
+ }
973
+
974
+ const originUserTxReceipt = await publicClient.waitForTransactionReceipt({
975
+ hash: txHash as `0x${string}`,
976
+ })
977
+
978
+ return {
979
+ originUserTxReceipt: originUserTxReceipt,
980
+ originMetaTxnReceipt: null,
981
+ destinationMetaTxnReceipt: null,
982
+ }
983
+ },
984
+ }
985
+ }
986
+
987
+ async function attemptGaslessDeposit({
988
+ paymasterUrl,
989
+ depositTokenAddress,
990
+ depositTokenAmount,
991
+ depositRecipient,
992
+ onOriginSend,
993
+ walletClient,
994
+ chain,
995
+ account,
996
+ relayerConfig,
997
+ sequenceProjectAccessKey,
998
+ originRelayer,
999
+ }: {
1000
+ paymasterUrl?: string
1001
+ depositTokenAddress: string
1002
+ depositTokenAmount: string
1003
+ depositRecipient: string
1004
+ onOriginSend?: () => void
1005
+ walletClient: WalletClient
1006
+ chain: Chain
1007
+ account: Account
1008
+ relayerConfig: RelayerEnvConfig
1009
+ sequenceProjectAccessKey: string
1010
+ originRelayer: Relayer.Standard.Rpc.RpcRelayer
1011
+ }): Promise<TransactionReceipt | null> {
1012
+ let originUserTxReceipt: TransactionReceipt | null = null
1013
+ const originChainId = chain.id
1014
+ console.log("[trails-sdk] originChainId", originChainId)
1015
+
1016
+ const publicClient = createPublicClient({
1017
+ chain,
1018
+ transport: http(),
1019
+ })
1020
+
1021
+ const intentEntrypoint = intentEntrypoints[chain.id]
1022
+ console.log("[trails-sdk] intentEntrypoint", intentEntrypoint)
1023
+
1024
+ let calls: Array<{
1025
+ to: string
1026
+ data: string
1027
+ value: string
1028
+ }> = []
1029
+
1030
+ if (paymasterUrl) {
1031
+ console.log("[trails-sdk] doing gasless with paymaster")
1032
+ const delegatorSmartAccount = await getDelegatorSmartAccount({
1033
+ publicClient,
1034
+ })
1035
+
1036
+ if (intentEntrypoint) {
1037
+ calls = await getDepositToIntentCalls({
1038
+ publicClient,
1039
+ walletClient,
1040
+ account,
1041
+ intentEntrypoint,
1042
+ depositTokenAddress: depositTokenAddress as `0x${string}`,
1043
+ depositTokenAmount: BigInt(depositTokenAmount),
1044
+ depositRecipient: depositRecipient as `0x${string}`,
1045
+ chain,
1046
+ })
1047
+ } else {
1048
+ calls = await getPaymasterGaslessTransaction({
1049
+ walletClient,
1050
+ chain,
1051
+ tokenAddress: depositTokenAddress as `0x${string}`,
1052
+ amount: BigInt(depositTokenAmount),
1053
+ recipient: depositRecipient as `0x${string}`,
1054
+ delegatorSmartAccount,
1055
+ })
1056
+ }
1057
+
1058
+ console.log("[trails-sdk] calls", calls)
1059
+
1060
+ const txHash = await sendPaymasterGaslessTransaction({
1061
+ walletClient,
1062
+ publicClient,
1063
+ chain,
1064
+ paymasterUrl,
1065
+ delegatorSmartAccount,
1066
+ calls,
1067
+ })
1068
+
1069
+ if (onOriginSend) {
1070
+ onOriginSend()
1071
+ }
1072
+
1073
+ const receipt = await publicClient.waitForTransactionReceipt({
1074
+ hash: txHash as `0x${string}`,
1075
+ })
1076
+ console.log("[trails-sdk] receipt", receipt)
1077
+ originUserTxReceipt = receipt
1078
+ } else {
1079
+ console.log("[trails-sdk] doing gasless with sequence wallet")
1080
+ const delegatorPrivateKey = generatePrivateKey()
1081
+ const delegatorAccount = privateKeyToAccount(delegatorPrivateKey)
1082
+ const delegatorClient = createWalletClient({
1083
+ account: delegatorAccount,
1084
+ chain,
1085
+ transport: http(),
1086
+ })
1087
+
1088
+ console.log("[trails-sdk] attempting to switch chain")
1089
+ await attemptSwitchChain({
1090
+ walletClient,
1091
+ desiredChainId: originChainId,
1092
+ })
1093
+
1094
+ console.log("[trails-sdk] creating sequence wallet")
1095
+ const sequenceWalletAddress = await simpleCreateSequenceWallet(
1096
+ delegatorAccount as any,
1097
+ relayerConfig,
1098
+ sequenceProjectAccessKey,
1099
+ )
1100
+ console.log("[trails-sdk] sequenceWalletAddress", sequenceWalletAddress)
1101
+
1102
+ if (intentEntrypoint) {
1103
+ calls = await getDepositToIntentCalls({
1104
+ publicClient,
1105
+ walletClient,
1106
+ account,
1107
+ intentEntrypoint,
1108
+ depositTokenAddress: depositTokenAddress as `0x${string}`,
1109
+ depositTokenAmount: BigInt(depositTokenAmount),
1110
+ depositRecipient: depositRecipient as `0x${string}`,
1111
+ chain,
1112
+ })
1113
+ } else {
1114
+ const { signature, deadline } = await getPermitSignature({
1115
+ publicClient,
1116
+ walletClient,
1117
+ signer: account.address,
1118
+ spender: sequenceWalletAddress,
1119
+ tokenAddress: depositTokenAddress as `0x${string}`,
1120
+ amount: BigInt(depositTokenAmount),
1121
+ chain,
1122
+ })
1123
+
1124
+ calls = getPermitCalls(
1125
+ account.address,
1126
+ sequenceWalletAddress,
1127
+ BigInt(depositTokenAmount),
1128
+ deadline,
1129
+ signature,
1130
+ depositRecipient as `0x${string}`,
1131
+ depositTokenAddress as `0x${string}`,
1132
+ )
1133
+ }
1134
+
1135
+ console.log("[trails-sdk] calls", calls)
1136
+
1137
+ const feeOptions = await getFeeOptions(
1138
+ originRelayer,
1139
+ sequenceWalletAddress,
1140
+ originChainId,
1141
+ calls.map((call) => ({
1142
+ to: call.to,
1143
+ value: BigInt(call.value),
1144
+ data: call.data,
1145
+ gasLimit: BigInt(0),
1146
+ delegateCall: false,
1147
+ onlyFallback: false,
1148
+ behaviorOnError: "revert",
1149
+ })) as Payload.Call[],
1150
+ )
1151
+
1152
+ console.log("[trails-sdk] feeOptions", feeOptions)
1153
+
1154
+ const sequenceTxHash = await sequenceSendTransaction(
1155
+ sequenceWalletAddress,
1156
+ delegatorClient,
1157
+ publicClient,
1158
+ calls,
1159
+ chain,
1160
+ relayerConfig,
1161
+ sequenceProjectAccessKey,
1162
+ )
1163
+ console.log("[trails-sdk] sequenceTxHash", sequenceTxHash)
1164
+ if (onOriginSend) {
1165
+ onOriginSend()
1166
+ }
1167
+
1168
+ const receipt = await publicClient.waitForTransactionReceipt({
1169
+ hash: sequenceTxHash as `0x${string}`,
1170
+ })
1171
+ console.log("[trails-sdk] receipt", receipt)
1172
+ originUserTxReceipt = receipt
1173
+ }
1174
+
1175
+ return originUserTxReceipt
1176
+ }
1177
+
1178
+ export async function attemptNonGaslessUserDeposit({
1179
+ originTokenAddress,
1180
+ firstPreconditionMin,
1181
+ onOriginSend,
1182
+ publicClient,
1183
+ walletClient,
1184
+ originChainId,
1185
+ chain,
1186
+ account,
1187
+ fee,
1188
+ dryMode,
1189
+ sourceTokenPriceUsd,
1190
+ destinationTokenPriceUsd,
1191
+ destinationTokenAmount,
1192
+ destinationTokenDecimals,
1193
+ sourceTokenDecimals,
1194
+ intentAddress,
1195
+ }: {
1196
+ originTokenAddress: string
1197
+ firstPreconditionMin: string
1198
+ onOriginSend?: () => void
1199
+ publicClient: PublicClient
1200
+ walletClient: WalletClient
1201
+ originChainId: number
1202
+ chain: Chain
1203
+ account: Account
1204
+ fee: string
1205
+ dryMode: boolean
1206
+ sourceTokenPriceUsd?: number | null
1207
+ destinationTokenPriceUsd?: number | null
1208
+ destinationTokenAmount: string
1209
+ destinationTokenDecimals: number
1210
+ sourceTokenDecimals: number
1211
+ intentAddress: string
1212
+ }): Promise<TransactionReceipt | null> {
1213
+ let originUserTxReceipt: TransactionReceipt | null = null
1214
+
1215
+ const needsNativeFee = await getNeedsLifiNativeFee({
1216
+ originTokenAddress,
1217
+ destinationTokenAmount,
1218
+ destinationTokenDecimals,
1219
+ sourceTokenDecimals,
1220
+ sourceTokenPriceUsd: sourceTokenPriceUsd ?? null,
1221
+ destinationTokenPriceUsd: destinationTokenPriceUsd ?? null,
1222
+ depositAmount: firstPreconditionMin,
1223
+ })
1224
+ let nativeFee = parseUnits("0.00005", 18).toString()
1225
+ if (originChainId === 137) {
1226
+ nativeFee = parseUnits("1.5", 18).toString()
1227
+ }
1228
+
1229
+ console.log("[trails-sdk] needsNativeFee", needsNativeFee)
1230
+
1231
+ const originCallParams = {
1232
+ to: originTokenAddress === zeroAddress ? intentAddress : originTokenAddress,
1233
+ data:
1234
+ originTokenAddress === zeroAddress
1235
+ ? "0x"
1236
+ : getERC20TransferData({
1237
+ recipient: intentAddress,
1238
+ amount: BigInt(firstPreconditionMin) + BigInt(fee),
1239
+ }),
1240
+ value:
1241
+ originTokenAddress === zeroAddress
1242
+ ? BigInt(firstPreconditionMin) + BigInt(fee)
1243
+ : "0",
1244
+ chainId: originChainId,
1245
+ chain,
1246
+ }
1247
+
1248
+ await attemptSwitchChain({
1249
+ walletClient,
1250
+ desiredChainId: originChainId,
1251
+ })
1252
+
1253
+ let useSendCalls = false
1254
+ const moreThan1Tx = needsNativeFee
1255
+
1256
+ if (moreThan1Tx) {
1257
+ try {
1258
+ // the reason for the timeout is some users experience this call to hang indefinitely on metamask on all chains.
1259
+ // not sure why this is happening, but it happens.
1260
+ const capabilities = await requestWithTimeout<Record<string, any>>(
1261
+ walletClient,
1262
+ [
1263
+ {
1264
+ method: "wallet_getCapabilities",
1265
+ params: [account.address],
1266
+ },
1267
+ ],
1268
+ 10000,
1269
+ )
1270
+
1271
+ console.log("[trails-sdk] capabilities", capabilities)
1272
+
1273
+ // Check if the chain supports atomic transactions
1274
+ const chainHex = `0x${originChainId.toString(16)}` as const
1275
+ const chainCapabilities = capabilities[chainHex]
1276
+ useSendCalls = chainCapabilities?.atomic?.status === "supported"
1277
+ } catch (error) {
1278
+ console.error("[trails-sdk] Error getting capabilities", error)
1279
+ }
1280
+ }
1281
+
1282
+ if (dryMode) {
1283
+ console.log("[trails-sdk] dry mode, skipping send calls")
1284
+ }
1285
+
1286
+ if (useSendCalls) {
1287
+ console.log("[trails-sdk] using sendCalls")
1288
+ } else {
1289
+ console.log("[trails-sdk] using sendTransaction")
1290
+ }
1291
+
1292
+ if (useSendCalls) {
1293
+ if (!dryMode) {
1294
+ const calls: Array<{
1295
+ to: `0x${string}`
1296
+ data: `0x${string}`
1297
+ value?: `0x${string}`
1298
+ }> = []
1299
+ if (needsNativeFee) {
1300
+ calls.push({
1301
+ to: intentAddress as `0x${string}`,
1302
+ data: "0x00",
1303
+ value: `0x${BigInt(nativeFee).toString(16)}` as `0x${string}`,
1304
+ })
1305
+ }
1306
+
1307
+ // Add the origin call
1308
+ calls.push({
1309
+ to: originCallParams.to as `0x${string}`,
1310
+ data: originCallParams.data as `0x${string}`,
1311
+ value: originCallParams.value
1312
+ ? `0x${BigInt(originCallParams.value).toString(16)}`
1313
+ : "0x0",
1314
+ })
1315
+
1316
+ // Send the batched call via EIP-7702
1317
+ const result = (await walletClient.request({
1318
+ method: "wallet_sendCalls",
1319
+ params: [
1320
+ {
1321
+ version: "2.0.0",
1322
+ chainId: `0x${originChainId.toString(16)}`,
1323
+ atomicRequired: true,
1324
+ calls,
1325
+ },
1326
+ ],
1327
+ })) as { requestId: `0x${string}` }
1328
+
1329
+ console.log("[trails-sdk] sendCalls result", result)
1330
+ const requestId = result.requestId || (result as any).id
1331
+
1332
+ // Poll to check if the tx has been submitted
1333
+ let txHash: `0x${string}` | undefined
1334
+ while (!txHash) {
1335
+ const status = (await walletClient.request({
1336
+ method: "wallet_getCallsStatus",
1337
+ params: [requestId],
1338
+ })) as {
1339
+ status: "pending" | "submitted" | "failed"
1340
+ transactionHash?: `0x${string}`
1341
+ error?: string
1342
+ }
1343
+
1344
+ console.log("[trails-sdk] getCallsStatus result", status)
1345
+ const receipt = (status as any)?.receipts?.[0]
1346
+
1347
+ if ((status as any).status === 200 && receipt?.transactionHash) {
1348
+ txHash = receipt.transactionHash
1349
+ break
1350
+ } else if ((status as any).status === 500) {
1351
+ throw new Error(`Transaction failed: ${status.error}`)
1352
+ }
1353
+
1354
+ // wait a bit before polling again
1355
+ await new Promise((r) => setTimeout(r, 2000))
1356
+ }
1357
+
1358
+ if (onOriginSend) {
1359
+ onOriginSend()
1360
+ }
1361
+
1362
+ const receipt = await publicClient.waitForTransactionReceipt({
1363
+ hash: txHash as `0x${string}`,
1364
+ })
1365
+ console.log("[trails-sdk] receipt", receipt)
1366
+ originUserTxReceipt = receipt
1367
+ }
1368
+ } else {
1369
+ if (!dryMode) {
1370
+ if (needsNativeFee) {
1371
+ const tx0 = await sendOriginTransaction(account, walletClient, {
1372
+ to: intentAddress,
1373
+ data: "0x00",
1374
+ value: nativeFee,
1375
+ chainId: originChainId,
1376
+ chain,
1377
+ } as any) // TODO: Add proper type
1378
+ console.log("[trails-sdk] origin tx", tx0)
1379
+ // Wait for transaction receipt
1380
+ const feeReceipt = await publicClient.waitForTransactionReceipt({
1381
+ hash: tx0,
1382
+ })
1383
+ console.log("[trails-sdk] nativeFeeReceipt", feeReceipt)
1384
+ }
1385
+
1386
+ const tx = await sendOriginTransaction(
1387
+ account,
1388
+ walletClient,
1389
+ originCallParams as any,
1390
+ ) // TODO: Add proper type
1391
+ console.log("[trails-sdk] origin tx", tx)
1392
+
1393
+ if (onOriginSend) {
1394
+ onOriginSend()
1395
+ }
1396
+
1397
+ // Wait for transaction receipt
1398
+ const receipt = await publicClient.waitForTransactionReceipt({
1399
+ hash: tx,
1400
+ })
1401
+ console.log("[trails-sdk] receipt", receipt)
1402
+ originUserTxReceipt = receipt
1403
+ }
1404
+ }
1405
+
1406
+ return originUserTxReceipt
1407
+ }
1408
+
1409
+ async function attemptUserDepositTx({
1410
+ originTokenAddress,
1411
+ gasless,
1412
+ paymasterUrl,
1413
+ chain,
1414
+ account,
1415
+ relayerConfig,
1416
+ sequenceProjectAccessKey,
1417
+ originRelayer,
1418
+ firstPreconditionMin,
1419
+ intentAddress,
1420
+ onOriginSend,
1421
+ publicClient,
1422
+ walletClient,
1423
+ destinationTokenDecimals,
1424
+ sourceTokenDecimals,
1425
+ fee,
1426
+ dryMode,
1427
+ sourceTokenPriceUsd,
1428
+ destinationTokenPriceUsd,
1429
+ destinationTokenAmount,
1430
+ }: {
1431
+ originTokenAddress: string
1432
+ gasless: boolean
1433
+ paymasterUrl?: string
1434
+ chain: Chain
1435
+ account: Account
1436
+ relayerConfig: RelayerEnvConfig
1437
+ sequenceProjectAccessKey: string
1438
+ originRelayer: Relayer.Standard.Rpc.RpcRelayer
1439
+ firstPreconditionMin: string
1440
+ intentAddress: string
1441
+ onOriginSend?: () => void
1442
+ publicClient: PublicClient
1443
+ walletClient: WalletClient
1444
+ destinationTokenDecimals: number
1445
+ sourceTokenDecimals: number
1446
+ destinationTokenAmount: string
1447
+ dryMode: boolean
1448
+ sourceTokenPriceUsd: number | null
1449
+ destinationTokenPriceUsd: number | null
1450
+ fee: string
1451
+ }): Promise<TransactionReceipt | null> {
1452
+ let originUserTxReceipt: TransactionReceipt | null = null
1453
+ const originChainId = chain.id
1454
+ const doGasless = getDoGasless(originTokenAddress, gasless, paymasterUrl)
1455
+ console.log("[trails-sdk] doGasless", doGasless, paymasterUrl)
1456
+ if (doGasless) {
1457
+ try {
1458
+ originUserTxReceipt = await attemptGaslessDeposit({
1459
+ paymasterUrl,
1460
+ depositTokenAddress: originTokenAddress,
1461
+ depositTokenAmount: firstPreconditionMin,
1462
+ depositRecipient: intentAddress,
1463
+ onOriginSend,
1464
+ walletClient,
1465
+ chain,
1466
+ account,
1467
+ relayerConfig,
1468
+ sequenceProjectAccessKey,
1469
+ originRelayer,
1470
+ })
1471
+ } catch (error) {
1472
+ console.log("[trails-sdk] gassless attempt failed", error)
1473
+ }
1474
+ }
1475
+
1476
+ // If gasless attempt failed, try to send a regular transaction
1477
+ if (!originUserTxReceipt) {
1478
+ originUserTxReceipt = await attemptNonGaslessUserDeposit({
1479
+ originTokenAddress,
1480
+ firstPreconditionMin,
1481
+ intentAddress,
1482
+ onOriginSend,
1483
+ publicClient,
1484
+ walletClient,
1485
+ originChainId,
1486
+ chain,
1487
+ account,
1488
+ fee,
1489
+ dryMode,
1490
+ sourceTokenPriceUsd,
1491
+ destinationTokenPriceUsd,
1492
+ destinationTokenAmount,
1493
+ destinationTokenDecimals,
1494
+ sourceTokenDecimals,
1495
+ })
1496
+ }
1497
+
1498
+ return originUserTxReceipt
1499
+ }
1500
+
1501
+ export function getDoGasless(
1502
+ originTokenAddress: string,
1503
+ gasless: boolean,
1504
+ paymasterUrl?: string,
1505
+ ): boolean {
1506
+ return Boolean(
1507
+ originTokenAddress !== zeroAddress && (gasless || paymasterUrl),
1508
+ )
1509
+ }
1510
+
1511
+ function getTransactionStateFromReceipt(
1512
+ receipt: TransactionReceipt | MetaTxnReceipt,
1513
+ chainId: number,
1514
+ label: string,
1515
+ ): TransactionState {
1516
+ let txHash: string = ""
1517
+ let state: TransactionStateStatus = "pending"
1518
+ if ("transactionHash" in receipt) {
1519
+ txHash = receipt.transactionHash
1520
+ state = receipt.status === "success" ? "confirmed" : "failed"
1521
+ } else if ("txnHash" in receipt) {
1522
+ txHash = receipt.txnHash
1523
+ state = receipt.status === "SUCCEEDED" ? "confirmed" : "failed"
1524
+ }
1525
+
1526
+ return {
1527
+ transactionHash: txHash,
1528
+ explorerUrl: getExplorerUrl({ txHash, chainId }),
1529
+ chainId,
1530
+ state,
1531
+ label,
1532
+ }
1533
+ }
1534
+
1535
+ async function sendMetaTxAndWaitForReceipt({
1536
+ metaTx,
1537
+ relayer,
1538
+ precondition,
1539
+ }: {
1540
+ metaTx: MetaTxn
1541
+ relayer: Relayer.Standard.Rpc.RpcRelayer
1542
+ precondition: IntentPrecondition
1543
+ }): Promise<MetaTxnReceipt | null> {
1544
+ let originMetaTxnReceipt: MetaTxnReceipt | null = null
1545
+ console.log("[trails-sdk] metaTx", metaTx)
1546
+ const opHash = await relayerSendMetaTx(relayer, metaTx, [precondition])
1547
+
1548
+ console.log("[trails-sdk] opHash", opHash)
1549
+
1550
+ // eslint-disable-next-line no-constant-condition
1551
+ while (true) {
1552
+ console.log(
1553
+ "[trails-sdk] polling status",
1554
+ metaTx.id as `0x${string}`,
1555
+ BigInt(metaTx.chainId),
1556
+ )
1557
+ const receipt: any = await getMetaTxStatus(
1558
+ relayer,
1559
+ metaTx.id,
1560
+ Number(metaTx.chainId),
1561
+ )
1562
+ console.log("[trails-sdk] status", receipt)
1563
+ if (receipt?.transactionHash) {
1564
+ originMetaTxnReceipt = receipt.data?.receipt
1565
+ break
1566
+ }
1567
+ await new Promise((resolve) => setTimeout(resolve, 1000))
1568
+ }
1569
+
1570
+ return originMetaTxnReceipt
1571
+ }
1572
+
1573
+ /**
1574
+ * Check if the account has enough balance for the deposit amount
1575
+ */
1576
+ async function checkAccountBalance({
1577
+ account,
1578
+ tokenAddress,
1579
+ depositAmount,
1580
+ publicClient,
1581
+ }: {
1582
+ account: Account
1583
+ tokenAddress: string
1584
+ depositAmount: string
1585
+ publicClient: PublicClient
1586
+ }): Promise<boolean> {
1587
+ try {
1588
+ let balance: bigint
1589
+
1590
+ if (tokenAddress === zeroAddress) {
1591
+ // Native token balance
1592
+ balance = await publicClient.getBalance({ address: account.address })
1593
+ } else {
1594
+ // ERC20 token balance
1595
+ balance = await publicClient.readContract({
1596
+ address: tokenAddress as `0x${string}`,
1597
+ abi: erc20Abi,
1598
+ functionName: "balanceOf",
1599
+ args: [account.address],
1600
+ })
1601
+ }
1602
+
1603
+ const requiredAmount = BigInt(depositAmount)
1604
+
1605
+ console.log("[trails-sdk] balance", balance)
1606
+ console.log("[trails-sdk] requiredAmount", requiredAmount)
1607
+ return balance >= requiredAmount
1608
+ } catch (error) {
1609
+ console.error("[trails-sdk] Error checking account balance:", error)
1610
+ return false
1611
+ }
1612
+ }
1613
+
1614
+ // ETH fee required by some bridges for low token amounts
1615
+ // TODO: update backend API to return the native fee requirement, if any
1616
+ function getNeedsLifiNativeFee({
1617
+ originTokenAddress,
1618
+ destinationTokenAmount,
1619
+ destinationTokenDecimals,
1620
+ sourceTokenDecimals,
1621
+ sourceTokenPriceUsd,
1622
+ destinationTokenPriceUsd,
1623
+ depositAmount,
1624
+ }: {
1625
+ originTokenAddress: string
1626
+ destinationTokenAmount: string
1627
+ destinationTokenDecimals: number
1628
+ sourceTokenDecimals: number
1629
+ sourceTokenPriceUsd: number | null
1630
+ destinationTokenPriceUsd: number | null
1631
+ depositAmount: string
1632
+ }): boolean {
1633
+ let needsNativeFee = false
1634
+ if (
1635
+ originTokenAddress !== zeroAddress &&
1636
+ sourceTokenPriceUsd &&
1637
+ destinationTokenPriceUsd &&
1638
+ depositAmount &&
1639
+ destinationTokenDecimals !== undefined &&
1640
+ sourceTokenDecimals !== undefined
1641
+ ) {
1642
+ // Convert from wei to token units using formatUnits
1643
+ const destinationAmount = Number(
1644
+ formatUnits(BigInt(destinationTokenAmount), destinationTokenDecimals),
1645
+ )
1646
+ const depositAmountFormatted = Number(
1647
+ formatUnits(BigInt(depositAmount), destinationTokenDecimals),
1648
+ )
1649
+ console.log("[trails-sdk] destinationAmount", destinationAmount)
1650
+ console.log("[trails-sdk] depositAmountFormatted", depositAmountFormatted)
1651
+ const destinationAmountUsd = destinationAmount * destinationTokenPriceUsd
1652
+ const depositAmountUsd = depositAmountFormatted * sourceTokenPriceUsd
1653
+ const diff = depositAmountUsd - destinationAmountUsd
1654
+ console.log(
1655
+ "[trails-sdk] destinationAmountUsd",
1656
+ destinationAmountUsd,
1657
+ "[trails-sdk] depositAmountUsd",
1658
+ depositAmountUsd,
1659
+ "[trails-sdk] diff",
1660
+ diff,
1661
+ )
1662
+ if (diff >= 0 && diff <= 0.02) {
1663
+ needsNativeFee = true
1664
+ }
1665
+ }
1666
+
1667
+ return needsNativeFee
1668
+ }
1669
+
1670
+ export type UseQuoteProps = {
1671
+ walletClient?: WalletClient
1672
+ fromTokenAddress?: string
1673
+ fromChainId?: number
1674
+ toTokenAddress?: string
1675
+ toChainId?: number
1676
+ toAmount?: string | bigint
1677
+ toRecipient?: string
1678
+ }
1679
+
1680
+ export function useQuote({
1681
+ walletClient,
1682
+ fromTokenAddress,
1683
+ fromChainId,
1684
+ toTokenAddress,
1685
+ toChainId,
1686
+ toAmount,
1687
+ toRecipient,
1688
+ }: UseQuoteProps = {}): {
1689
+ quote: {
1690
+ fromAmount: string
1691
+ } | null
1692
+ swap:
1693
+ | (() => Promise<{
1694
+ originTransaction: {
1695
+ transactionHash: string | undefined
1696
+ receipt: TransactionReceipt | MetaTxnReceipt | null
1697
+ }
1698
+ destinationTransaction: {
1699
+ transactionHash: string | undefined
1700
+ receipt: MetaTxnReceipt | null
1701
+ }
1702
+ } | null>)
1703
+ | null
1704
+ isLoadingQuote: boolean
1705
+ quoteError: unknown
1706
+ } {
1707
+ const apiClient = useAPIClient()
1708
+ const { getRelayer } = useRelayers({
1709
+ env: "dev",
1710
+ useV3Relayers: DEFAULT_USE_V3_RELAYERS,
1711
+ })
1712
+ const indexerGatewayClient = useIndexerGatewayClient()
1713
+
1714
+ const { supportedTokens } = useSupportedTokens()
1715
+
1716
+ const { tokenBalance: originTokenBalance } = useAccountTokenBalance({
1717
+ account: walletClient?.account?.address,
1718
+ token: fromTokenAddress,
1719
+ chainId: fromChainId,
1720
+ indexerGatewayClient,
1721
+ apiClient,
1722
+ })
1723
+
1724
+ const destToken =
1725
+ toTokenAddress && toChainId
1726
+ ? {
1727
+ tokenId: toTokenAddress,
1728
+ chainId: toChainId,
1729
+ contractAddress: toTokenAddress,
1730
+ }
1731
+ : null
1732
+
1733
+ const { tokenPrice: destinationTokenPrice } = useTokenPrice(
1734
+ destToken,
1735
+ apiClient,
1736
+ )
1737
+
1738
+ const { data, isLoading, error } = useQuery({
1739
+ queryKey: [
1740
+ "quote",
1741
+ fromTokenAddress,
1742
+ fromChainId,
1743
+ toTokenAddress,
1744
+ toChainId,
1745
+ toAmount?.toString(),
1746
+ toRecipient,
1747
+ ],
1748
+ queryFn: async () => {
1749
+ if (
1750
+ !walletClient ||
1751
+ !apiClient ||
1752
+ !fromTokenAddress ||
1753
+ !toTokenAddress ||
1754
+ !toAmount ||
1755
+ !toRecipient ||
1756
+ !fromChainId ||
1757
+ !toChainId
1758
+ ) {
1759
+ return null
1760
+ }
1761
+
1762
+ const originTokenAmount = originTokenBalance?.balance ?? "0"
1763
+ const sequenceProjectAccessKey = ""
1764
+ const destinationRelayer = getRelayer(toChainId)
1765
+ const originRelayer = getRelayer(fromChainId)
1766
+ console.log("originRelayer", originRelayer)
1767
+ console.log("destinationRelayer", destinationRelayer)
1768
+ const sourceTokenPriceUsd = originTokenBalance?.price?.value ?? 0
1769
+ const destinationTokenPriceUsd = destinationTokenPrice?.price?.value ?? 0
1770
+
1771
+ const originToken = supportedTokens?.find(
1772
+ (token) =>
1773
+ token.contractAddress === fromTokenAddress &&
1774
+ token.chainId === fromChainId,
1775
+ )
1776
+ const destinationToken = supportedTokens?.find(
1777
+ (token) =>
1778
+ token.contractAddress === toTokenAddress &&
1779
+ token.chainId === toChainId,
1780
+ )
1781
+
1782
+ const sourceTokenDecimals = originToken?.decimals ?? 18
1783
+ const destinationTokenDecimals = destinationToken?.decimals ?? 18
1784
+ const destinationTokenSymbol = destinationToken?.symbol ?? ""
1785
+
1786
+ const options = {
1787
+ account: walletClient.account!,
1788
+ originTokenAddress: fromTokenAddress,
1789
+ originChainId: fromChainId,
1790
+ originTokenAmount: originTokenAmount,
1791
+ destinationChainId: toChainId,
1792
+ recipient: toRecipient,
1793
+ destinationTokenAddress: toTokenAddress,
1794
+ destinationTokenAmount: toAmount.toString(),
1795
+ destinationTokenSymbol: destinationTokenSymbol,
1796
+ sequenceProjectAccessKey,
1797
+ client: walletClient,
1798
+ apiClient,
1799
+ originRelayer,
1800
+ destinationRelayer,
1801
+ sourceTokenPriceUsd,
1802
+ destinationTokenPriceUsd,
1803
+ sourceTokenDecimals,
1804
+ destinationTokenDecimals,
1805
+ fee: "0",
1806
+ dryMode: false,
1807
+ onTransactionStateChange: () => {},
1808
+ relayerConfig: {},
1809
+ }
1810
+
1811
+ console.log("[trails-sdk] options", options)
1812
+
1813
+ const { intentAddress, originSendAmount, send } =
1814
+ await prepareSend(options)
1815
+ console.log("[trails-sdk] Intent address:", intentAddress?.toString())
1816
+
1817
+ const quote = {
1818
+ fromAmount: originSendAmount,
1819
+ }
1820
+
1821
+ const swap = async () => {
1822
+ const { originUserTxReceipt, destinationMetaTxnReceipt } = await send()
1823
+
1824
+ return {
1825
+ originTransaction: {
1826
+ transactionHash: originUserTxReceipt?.transactionHash,
1827
+ receipt: originUserTxReceipt,
1828
+ },
1829
+ destinationTransaction: {
1830
+ transactionHash: destinationMetaTxnReceipt?.txnHash,
1831
+ receipt: destinationMetaTxnReceipt,
1832
+ },
1833
+ }
1834
+ }
1835
+
1836
+ return {
1837
+ quote,
1838
+ swap,
1839
+ }
1840
+ },
1841
+ })
1842
+
1843
+ return {
1844
+ quote: data?.quote || null,
1845
+ swap: data?.swap || null,
1846
+ isLoadingQuote: isLoading,
1847
+ quoteError: error,
1848
+ }
1849
+ }