0xtrails 0.2.5 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. package/dist/aave.d.ts +2 -0
  2. package/dist/aave.d.ts.map +1 -1
  3. package/dist/abortController.d.ts +8 -0
  4. package/dist/abortController.d.ts.map +1 -0
  5. package/dist/{ccip-CXlshvBY.js → ccip-BMB3uDZt.js} +1 -1
  6. package/dist/config.d.ts +0 -5
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/constants.d.ts +4 -4
  9. package/dist/constants.d.ts.map +1 -1
  10. package/dist/error.d.ts +4 -1
  11. package/dist/error.d.ts.map +1 -1
  12. package/dist/fees.d.ts +19 -0
  13. package/dist/fees.d.ts.map +1 -0
  14. package/dist/{index-_QuyGrjU.js → index-QXPUrZVv.js} +48719 -50852
  15. package/dist/index.d.ts +9 -8
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +811 -784
  18. package/dist/intentReceiptMonitor.d.ts +24 -0
  19. package/dist/intentReceiptMonitor.d.ts.map +1 -0
  20. package/dist/intentReceiptPoller.d.ts +69 -0
  21. package/dist/intentReceiptPoller.d.ts.map +1 -0
  22. package/dist/intents.d.ts +15 -11
  23. package/dist/intents.d.ts.map +1 -1
  24. package/dist/morpho.d.ts +6 -5
  25. package/dist/morpho.d.ts.map +1 -1
  26. package/dist/mutations.d.ts +16 -0
  27. package/dist/mutations.d.ts.map +1 -0
  28. package/dist/preconditions.d.ts +5 -4
  29. package/dist/preconditions.d.ts.map +1 -1
  30. package/dist/prepareSend.d.ts +7 -258
  31. package/dist/prepareSend.d.ts.map +1 -1
  32. package/dist/prices.d.ts +9 -6
  33. package/dist/prices.d.ts.map +1 -1
  34. package/dist/sequenceWallet.d.ts +3 -16
  35. package/dist/sequenceWallet.d.ts.map +1 -1
  36. package/dist/tokenBalances.d.ts +17 -13
  37. package/dist/tokenBalances.d.ts.map +1 -1
  38. package/dist/trails.d.ts +24 -40
  39. package/dist/trails.d.ts.map +1 -1
  40. package/dist/transactionIntent/constants.d.ts +7 -0
  41. package/dist/transactionIntent/constants.d.ts.map +1 -0
  42. package/dist/transactionIntent/deposits/depositOrchestrator.d.ts +44 -0
  43. package/dist/transactionIntent/deposits/depositOrchestrator.d.ts.map +1 -0
  44. package/dist/transactionIntent/deposits/gaslessDeposit.d.ts +30 -0
  45. package/dist/transactionIntent/deposits/gaslessDeposit.d.ts.map +1 -0
  46. package/dist/transactionIntent/deposits/index.d.ts +4 -0
  47. package/dist/transactionIntent/deposits/index.d.ts.map +1 -0
  48. package/dist/transactionIntent/deposits/standardDeposit.d.ts +30 -0
  49. package/dist/transactionIntent/deposits/standardDeposit.d.ts.map +1 -0
  50. package/dist/transactionIntent/execution/index.d.ts +2 -0
  51. package/dist/transactionIntent/execution/index.d.ts.map +1 -0
  52. package/dist/transactionIntent/execution/transactionState.d.ts +5 -0
  53. package/dist/transactionIntent/execution/transactionState.d.ts.map +1 -0
  54. package/dist/transactionIntent/handlers/crossChain.d.ts +82 -0
  55. package/dist/transactionIntent/handlers/crossChain.d.ts.map +1 -0
  56. package/dist/transactionIntent/handlers/index.d.ts +4 -0
  57. package/dist/transactionIntent/handlers/index.d.ts.map +1 -0
  58. package/dist/transactionIntent/handlers/sameChainDifferentToken.d.ts +62 -0
  59. package/dist/transactionIntent/handlers/sameChainDifferentToken.d.ts.map +1 -0
  60. package/dist/transactionIntent/handlers/sameChainSameToken.d.ts +72 -0
  61. package/dist/transactionIntent/handlers/sameChainSameToken.d.ts.map +1 -0
  62. package/dist/transactionIntent/index.d.ts +9 -0
  63. package/dist/transactionIntent/index.d.ts.map +1 -0
  64. package/dist/transactionIntent/quote/feeExtractors.d.ts +17 -0
  65. package/dist/transactionIntent/quote/feeExtractors.d.ts.map +1 -0
  66. package/dist/transactionIntent/quote/index.d.ts +4 -0
  67. package/dist/transactionIntent/quote/index.d.ts.map +1 -0
  68. package/dist/transactionIntent/quote/normalizeQuote.d.ts +34 -0
  69. package/dist/transactionIntent/quote/normalizeQuote.d.ts.map +1 -0
  70. package/dist/transactionIntent/quote/quoteHelpers.d.ts +5 -0
  71. package/dist/transactionIntent/quote/quoteHelpers.d.ts.map +1 -0
  72. package/dist/transactionIntent/types.d.ts +131 -0
  73. package/dist/transactionIntent/types.d.ts.map +1 -0
  74. package/dist/transactionIntent/utils/balanceChecker.d.ts +18 -0
  75. package/dist/transactionIntent/utils/balanceChecker.d.ts.map +1 -0
  76. package/dist/transactionIntent/utils/index.d.ts +4 -0
  77. package/dist/transactionIntent/utils/index.d.ts.map +1 -0
  78. package/dist/transactionIntent/utils/lifiHelpers.d.ts +10 -0
  79. package/dist/transactionIntent/utils/lifiHelpers.d.ts.map +1 -0
  80. package/dist/transactionIntent/utils/testnetHelpers.d.ts +3 -0
  81. package/dist/transactionIntent/utils/testnetHelpers.d.ts.map +1 -0
  82. package/dist/transactionIntent/validators.d.ts +6 -0
  83. package/dist/transactionIntent/validators.d.ts.map +1 -0
  84. package/dist/transactions.d.ts +6 -3
  85. package/dist/transactions.d.ts.map +1 -1
  86. package/dist/widget/components/AccountIntentTransactionHistoryButton.d.ts +4 -0
  87. package/dist/widget/components/AccountIntentTransactionHistoryButton.d.ts.map +1 -0
  88. package/dist/widget/components/AccountSettings.d.ts.map +1 -1
  89. package/dist/widget/components/ChainFilterDropdown.d.ts.map +1 -1
  90. package/dist/widget/components/ClassicSwap.d.ts +2 -3
  91. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  92. package/dist/widget/components/ConfigDisplay.d.ts.map +1 -1
  93. package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
  94. package/dist/widget/components/ConnectedWallets.d.ts.map +1 -1
  95. package/dist/widget/components/DynamicInputStyles.d.ts +18 -0
  96. package/dist/widget/components/DynamicInputStyles.d.ts.map +1 -0
  97. package/dist/widget/components/DynamicSizeInputField.d.ts +13 -0
  98. package/dist/widget/components/DynamicSizeInputField.d.ts.map +1 -0
  99. package/dist/widget/components/Earn.d.ts +2 -3
  100. package/dist/widget/components/Earn.d.ts.map +1 -1
  101. package/dist/widget/components/ErrorAnimationIcon.d.ts +2 -0
  102. package/dist/widget/components/ErrorAnimationIcon.d.ts.map +1 -0
  103. package/dist/widget/components/FeeBreakdown.d.ts +9 -0
  104. package/dist/widget/components/FeeBreakdown.d.ts.map +1 -0
  105. package/dist/widget/components/FeeOptions.d.ts +5 -13
  106. package/dist/widget/components/FeeOptions.d.ts.map +1 -1
  107. package/dist/widget/components/Fund.d.ts +2 -3
  108. package/dist/widget/components/Fund.d.ts.map +1 -1
  109. package/dist/widget/components/FundMethods.d.ts.map +1 -1
  110. package/dist/widget/components/FundSwap.d.ts +2 -3
  111. package/dist/widget/components/FundSwap.d.ts.map +1 -1
  112. package/dist/widget/components/FundingMethodSelectorButton.d.ts.map +1 -1
  113. package/dist/widget/components/Identicon.d.ts.map +1 -1
  114. package/dist/widget/components/MeshConnectExchanges.d.ts +0 -3
  115. package/dist/widget/components/MeshConnectExchanges.d.ts.map +1 -1
  116. package/dist/widget/components/Modal.d.ts.map +1 -1
  117. package/dist/widget/components/Pay.d.ts +2 -3
  118. package/dist/widget/components/Pay.d.ts.map +1 -1
  119. package/dist/widget/components/PoolDeposit.d.ts +3 -3
  120. package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
  121. package/dist/widget/components/PoolWithdraw.d.ts +3 -20
  122. package/dist/widget/components/PoolWithdraw.d.ts.map +1 -1
  123. package/dist/widget/components/QuoteDetails.d.ts +2 -0
  124. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  125. package/dist/widget/components/Receipt.d.ts.map +1 -1
  126. package/dist/widget/components/RecipientSelectorButton.d.ts.map +1 -1
  127. package/dist/widget/components/ScreenHeader.d.ts.map +1 -1
  128. package/dist/widget/components/Swap.d.ts +2 -3
  129. package/dist/widget/components/Swap.d.ts.map +1 -1
  130. package/dist/widget/components/ThemeProvider.d.ts.map +1 -1
  131. package/dist/widget/components/TokenDisplayNonSelectable.d.ts +11 -0
  132. package/dist/widget/components/TokenDisplayNonSelectable.d.ts.map +1 -0
  133. package/dist/widget/components/TokenSelector.d.ts.map +1 -1
  134. package/dist/widget/components/TokenSelectorButton.d.ts.map +1 -1
  135. package/dist/widget/components/Tooltip.d.ts +9 -0
  136. package/dist/widget/components/Tooltip.d.ts.map +1 -0
  137. package/dist/widget/components/TransferPendingVertical.d.ts.map +1 -1
  138. package/dist/widget/components/WaasFeeOptions.d.ts +1 -0
  139. package/dist/widget/components/WaasFeeOptions.d.ts.map +1 -1
  140. package/dist/widget/components/WalletConfirmation.d.ts.map +1 -1
  141. package/dist/widget/components/WalletConnect.d.ts.map +1 -1
  142. package/dist/widget/css/compiled.css +2 -2
  143. package/dist/widget/hooks/useCheckout.d.ts +17 -4
  144. package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
  145. package/dist/widget/hooks/useDefaultTokenSelection.d.ts.map +1 -1
  146. package/dist/widget/hooks/useQuote.d.ts +82 -0
  147. package/dist/widget/hooks/useQuote.d.ts.map +1 -0
  148. package/dist/widget/hooks/useSelectedFeeToken.d.ts +1 -0
  149. package/dist/widget/hooks/useSelectedFeeToken.d.ts.map +1 -1
  150. package/dist/widget/hooks/useSendForm.d.ts +5 -6
  151. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  152. package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
  153. package/dist/widget/hooks/useWalletConnectionContext.d.ts +25 -0
  154. package/dist/widget/hooks/useWalletConnectionContext.d.ts.map +1 -0
  155. package/dist/widget/index.js +2 -2
  156. package/dist/widget/widget.d.ts +17 -7
  157. package/dist/widget/widget.d.ts.map +1 -1
  158. package/package.json +19 -21
  159. package/src/aave.ts +54 -1
  160. package/src/abortController.ts +35 -0
  161. package/src/config.ts +57 -58
  162. package/src/constants.ts +11 -9
  163. package/src/error.ts +21 -3
  164. package/src/fees.ts +210 -0
  165. package/src/index.ts +35 -13
  166. package/src/intentReceiptMonitor.ts +102 -0
  167. package/src/intentReceiptPoller.ts +299 -0
  168. package/src/intents.ts +205 -171
  169. package/src/morpho.ts +58 -9
  170. package/src/mutations.ts +129 -0
  171. package/src/preconditions.ts +16 -21
  172. package/src/prepareSend.ts +92 -4699
  173. package/src/prices.ts +26 -22
  174. package/src/relaySdk.ts +2 -2
  175. package/src/sequenceWallet.ts +6 -73
  176. package/src/tokenBalances.ts +175 -69
  177. package/src/trails.ts +230 -722
  178. package/src/transactionIntent/constants.ts +11 -0
  179. package/src/transactionIntent/deposits/depositOrchestrator.ts +210 -0
  180. package/src/transactionIntent/deposits/gaslessDeposit.ts +588 -0
  181. package/src/transactionIntent/deposits/index.ts +3 -0
  182. package/src/transactionIntent/deposits/standardDeposit.ts +379 -0
  183. package/src/transactionIntent/execution/index.ts +1 -0
  184. package/src/transactionIntent/execution/transactionState.ts +35 -0
  185. package/src/transactionIntent/handlers/crossChain.ts +1707 -0
  186. package/src/transactionIntent/handlers/index.ts +3 -0
  187. package/src/transactionIntent/handlers/sameChainDifferentToken.ts +323 -0
  188. package/src/transactionIntent/handlers/sameChainSameToken.ts +712 -0
  189. package/src/transactionIntent/index.ts +9 -0
  190. package/src/transactionIntent/quote/feeExtractors.ts +81 -0
  191. package/src/transactionIntent/quote/index.ts +3 -0
  192. package/src/transactionIntent/quote/normalizeQuote.ts +367 -0
  193. package/src/transactionIntent/quote/quoteHelpers.ts +53 -0
  194. package/src/transactionIntent/types.ts +157 -0
  195. package/src/transactionIntent/utils/balanceChecker.ts +96 -0
  196. package/src/transactionIntent/utils/index.ts +3 -0
  197. package/src/transactionIntent/utils/lifiHelpers.ts +68 -0
  198. package/src/transactionIntent/utils/testnetHelpers.ts +10 -0
  199. package/src/transactionIntent/validators.ts +57 -0
  200. package/src/transactions.ts +98 -71
  201. package/src/widget/compiled.css +2 -2
  202. package/src/widget/components/AccountIntentTransactionHistory.tsx +36 -36
  203. package/src/widget/components/AccountIntentTransactionHistoryButton.tsx +22 -0
  204. package/src/widget/components/AccountSettings.tsx +70 -41
  205. package/src/widget/components/ChainFilterDropdown.tsx +24 -3
  206. package/src/widget/components/ClassicSwap.tsx +44 -107
  207. package/src/widget/components/ConfigDisplay.tsx +0 -11
  208. package/src/widget/components/ConnectWallet.tsx +4 -1
  209. package/src/widget/components/ConnectedWallets.tsx +51 -25
  210. package/src/widget/components/DynamicInputStyles.tsx +76 -0
  211. package/src/widget/components/DynamicSizeInputField.tsx +109 -0
  212. package/src/widget/components/Earn.tsx +34 -45
  213. package/src/widget/components/ErrorAnimationIcon.tsx +130 -0
  214. package/src/widget/components/FeeBreakdown.tsx +155 -0
  215. package/src/widget/components/FeeOption.tsx +2 -2
  216. package/src/widget/components/FeeOptions.tsx +151 -112
  217. package/src/widget/components/Fund.tsx +10 -29
  218. package/src/widget/components/FundMethods.tsx +4 -3
  219. package/src/widget/components/FundSwap.tsx +2 -3
  220. package/src/widget/components/FundingMethodSelectorButton.tsx +24 -14
  221. package/src/widget/components/Identicon.tsx +164 -95
  222. package/src/widget/components/MeshConnectExchanges.tsx +2 -15
  223. package/src/widget/components/Modal.tsx +0 -12
  224. package/src/widget/components/Pay.tsx +72 -75
  225. package/src/widget/components/PoolDeposit.tsx +221 -242
  226. package/src/widget/components/PoolWithdraw.tsx +347 -469
  227. package/src/widget/components/PriceImpactWarning.tsx +1 -1
  228. package/src/widget/components/QuoteDetails.tsx +906 -484
  229. package/src/widget/components/Receipt.tsx +16 -2
  230. package/src/widget/components/RecipientSelectorButton.tsx +7 -5
  231. package/src/widget/components/Recipients.tsx +1 -1
  232. package/src/widget/components/ScreenHeader.tsx +60 -36
  233. package/src/widget/components/Swap.tsx +2 -3
  234. package/src/widget/components/ThemeProvider.tsx +2 -1
  235. package/src/widget/components/TokenDisplayNonSelectable.tsx +40 -0
  236. package/src/widget/components/TokenImage.tsx +1 -1
  237. package/src/widget/components/TokenSelector.tsx +62 -53
  238. package/src/widget/components/TokenSelectorButton.tsx +38 -15
  239. package/src/widget/components/Tooltip.tsx +51 -0
  240. package/src/widget/components/TransferPendingVertical.tsx +12 -8
  241. package/src/widget/components/WaasFeeOptions.tsx +139 -4
  242. package/src/widget/components/WalletConfirmation.tsx +23 -13
  243. package/src/widget/components/WalletConnect.tsx +93 -29
  244. package/src/widget/hooks/useAmountUsd.ts +9 -9
  245. package/src/widget/hooks/useCheckout.ts +97 -9
  246. package/src/widget/hooks/useDefaultTokenSelection.tsx +27 -21
  247. package/src/widget/hooks/useQuote.ts +466 -0
  248. package/src/widget/hooks/useSelectedFeeToken.tsx +32 -37
  249. package/src/widget/hooks/useSendForm.ts +45 -51
  250. package/src/widget/hooks/useTokenList.ts +34 -26
  251. package/src/widget/hooks/useWalletConnectionContext.tsx +128 -0
  252. package/src/widget/widget.tsx +365 -390
  253. package/dist/apiClient.d.ts +0 -9
  254. package/dist/apiClient.d.ts.map +0 -1
  255. package/dist/intentEntrypoint.d.ts +0 -114
  256. package/dist/intentEntrypoint.d.ts.map +0 -1
  257. package/dist/metaTxnMonitor.d.ts +0 -15
  258. package/dist/metaTxnMonitor.d.ts.map +0 -1
  259. package/dist/metaTxns.d.ts +0 -11
  260. package/dist/metaTxns.d.ts.map +0 -1
  261. package/dist/relayer.d.ts +0 -43
  262. package/dist/relayer.d.ts.map +0 -1
  263. package/src/apiClient.ts +0 -35
  264. package/src/intentEntrypoint.ts +0 -203
  265. package/src/metaTxnMonitor.ts +0 -171
  266. package/src/metaTxns.ts +0 -45
  267. package/src/relayer.ts +0 -289
@@ -0,0 +1,712 @@
1
+ import type { Account, Chain, WalletClient, TransactionReceipt } from "viem"
2
+ import { createPublicClient, http, formatUnits } from "viem"
3
+ import type { TrailsAPIClient } from "@0xsequence/trails-api"
4
+ import type { MetaTxnReceipt, PrepareSendReturn, SendReturn } from "../types.js"
5
+ import type { TransactionState } from "../../transactions.js"
6
+ import type { CheckoutOnHandlers } from "../../widget/hooks/useCheckout.js"
7
+ import type {
8
+ Intent,
9
+ CommitIntentResponse,
10
+ DepositSignature,
11
+ ExecuteIntentResponse,
12
+ } from "@0xsequence/trails-api"
13
+ import type { SelectedFeeToken } from "../types.js"
14
+ import { logger } from "../../logger.js"
15
+ import {
16
+ isTestnetDebugMode,
17
+ getTestnetOriginTokenAddress,
18
+ } from "../utils/testnetHelpers.js"
19
+ import { checkAccountBalance } from "../utils/balanceChecker.js"
20
+ import { getTestnetChainInfo } from "../../chains.js"
21
+ import { getIsCustomCalldata } from "../../contractUtils.js"
22
+ import {
23
+ buildSameChainSameTokenTransactionParams,
24
+ getIntent,
25
+ commitIntent,
26
+ } from "../../intents.js"
27
+ import { estimateGasLimit } from "../../estimate.js"
28
+ import { getNormalizedQuoteObject } from "../quote/normalizeQuote.js"
29
+ import { attemptSwitchChain } from "../../chainSwitch.js"
30
+ import { attemptUserDepositTx } from "../deposits/depositOrchestrator.js"
31
+ import { getIntentArgs } from "../quote/quoteHelpers.js"
32
+ import { TradeType } from "../types.js"
33
+ import { trackPaymentCompleted, trackPaymentError } from "../../analytics.js"
34
+ import { updatePersistentToast } from "../../toast.js"
35
+ import { getChainInfo } from "../../chains.js"
36
+ import { getTransactionStateFromReceipt } from "../execution/transactionState.js"
37
+ import { calcAmountUsdPrice } from "../../prices.js"
38
+ import { pollIntentReceipt } from "../../intentReceiptPoller.js"
39
+
40
+ /**
41
+ * @description
42
+ * This handler manages simple same-chain token transfers (e.g., using USDC to transact with USDC on the same chain).
43
+ * It supports both standard transfers and custom calldata execution.
44
+ *
45
+ * Key characteristics:
46
+ * - Uses the Trails API with getIntent and commitIntent for setup
47
+ * - Single transaction leg (user transfer)
48
+ * - No cross-chain bridging or token swapping required
49
+ * - Direct transaction submission via attemptUserDepositTx (simpler than cross-chain polling)
50
+ * - Supports both gasless and standard fee payment modes
51
+ *
52
+ * Transaction Flow:
53
+ * 1. Get intent via getIntent (Intent object with validation)
54
+ * 2. Commit intent via commitIntent (Record created in backend)
55
+ * 3. User submits transaction via attemptUserDepositTx (handles both gasless and standard)
56
+ * 4. Wait for transaction receipt (direct receipt polling, not via waitIntentReceipt API)
57
+ * 5. Update transaction state and invoke callbacks
58
+ *
59
+ * Error Handling:
60
+ * - Balance validation before transaction submission
61
+ * - Gas estimation for accurate fee calculation
62
+ * - Proper error propagation with checkout error callbacks
63
+ * - Fallback logging for debugging
64
+ *
65
+ * State Management:
66
+ * - transactionStates[0]: User transfer transaction
67
+ * - Updates propagated via onTransactionStateChange callback
68
+ * - Analytics tracking for payment completion or errors
69
+ * - Support for gasless fee options via gasFeeOptions
70
+ */
71
+ export async function handleSameChainSameToken({
72
+ mainSignerAddress,
73
+ originTokenAddress,
74
+ originTokenDecimals,
75
+ originTokenSymbol,
76
+ destinationTokenSymbol,
77
+ swapAmount,
78
+ destinationCalldata,
79
+ recipient,
80
+ walletClient,
81
+ onTransactionStateChange,
82
+ dryMode,
83
+ account,
84
+ chain,
85
+ transactionStates,
86
+ sourceTokenPriceUsd,
87
+ destinationTokenPriceUsd,
88
+ originNativeTokenPriceUsd,
89
+ slippageTolerance,
90
+ checkoutOnHandlers,
91
+ mode,
92
+ fundMethod,
93
+ paymasterUrl,
94
+ selectedFeeToken,
95
+ walletId,
96
+ trailsClient,
97
+ abortSignal,
98
+ commitIntentFn,
99
+ executeIntentFn,
100
+ }: {
101
+ mainSignerAddress: string
102
+ originTokenAddress: string
103
+ originTokenDecimals: number
104
+ originTokenSymbol: string
105
+ destinationTokenSymbol: string
106
+ swapAmount: string
107
+ destinationCalldata?: string
108
+ recipient: string
109
+ originChainId: number
110
+ walletClient: WalletClient
111
+ onTransactionStateChange: (transactionStates: TransactionState[]) => void
112
+ dryMode: boolean
113
+ account: Account
114
+ chain: Chain
115
+ transactionStates: TransactionState[]
116
+ sourceTokenPriceUsd?: number | null
117
+ destinationTokenPriceUsd?: number | null
118
+ originNativeTokenPriceUsd?: number | null
119
+ slippageTolerance: string
120
+ checkoutOnHandlers?: CheckoutOnHandlers
121
+ mode?: "pay" | "fund" | "earn" | "swap" | "receive"
122
+ fundMethod?: string
123
+ paymasterUrl?: string
124
+ selectedFeeToken?: SelectedFeeToken
125
+ walletId?: string
126
+ trailsClient: TrailsAPIClient
127
+ abortSignal?: AbortSignal
128
+ commitIntentFn?: (intent: Intent) => Promise<CommitIntentResponse>
129
+ executeIntentFn?: (params: {
130
+ intentId: string
131
+ depositTransactionHash?: string
132
+ depositSignature?: DepositSignature
133
+ }) => Promise<ExecuteIntentResponse>
134
+ }): Promise<PrepareSendReturn> {
135
+ logger.console.log("[trails-sdk] isToSameToken && isToSameChain")
136
+ const testnet = isTestnetDebugMode()
137
+ const effectiveOriginChain = testnet ? getTestnetChainInfo(chain)! : chain
138
+ const effectiveOriginChainId = effectiveOriginChain.id
139
+ const effectiveOriginTokenAddress = testnet
140
+ ? getTestnetOriginTokenAddress(effectiveOriginChainId)
141
+ : originTokenAddress
142
+ const effectivePublicClient = createPublicClient({
143
+ chain: effectiveOriginChain,
144
+ transport: http(),
145
+ })
146
+
147
+ let noSufficientBalance = false
148
+
149
+ const { hasEnoughBalance } = await checkAccountBalance({
150
+ account,
151
+ tokenAddress: originTokenAddress,
152
+ depositAmount: swapAmount,
153
+ publicClient: effectivePublicClient,
154
+ })
155
+
156
+ if (!hasEnoughBalance) {
157
+ noSufficientBalance = true
158
+ }
159
+
160
+ const hasCustomCalldata = getIsCustomCalldata(destinationCalldata)
161
+
162
+ // For same-chain transactions, use Intent flow to support gasless deposits
163
+ const destinationSalt = Date.now().toString()
164
+ const intentArgs = getIntentArgs(
165
+ mainSignerAddress,
166
+ effectiveOriginChainId,
167
+ effectiveOriginTokenAddress,
168
+ swapAmount, // originTokenAmount
169
+ effectiveOriginChainId, // same chain
170
+ effectiveOriginTokenAddress, // same token
171
+ "0", // destinationTokenAmount (exact input)
172
+ recipient,
173
+ destinationCalldata,
174
+ destinationSalt,
175
+ slippageTolerance,
176
+ TradeType.EXACT_INPUT,
177
+ "", // quoteProvider - empty for same-chain
178
+ )
179
+
180
+ logger.console.log(
181
+ "[trails-sdk] Creating intent for same-chain with args:",
182
+ intentArgs,
183
+ )
184
+
185
+ const { intent, gasFeeOptions } = await getIntent(trailsClient, intentArgs, {
186
+ originTokenSymbol,
187
+ destinationTokenSymbol,
188
+ feeTokenSymbol: originTokenSymbol,
189
+ })
190
+
191
+ logger.console.log("[trails-sdk] Got intent:", intent)
192
+ logger.console.log("[trails-sdk] Got gasFeeOptions:", gasFeeOptions)
193
+
194
+ if (!intent.preconditions?.length || !intent.calls?.length) {
195
+ throw new Error("Invalid intent")
196
+ }
197
+
198
+ // Build origin call params and estimate gas limit for the quote
199
+ const originCallParamsBase = buildSameChainSameTokenTransactionParams({
200
+ hasCustomCalldata,
201
+ recipient,
202
+ effectiveOriginTokenAddress,
203
+ destinationCalldata,
204
+ swapAmount,
205
+ effectiveOriginChainId,
206
+ effectiveOriginChain,
207
+ })
208
+
209
+ logger.console.log(
210
+ "[trails-sdk][gas-estimation] About to estimate gas limit for quote with params:",
211
+ {
212
+ account: account.address,
213
+ to: originCallParamsBase.to,
214
+ data: originCallParamsBase.data,
215
+ value: originCallParamsBase.value,
216
+ },
217
+ )
218
+
219
+ const estimatedGasLimitForQuote = await estimateGasLimit(
220
+ effectivePublicClient,
221
+ {
222
+ account: account.address,
223
+ to: originCallParamsBase.to,
224
+ data: originCallParamsBase.data,
225
+ value: BigInt(originCallParamsBase.value),
226
+ },
227
+ "quote",
228
+ )
229
+
230
+ logger.console.log(
231
+ "[trails-sdk][gas-estimation] Estimated gas limit for quote:",
232
+ estimatedGasLimitForQuote,
233
+ )
234
+
235
+ const quote = await getNormalizedQuoteObject({
236
+ originDepositAddress: intent.originIntentAddress,
237
+ destinationDepositAddress: intent.destinationIntentAddress,
238
+ destinationAddress: recipient,
239
+ destinationCalldata,
240
+ originAmount: swapAmount,
241
+ destinationAmount: swapAmount,
242
+ originTokenPriceUsd: sourceTokenPriceUsd?.toString() || null,
243
+ destinationTokenPriceUsd: destinationTokenPriceUsd?.toString() || null,
244
+ originTokenAddress: effectiveOriginTokenAddress,
245
+ destinationTokenAddress: effectiveOriginTokenAddress,
246
+ transactionStates,
247
+ originChainId: effectiveOriginChainId,
248
+ destinationChainId: effectiveOriginChainId,
249
+ originNativeTokenPriceUsd,
250
+ slippageTolerance,
251
+ quoteProvider: intent.quote?.quoteProvider || "",
252
+ noSufficientBalance,
253
+ estimatedGasLimit: estimatedGasLimitForQuote,
254
+ intent,
255
+ })
256
+
257
+ // Call onCheckoutQuote callback if provided
258
+ if (checkoutOnHandlers?.triggerCheckoutQuote) {
259
+ checkoutOnHandlers.triggerCheckoutQuote(quote)
260
+ }
261
+
262
+ // Log gasless fee options availability
263
+ if (gasFeeOptions && walletId !== "sequence-waas") {
264
+ logger.console.log(
265
+ "[trails-sdk] [GASLESS-FLOW] [FEE-SELECT] Gasless enabled for same-chain. Using gasFeeOptions from QuoteIntent response",
266
+ { originChainId: effectiveOriginChainId, gasFeeOptions },
267
+ )
268
+ }
269
+
270
+ return {
271
+ quote,
272
+ feeOptions: gasFeeOptions,
273
+ send: async ({
274
+ onOriginSend,
275
+ selectedFeeToken: runtimeSelectedFeeToken,
276
+ }: {
277
+ onOriginSend?: () => void
278
+ selectedFeeToken?: any
279
+ }): Promise<SendReturn> => {
280
+ // Use runtime selectedFeeToken if provided, otherwise fall back to the one from prepareSend
281
+ const effectiveSelectedFeeToken =
282
+ runtimeSelectedFeeToken ?? selectedFeeToken
283
+ const effectiveGasless =
284
+ effectiveSelectedFeeToken !== null &&
285
+ effectiveSelectedFeeToken !== undefined &&
286
+ walletId !== "sequence-waas"
287
+ logger.console.log(
288
+ "[trails-sdk] [FEE-SELECT] [GASLESS-FLOW] send() called for same-chain:",
289
+ {
290
+ runtimeSelectedFeeToken,
291
+ prepareTimeSelectedFeeToken: selectedFeeToken,
292
+ effectiveSelectedFeeToken,
293
+ effectiveGasless,
294
+ },
295
+ )
296
+ try {
297
+ const { hasEnoughBalance, balanceError } = await checkAccountBalance({
298
+ account,
299
+ tokenAddress: effectiveOriginTokenAddress,
300
+ depositAmount: swapAmount,
301
+ publicClient: effectivePublicClient,
302
+ })
303
+
304
+ if (!hasEnoughBalance) {
305
+ throw balanceError
306
+ }
307
+
308
+ const depositAmountFormatted = Number(
309
+ formatUnits(BigInt(swapAmount), originTokenDecimals),
310
+ )
311
+ const depositAmountUsd = calcAmountUsdPrice({
312
+ amount: depositAmountFormatted,
313
+ usdPrice: sourceTokenPriceUsd,
314
+ })
315
+
316
+ const hasCustomCalldata = getIsCustomCalldata(destinationCalldata)
317
+
318
+ // Build origin call params (reusing the same logic as quote estimation)
319
+ const originCallParamsBase = buildSameChainSameTokenTransactionParams({
320
+ hasCustomCalldata,
321
+ recipient,
322
+ effectiveOriginTokenAddress,
323
+ destinationCalldata,
324
+ swapAmount,
325
+ effectiveOriginChainId,
326
+ effectiveOriginChain,
327
+ })
328
+
329
+ // Estimate gas limit for consistency with actual transaction
330
+ const gasLimit = await estimateGasLimit(
331
+ effectivePublicClient,
332
+ {
333
+ account: account.address,
334
+ to: originCallParamsBase.to,
335
+ data: originCallParamsBase.data,
336
+ value: BigInt(originCallParamsBase.value),
337
+ },
338
+ "send",
339
+ )
340
+
341
+ const originCallParams = {
342
+ ...originCallParamsBase,
343
+ gasLimit,
344
+ }
345
+
346
+ logger.console.log("[trails-sdk] origin call params", originCallParams)
347
+
348
+ let depositUserTxnReceipt: TransactionReceipt | null = null
349
+ const originMetaTxnReceipt: MetaTxnReceipt | null = null
350
+ const destinationMetaTxnReceipt: MetaTxnReceipt | null = null
351
+
352
+ // Commit the intent
353
+ const commitIntentFnToUse =
354
+ commitIntentFn ||
355
+ ((intent: Intent) =>
356
+ commitIntent(trailsClient, intent, {
357
+ originTokenSymbol,
358
+ destinationTokenSymbol,
359
+ }))
360
+ await commitIntentFnToUse(intent)
361
+
362
+ logger.console.log(
363
+ "[trails-sdk] Intent committed for same-chain transaction",
364
+ )
365
+
366
+ await attemptSwitchChain({
367
+ walletClient,
368
+ desiredChainId: effectiveOriginChainId,
369
+ })
370
+ if (!dryMode) {
371
+ // For gasless flows on same-chain same-token, we need to track 2 transactions:
372
+ // [0] = Deposit transaction (relayed)
373
+ // [1] = Origin intent transaction (executed on chain after deposit is confirmed)
374
+
375
+ // Initialize local transaction states for gasless flows to track both deposit and execute
376
+ const createInitialStates = (): TransactionState[] => {
377
+ const depositState: TransactionState = {
378
+ transactionHash: "",
379
+ explorerUrl: "",
380
+ chainId: effectiveOriginChainId,
381
+ state: "pending",
382
+ label: effectiveGasless ? "Deposit" : "Execute",
383
+ }
384
+
385
+ const executeState: TransactionState = {
386
+ transactionHash: "",
387
+ explorerUrl: "",
388
+ chainId: effectiveOriginChainId,
389
+ state: "pending",
390
+ label: "Execute",
391
+ }
392
+
393
+ return effectiveGasless
394
+ ? [depositState, executeState]
395
+ : [depositState]
396
+ }
397
+
398
+ const localTransactionStates = createInitialStates()
399
+
400
+ try {
401
+ onTransactionStateChange(localTransactionStates)
402
+ } catch (error) {
403
+ logger.console.error(
404
+ "[trails-sdk] Error calling onTransactionStateChange:",
405
+ error,
406
+ )
407
+ }
408
+
409
+ // For gasless flows, pass the localTransactionStates instead of the input parameter
410
+ // This ensures we track both deposit and execute transactions
411
+ const statesForDeposit = effectiveGasless
412
+ ? localTransactionStates
413
+ : transactionStates
414
+
415
+ // Use attemptUserDepositTx which handles both gasless and non-gasless flows
416
+ // For non-gasless same-chain same-token, pass recipient to send directly to user instead of intent contract
417
+ depositUserTxnReceipt = await attemptUserDepositTx({
418
+ originTokenAddress: effectiveOriginTokenAddress,
419
+ paymasterUrl,
420
+ chain: effectiveOriginChain,
421
+ account,
422
+ firstPreconditionMin: swapAmount,
423
+ originIntentAddress: intent.originIntentAddress,
424
+ onOriginSend,
425
+ publicClient: effectivePublicClient,
426
+ walletClient,
427
+ destinationTokenDecimals: originTokenDecimals,
428
+ sourceTokenDecimals: originTokenDecimals,
429
+ fee: "0",
430
+ dryMode,
431
+ sourceTokenPriceUsd: sourceTokenPriceUsd ?? null,
432
+ destinationTokenPriceUsd: destinationTokenPriceUsd ?? null,
433
+ swapAmount,
434
+ onTransactionStateChange,
435
+ transactionStates: statesForDeposit,
436
+ fundMethod,
437
+ originTokenSymbol,
438
+ destinationTokenSymbol,
439
+ depositAmountUsd,
440
+ feeOptions: gasFeeOptions,
441
+ trailsClient,
442
+ selectedFeeToken: effectiveSelectedFeeToken,
443
+ walletId,
444
+ abortSignal,
445
+ checkoutOnHandlers,
446
+ intentId: intent.intentId,
447
+ executeIntentFn,
448
+ depositRecipientOverride: recipient,
449
+ isSameChainSameToken: true,
450
+ })
451
+
452
+ // attemptUserDepositTx handles both gasless and non-gasless flows
453
+ // Transaction state updates and toasts are handled within that function
454
+ logger.console.log("[trails-sdk] Deposit transaction completed", {
455
+ hasReceipt: !!depositUserTxnReceipt,
456
+ isGasless: effectiveGasless,
457
+ })
458
+
459
+ // For gasless flows, start polling for both deposit and origin transactions via API
460
+ if (effectiveGasless && !depositUserTxnReceipt) {
461
+ logger.console.log(
462
+ "[trails-sdk] Starting unified polling for gasless same-chain transaction with 2 states",
463
+ )
464
+ try {
465
+ await pollIntentReceipt({
466
+ intentId: intent.intentId,
467
+ trailsClient,
468
+ callbacks: {
469
+ onDepositTransactionFound: async (txHash) => {
470
+ logger.console.log(
471
+ "[trails-sdk] Deposit transaction discovered for same-chain gasless flow",
472
+ { txHash },
473
+ )
474
+
475
+ if (localTransactionStates[0]) {
476
+ try {
477
+ const depositTxReceipt =
478
+ await effectivePublicClient.getTransactionReceipt({
479
+ hash: txHash as `0x${string}`,
480
+ })
481
+ logger.console.log(
482
+ "[trails-sdk] Deposit transaction receipt fetched",
483
+ {
484
+ txHash,
485
+ blockNumber: depositTxReceipt?.blockNumber,
486
+ status: depositTxReceipt?.status,
487
+ },
488
+ )
489
+
490
+ localTransactionStates[0] =
491
+ getTransactionStateFromReceipt(
492
+ depositTxReceipt,
493
+ effectiveOriginChainId,
494
+ localTransactionStates[0]?.label,
495
+ )
496
+ onTransactionStateChange(localTransactionStates)
497
+ } catch (error) {
498
+ logger.console.error(
499
+ "[trails-sdk] Error fetching deposit transaction receipt:",
500
+ error,
501
+ )
502
+ }
503
+ }
504
+ },
505
+ onOriginTransactionFound: async (originTxHash) => {
506
+ logger.console.log(
507
+ "[trails-sdk] Origin intent transaction discovered for same-chain gasless flow",
508
+ { originTxHash },
509
+ )
510
+
511
+ // For same-chain gasless, localTransactionStates[1] is the origin intent transaction
512
+ if (localTransactionStates[1]) {
513
+ try {
514
+ const originTxReceipt =
515
+ await effectivePublicClient.getTransactionReceipt({
516
+ hash: originTxHash as `0x${string}`,
517
+ })
518
+ logger.console.log(
519
+ "[trails-sdk] Origin transaction receipt fetched",
520
+ {
521
+ originTxHash,
522
+ blockNumber: originTxReceipt?.blockNumber,
523
+ status: originTxReceipt?.status,
524
+ },
525
+ )
526
+
527
+ localTransactionStates[1] =
528
+ getTransactionStateFromReceipt(
529
+ originTxReceipt,
530
+ effectiveOriginChainId,
531
+ localTransactionStates[1]?.label,
532
+ )
533
+ onTransactionStateChange(localTransactionStates)
534
+ } catch (error) {
535
+ logger.console.error(
536
+ "[trails-sdk] Error fetching origin transaction receipt:",
537
+ error,
538
+ )
539
+ }
540
+ }
541
+ },
542
+ },
543
+ maxWaitTime: 120000, // 120 seconds max wait for both transactions
544
+ })
545
+ } catch (error) {
546
+ logger.console.error(
547
+ "[trails-sdk] Error polling for gasless same-chain transactions:",
548
+ error,
549
+ )
550
+ }
551
+ }
552
+
553
+ if (depositUserTxnReceipt) {
554
+ try {
555
+ const updatedStates = [
556
+ getTransactionStateFromReceipt(
557
+ depositUserTxnReceipt,
558
+ effectiveOriginChainId,
559
+ transactionStates[0]?.label,
560
+ ),
561
+ ]
562
+ onTransactionStateChange(updatedStates)
563
+ } catch (error) {
564
+ logger.console.error(
565
+ "[trails-sdk] Error calling onTransactionStateChange:",
566
+ error,
567
+ )
568
+ }
569
+ }
570
+
571
+ // Show conditional toast based on transaction status
572
+ const chainInfo = getChainInfo(effectiveOriginChainId)
573
+ if (depositUserTxnReceipt) {
574
+ if (depositUserTxnReceipt?.status === "success") {
575
+ updatePersistentToast(
576
+ "Transfer Confirmed",
577
+ `Your transaction on ${chainInfo?.name || "chain"} has been confirmed`,
578
+ "info",
579
+ )
580
+ } else {
581
+ updatePersistentToast(
582
+ "Transfer Failed",
583
+ `Your transaction on ${chainInfo?.name || "chain"} failed`,
584
+ "error",
585
+ )
586
+ }
587
+ }
588
+
589
+ // Track payment completion for same-chain same-token transaction
590
+ if (
591
+ depositUserTxnReceipt &&
592
+ depositUserTxnReceipt.status === "success"
593
+ ) {
594
+ // Check if any transaction failed or was refunded
595
+ const hasAnyCallFailed = transactionStates.some((tx) =>
596
+ tx?.decodedGuestModuleEvents?.some(
597
+ (e: any) => e?.type === "CallFailed",
598
+ ),
599
+ )
600
+ const hasAnyRefunded = transactionStates.some((tx) => tx?.refunded)
601
+ const txStatus =
602
+ !hasAnyCallFailed && !hasAnyRefunded ? "success" : "fail"
603
+
604
+ // Always track payment completion regardless of success/failure
605
+ trackPaymentCompleted({
606
+ userAddress: account.address,
607
+ originTxHash: depositUserTxnReceipt.transactionHash,
608
+ originChainId: effectiveOriginChainId, // Same chain
609
+ mode,
610
+ fundMethod,
611
+ originTokenAddress,
612
+ depositTokenAmountUsd: depositAmountUsd?.toString(),
613
+ destinationTokenAmountUsd: depositAmountUsd?.toString(), // same as deposit amount
614
+ })
615
+
616
+ // Call onCheckoutComplete callback with transaction status
617
+ if (checkoutOnHandlers?.triggerCheckoutComplete) {
618
+ checkoutOnHandlers.triggerCheckoutComplete(
619
+ txStatus,
620
+ account.address,
621
+ )
622
+ }
623
+ } else if (depositUserTxnReceipt) {
624
+ trackPaymentError({
625
+ error: "Transaction failed",
626
+ userAddress: account.address,
627
+ mode,
628
+ fundMethod,
629
+ originTokenAddress,
630
+ })
631
+
632
+ // Call onCheckoutError callback if provided
633
+ if (checkoutOnHandlers?.triggerCheckoutError) {
634
+ checkoutOnHandlers.triggerCheckoutError("Transaction failed")
635
+ }
636
+ } else if (effectiveGasless && !depositUserTxnReceipt) {
637
+ // For gasless flows with polling, check if both transactions are confirmed
638
+ const depositConfirmed =
639
+ localTransactionStates[0]?.state === "confirmed"
640
+ const originConfirmed =
641
+ localTransactionStates[1]?.state === "confirmed"
642
+
643
+ if (depositConfirmed && originConfirmed) {
644
+ logger.console.log(
645
+ "[trails-sdk] Gasless same-chain transaction fully completed via polling",
646
+ )
647
+ const txStatus = "success"
648
+
649
+ trackPaymentCompleted({
650
+ userAddress: account.address,
651
+ originTxHash:
652
+ localTransactionStates[0]?.transactionHash ||
653
+ localTransactionStates[1]?.transactionHash,
654
+ originChainId: effectiveOriginChainId,
655
+ mode,
656
+ fundMethod,
657
+ originTokenAddress,
658
+ depositTokenAmountUsd: depositAmountUsd?.toString(),
659
+ destinationTokenAmountUsd: depositAmountUsd?.toString(),
660
+ })
661
+
662
+ // Call onCheckoutComplete callback with transaction status
663
+ if (checkoutOnHandlers?.triggerCheckoutComplete) {
664
+ checkoutOnHandlers.triggerCheckoutComplete(
665
+ txStatus,
666
+ account.address,
667
+ )
668
+ }
669
+ } else if (depositConfirmed) {
670
+ logger.console.log(
671
+ "[trails-sdk] Gasless same-chain deposit confirmed, waiting for origin transaction",
672
+ )
673
+ }
674
+ }
675
+ }
676
+
677
+ return {
678
+ depositUserTxnReceipt,
679
+ originMetaTxnReceipt,
680
+ destinationMetaTxnReceipt,
681
+ totalCompletionSeconds: 0,
682
+ }
683
+ } catch (error) {
684
+ const errorMessage =
685
+ error instanceof Error
686
+ ? error.message
687
+ : "Unknown error occurred during transaction"
688
+ logger.console.error(
689
+ "[trails-sdk] Error in sendHandlerForSameChainSameToken:",
690
+ error,
691
+ )
692
+
693
+ // Track payment error
694
+ trackPaymentError({
695
+ error: errorMessage,
696
+ userAddress: account.address,
697
+ mode,
698
+ fundMethod,
699
+ originTokenAddress,
700
+ })
701
+
702
+ // Call onCheckoutError callback if provided
703
+ if (checkoutOnHandlers?.triggerCheckoutError) {
704
+ checkoutOnHandlers.triggerCheckoutError(errorMessage)
705
+ }
706
+
707
+ // Re-throw the error so caller can handle if needed
708
+ throw error
709
+ }
710
+ },
711
+ }
712
+ }