0xtrails 0.8.2 → 0.8.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.
- package/dist/aave.d.ts.map +1 -1
- package/dist/{ccip-ru_Yzdas.js → ccip-Bs-QcZXm.js} +13 -13
- package/dist/constants.d.ts +2 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/fees.d.ts +11 -17
- package/dist/fees.d.ts.map +1 -1
- package/dist/{index-Si7cO9V7.js → index-C_EsqqSn.js} +20320 -20063
- package/dist/index.js +425 -847
- package/dist/intents.d.ts +1 -2
- package/dist/intents.d.ts.map +1 -1
- package/dist/prepareSend.d.ts.map +1 -1
- package/dist/recover.d.ts +8 -9
- package/dist/recover.d.ts.map +1 -1
- package/dist/tokenBalances.d.ts +51 -0
- package/dist/tokenBalances.d.ts.map +1 -1
- package/dist/trailsRouter.d.ts +15 -0
- package/dist/trailsRouter.d.ts.map +1 -1
- package/dist/transactionIntent/deposits/depositOrchestrator.d.ts +1 -3
- package/dist/transactionIntent/deposits/depositOrchestrator.d.ts.map +1 -1
- package/dist/transactionIntent/deposits/standardDeposit.d.ts +1 -3
- package/dist/transactionIntent/deposits/standardDeposit.d.ts.map +1 -1
- package/dist/transactionIntent/handlers/crossChain.d.ts +2 -4
- package/dist/transactionIntent/handlers/crossChain.d.ts.map +1 -1
- package/dist/transactionIntent/handlers/sameChainSameToken.d.ts +5 -4
- package/dist/transactionIntent/handlers/sameChainSameToken.d.ts.map +1 -1
- package/dist/transactionIntent/quote/normalizeQuote.d.ts +1 -1
- package/dist/transactionIntent/quote/normalizeQuote.d.ts.map +1 -1
- package/dist/transactionIntent/quote/quoteHelpers.d.ts +1 -1
- package/dist/transactionIntent/quote/quoteHelpers.d.ts.map +1 -1
- package/dist/transactionIntent/types.d.ts +11 -18
- package/dist/transactionIntent/types.d.ts.map +1 -1
- package/dist/widget/components/AccountIntentTransactionHistory.d.ts.map +1 -1
- package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
- package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
- package/dist/widget/components/SlippageToleranceSettings.d.ts +2 -1
- package/dist/widget/components/SlippageToleranceSettings.d.ts.map +1 -1
- package/dist/widget/css/compiled.css +1 -1
- package/dist/widget/hooks/useQuote.d.ts +94 -35
- package/dist/widget/hooks/useQuote.d.ts.map +1 -1
- package/dist/widget/hooks/useSendForm.d.ts +2 -2
- package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
- package/dist/widget/hooks/useTrailsSendTransaction.d.ts.map +1 -1
- package/dist/widget/index.js +1 -1
- package/package.json +2 -2
- package/src/aave.ts +4 -0
- package/src/constants.ts +4 -0
- package/src/fees.ts +47 -72
- package/src/intents.ts +1 -3
- package/src/morpho.ts +1 -1
- package/src/prepareSend.ts +42 -6
- package/src/recover.ts +116 -172
- package/src/tokenBalances.ts +301 -1
- package/src/trailsRouter.ts +77 -0
- package/src/transactionIntent/deposits/depositOrchestrator.ts +0 -6
- package/src/transactionIntent/deposits/standardDeposit.ts +167 -184
- package/src/transactionIntent/handlers/crossChain.ts +8 -11
- package/src/transactionIntent/handlers/sameChainSameToken.ts +619 -608
- package/src/transactionIntent/quote/normalizeQuote.ts +32 -46
- package/src/transactionIntent/quote/quoteHelpers.ts +4 -2
- package/src/transactionIntent/types.ts +11 -18
- package/src/widget/compiled.css +1 -1
- package/src/widget/components/AccountIntentTransactionHistory.tsx +50 -18
- package/src/widget/components/ClassicSwap.tsx +25 -30
- package/src/widget/components/QuoteDetails.tsx +18 -27
- package/src/widget/components/SlippageToleranceSettings.tsx +55 -25
- package/src/widget/hooks/useQuote.ts +317 -79
- package/src/widget/hooks/useSendForm.ts +123 -764
- package/src/widget/hooks/useTrailsSendTransaction.ts +0 -2
|
@@ -42,6 +42,7 @@ import { pollIntentReceipt } from "../../intentReceiptPoller.js"
|
|
|
42
42
|
import { isNativeToken } from "../../utils.js"
|
|
43
43
|
import { getAccountTransactionHistory } from "../../transactions.js"
|
|
44
44
|
import { POLLING_INTERVALS } from "../constants.js"
|
|
45
|
+
import { invalidateTokenBalancesCache } from "../../tokenBalances.js"
|
|
45
46
|
|
|
46
47
|
export async function handleSameChainSameToken({
|
|
47
48
|
mainSignerAddress,
|
|
@@ -54,7 +55,6 @@ export async function handleSameChainSameToken({
|
|
|
54
55
|
recipient,
|
|
55
56
|
walletClient,
|
|
56
57
|
onTransactionStateChange,
|
|
57
|
-
dryMode,
|
|
58
58
|
account,
|
|
59
59
|
chain,
|
|
60
60
|
transactionStates,
|
|
@@ -77,6 +77,7 @@ export async function handleSameChainSameToken({
|
|
|
77
77
|
isSmartWallet,
|
|
78
78
|
trailsApiKey,
|
|
79
79
|
trailsApiUrl,
|
|
80
|
+
tradeType = TradeType.EXACT_INPUT,
|
|
80
81
|
}: {
|
|
81
82
|
mainSignerAddress: string
|
|
82
83
|
originTokenAddress: string
|
|
@@ -89,14 +90,13 @@ export async function handleSameChainSameToken({
|
|
|
89
90
|
originChainId: number
|
|
90
91
|
walletClient: WalletClient
|
|
91
92
|
onTransactionStateChange: (transactionStates: TransactionState[]) => void
|
|
92
|
-
dryMode: boolean
|
|
93
93
|
account: Account
|
|
94
94
|
chain: Chain
|
|
95
95
|
transactionStates: TransactionState[]
|
|
96
96
|
sourceTokenPriceUsd?: number | null
|
|
97
97
|
destinationTokenPriceUsd?: number | null
|
|
98
98
|
originNativeTokenPriceUsd?: number | null
|
|
99
|
-
slippageTolerance: string
|
|
99
|
+
slippageTolerance: string | null
|
|
100
100
|
swapProvider?: RouteProvider | null
|
|
101
101
|
checkoutOnHandlers?: Partial<CheckoutOnHandlers>
|
|
102
102
|
mode?: "pay" | "fund" | "earn" | "swap"
|
|
@@ -116,6 +116,8 @@ export async function handleSameChainSameToken({
|
|
|
116
116
|
isSmartWallet?: boolean
|
|
117
117
|
trailsApiKey?: string
|
|
118
118
|
trailsApiUrl?: string
|
|
119
|
+
/** Trade type for the transaction. Defaults to EXACT_INPUT. */
|
|
120
|
+
tradeType?: TradeType
|
|
119
121
|
}): Promise<PrepareSendReturn> {
|
|
120
122
|
logger.console.log("[trails-sdk] isToSameToken && isToSameChain")
|
|
121
123
|
const testnet = isTestnetDebugMode()
|
|
@@ -273,143 +275,145 @@ export async function handleSameChainSameToken({
|
|
|
273
275
|
|
|
274
276
|
let originUserTxReceipt: TransactionReceipt | null = null
|
|
275
277
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
onTransactionStateChange([
|
|
280
|
-
{
|
|
281
|
-
transactionHash: "",
|
|
282
|
-
explorerUrl: "",
|
|
283
|
-
chainId: effectiveOriginChainId,
|
|
284
|
-
state: "pending",
|
|
285
|
-
label: "Execute",
|
|
286
|
-
},
|
|
287
|
-
])
|
|
288
|
-
} catch (error) {
|
|
289
|
-
logger.console.error(
|
|
290
|
-
"[trails-sdk] Error calling onTransactionStateChange:",
|
|
291
|
-
error,
|
|
292
|
-
)
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Show persistent toast for checkout flow
|
|
296
|
-
updatePersistentToast(
|
|
297
|
-
"Payment Started",
|
|
298
|
-
"Waiting for wallet confirmation...",
|
|
299
|
-
"info",
|
|
300
|
-
)
|
|
301
|
-
|
|
302
|
-
logger.console.log(
|
|
303
|
-
"[trails-sdk] origin call params",
|
|
304
|
-
originCallParams,
|
|
305
|
-
)
|
|
306
|
-
|
|
307
|
-
// Use sendOriginTransaction helper which handles gas estimation, fee boosting, and tracking
|
|
308
|
-
const txHash = await sendOriginTransaction(
|
|
309
|
-
account,
|
|
310
|
-
walletClient,
|
|
311
|
-
originCallParams as any,
|
|
278
|
+
// Update transaction state to pending
|
|
279
|
+
try {
|
|
280
|
+
onTransactionStateChange([
|
|
312
281
|
{
|
|
313
|
-
|
|
282
|
+
transactionHash: "",
|
|
283
|
+
explorerUrl: "",
|
|
284
|
+
chainId: effectiveOriginChainId,
|
|
285
|
+
state: "pending",
|
|
286
|
+
label: "Execute",
|
|
314
287
|
},
|
|
288
|
+
])
|
|
289
|
+
} catch (error) {
|
|
290
|
+
logger.console.error(
|
|
291
|
+
"[trails-sdk] Error calling onTransactionStateChange:",
|
|
292
|
+
error,
|
|
315
293
|
)
|
|
294
|
+
}
|
|
316
295
|
|
|
317
|
-
|
|
296
|
+
// Show persistent toast for checkout flow
|
|
297
|
+
updatePersistentToast(
|
|
298
|
+
"Payment Started",
|
|
299
|
+
"Waiting for wallet confirmation...",
|
|
300
|
+
"info",
|
|
301
|
+
)
|
|
318
302
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
303
|
+
logger.console.log(
|
|
304
|
+
"[trails-sdk] origin call params",
|
|
305
|
+
originCallParams,
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
// Use sendOriginTransaction helper which handles gas estimation, fee boosting, and tracking
|
|
309
|
+
const txHash = await sendOriginTransaction(
|
|
310
|
+
account,
|
|
311
|
+
walletClient,
|
|
312
|
+
originCallParams as any,
|
|
313
|
+
{
|
|
314
|
+
depositTokenAmountUsd: depositAmountUsd?.toString(),
|
|
315
|
+
},
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
logger.console.log("[trails-sdk] origin tx", txHash)
|
|
319
|
+
|
|
320
|
+
if (onOriginSend) {
|
|
321
|
+
onOriginSend()
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Wait for transaction receipt
|
|
325
|
+
const receipt = await effectivePublicClient.waitForTransactionReceipt(
|
|
326
|
+
{
|
|
327
|
+
hash: txHash,
|
|
328
|
+
},
|
|
329
|
+
)
|
|
330
|
+
logger.console.log("[trails-sdk] receipt", receipt)
|
|
331
|
+
originUserTxReceipt = receipt
|
|
322
332
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
333
|
+
// Track transaction confirmation
|
|
334
|
+
trackTransactionConfirmed({
|
|
335
|
+
transactionHash: txHash,
|
|
336
|
+
chainId: effectiveOriginChainId,
|
|
337
|
+
userAddress: account.address,
|
|
338
|
+
blockNumber: Number(receipt.blockNumber),
|
|
339
|
+
originTokenAddress: effectiveOriginTokenAddress,
|
|
340
|
+
depositTokenAmountUsd: depositAmountUsd?.toString(),
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
// Remove persistent toast and show success
|
|
344
|
+
const chainInfo = getChainInfo(effectiveOriginChainId)
|
|
345
|
+
updatePersistentToast(
|
|
346
|
+
"Transfer Confirmed",
|
|
347
|
+
`Your transaction on ${(chainInfo as any)?.name || "chain"} has been confirmed`,
|
|
348
|
+
"info",
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
// Update transaction state to completed
|
|
352
|
+
try {
|
|
353
|
+
onTransactionStateChange([
|
|
354
|
+
getTransactionStateFromReceipt(
|
|
355
|
+
originUserTxReceipt,
|
|
356
|
+
effectiveOriginChainId,
|
|
357
|
+
transactionStates[0]?.label || "Execute",
|
|
358
|
+
),
|
|
359
|
+
])
|
|
360
|
+
} catch (error) {
|
|
361
|
+
logger.console.error(
|
|
362
|
+
"[trails-sdk] Error calling onTransactionStateChange:",
|
|
363
|
+
error,
|
|
364
|
+
)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Track payment completion for same-chain same-token transaction
|
|
368
|
+
if (originUserTxReceipt && originUserTxReceipt.status === "success") {
|
|
369
|
+
trackPaymentCompleted({
|
|
335
370
|
userAddress: account.address,
|
|
336
|
-
|
|
371
|
+
originTxHash: originUserTxReceipt.transactionHash,
|
|
372
|
+
originChainId: effectiveOriginChainId,
|
|
373
|
+
mode,
|
|
374
|
+
fundMethod,
|
|
337
375
|
originTokenAddress: effectiveOriginTokenAddress,
|
|
376
|
+
originTokenSymbol,
|
|
377
|
+
originTokenAmount: swapAmount,
|
|
378
|
+
originTokenAmountFormatted: depositAmountFormatted.toString(),
|
|
379
|
+
destinationTokenAddress: effectiveOriginTokenAddress, // same chain same token
|
|
380
|
+
destinationTokenSymbol: destinationTokenSymbol,
|
|
381
|
+
depositTokenAmount: swapAmount,
|
|
382
|
+
depositTokenAmountFormatted: depositAmountFormatted.toString(),
|
|
338
383
|
depositTokenAmountUsd: depositAmountUsd?.toString(),
|
|
384
|
+
destinationTokenAmount: swapAmount, // same as deposit amount
|
|
385
|
+
destinationTokenAmountFormatted:
|
|
386
|
+
depositAmountFormatted.toString(), // same as deposit amount
|
|
387
|
+
destinationTokenAmountUsd: depositAmountUsd?.toString(), // same as deposit amount
|
|
388
|
+
originTokenDecimals: originTokenDecimals,
|
|
389
|
+
destinationTokenDecimals: originTokenDecimals, // same chain same token
|
|
339
390
|
})
|
|
340
391
|
|
|
341
|
-
//
|
|
342
|
-
|
|
343
|
-
updatePersistentToast(
|
|
344
|
-
"Transfer Confirmed",
|
|
345
|
-
`Your transaction on ${(chainInfo as any)?.name || "chain"} has been confirmed`,
|
|
346
|
-
"info",
|
|
347
|
-
)
|
|
392
|
+
// Invalidate token balances cache on successful transactions
|
|
393
|
+
invalidateTokenBalancesCache(account.address)
|
|
348
394
|
|
|
349
|
-
//
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
effectiveOriginChainId,
|
|
355
|
-
transactionStates[0]?.label || "Execute",
|
|
356
|
-
),
|
|
357
|
-
])
|
|
358
|
-
} catch (error) {
|
|
359
|
-
logger.console.error(
|
|
360
|
-
"[trails-sdk] Error calling onTransactionStateChange:",
|
|
361
|
-
error,
|
|
395
|
+
// Call onCheckoutComplete callback if provided
|
|
396
|
+
if (checkoutOnHandlers?.triggerCheckoutComplete) {
|
|
397
|
+
checkoutOnHandlers.triggerCheckoutComplete(
|
|
398
|
+
"success",
|
|
399
|
+
account.address,
|
|
362
400
|
)
|
|
363
401
|
}
|
|
402
|
+
} else if (originUserTxReceipt) {
|
|
403
|
+
trackPaymentError({
|
|
404
|
+
error: "Transaction failed",
|
|
405
|
+
userAddress: account.address,
|
|
406
|
+
mode,
|
|
407
|
+
fundMethod,
|
|
408
|
+
originTokenAddress: effectiveOriginTokenAddress,
|
|
409
|
+
})
|
|
364
410
|
|
|
365
|
-
//
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
) {
|
|
370
|
-
|
|
371
|
-
userAddress: account.address,
|
|
372
|
-
originTxHash: originUserTxReceipt.transactionHash,
|
|
373
|
-
originChainId: effectiveOriginChainId,
|
|
374
|
-
mode,
|
|
375
|
-
fundMethod,
|
|
376
|
-
originTokenAddress: effectiveOriginTokenAddress,
|
|
377
|
-
originTokenSymbol,
|
|
378
|
-
originTokenAmount: swapAmount,
|
|
379
|
-
originTokenAmountFormatted: depositAmountFormatted.toString(),
|
|
380
|
-
destinationTokenAddress: effectiveOriginTokenAddress, // same chain same token
|
|
381
|
-
destinationTokenSymbol: destinationTokenSymbol,
|
|
382
|
-
depositTokenAmount: swapAmount,
|
|
383
|
-
depositTokenAmountFormatted: depositAmountFormatted.toString(),
|
|
384
|
-
depositTokenAmountUsd: depositAmountUsd?.toString(),
|
|
385
|
-
destinationTokenAmount: swapAmount, // same as deposit amount
|
|
386
|
-
destinationTokenAmountFormatted:
|
|
387
|
-
depositAmountFormatted.toString(), // same as deposit amount
|
|
388
|
-
destinationTokenAmountUsd: depositAmountUsd?.toString(), // same as deposit amount
|
|
389
|
-
originTokenDecimals: originTokenDecimals,
|
|
390
|
-
destinationTokenDecimals: originTokenDecimals, // same chain same token
|
|
391
|
-
})
|
|
392
|
-
|
|
393
|
-
// Call onCheckoutComplete callback if provided
|
|
394
|
-
if (checkoutOnHandlers?.triggerCheckoutComplete) {
|
|
395
|
-
checkoutOnHandlers.triggerCheckoutComplete(
|
|
396
|
-
"success",
|
|
397
|
-
account.address,
|
|
398
|
-
)
|
|
399
|
-
}
|
|
400
|
-
} else if (originUserTxReceipt) {
|
|
401
|
-
trackPaymentError({
|
|
402
|
-
error: "Transaction failed",
|
|
403
|
-
userAddress: account.address,
|
|
404
|
-
mode,
|
|
405
|
-
fundMethod,
|
|
406
|
-
originTokenAddress: effectiveOriginTokenAddress,
|
|
407
|
-
})
|
|
408
|
-
|
|
409
|
-
// Call onCheckoutError callback if provided
|
|
410
|
-
if (checkoutOnHandlers?.triggerCheckoutError) {
|
|
411
|
-
checkoutOnHandlers.triggerCheckoutError("Transaction failed")
|
|
412
|
-
}
|
|
411
|
+
// Invalidate token balances cache even on failure (gas was spent)
|
|
412
|
+
invalidateTokenBalancesCache(account.address)
|
|
413
|
+
|
|
414
|
+
// Call onCheckoutError callback if provided
|
|
415
|
+
if (checkoutOnHandlers?.triggerCheckoutError) {
|
|
416
|
+
checkoutOnHandlers.triggerCheckoutError("Transaction failed")
|
|
413
417
|
}
|
|
414
418
|
}
|
|
415
419
|
|
|
@@ -450,18 +454,20 @@ export async function handleSameChainSameToken({
|
|
|
450
454
|
}
|
|
451
455
|
|
|
452
456
|
// For same-chain transactions, use Intent flow to support gasless deposits
|
|
457
|
+
// When using EXACT_OUTPUT, both origin and destination amounts should be swapAmount
|
|
458
|
+
// since it's same-chain same-token (no swap happening)
|
|
453
459
|
const intentArgs = await getIntentArgs(
|
|
454
460
|
mainSignerAddress,
|
|
455
461
|
effectiveOriginChainId,
|
|
456
462
|
effectiveOriginTokenAddress,
|
|
457
|
-
swapAmount, // originTokenAmount
|
|
463
|
+
swapAmount, // originTokenAmount - same for both trade types in same-chain same-token
|
|
458
464
|
effectiveOriginChainId, // same chain
|
|
459
465
|
effectiveOriginTokenAddress, // same token
|
|
460
|
-
"0", // destinationTokenAmount
|
|
466
|
+
tradeType === TradeType.EXACT_OUTPUT ? swapAmount : "0", // destinationTokenAmount
|
|
461
467
|
recipient,
|
|
462
468
|
destinationCalldata,
|
|
463
469
|
slippageTolerance,
|
|
464
|
-
|
|
470
|
+
tradeType,
|
|
465
471
|
swapProvider,
|
|
466
472
|
null,
|
|
467
473
|
undefined, // connector - not available in this context
|
|
@@ -691,487 +697,548 @@ export async function handleSameChainSameToken({
|
|
|
691
697
|
walletClient,
|
|
692
698
|
desiredChainId: effectiveOriginChainId,
|
|
693
699
|
})
|
|
694
|
-
if (!dryMode) {
|
|
695
|
-
// For gasless flows on same-chain same-token, we need to track 2 transactions:
|
|
696
|
-
// [0] = Deposit transaction (relayed)
|
|
697
|
-
// [1] = Origin intent transaction (executed on chain after deposit is confirmed)
|
|
698
|
-
|
|
699
|
-
// Initialize local transaction states for gasless flows to track both deposit and execute
|
|
700
|
-
const createInitialStates = (): TransactionState[] => {
|
|
701
|
-
const depositState: TransactionState = {
|
|
702
|
-
transactionHash: "",
|
|
703
|
-
explorerUrl: "",
|
|
704
|
-
chainId: effectiveOriginChainId,
|
|
705
|
-
state: "pending",
|
|
706
|
-
label: "Deposit",
|
|
707
|
-
}
|
|
708
700
|
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
701
|
+
// For gasless flows on same-chain same-token, we need to track 2 transactions:
|
|
702
|
+
// [0] = Deposit transaction (relayed)
|
|
703
|
+
// [1] = Origin intent transaction (executed on chain after deposit is confirmed)
|
|
704
|
+
|
|
705
|
+
// Initialize local transaction states for gasless flows to track both deposit and execute
|
|
706
|
+
const createInitialStates = (): TransactionState[] => {
|
|
707
|
+
const depositState: TransactionState = {
|
|
708
|
+
transactionHash: "",
|
|
709
|
+
explorerUrl: "",
|
|
710
|
+
chainId: effectiveOriginChainId,
|
|
711
|
+
state: "pending",
|
|
712
|
+
label: "Deposit",
|
|
713
|
+
}
|
|
716
714
|
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
715
|
+
const executeState: TransactionState = {
|
|
716
|
+
transactionHash: "",
|
|
717
|
+
explorerUrl: "",
|
|
718
|
+
chainId: effectiveOriginChainId,
|
|
719
|
+
state: "pending",
|
|
720
|
+
label: "Execute",
|
|
720
721
|
}
|
|
721
722
|
|
|
722
|
-
|
|
723
|
+
return effectiveGasless
|
|
724
|
+
? [depositState, executeState]
|
|
725
|
+
: [depositState]
|
|
726
|
+
}
|
|
723
727
|
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
} catch (error) {
|
|
733
|
-
logger.console.error(
|
|
734
|
-
"[trails-sdk] Error calling onTransactionStateChange:",
|
|
735
|
-
error,
|
|
728
|
+
const localTransactionStates = createInitialStates()
|
|
729
|
+
|
|
730
|
+
try {
|
|
731
|
+
onTransactionStateChange(localTransactionStates)
|
|
732
|
+
// Also trigger checkout status update if handler is provided
|
|
733
|
+
if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
|
|
734
|
+
checkoutOnHandlers.triggerCheckoutStatusUpdate(
|
|
735
|
+
localTransactionStates,
|
|
736
736
|
)
|
|
737
737
|
}
|
|
738
|
+
} catch (error) {
|
|
739
|
+
logger.console.error(
|
|
740
|
+
"[trails-sdk] Error calling onTransactionStateChange:",
|
|
741
|
+
error,
|
|
742
|
+
)
|
|
743
|
+
}
|
|
738
744
|
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
745
|
+
// For gasless flows, pass the localTransactionStates instead of the input parameter
|
|
746
|
+
// This ensures we track both deposit and execute transactions
|
|
747
|
+
const statesForDeposit = effectiveGasless
|
|
748
|
+
? localTransactionStates
|
|
749
|
+
: transactionStates
|
|
750
|
+
|
|
751
|
+
// Use attemptUserDepositTx which handles both gasless and non-gasless flows
|
|
752
|
+
// For non-gasless same-chain same-token, pass recipient to send directly to user instead of intent contract
|
|
753
|
+
depositUserTxnReceipt = await attemptUserDepositTx({
|
|
754
|
+
originTokenAddress: effectiveOriginTokenAddress,
|
|
755
|
+
paymasterUrl,
|
|
756
|
+
chain: effectiveOriginChain,
|
|
757
|
+
account,
|
|
758
|
+
depositAmount: swapAmount,
|
|
759
|
+
originIntentAddress: intent.originIntentAddress,
|
|
760
|
+
onOriginSend,
|
|
761
|
+
publicClient: effectivePublicClient,
|
|
762
|
+
walletClient,
|
|
763
|
+
destinationTokenDecimals: originTokenDecimals,
|
|
764
|
+
sourceTokenDecimals: originTokenDecimals,
|
|
765
|
+
sourceTokenPriceUsd: sourceTokenPriceUsd ?? null,
|
|
766
|
+
destinationTokenPriceUsd: destinationTokenPriceUsd ?? null,
|
|
767
|
+
swapAmount,
|
|
768
|
+
onTransactionStateChange,
|
|
769
|
+
transactionStates: statesForDeposit,
|
|
770
|
+
fundMethod,
|
|
771
|
+
originTokenSymbol,
|
|
772
|
+
destinationTokenSymbol,
|
|
773
|
+
depositAmountUsd,
|
|
774
|
+
feeOptions: gasFeeOptions,
|
|
775
|
+
trailsContracts,
|
|
776
|
+
trailsClient,
|
|
777
|
+
selectedFeeOption: effectiveSelectedFeeOption,
|
|
778
|
+
walletId,
|
|
779
|
+
abortSignal,
|
|
780
|
+
checkoutOnHandlers,
|
|
781
|
+
intentId: intent.intentId,
|
|
782
|
+
executeIntentFn,
|
|
783
|
+
depositRecipientOverride: recipient,
|
|
784
|
+
isSameChainSameToken: true,
|
|
785
|
+
destinationCalldata,
|
|
786
|
+
})
|
|
744
787
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
account,
|
|
752
|
-
depositAmount: swapAmount,
|
|
753
|
-
originIntentAddress: intent.originIntentAddress,
|
|
754
|
-
onOriginSend,
|
|
755
|
-
publicClient: effectivePublicClient,
|
|
756
|
-
walletClient,
|
|
757
|
-
destinationTokenDecimals: originTokenDecimals,
|
|
758
|
-
sourceTokenDecimals: originTokenDecimals,
|
|
759
|
-
fee: "0",
|
|
760
|
-
dryMode,
|
|
761
|
-
sourceTokenPriceUsd: sourceTokenPriceUsd ?? null,
|
|
762
|
-
destinationTokenPriceUsd: destinationTokenPriceUsd ?? null,
|
|
763
|
-
swapAmount,
|
|
764
|
-
onTransactionStateChange,
|
|
765
|
-
transactionStates: statesForDeposit,
|
|
766
|
-
fundMethod,
|
|
767
|
-
originTokenSymbol,
|
|
768
|
-
destinationTokenSymbol,
|
|
769
|
-
depositAmountUsd,
|
|
770
|
-
feeOptions: gasFeeOptions,
|
|
771
|
-
trailsContracts,
|
|
772
|
-
trailsClient,
|
|
773
|
-
selectedFeeOption: effectiveSelectedFeeOption,
|
|
774
|
-
walletId,
|
|
775
|
-
abortSignal,
|
|
776
|
-
checkoutOnHandlers,
|
|
777
|
-
intentId: intent.intentId,
|
|
778
|
-
executeIntentFn,
|
|
779
|
-
depositRecipientOverride: recipient,
|
|
780
|
-
isSameChainSameToken: true,
|
|
781
|
-
destinationCalldata,
|
|
782
|
-
})
|
|
788
|
+
// attemptUserDepositTx handles both gasless and non-gasless flows
|
|
789
|
+
// Transaction state updates and toasts are handled within that function
|
|
790
|
+
logger.console.log("[trails-sdk] Deposit transaction completed", {
|
|
791
|
+
hasReceipt: !!depositUserTxnReceipt,
|
|
792
|
+
isGasless: effectiveGasless,
|
|
793
|
+
})
|
|
783
794
|
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
795
|
+
// For QR code mode, we need to detect the deposit first, call executeIntent, then start polling
|
|
796
|
+
// For gasless flows, we can start polling immediately
|
|
797
|
+
if (fundMethod === "qr-code" || fundMethod === "exchange") {
|
|
798
|
+
logger.console.log(
|
|
799
|
+
"[trails-sdk] QR code mode: waiting for deposit transaction before starting polling",
|
|
800
|
+
)
|
|
801
|
+
// Use the same approach as cross-chain: check for deposit via indexer
|
|
802
|
+
const checkForDepositTx = async () => {
|
|
803
|
+
while (true) {
|
|
804
|
+
// Check if we should abort
|
|
805
|
+
if (abortSignal?.aborted) {
|
|
806
|
+
logger.console.log(
|
|
807
|
+
"[trails-sdk] Aborting deposit tx check due to abort signal",
|
|
808
|
+
)
|
|
809
|
+
return null
|
|
810
|
+
}
|
|
790
811
|
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
"[trails-sdk] QR code mode: waiting for deposit transaction before starting polling",
|
|
796
|
-
)
|
|
797
|
-
// Use the same approach as cross-chain: check for deposit via indexer
|
|
798
|
-
const checkForDepositTx = async () => {
|
|
799
|
-
while (true) {
|
|
800
|
-
// Check if we should abort
|
|
801
|
-
if (abortSignal?.aborted) {
|
|
802
|
-
logger.console.log(
|
|
803
|
-
"[trails-sdk] Aborting deposit tx check due to abort signal",
|
|
812
|
+
try {
|
|
813
|
+
if (!sequenceIndexerUrl) {
|
|
814
|
+
throw new Error(
|
|
815
|
+
"sequenceIndexerUrl is required for QR code mode",
|
|
804
816
|
)
|
|
805
|
-
return null
|
|
806
817
|
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
throw new Error(
|
|
811
|
-
"sequenceIndexerUrl is required for QR code mode",
|
|
812
|
-
)
|
|
813
|
-
}
|
|
814
|
-
if (!sequenceProjectAccessKey) {
|
|
815
|
-
throw new Error(
|
|
816
|
-
"sequenceProjectAccessKey is required for QR code mode",
|
|
817
|
-
)
|
|
818
|
-
}
|
|
819
|
-
const response = await getAccountTransactionHistory({
|
|
820
|
-
chainId: effectiveOriginChainId,
|
|
821
|
-
accountAddress: intent.originIntentAddress,
|
|
822
|
-
abortSignal,
|
|
823
|
-
apiKey: sequenceProjectAccessKey,
|
|
824
|
-
indexerUrl: sequenceIndexerUrl,
|
|
825
|
-
})
|
|
826
|
-
logger.console.log(
|
|
827
|
-
"[trails-sdk] getAccountTransactionHistory response",
|
|
828
|
-
response,
|
|
818
|
+
if (!sequenceProjectAccessKey) {
|
|
819
|
+
throw new Error(
|
|
820
|
+
"sequenceProjectAccessKey is required for QR code mode",
|
|
829
821
|
)
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
depositAddress: intent.originIntentAddress,
|
|
851
|
-
requiredAmount: quote.originAmount,
|
|
852
|
-
tokenAddress: effectiveOriginTokenAddress,
|
|
853
|
-
},
|
|
822
|
+
}
|
|
823
|
+
const response = await getAccountTransactionHistory({
|
|
824
|
+
chainId: effectiveOriginChainId,
|
|
825
|
+
accountAddress: intent.originIntentAddress,
|
|
826
|
+
abortSignal,
|
|
827
|
+
apiKey: sequenceProjectAccessKey,
|
|
828
|
+
indexerUrl: sequenceIndexerUrl,
|
|
829
|
+
})
|
|
830
|
+
logger.console.log(
|
|
831
|
+
"[trails-sdk] getAccountTransactionHistory response",
|
|
832
|
+
response,
|
|
833
|
+
)
|
|
834
|
+
if (response.transactions.length > 0) {
|
|
835
|
+
const tx = response.transactions[0]
|
|
836
|
+
if (!tx?.txnHash) {
|
|
837
|
+
await new Promise((resolve) =>
|
|
838
|
+
setTimeout(
|
|
839
|
+
resolve,
|
|
840
|
+
POLLING_INTERVALS.TRANSACTION_HISTORY,
|
|
841
|
+
),
|
|
854
842
|
)
|
|
843
|
+
continue
|
|
844
|
+
}
|
|
845
|
+
const depositTxReceipt =
|
|
846
|
+
await effectivePublicClient.getTransactionReceipt({
|
|
847
|
+
hash: tx.txnHash as `0x${string}`,
|
|
848
|
+
})
|
|
855
849
|
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
850
|
+
// Validate that the deposit amount is sufficient before proceeding
|
|
851
|
+
logger.console.log(
|
|
852
|
+
"[trails-sdk] Checking if deposit amount is sufficient",
|
|
853
|
+
{
|
|
854
|
+
depositAddress: intent.originIntentAddress,
|
|
855
|
+
requiredAmount: quote.originAmount,
|
|
860
856
|
tokenAddress: effectiveOriginTokenAddress,
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
})
|
|
857
|
+
},
|
|
858
|
+
)
|
|
864
859
|
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
Number(balanceCheck.requiredAmount)) *
|
|
874
|
-
100
|
|
875
|
-
).toFixed(2),
|
|
876
|
-
},
|
|
877
|
-
)
|
|
878
|
-
// Continue polling - don't break out of the loop yet
|
|
879
|
-
await new Promise((resolve) =>
|
|
880
|
-
setTimeout(
|
|
881
|
-
resolve,
|
|
882
|
-
POLLING_INTERVALS.TRANSACTION_HISTORY,
|
|
883
|
-
),
|
|
884
|
-
)
|
|
885
|
-
continue
|
|
886
|
-
}
|
|
860
|
+
const balanceCheck = await checkAccountBalance({
|
|
861
|
+
account: {
|
|
862
|
+
address: intent.originIntentAddress as `0x${string}`,
|
|
863
|
+
} as Account,
|
|
864
|
+
tokenAddress: effectiveOriginTokenAddress,
|
|
865
|
+
depositAmount: quote.originAmount,
|
|
866
|
+
publicClient: effectivePublicClient,
|
|
867
|
+
})
|
|
887
868
|
|
|
888
|
-
|
|
889
|
-
|
|
869
|
+
if (!balanceCheck.hasEnoughBalance) {
|
|
870
|
+
logger.console.warn(
|
|
871
|
+
"[trails-sdk] Deposit amount is insufficient, continuing to poll",
|
|
890
872
|
{
|
|
891
873
|
balance: balanceCheck.balanceFormatted,
|
|
892
874
|
required: balanceCheck.requiredAmountFormatted,
|
|
875
|
+
percentComplete: (
|
|
876
|
+
(Number(balanceCheck.balance) /
|
|
877
|
+
Number(balanceCheck.requiredAmount)) *
|
|
878
|
+
100
|
|
879
|
+
).toFixed(2),
|
|
893
880
|
},
|
|
894
881
|
)
|
|
882
|
+
// Continue polling - don't break out of the loop yet
|
|
883
|
+
await new Promise((resolve) =>
|
|
884
|
+
setTimeout(
|
|
885
|
+
resolve,
|
|
886
|
+
POLLING_INTERVALS.TRANSACTION_HISTORY,
|
|
887
|
+
),
|
|
888
|
+
)
|
|
889
|
+
continue
|
|
890
|
+
}
|
|
895
891
|
|
|
896
|
-
|
|
892
|
+
logger.console.log(
|
|
893
|
+
"[trails-sdk] Deposit amount is sufficient, proceeding",
|
|
894
|
+
{
|
|
895
|
+
balance: balanceCheck.balanceFormatted,
|
|
896
|
+
required: balanceCheck.requiredAmountFormatted,
|
|
897
|
+
},
|
|
898
|
+
)
|
|
899
|
+
|
|
900
|
+
depositUserTxnReceipt = depositTxReceipt
|
|
897
901
|
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
+
localTransactionStates[0] = getTransactionStateFromReceipt(
|
|
903
|
+
depositTxReceipt,
|
|
904
|
+
effectiveOriginChainId,
|
|
905
|
+
localTransactionStates[0]?.label,
|
|
906
|
+
)
|
|
907
|
+
onTransactionStateChange(localTransactionStates)
|
|
908
|
+
if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
|
|
909
|
+
checkoutOnHandlers.triggerCheckoutStatusUpdate(
|
|
910
|
+
localTransactionStates,
|
|
902
911
|
)
|
|
903
|
-
|
|
904
|
-
if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
|
|
905
|
-
checkoutOnHandlers.triggerCheckoutStatusUpdate(
|
|
906
|
-
localTransactionStates,
|
|
907
|
-
)
|
|
908
|
-
}
|
|
912
|
+
}
|
|
909
913
|
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
914
|
+
// Call executeIntent after detecting deposit transaction (for QR code mode)
|
|
915
|
+
// This triggers the backend to start executing the intent
|
|
916
|
+
// Must be called BEFORE waitIntentReceipt polling starts
|
|
917
|
+
if (
|
|
918
|
+
depositTxReceipt.status === "success" &&
|
|
919
|
+
intent.intentId
|
|
920
|
+
) {
|
|
921
|
+
logger.console.log(
|
|
922
|
+
"[trails-sdk] Calling executeIntent with detected deposit transaction hash (QR code mode)",
|
|
923
|
+
{
|
|
924
|
+
intentId: intent.intentId,
|
|
925
|
+
txHash: depositTxReceipt.transactionHash,
|
|
926
|
+
fundMethod,
|
|
927
|
+
},
|
|
928
|
+
)
|
|
929
|
+
try {
|
|
930
|
+
const executeIntentFnToUse =
|
|
931
|
+
executeIntentFn ||
|
|
932
|
+
trailsClient.executeIntent.bind(trailsClient)
|
|
933
|
+
await executeIntentFnToUse({
|
|
934
|
+
intentId: intent.intentId,
|
|
935
|
+
depositTransactionHash:
|
|
936
|
+
depositTxReceipt.transactionHash,
|
|
937
|
+
})
|
|
917
938
|
logger.console.log(
|
|
918
|
-
"[trails-sdk]
|
|
919
|
-
{
|
|
920
|
-
intentId: intent.intentId,
|
|
921
|
-
txHash: depositTxReceipt.transactionHash,
|
|
922
|
-
fundMethod,
|
|
923
|
-
},
|
|
939
|
+
"[trails-sdk] executeIntent completed successfully (QR code mode)",
|
|
924
940
|
)
|
|
925
|
-
try {
|
|
926
|
-
const executeIntentFnToUse =
|
|
927
|
-
executeIntentFn ||
|
|
928
|
-
trailsClient.executeIntent.bind(trailsClient)
|
|
929
|
-
await executeIntentFnToUse({
|
|
930
|
-
intentId: intent.intentId,
|
|
931
|
-
depositTransactionHash:
|
|
932
|
-
depositTxReceipt.transactionHash,
|
|
933
|
-
})
|
|
934
|
-
logger.console.log(
|
|
935
|
-
"[trails-sdk] executeIntent completed successfully (QR code mode)",
|
|
936
|
-
)
|
|
937
941
|
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
}
|
|
942
|
+
// Now start polling for intent receipt
|
|
943
|
+
break
|
|
944
|
+
} catch (error) {
|
|
945
|
+
logger.console.error(
|
|
946
|
+
"[trails-sdk] Error calling executeIntent (QR code mode):",
|
|
947
|
+
error,
|
|
948
|
+
)
|
|
946
949
|
}
|
|
950
|
+
}
|
|
947
951
|
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
}
|
|
951
|
-
break
|
|
952
|
+
if (onOriginSend) {
|
|
953
|
+
onOriginSend()
|
|
952
954
|
}
|
|
953
|
-
|
|
954
|
-
logger.console.error("Error checking for deposit tx", error)
|
|
955
|
+
break
|
|
955
956
|
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
)
|
|
957
|
+
} catch (error) {
|
|
958
|
+
logger.console.error("Error checking for deposit tx", error)
|
|
959
959
|
}
|
|
960
|
+
await new Promise((resolve) =>
|
|
961
|
+
setTimeout(resolve, POLLING_INTERVALS.TRANSACTION_HISTORY),
|
|
962
|
+
)
|
|
960
963
|
}
|
|
964
|
+
}
|
|
961
965
|
|
|
962
|
-
|
|
963
|
-
|
|
966
|
+
// Wait for deposit and executeIntent
|
|
967
|
+
await checkForDepositTx()
|
|
964
968
|
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
969
|
+
// Now start polling for intent receipt after executeIntent has been called
|
|
970
|
+
logger.console.log(
|
|
971
|
+
"[trails-sdk] Starting polling for intent receipt after executeIntent (QR code mode)",
|
|
972
|
+
)
|
|
973
|
+
try {
|
|
974
|
+
await pollIntentReceipt({
|
|
975
|
+
intentId: intent.intentId,
|
|
976
|
+
trailsClient,
|
|
977
|
+
callbacks: {
|
|
978
|
+
onOriginTransactionFound: async (originTxHash) => {
|
|
979
|
+
logger.console.log(
|
|
980
|
+
"[trails-sdk] Origin intent transaction discovered for same-chain QR code flow",
|
|
981
|
+
{ originTxHash },
|
|
982
|
+
)
|
|
983
|
+
|
|
984
|
+
if (localTransactionStates[1]) {
|
|
985
|
+
try {
|
|
986
|
+
const originTxReceipt =
|
|
987
|
+
await effectivePublicClient.getTransactionReceipt({
|
|
988
|
+
hash: originTxHash as `0x${string}`,
|
|
989
|
+
})
|
|
979
990
|
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
effectiveOriginChainId,
|
|
991
|
-
localTransactionStates[1]?.label,
|
|
992
|
-
)
|
|
993
|
-
onTransactionStateChange(localTransactionStates)
|
|
994
|
-
if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
|
|
995
|
-
checkoutOnHandlers.triggerCheckoutStatusUpdate(
|
|
996
|
-
localTransactionStates,
|
|
997
|
-
)
|
|
998
|
-
}
|
|
999
|
-
|
|
1000
|
-
originIntentTransaction = {
|
|
1001
|
-
txnHash: originTxHash,
|
|
1002
|
-
} as IntentTransaction
|
|
1003
|
-
} catch (error) {
|
|
1004
|
-
logger.console.error(
|
|
1005
|
-
"[trails-sdk] Error fetching origin transaction receipt:",
|
|
1006
|
-
error,
|
|
991
|
+
localTransactionStates[1] =
|
|
992
|
+
getTransactionStateFromReceipt(
|
|
993
|
+
originTxReceipt,
|
|
994
|
+
effectiveOriginChainId,
|
|
995
|
+
localTransactionStates[1]?.label,
|
|
996
|
+
)
|
|
997
|
+
onTransactionStateChange(localTransactionStates)
|
|
998
|
+
if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
|
|
999
|
+
checkoutOnHandlers.triggerCheckoutStatusUpdate(
|
|
1000
|
+
localTransactionStates,
|
|
1007
1001
|
)
|
|
1008
1002
|
}
|
|
1003
|
+
|
|
1004
|
+
originIntentTransaction = {
|
|
1005
|
+
txnHash: originTxHash,
|
|
1006
|
+
} as IntentTransaction
|
|
1007
|
+
} catch (error) {
|
|
1008
|
+
logger.console.error(
|
|
1009
|
+
"[trails-sdk] Error fetching origin transaction receipt:",
|
|
1010
|
+
error,
|
|
1011
|
+
)
|
|
1009
1012
|
}
|
|
1010
|
-
}
|
|
1013
|
+
}
|
|
1011
1014
|
},
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
}
|
|
1020
|
-
} else if (effectiveGasless && !depositUserTxnReceipt) {
|
|
1021
|
-
// For gasless flows (non-QR code), start polling immediately
|
|
1022
|
-
logger.console.log(
|
|
1023
|
-
"[trails-sdk] Starting unified polling for gasless same-chain transaction with 2 states",
|
|
1015
|
+
},
|
|
1016
|
+
maxWaitTime: 120000, // 120 seconds max wait
|
|
1017
|
+
})
|
|
1018
|
+
} catch (error) {
|
|
1019
|
+
logger.console.error(
|
|
1020
|
+
"[trails-sdk] Error polling for intent receipt (QR code mode):",
|
|
1021
|
+
error,
|
|
1024
1022
|
)
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1023
|
+
}
|
|
1024
|
+
} else if (effectiveGasless && !depositUserTxnReceipt) {
|
|
1025
|
+
// For gasless flows (non-QR code), start polling immediately
|
|
1026
|
+
logger.console.log(
|
|
1027
|
+
"[trails-sdk] Starting unified polling for gasless same-chain transaction with 2 states",
|
|
1028
|
+
)
|
|
1029
|
+
try {
|
|
1030
|
+
await pollIntentReceipt({
|
|
1031
|
+
intentId: intent.intentId,
|
|
1032
|
+
trailsClient,
|
|
1033
|
+
callbacks: {
|
|
1034
|
+
onDepositTransactionFound: async (txHash) => {
|
|
1035
|
+
logger.console.log(
|
|
1036
|
+
"[trails-sdk] Deposit transaction discovered for same-chain gasless flow",
|
|
1037
|
+
{ txHash },
|
|
1038
|
+
)
|
|
1039
|
+
|
|
1040
|
+
if (localTransactionStates[0]) {
|
|
1041
|
+
try {
|
|
1042
|
+
const depositTxReceipt =
|
|
1043
|
+
await effectivePublicClient.getTransactionReceipt({
|
|
1044
|
+
hash: txHash as `0x${string}`,
|
|
1045
|
+
})
|
|
1035
1046
|
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
effectiveOriginChainId,
|
|
1047
|
-
localTransactionStates[0]?.label,
|
|
1048
|
-
)
|
|
1049
|
-
onTransactionStateChange(localTransactionStates)
|
|
1050
|
-
if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
|
|
1051
|
-
checkoutOnHandlers.triggerCheckoutStatusUpdate(
|
|
1052
|
-
localTransactionStates,
|
|
1053
|
-
)
|
|
1054
|
-
}
|
|
1055
|
-
|
|
1056
|
-
depositUserTxnReceipt = depositTxReceipt
|
|
1057
|
-
} catch (error) {
|
|
1058
|
-
logger.console.error(
|
|
1059
|
-
"[trails-sdk] Error fetching deposit transaction receipt:",
|
|
1060
|
-
error,
|
|
1047
|
+
localTransactionStates[0] =
|
|
1048
|
+
getTransactionStateFromReceipt(
|
|
1049
|
+
depositTxReceipt,
|
|
1050
|
+
effectiveOriginChainId,
|
|
1051
|
+
localTransactionStates[0]?.label,
|
|
1052
|
+
)
|
|
1053
|
+
onTransactionStateChange(localTransactionStates)
|
|
1054
|
+
if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
|
|
1055
|
+
checkoutOnHandlers.triggerCheckoutStatusUpdate(
|
|
1056
|
+
localTransactionStates,
|
|
1061
1057
|
)
|
|
1062
1058
|
}
|
|
1059
|
+
|
|
1060
|
+
depositUserTxnReceipt = depositTxReceipt
|
|
1061
|
+
} catch (error) {
|
|
1062
|
+
logger.console.error(
|
|
1063
|
+
"[trails-sdk] Error fetching deposit transaction receipt:",
|
|
1064
|
+
error,
|
|
1065
|
+
)
|
|
1063
1066
|
}
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1067
|
+
}
|
|
1068
|
+
},
|
|
1069
|
+
onOriginTransactionFound: async (originTxHash) => {
|
|
1070
|
+
logger.console.log(
|
|
1071
|
+
"[trails-sdk] Origin intent transaction discovered for same-chain gasless flow",
|
|
1072
|
+
{ originTxHash },
|
|
1073
|
+
)
|
|
1070
1074
|
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
)
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
originIntentTransaction = {
|
|
1092
|
-
txnHash: originTxHash,
|
|
1093
|
-
} as IntentTransaction
|
|
1094
|
-
} catch (error) {
|
|
1095
|
-
logger.console.error(
|
|
1096
|
-
"[trails-sdk] Error fetching origin transaction receipt:",
|
|
1097
|
-
error,
|
|
1075
|
+
if (localTransactionStates[1]) {
|
|
1076
|
+
try {
|
|
1077
|
+
const originTxReceipt =
|
|
1078
|
+
await effectivePublicClient.getTransactionReceipt({
|
|
1079
|
+
hash: originTxHash as `0x${string}`,
|
|
1080
|
+
})
|
|
1081
|
+
|
|
1082
|
+
localTransactionStates[1] =
|
|
1083
|
+
getTransactionStateFromReceipt(
|
|
1084
|
+
originTxReceipt,
|
|
1085
|
+
effectiveOriginChainId,
|
|
1086
|
+
localTransactionStates[1]?.label,
|
|
1087
|
+
)
|
|
1088
|
+
onTransactionStateChange(localTransactionStates)
|
|
1089
|
+
if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
|
|
1090
|
+
checkoutOnHandlers.triggerCheckoutStatusUpdate(
|
|
1091
|
+
localTransactionStates,
|
|
1098
1092
|
)
|
|
1099
1093
|
}
|
|
1094
|
+
|
|
1095
|
+
originIntentTransaction = {
|
|
1096
|
+
txnHash: originTxHash,
|
|
1097
|
+
} as IntentTransaction
|
|
1098
|
+
} catch (error) {
|
|
1099
|
+
logger.console.error(
|
|
1100
|
+
"[trails-sdk] Error fetching origin transaction receipt:",
|
|
1101
|
+
error,
|
|
1102
|
+
)
|
|
1100
1103
|
}
|
|
1101
|
-
}
|
|
1104
|
+
}
|
|
1102
1105
|
},
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1106
|
+
},
|
|
1107
|
+
maxWaitTime: 120000, // 120 seconds max wait for both transactions
|
|
1108
|
+
})
|
|
1109
|
+
} catch (error) {
|
|
1110
|
+
logger.console.error(
|
|
1111
|
+
"[trails-sdk] Error polling for gasless same-chain transactions:",
|
|
1112
|
+
error,
|
|
1113
|
+
)
|
|
1111
1114
|
}
|
|
1115
|
+
}
|
|
1112
1116
|
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
}
|
|
1129
|
-
} catch (error) {
|
|
1130
|
-
logger.console.error(
|
|
1131
|
-
"[trails-sdk] Error calling onTransactionStateChange:",
|
|
1132
|
-
error,
|
|
1133
|
-
)
|
|
1117
|
+
// For non-gasless flows, update the transaction state with the deposit receipt
|
|
1118
|
+
// For gasless flows, skip this as localTransactionStates is already maintained by polling
|
|
1119
|
+
if (depositUserTxnReceipt && !effectiveGasless) {
|
|
1120
|
+
try {
|
|
1121
|
+
const updatedStates = [
|
|
1122
|
+
getTransactionStateFromReceipt(
|
|
1123
|
+
depositUserTxnReceipt,
|
|
1124
|
+
effectiveOriginChainId,
|
|
1125
|
+
transactionStates[0]?.label,
|
|
1126
|
+
),
|
|
1127
|
+
]
|
|
1128
|
+
onTransactionStateChange(updatedStates)
|
|
1129
|
+
// Also trigger checkout status update if handler is provided
|
|
1130
|
+
if (checkoutOnHandlers?.triggerCheckoutStatusUpdate) {
|
|
1131
|
+
checkoutOnHandlers.triggerCheckoutStatusUpdate(updatedStates)
|
|
1134
1132
|
}
|
|
1133
|
+
} catch (error) {
|
|
1134
|
+
logger.console.error(
|
|
1135
|
+
"[trails-sdk] Error calling onTransactionStateChange:",
|
|
1136
|
+
error,
|
|
1137
|
+
)
|
|
1135
1138
|
}
|
|
1139
|
+
}
|
|
1136
1140
|
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
}
|
|
1141
|
+
// Show conditional toast based on transaction status
|
|
1142
|
+
const chainInfo = getChainInfo(effectiveOriginChainId)
|
|
1143
|
+
if (depositUserTxnReceipt) {
|
|
1144
|
+
if (depositUserTxnReceipt?.status === "success") {
|
|
1145
|
+
updatePersistentToast(
|
|
1146
|
+
"Transfer Confirmed",
|
|
1147
|
+
`Your transaction on ${chainInfo?.name || "chain"} has been confirmed`,
|
|
1148
|
+
"info",
|
|
1149
|
+
)
|
|
1150
|
+
} else {
|
|
1151
|
+
updatePersistentToast(
|
|
1152
|
+
"Transfer Failed",
|
|
1153
|
+
`Your transaction on ${chainInfo?.name || "chain"} failed`,
|
|
1154
|
+
"error",
|
|
1155
|
+
)
|
|
1153
1156
|
}
|
|
1157
|
+
}
|
|
1154
1158
|
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1159
|
+
// Track payment completion for same-chain same-token transaction
|
|
1160
|
+
if (
|
|
1161
|
+
depositUserTxnReceipt &&
|
|
1162
|
+
depositUserTxnReceipt.status === "success"
|
|
1163
|
+
) {
|
|
1164
|
+
// Check if any transaction failed or was refunded
|
|
1165
|
+
const hasAnyCallFailed = transactionStates.some((tx) =>
|
|
1166
|
+
tx?.decodedGuestModuleEvents?.some(
|
|
1167
|
+
(e: any) => e?.type === "CallFailed",
|
|
1168
|
+
),
|
|
1169
|
+
)
|
|
1170
|
+
const hasAnyRefunded = transactionStates.some((tx) => tx?.refunded)
|
|
1171
|
+
const txStatus =
|
|
1172
|
+
!hasAnyCallFailed && !hasAnyRefunded ? "success" : "fail"
|
|
1173
|
+
|
|
1174
|
+
// Always track payment completion regardless of success/failure
|
|
1175
|
+
trackPaymentCompleted({
|
|
1176
|
+
userAddress: account.address,
|
|
1177
|
+
originTxHash: depositUserTxnReceipt.transactionHash,
|
|
1178
|
+
originChainId: effectiveOriginChainId, // Same chain
|
|
1179
|
+
mode,
|
|
1180
|
+
fundMethod,
|
|
1181
|
+
originTokenAddress,
|
|
1182
|
+
originTokenSymbol,
|
|
1183
|
+
originTokenAmount: swapAmount,
|
|
1184
|
+
originTokenAmountFormatted: depositAmountFormatted.toString(),
|
|
1185
|
+
destinationTokenAddress: originTokenAddress, // same chain same token
|
|
1186
|
+
destinationTokenSymbol: destinationTokenSymbol,
|
|
1187
|
+
depositTokenAmount: swapAmount,
|
|
1188
|
+
depositTokenAmountFormatted: depositAmountFormatted.toString(),
|
|
1189
|
+
depositTokenAmountUsd: depositAmountUsd?.toString(),
|
|
1190
|
+
destinationTokenAmount: swapAmount, // same as deposit amount
|
|
1191
|
+
destinationTokenAmountFormatted: depositAmountFormatted.toString(), // same as deposit amount
|
|
1192
|
+
destinationTokenAmountUsd: depositAmountUsd?.toString(), // same as deposit amount
|
|
1193
|
+
originTokenDecimals: originTokenDecimals,
|
|
1194
|
+
destinationTokenDecimals: originTokenDecimals, // same chain same token
|
|
1195
|
+
})
|
|
1196
|
+
|
|
1197
|
+
// Invalidate token balances cache after transaction (success or failure - gas was spent)
|
|
1198
|
+
invalidateTokenBalancesCache(account.address)
|
|
1199
|
+
|
|
1200
|
+
// Call onCheckoutComplete callback with transaction status
|
|
1201
|
+
if (checkoutOnHandlers?.triggerCheckoutComplete) {
|
|
1202
|
+
checkoutOnHandlers.triggerCheckoutComplete(
|
|
1203
|
+
txStatus,
|
|
1204
|
+
account.address,
|
|
1205
|
+
)
|
|
1206
|
+
}
|
|
1207
|
+
} else if (depositUserTxnReceipt) {
|
|
1208
|
+
trackPaymentError({
|
|
1209
|
+
error: "Transaction failed",
|
|
1210
|
+
userAddress: account.address,
|
|
1211
|
+
mode,
|
|
1212
|
+
fundMethod,
|
|
1213
|
+
originTokenAddress,
|
|
1214
|
+
})
|
|
1215
|
+
|
|
1216
|
+
// Invalidate token balances cache even on failure (gas was spent)
|
|
1217
|
+
invalidateTokenBalancesCache(account.address)
|
|
1218
|
+
|
|
1219
|
+
// Call onCheckoutError callback if provided
|
|
1220
|
+
if (checkoutOnHandlers?.triggerCheckoutError) {
|
|
1221
|
+
checkoutOnHandlers.triggerCheckoutError("Transaction failed")
|
|
1222
|
+
}
|
|
1223
|
+
} else if (effectiveGasless && !depositUserTxnReceipt) {
|
|
1224
|
+
// For gasless flows with polling, check if both transactions are confirmed
|
|
1225
|
+
const depositConfirmed =
|
|
1226
|
+
localTransactionStates[0]?.state === "confirmed"
|
|
1227
|
+
const originConfirmed =
|
|
1228
|
+
localTransactionStates[1]?.state === "confirmed"
|
|
1229
|
+
|
|
1230
|
+
if (depositConfirmed && originConfirmed) {
|
|
1231
|
+
logger.console.log(
|
|
1232
|
+
"[trails-sdk] Gasless same-chain transaction fully completed via polling",
|
|
1165
1233
|
)
|
|
1166
|
-
const
|
|
1167
|
-
const txStatus =
|
|
1168
|
-
!hasAnyCallFailed && !hasAnyRefunded ? "success" : "fail"
|
|
1234
|
+
const txStatus = "success"
|
|
1169
1235
|
|
|
1170
|
-
// Always track payment completion regardless of success/failure
|
|
1171
1236
|
trackPaymentCompleted({
|
|
1172
1237
|
userAddress: account.address,
|
|
1173
|
-
originTxHash:
|
|
1174
|
-
|
|
1238
|
+
originTxHash:
|
|
1239
|
+
localTransactionStates[0]?.transactionHash ||
|
|
1240
|
+
localTransactionStates[1]?.transactionHash,
|
|
1241
|
+
originChainId: effectiveOriginChainId,
|
|
1175
1242
|
mode,
|
|
1176
1243
|
fundMethod,
|
|
1177
1244
|
originTokenAddress,
|
|
@@ -1186,11 +1253,14 @@ export async function handleSameChainSameToken({
|
|
|
1186
1253
|
destinationTokenAmount: swapAmount, // same as deposit amount
|
|
1187
1254
|
destinationTokenAmountFormatted:
|
|
1188
1255
|
depositAmountFormatted.toString(), // same as deposit amount
|
|
1189
|
-
destinationTokenAmountUsd: depositAmountUsd?.toString(),
|
|
1256
|
+
destinationTokenAmountUsd: depositAmountUsd?.toString(),
|
|
1190
1257
|
originTokenDecimals: originTokenDecimals,
|
|
1191
1258
|
destinationTokenDecimals: originTokenDecimals, // same chain same token
|
|
1192
1259
|
})
|
|
1193
1260
|
|
|
1261
|
+
// Invalidate token balances cache after transaction (success or failure - gas was spent)
|
|
1262
|
+
invalidateTokenBalancesCache(account.address)
|
|
1263
|
+
|
|
1194
1264
|
// Call onCheckoutComplete callback with transaction status
|
|
1195
1265
|
if (checkoutOnHandlers?.triggerCheckoutComplete) {
|
|
1196
1266
|
checkoutOnHandlers.triggerCheckoutComplete(
|
|
@@ -1198,69 +1268,10 @@ export async function handleSameChainSameToken({
|
|
|
1198
1268
|
account.address,
|
|
1199
1269
|
)
|
|
1200
1270
|
}
|
|
1201
|
-
} else if (
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
mode,
|
|
1206
|
-
fundMethod,
|
|
1207
|
-
originTokenAddress,
|
|
1208
|
-
})
|
|
1209
|
-
|
|
1210
|
-
// Call onCheckoutError callback if provided
|
|
1211
|
-
if (checkoutOnHandlers?.triggerCheckoutError) {
|
|
1212
|
-
checkoutOnHandlers.triggerCheckoutError("Transaction failed")
|
|
1213
|
-
}
|
|
1214
|
-
} else if (effectiveGasless && !depositUserTxnReceipt) {
|
|
1215
|
-
// For gasless flows with polling, check if both transactions are confirmed
|
|
1216
|
-
const depositConfirmed =
|
|
1217
|
-
localTransactionStates[0]?.state === "confirmed"
|
|
1218
|
-
const originConfirmed =
|
|
1219
|
-
localTransactionStates[1]?.state === "confirmed"
|
|
1220
|
-
|
|
1221
|
-
if (depositConfirmed && originConfirmed) {
|
|
1222
|
-
logger.console.log(
|
|
1223
|
-
"[trails-sdk] Gasless same-chain transaction fully completed via polling",
|
|
1224
|
-
)
|
|
1225
|
-
const txStatus = "success"
|
|
1226
|
-
|
|
1227
|
-
trackPaymentCompleted({
|
|
1228
|
-
userAddress: account.address,
|
|
1229
|
-
originTxHash:
|
|
1230
|
-
localTransactionStates[0]?.transactionHash ||
|
|
1231
|
-
localTransactionStates[1]?.transactionHash,
|
|
1232
|
-
originChainId: effectiveOriginChainId,
|
|
1233
|
-
mode,
|
|
1234
|
-
fundMethod,
|
|
1235
|
-
originTokenAddress,
|
|
1236
|
-
originTokenSymbol,
|
|
1237
|
-
originTokenAmount: swapAmount,
|
|
1238
|
-
originTokenAmountFormatted: depositAmountFormatted.toString(),
|
|
1239
|
-
destinationTokenAddress: originTokenAddress, // same chain same token
|
|
1240
|
-
destinationTokenSymbol: destinationTokenSymbol,
|
|
1241
|
-
depositTokenAmount: swapAmount,
|
|
1242
|
-
depositTokenAmountFormatted: depositAmountFormatted.toString(),
|
|
1243
|
-
depositTokenAmountUsd: depositAmountUsd?.toString(),
|
|
1244
|
-
destinationTokenAmount: swapAmount, // same as deposit amount
|
|
1245
|
-
destinationTokenAmountFormatted:
|
|
1246
|
-
depositAmountFormatted.toString(), // same as deposit amount
|
|
1247
|
-
destinationTokenAmountUsd: depositAmountUsd?.toString(),
|
|
1248
|
-
originTokenDecimals: originTokenDecimals,
|
|
1249
|
-
destinationTokenDecimals: originTokenDecimals, // same chain same token
|
|
1250
|
-
})
|
|
1251
|
-
|
|
1252
|
-
// Call onCheckoutComplete callback with transaction status
|
|
1253
|
-
if (checkoutOnHandlers?.triggerCheckoutComplete) {
|
|
1254
|
-
checkoutOnHandlers.triggerCheckoutComplete(
|
|
1255
|
-
txStatus,
|
|
1256
|
-
account.address,
|
|
1257
|
-
)
|
|
1258
|
-
}
|
|
1259
|
-
} else if (depositConfirmed) {
|
|
1260
|
-
logger.console.log(
|
|
1261
|
-
"[trails-sdk] Gasless same-chain deposit confirmed, waiting for origin transaction",
|
|
1262
|
-
)
|
|
1263
|
-
}
|
|
1271
|
+
} else if (depositConfirmed) {
|
|
1272
|
+
logger.console.log(
|
|
1273
|
+
"[trails-sdk] Gasless same-chain deposit confirmed, waiting for origin transaction",
|
|
1274
|
+
)
|
|
1264
1275
|
}
|
|
1265
1276
|
}
|
|
1266
1277
|
|