0xtrails 0.4.1 → 0.4.3

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 (55) hide show
  1. package/dist/aave.d.ts.map +1 -1
  2. package/dist/{ccip-Bc-mZIIK.js → ccip-C9CA3tIi.js} +5 -5
  3. package/dist/{index-BWGjgMLm.js → index-CNB57v0n.js} +15676 -15527
  4. package/dist/index.d.ts +1 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +501 -474
  7. package/dist/intents.d.ts +1 -1
  8. package/dist/intents.d.ts.map +1 -1
  9. package/dist/morpho.d.ts.map +1 -1
  10. package/dist/mutations.d.ts +2 -2
  11. package/dist/mutations.d.ts.map +1 -1
  12. package/dist/prepareSend.d.ts.map +1 -1
  13. package/dist/trails.d.ts +9 -10
  14. package/dist/trails.d.ts.map +1 -1
  15. package/dist/transactionIntent/deposits/depositOrchestrator.d.ts +3 -2
  16. package/dist/transactionIntent/deposits/depositOrchestrator.d.ts.map +1 -1
  17. package/dist/transactionIntent/deposits/gaslessDeposit.d.ts +1 -1
  18. package/dist/transactionIntent/deposits/gaslessDeposit.d.ts.map +1 -1
  19. package/dist/transactionIntent/deposits/standardDeposit.d.ts +3 -2
  20. package/dist/transactionIntent/deposits/standardDeposit.d.ts.map +1 -1
  21. package/dist/transactionIntent/handlers/crossChain.d.ts +1 -1
  22. package/dist/transactionIntent/handlers/crossChain.d.ts.map +1 -1
  23. package/dist/transactionIntent/handlers/sameChainSameToken.d.ts +3 -2
  24. package/dist/transactionIntent/handlers/sameChainSameToken.d.ts.map +1 -1
  25. package/dist/transactionIntent/quote/quoteHelpers.d.ts.map +1 -1
  26. package/dist/transactionIntent/types.d.ts +1 -1
  27. package/dist/transactionIntent/types.d.ts.map +1 -1
  28. package/dist/widget/components/FeeOptions.d.ts +2 -1
  29. package/dist/widget/components/FeeOptions.d.ts.map +1 -1
  30. package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
  31. package/dist/widget/hooks/useQuote.d.ts +31 -1
  32. package/dist/widget/hooks/useQuote.d.ts.map +1 -1
  33. package/dist/widget/hooks/useSendForm.d.ts +1 -1
  34. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  35. package/dist/widget/index.js +1 -1
  36. package/package.json +7 -7
  37. package/src/aave.ts +1 -0
  38. package/src/index.ts +1 -1
  39. package/src/intents.ts +2 -2
  40. package/src/morpho.ts +3 -2
  41. package/src/mutations.ts +53 -5
  42. package/src/prepareSend.ts +16 -1
  43. package/src/trails.ts +57 -65
  44. package/src/transactionIntent/deposits/depositOrchestrator.ts +4 -1
  45. package/src/transactionIntent/deposits/gaslessDeposit.ts +89 -44
  46. package/src/transactionIntent/deposits/standardDeposit.ts +198 -39
  47. package/src/transactionIntent/handlers/crossChain.ts +84 -9
  48. package/src/transactionIntent/handlers/sameChainSameToken.ts +54 -12
  49. package/src/transactionIntent/quote/quoteHelpers.ts +4 -11
  50. package/src/transactionIntent/types.ts +1 -1
  51. package/src/widget/components/FeeOptions.tsx +2 -1
  52. package/src/widget/components/PoolDeposit.tsx +68 -4
  53. package/src/widget/hooks/useDebugScreens.ts +11 -11
  54. package/src/widget/hooks/useQuote.ts +37 -2
  55. package/src/widget/hooks/useSendForm.ts +2 -2
@@ -5,7 +5,7 @@ import type {
5
5
  WalletClient,
6
6
  TransactionReceipt,
7
7
  } from "viem"
8
- import { parseUnits } from "viem"
8
+ import { parseUnits, encodeFunctionData } from "viem"
9
9
  import type { TransactionState } from "../../transactions.js"
10
10
  import type { CheckoutOnHandlers } from "../../widget/hooks/useCheckout.js"
11
11
  import { logger } from "../../logger.js"
@@ -15,12 +15,15 @@ import {
15
15
  buildSameChainSameTokenTransactionParams,
16
16
  sendOriginTransaction,
17
17
  } from "../../intents.js"
18
+ import { getIsCustomCalldata } from "../../contractUtils.js"
18
19
  import { estimateGasLimit } from "../../estimate.js"
19
20
  import { attemptSwitchChain } from "../../chainSwitch.js"
20
21
  import { requestWithTimeout } from "../../utils.js"
21
22
  import { updatePersistentToast } from "../../toast.js"
22
23
  import { getChainInfo } from "../../chains.js"
23
24
  import { trackTransactionConfirmed } from "../../analytics.js"
25
+ import { zeroAddress } from "viem"
26
+ import { getIsUserRejectionError } from "../../error.js"
24
27
 
25
28
  export async function attemptStandardDeposit({
26
29
  originTokenAddress,
@@ -47,6 +50,7 @@ export async function attemptStandardDeposit({
47
50
  checkoutOnHandlers,
48
51
  isSameChainSameToken,
49
52
  recipient,
53
+ destinationCalldata,
50
54
  }: {
51
55
  originTokenAddress: string
52
56
  firstPreconditionMin: string
@@ -69,10 +73,12 @@ export async function attemptStandardDeposit({
69
73
  originTokenSymbol: string
70
74
  destinationTokenSymbol: string
71
75
  depositAmountUsd: number
72
- checkoutOnHandlers?: CheckoutOnHandlers
76
+ checkoutOnHandlers?: Partial<CheckoutOnHandlers>
73
77
  isSameChainSameToken?: boolean
74
78
  recipient?: string
79
+ destinationCalldata?: string
75
80
  }): Promise<TransactionReceipt | null> {
81
+ console.log("IN ATTEMPT STANDARD DEPOSIT!")
76
82
  let depositUserTxnReceipt: TransactionReceipt | null = null
77
83
  const usingLIfi = false
78
84
  let needsNativeFee = false
@@ -100,9 +106,10 @@ export async function attemptStandardDeposit({
100
106
  // For cross-chain deposits, send to intent address for backend processing
101
107
  const originCallParamsBase = isSameChainSameToken
102
108
  ? buildSameChainSameTokenTransactionParams({
103
- hasCustomCalldata: false,
109
+ hasCustomCalldata: getIsCustomCalldata(destinationCalldata),
104
110
  recipient: recipient || originIntentAddress,
105
111
  effectiveOriginTokenAddress: originTokenAddress,
112
+ destinationCalldata,
106
113
  swapAmount: firstPreconditionMin,
107
114
  effectiveOriginChainId: originChainId,
108
115
  effectiveOriginChain: chain,
@@ -116,6 +123,134 @@ export async function attemptStandardDeposit({
116
123
  chain,
117
124
  })
118
125
 
126
+ const hasCustomCalldata = isSameChainSameToken
127
+ ? getIsCustomCalldata(destinationCalldata)
128
+ : false
129
+
130
+ if (
131
+ isSameChainSameToken &&
132
+ originTokenAddress !== zeroAddress &&
133
+ hasCustomCalldata &&
134
+ !dryMode &&
135
+ recipient
136
+ ) {
137
+ // ERC20 custom calldata requires approval to the recipient contract
138
+ const erc20Abi = [
139
+ {
140
+ name: "allowance",
141
+ type: "function",
142
+ stateMutability: "view",
143
+ inputs: [
144
+ { name: "owner", type: "address" },
145
+ { name: "spender", type: "address" },
146
+ ],
147
+ outputs: [{ type: "uint256" }],
148
+ } as const,
149
+ {
150
+ name: "approve",
151
+ type: "function",
152
+ stateMutability: "nonpayable",
153
+ inputs: [
154
+ { name: "spender", type: "address" },
155
+ { name: "amount", type: "uint256" },
156
+ ],
157
+ outputs: [{ type: "bool" }],
158
+ } as const,
159
+ ] as const
160
+
161
+ const currentAllowance = (await publicClient.readContract({
162
+ address: originTokenAddress as `0x${string}`,
163
+ abi: erc20Abi,
164
+ functionName: "allowance",
165
+ args: [account.address, recipient as `0x${string}`],
166
+ })) as bigint
167
+
168
+ const requiredAmount = BigInt(firstPreconditionMin)
169
+
170
+ if (currentAllowance < requiredAmount) {
171
+ logger.console.log(
172
+ "[trails-sdk] Insufficient allowance, sending approve transaction...",
173
+ )
174
+
175
+ const approveData = encodeFunctionData({
176
+ abi: erc20Abi,
177
+ functionName: "approve",
178
+ args: [recipient as `0x${string}`, requiredAmount],
179
+ }) as `0x${string}`
180
+
181
+ let approveHash: `0x${string}`
182
+ let approveReceipt: TransactionReceipt
183
+ try {
184
+ approveHash = await sendOriginTransaction(
185
+ account,
186
+ walletClient,
187
+ {
188
+ to: originTokenAddress as `0x${string}`,
189
+ data: approveData,
190
+ value: 0n,
191
+ chain,
192
+ },
193
+ {
194
+ transactionType: "token_approve",
195
+ depositTokenAmountUsd: depositAmountUsd?.toString(),
196
+ },
197
+ )
198
+
199
+ approveReceipt = await publicClient.waitForTransactionReceipt({
200
+ hash: approveHash,
201
+ })
202
+ } catch (error) {
203
+ // Check if this is a user rejection error
204
+ if (getIsUserRejectionError(error)) {
205
+ if (checkoutOnHandlers?.triggerCheckoutSignatureRejected) {
206
+ checkoutOnHandlers.triggerCheckoutSignatureRejected(error)
207
+ }
208
+ }
209
+ throw error
210
+ }
211
+
212
+ logger.console.log(
213
+ "[trails-sdk] Approve transaction confirmed:",
214
+ approveReceipt,
215
+ )
216
+
217
+ // Poll to ensure allowance is updated on-chain before proceeding
218
+ let updatedAllowance = currentAllowance
219
+ const maxPollAttempts = 15 // 30 seconds at 2s intervals
220
+ let pollAttempts = 0
221
+
222
+ while (
223
+ updatedAllowance < requiredAmount &&
224
+ pollAttempts < maxPollAttempts
225
+ ) {
226
+ await new Promise((resolve) => setTimeout(resolve, 2000)) // Wait 2 seconds
227
+ updatedAllowance = (await publicClient.readContract({
228
+ address: originTokenAddress as `0x${string}`,
229
+ abi: erc20Abi,
230
+ functionName: "allowance",
231
+ args: [account.address, recipient as `0x${string}`],
232
+ })) as bigint
233
+ pollAttempts++
234
+ logger.console.log(
235
+ `[trails-sdk] Polling allowance (attempt ${pollAttempts}): ${updatedAllowance.toString()}`,
236
+ )
237
+ }
238
+
239
+ if (updatedAllowance < requiredAmount) {
240
+ throw new Error(
241
+ "Allowance not updated after approve transaction - RPC may be delayed or transaction failed",
242
+ )
243
+ }
244
+
245
+ logger.console.log(
246
+ "[trails-sdk] Allowance confirmed updated:",
247
+ updatedAllowance.toString(),
248
+ )
249
+ } else {
250
+ logger.console.log("[trails-sdk] Sufficient allowance, skipping approve")
251
+ }
252
+ }
253
+
119
254
  // Estimate gas limit for consistency with actual transaction
120
255
  const gasLimit = await estimateGasLimit(
121
256
  publicClient,
@@ -276,31 +411,44 @@ export async function attemptStandardDeposit({
276
411
  checkoutOnHandlers.triggerCheckoutSignatureRequest()
277
412
  }
278
413
 
279
- const txHashNativeFee = await sendOriginTransaction(
280
- account,
281
- walletClient,
282
- {
283
- to: originIntentAddress,
284
- data: "0x00",
285
- value: nativeFee,
286
- chainId: originChainId,
287
- chain,
288
- } as any,
289
- {
290
- depositTokenAmountUsd: depositAmountUsd?.toString(),
291
- },
292
- ) // TODO: Add proper type
293
- logger.console.log("[trails-sdk] origin tx native fee", txHashNativeFee)
414
+ try {
415
+ const txHashNativeFee = await sendOriginTransaction(
416
+ account,
417
+ walletClient,
418
+ {
419
+ to: originIntentAddress,
420
+ data: "0x00",
421
+ value: nativeFee,
422
+ chainId: originChainId,
423
+ chain,
424
+ } as any,
425
+ {
426
+ depositTokenAmountUsd: depositAmountUsd?.toString(),
427
+ },
428
+ ) // TODO: Add proper type
429
+ logger.console.log(
430
+ "[trails-sdk] origin tx native fee",
431
+ txHashNativeFee,
432
+ )
294
433
 
295
- // Trigger signature confirmed callback after successful signing
296
- if (checkoutOnHandlers?.triggerCheckoutSignatureConfirmed) {
297
- checkoutOnHandlers.triggerCheckoutSignatureConfirmed()
434
+ // Trigger signature confirmed callback after successful signing
435
+ if (checkoutOnHandlers?.triggerCheckoutSignatureConfirmed) {
436
+ checkoutOnHandlers.triggerCheckoutSignatureConfirmed()
437
+ }
438
+ // Wait for transaction receipt
439
+ const feeReceipt = await publicClient.waitForTransactionReceipt({
440
+ hash: txHashNativeFee,
441
+ })
442
+ logger.console.log("[trails-sdk] nativeFeeReceipt", feeReceipt)
443
+ } catch (error) {
444
+ // Check if this is a user rejection error
445
+ if (getIsUserRejectionError(error)) {
446
+ if (checkoutOnHandlers?.triggerCheckoutSignatureRejected) {
447
+ checkoutOnHandlers.triggerCheckoutSignatureRejected(error)
448
+ }
449
+ }
450
+ throw error
298
451
  }
299
- // Wait for transaction receipt
300
- const feeReceipt = await publicClient.waitForTransactionReceipt({
301
- hash: txHashNativeFee,
302
- })
303
- logger.console.log("[trails-sdk] nativeFeeReceipt", feeReceipt)
304
452
  }
305
453
 
306
454
  // Show persistent toast for checkout flow
@@ -315,19 +463,30 @@ export async function attemptStandardDeposit({
315
463
  checkoutOnHandlers.triggerCheckoutSignatureRequest()
316
464
  }
317
465
 
318
- const txHash = await sendOriginTransaction(
319
- account,
320
- walletClient,
321
- originCallParams as any,
322
- {
323
- depositTokenAmountUsd: depositAmountUsd?.toString(),
324
- },
325
- ) // TODO: Add proper type
326
- logger.console.log("[trails-sdk] origin tx", txHash)
327
-
328
- // Trigger signature confirmed callback after successful signing
329
- if (checkoutOnHandlers?.triggerCheckoutSignatureConfirmed) {
330
- checkoutOnHandlers.triggerCheckoutSignatureConfirmed()
466
+ let txHash: `0x${string}`
467
+ try {
468
+ txHash = await sendOriginTransaction(
469
+ account,
470
+ walletClient,
471
+ originCallParams as any,
472
+ {
473
+ depositTokenAmountUsd: depositAmountUsd?.toString(),
474
+ },
475
+ ) // TODO: Add proper type
476
+ logger.console.log("[trails-sdk] origin tx", txHash)
477
+
478
+ // Trigger signature confirmed callback after successful signing
479
+ if (checkoutOnHandlers?.triggerCheckoutSignatureConfirmed) {
480
+ checkoutOnHandlers.triggerCheckoutSignatureConfirmed()
481
+ }
482
+ } catch (error) {
483
+ // Check if this is a user rejection error
484
+ if (getIsUserRejectionError(error)) {
485
+ if (checkoutOnHandlers?.triggerCheckoutSignatureRejected) {
486
+ checkoutOnHandlers.triggerCheckoutSignatureRejected(error)
487
+ }
488
+ }
489
+ throw error
331
490
  }
332
491
 
333
492
  if (onOriginSend) {
@@ -43,7 +43,7 @@ import {
43
43
  } from "../../sequenceWallet.js"
44
44
  import { getIntentArgs } from "../quote/quoteHelpers.js"
45
45
  import {
46
- getIntent,
46
+ quoteIntent,
47
47
  commitIntent,
48
48
  buildCrossChainDepositParams,
49
49
  } from "../../intents.js"
@@ -177,7 +177,7 @@ export async function handleCrossChain({
177
177
  quoteProvider?: string | null
178
178
  fundMethod?: string
179
179
  mode?: "pay" | "fund" | "earn" | "swap" | "receive"
180
- checkoutOnHandlers?: CheckoutOnHandlers
180
+ checkoutOnHandlers?: Partial<CheckoutOnHandlers>
181
181
  selectedFeeToken?: SelectedFeeToken
182
182
  walletId?: string
183
183
  abortSignal?: AbortSignal
@@ -324,6 +324,10 @@ export async function handleCrossChain({
324
324
  )
325
325
 
326
326
  onTransactionStateChange(transactionStates)
327
+ // Also trigger checkout status update if handler is provided
328
+ if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
329
+ checkoutOnHandlers.triggerCheckoutStatusUpdate(transactionStates)
330
+ }
327
331
 
328
332
  const attestation = await waitForAttestation()
329
333
 
@@ -407,6 +411,10 @@ export async function handleCrossChain({
407
411
  }
408
412
 
409
413
  onTransactionStateChange(transactionStates)
414
+ // Also trigger checkout status update if handler is provided
415
+ if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
416
+ checkoutOnHandlers.triggerCheckoutStatusUpdate(transactionStates)
417
+ }
410
418
 
411
419
  return {
412
420
  depositUserTxnReceipt: receipt,
@@ -438,13 +446,17 @@ export async function handleCrossChain({
438
446
 
439
447
  logger.console.log("[trails-sdk] Creating intent with args:", intentArgs)
440
448
 
441
- const { intent, gasFeeOptions } = await getIntent(trailsClient, intentArgs, {
442
- originTokenSymbol,
443
- destinationTokenSymbol,
444
- feeTokenSymbol: originTokenSymbol,
445
- })
446
- logger.console.log("[trails-sdk] Got intent:", intent)
447
- logger.console.log("[trails-sdk] Got gasFeeOptions:", gasFeeOptions)
449
+ const { intent, gasFeeOptions } = await quoteIntent(
450
+ trailsClient,
451
+ intentArgs,
452
+ {
453
+ originTokenSymbol,
454
+ destinationTokenSymbol,
455
+ feeTokenSymbol: originTokenSymbol,
456
+ },
457
+ )
458
+ logger.console.log("[trails-sdk] Quote intent:", intent)
459
+ logger.console.log("[trails-sdk] Quote intent gasFeeOptions:", gasFeeOptions)
448
460
 
449
461
  if (!intent.preconditions?.length || !intent.calls?.length) {
450
462
  throw new Error("Invalid intent")
@@ -747,6 +759,7 @@ export async function handleCrossChain({
747
759
  selectedFeeToken: effectiveSelectedFeeToken,
748
760
  selectedFeeTokenType: typeof effectiveSelectedFeeToken,
749
761
  selectedFeeTokenValue: JSON.stringify(effectiveSelectedFeeToken),
762
+ checkoutOnHandlers,
750
763
  },
751
764
  )
752
765
 
@@ -774,11 +787,13 @@ export async function handleCrossChain({
774
787
  destinationTokenSymbol,
775
788
  depositAmountUsd,
776
789
  feeOptions: gasFeeOptions,
790
+ checkoutOnHandlers,
777
791
  trailsClient,
778
792
  selectedFeeToken: effectiveSelectedFeeToken,
779
793
  walletId,
780
794
  abortSignal,
781
795
  intentId: intent.intentId,
796
+ executeIntentFn,
782
797
  })
783
798
 
784
799
  // In gasless mode, depositUserTxnReceipt will be null because the transaction
@@ -881,6 +896,10 @@ export async function handleCrossChain({
881
896
  }
882
897
 
883
898
  onTransactionStateChange(transactionStates)
899
+ // Also trigger checkout status update if handler is provided
900
+ if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
901
+ checkoutOnHandlers.triggerCheckoutStatusUpdate(transactionStates)
902
+ }
884
903
 
885
904
  setTimeout(() => {
886
905
  const destinationChain = getChainInfo(destinationChainId)
@@ -948,6 +967,12 @@ export async function handleCrossChain({
948
967
  transactionStates[0]?.label,
949
968
  )
950
969
  onTransactionStateChange(transactionStates)
970
+ // Also trigger checkout status update if handler is provided
971
+ if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
972
+ checkoutOnHandlers.triggerCheckoutStatusUpdate(
973
+ transactionStates,
974
+ )
975
+ }
951
976
 
952
977
  if (onOriginSend) {
953
978
  onOriginSend()
@@ -1014,6 +1039,12 @@ export async function handleCrossChain({
1014
1039
  transactionStates[2]?.label,
1015
1040
  )
1016
1041
  onTransactionStateChange(transactionStates)
1042
+ // Also trigger checkout status update if handler is provided
1043
+ if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
1044
+ checkoutOnHandlers.triggerCheckoutStatusUpdate(
1045
+ transactionStates,
1046
+ )
1047
+ }
1017
1048
 
1018
1049
  return destinationDepositTxReceipt
1019
1050
  }
@@ -1087,6 +1118,12 @@ export async function handleCrossChain({
1087
1118
  transactionStates[0]?.label,
1088
1119
  )
1089
1120
  onTransactionStateChange(transactionStates)
1121
+ // Also trigger checkout status update if handler is provided
1122
+ if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
1123
+ checkoutOnHandlers.triggerCheckoutStatusUpdate(
1124
+ transactionStates,
1125
+ )
1126
+ }
1090
1127
 
1091
1128
  // Decode events for the deposit transaction
1092
1129
  try {
@@ -1142,6 +1179,12 @@ export async function handleCrossChain({
1142
1179
  }
1143
1180
 
1144
1181
  onTransactionStateChange(transactionStates)
1182
+ // Also trigger checkout status update if handler is provided
1183
+ if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
1184
+ checkoutOnHandlers.triggerCheckoutStatusUpdate(
1185
+ transactionStates,
1186
+ )
1187
+ }
1145
1188
  } catch (error) {
1146
1189
  logger.console.error(
1147
1190
  "Error decoding deposit tx events",
@@ -1187,6 +1230,12 @@ export async function handleCrossChain({
1187
1230
  transactionStates[1]?.label,
1188
1231
  )
1189
1232
  onTransactionStateChange(transactionStates)
1233
+ // Also trigger checkout status update if handler is provided
1234
+ if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
1235
+ checkoutOnHandlers.triggerCheckoutStatusUpdate(
1236
+ transactionStates,
1237
+ )
1238
+ }
1190
1239
  }
1191
1240
 
1192
1241
  // Decode events for the origin transaction
@@ -1236,6 +1285,12 @@ export async function handleCrossChain({
1236
1285
  }
1237
1286
 
1238
1287
  onTransactionStateChange(transactionStates)
1288
+ // Also trigger checkout status update if handler is provided
1289
+ if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
1290
+ checkoutOnHandlers.triggerCheckoutStatusUpdate(
1291
+ transactionStates,
1292
+ )
1293
+ }
1239
1294
  } catch (error) {
1240
1295
  logger.console.error(
1241
1296
  "Error decoding origin tx events",
@@ -1276,6 +1331,12 @@ export async function handleCrossChain({
1276
1331
  transactionStates[2]?.label,
1277
1332
  )
1278
1333
  onTransactionStateChange(transactionStates)
1334
+ // Also trigger checkout status update if handler is provided
1335
+ if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
1336
+ checkoutOnHandlers.triggerCheckoutStatusUpdate(
1337
+ transactionStates,
1338
+ )
1339
+ }
1279
1340
  }
1280
1341
 
1281
1342
  // Decode events for the destination transaction
@@ -1334,6 +1395,12 @@ export async function handleCrossChain({
1334
1395
  }
1335
1396
 
1336
1397
  onTransactionStateChange(transactionStates)
1398
+ // Also trigger checkout status update if handler is provided
1399
+ if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
1400
+ checkoutOnHandlers.triggerCheckoutStatusUpdate(
1401
+ transactionStates,
1402
+ )
1403
+ }
1337
1404
  } catch (error) {
1338
1405
  logger.console.error(
1339
1406
  "Error decoding destination tx events",
@@ -1626,6 +1693,10 @@ export async function handleCrossChain({
1626
1693
  ) {
1627
1694
  transactionStates[1].state = "aborted"
1628
1695
  onTransactionStateChange(transactionStates)
1696
+ // Also trigger checkout status update if handler is provided
1697
+ if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
1698
+ checkoutOnHandlers.triggerCheckoutStatusUpdate(transactionStates)
1699
+ }
1629
1700
  }
1630
1701
  if (
1631
1702
  transactionStates[2] &&
@@ -1634,6 +1705,10 @@ export async function handleCrossChain({
1634
1705
  ) {
1635
1706
  transactionStates[2].state = "aborted"
1636
1707
  onTransactionStateChange(transactionStates)
1708
+ // Also trigger checkout status update if handler is provided
1709
+ if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
1710
+ checkoutOnHandlers.triggerCheckoutStatusUpdate(transactionStates)
1711
+ }
1637
1712
  }
1638
1713
 
1639
1714
  // Track payment error if transactions didn't complete successfully
@@ -1,5 +1,5 @@
1
1
  import type { Account, Chain, WalletClient, TransactionReceipt } from "viem"
2
- import { createPublicClient, http, formatUnits } from "viem"
2
+ import { createPublicClient, http, formatUnits, ethAddress } from "viem"
3
3
  import type { TrailsApi } from "@0xsequence/trails-api"
4
4
  import type { MetaTxnReceipt, PrepareSendReturn, SendReturn } from "../types.js"
5
5
  import type { TransactionState } from "../../transactions.js"
@@ -21,7 +21,7 @@ import { getTestnetChainInfo } from "../../chains.js"
21
21
  import { getIsCustomCalldata } from "../../contractUtils.js"
22
22
  import {
23
23
  buildSameChainSameTokenTransactionParams,
24
- getIntent,
24
+ quoteIntent,
25
25
  commitIntent,
26
26
  } from "../../intents.js"
27
27
  import { estimateGasLimit } from "../../estimate.js"
@@ -36,6 +36,7 @@ import { getChainInfo } from "../../chains.js"
36
36
  import { getTransactionStateFromReceipt } from "../execution/transactionState.js"
37
37
  import { calcAmountUsdPrice } from "../../prices.js"
38
38
  import { pollIntentReceipt } from "../../intentReceiptPoller.js"
39
+ import { zeroAddress } from "viem"
39
40
 
40
41
  /**
41
42
  * @description
@@ -87,6 +88,7 @@ export async function handleSameChainSameToken({
87
88
  destinationTokenPriceUsd,
88
89
  originNativeTokenPriceUsd,
89
90
  slippageTolerance,
91
+ quoteProvider,
90
92
  checkoutOnHandlers,
91
93
  mode,
92
94
  fundMethod,
@@ -117,7 +119,8 @@ export async function handleSameChainSameToken({
117
119
  destinationTokenPriceUsd?: number | null
118
120
  originNativeTokenPriceUsd?: number | null
119
121
  slippageTolerance: string
120
- checkoutOnHandlers?: CheckoutOnHandlers
122
+ quoteProvider?: string | null
123
+ checkoutOnHandlers?: Partial<CheckoutOnHandlers>
121
124
  mode?: "pay" | "fund" | "earn" | "swap" | "receive"
122
125
  fundMethod?: string
123
126
  paymasterUrl?: string
@@ -174,7 +177,7 @@ export async function handleSameChainSameToken({
174
177
  destinationSalt,
175
178
  slippageTolerance,
176
179
  TradeType.EXACT_INPUT,
177
- "", // quoteProvider - empty for same-chain
180
+ quoteProvider || "",
178
181
  )
179
182
 
180
183
  logger.console.log(
@@ -182,14 +185,18 @@ export async function handleSameChainSameToken({
182
185
  intentArgs,
183
186
  )
184
187
 
185
- const { intent, gasFeeOptions } = await getIntent(trailsClient, intentArgs, {
186
- originTokenSymbol,
187
- destinationTokenSymbol,
188
- feeTokenSymbol: originTokenSymbol,
189
- })
188
+ const { intent, gasFeeOptions } = await quoteIntent(
189
+ trailsClient,
190
+ intentArgs,
191
+ {
192
+ originTokenSymbol,
193
+ destinationTokenSymbol,
194
+ feeTokenSymbol: originTokenSymbol,
195
+ },
196
+ )
190
197
 
191
- logger.console.log("[trails-sdk] Got intent:", intent)
192
- logger.console.log("[trails-sdk] Got gasFeeOptions:", gasFeeOptions)
198
+ logger.console.log("[trails-sdk] Quote intent:", intent)
199
+ logger.console.log("[trails-sdk] Quote intent gasFeeOptions:", gasFeeOptions)
193
200
 
194
201
  if (!intent.preconditions?.length || !intent.calls?.length) {
195
202
  throw new Error("Invalid intent")
@@ -280,9 +287,21 @@ export async function handleSameChainSameToken({
280
287
  // Use runtime selectedFeeToken if provided, otherwise fall back to the one from prepareSend
281
288
  const effectiveSelectedFeeToken =
282
289
  runtimeSelectedFeeToken ?? selectedFeeToken
290
+ // Check if gasless flow is enabled:
291
+ // - selectedFeeToken must exist and not be null/undefined
292
+ // - tokenAddress must not be native token (zeroAddress or ethAddress)
293
+ // - walletId must not be sequence-waas
283
294
  const effectiveGasless =
284
295
  effectiveSelectedFeeToken !== null &&
285
296
  effectiveSelectedFeeToken !== undefined &&
297
+ effectiveSelectedFeeToken.tokenAddress !== undefined &&
298
+ effectiveSelectedFeeToken.tokenAddress !== null &&
299
+ effectiveSelectedFeeToken.tokenAddress !== zeroAddress &&
300
+ effectiveSelectedFeeToken.tokenAddress.toLowerCase() !==
301
+ zeroAddress.toLowerCase() &&
302
+ effectiveSelectedFeeToken.tokenAddress !== ethAddress &&
303
+ effectiveSelectedFeeToken.tokenAddress.toLowerCase() !==
304
+ ethAddress.toLowerCase() &&
286
305
  walletId !== "sequence-waas"
287
306
  logger.console.log(
288
307
  "[trails-sdk] [FEE-SELECT] [GASLESS-FLOW] send() called for same-chain:",
@@ -379,7 +398,7 @@ export async function handleSameChainSameToken({
379
398
  explorerUrl: "",
380
399
  chainId: effectiveOriginChainId,
381
400
  state: "pending",
382
- label: effectiveGasless ? "Deposit" : "Execute",
401
+ label: "Deposit",
383
402
  }
384
403
 
385
404
  const executeState: TransactionState = {
@@ -399,6 +418,12 @@ export async function handleSameChainSameToken({
399
418
 
400
419
  try {
401
420
  onTransactionStateChange(localTransactionStates)
421
+ // Also trigger checkout status update if handler is provided
422
+ if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
423
+ checkoutOnHandlers.triggerCheckoutStatusUpdate(
424
+ localTransactionStates,
425
+ )
426
+ }
402
427
  } catch (error) {
403
428
  logger.console.error(
404
429
  "[trails-sdk] Error calling onTransactionStateChange:",
@@ -447,6 +472,7 @@ export async function handleSameChainSameToken({
447
472
  executeIntentFn,
448
473
  depositRecipientOverride: recipient,
449
474
  isSameChainSameToken: true,
475
+ destinationCalldata,
450
476
  })
451
477
 
452
478
  // attemptUserDepositTx handles both gasless and non-gasless flows
@@ -494,6 +520,12 @@ export async function handleSameChainSameToken({
494
520
  localTransactionStates[0]?.label,
495
521
  )
496
522
  onTransactionStateChange(localTransactionStates)
523
+ // Also trigger checkout status update if handler is provided
524
+ if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
525
+ checkoutOnHandlers.triggerCheckoutStatusUpdate(
526
+ localTransactionStates,
527
+ )
528
+ }
497
529
  } catch (error) {
498
530
  logger.console.error(
499
531
  "[trails-sdk] Error fetching deposit transaction receipt:",
@@ -531,6 +563,12 @@ export async function handleSameChainSameToken({
531
563
  localTransactionStates[1]?.label,
532
564
  )
533
565
  onTransactionStateChange(localTransactionStates)
566
+ // Also trigger checkout status update if handler is provided
567
+ if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
568
+ checkoutOnHandlers.triggerCheckoutStatusUpdate(
569
+ localTransactionStates,
570
+ )
571
+ }
534
572
  } catch (error) {
535
573
  logger.console.error(
536
574
  "[trails-sdk] Error fetching origin transaction receipt:",
@@ -560,6 +598,10 @@ export async function handleSameChainSameToken({
560
598
  ),
561
599
  ]
562
600
  onTransactionStateChange(updatedStates)
601
+ // Also trigger checkout status update if handler is provided
602
+ if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
603
+ checkoutOnHandlers.triggerCheckoutStatusUpdate(updatedStates)
604
+ }
563
605
  } catch (error) {
564
606
  logger.console.error(
565
607
  "[trails-sdk] Error calling onTransactionStateChange:",