0xtrails 0.1.3 → 0.1.4

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 (111) hide show
  1. package/dist/address.d.ts.map +1 -1
  2. package/dist/analytics.d.ts +18 -0
  3. package/dist/analytics.d.ts.map +1 -1
  4. package/dist/apiClient.d.ts +1 -1
  5. package/dist/apiClient.d.ts.map +1 -1
  6. package/dist/{ccip-CWd4g9uZ.js → ccip-dLSEJjCf.js} +55 -55
  7. package/dist/cctpqueue.d.ts +1 -1
  8. package/dist/cctpqueue.d.ts.map +1 -1
  9. package/dist/constants.d.ts +1 -0
  10. package/dist/constants.d.ts.map +1 -1
  11. package/dist/decoders.d.ts +58 -0
  12. package/dist/decoders.d.ts.map +1 -0
  13. package/dist/ens.d.ts +6 -0
  14. package/dist/ens.d.ts.map +1 -1
  15. package/dist/error.d.ts +7 -0
  16. package/dist/error.d.ts.map +1 -1
  17. package/dist/{index-BTUBzx4R.js → index-BXbaLmtt.js} +25694 -24507
  18. package/dist/index.js +2 -2
  19. package/dist/intents.d.ts +4 -4
  20. package/dist/intents.d.ts.map +1 -1
  21. package/dist/metaTxns.d.ts +1 -1
  22. package/dist/metaTxns.d.ts.map +1 -1
  23. package/dist/mode.d.ts +1 -1
  24. package/dist/mode.d.ts.map +1 -1
  25. package/dist/preconditions.d.ts +1 -1
  26. package/dist/preconditions.d.ts.map +1 -1
  27. package/dist/prepareSend.d.ts +30 -24
  28. package/dist/prepareSend.d.ts.map +1 -1
  29. package/dist/prices.d.ts +1 -1
  30. package/dist/prices.d.ts.map +1 -1
  31. package/dist/proxyCaller.d.ts +0 -1
  32. package/dist/proxyCaller.d.ts.map +1 -1
  33. package/dist/tokenBalances.d.ts +1 -1
  34. package/dist/tokenBalances.d.ts.map +1 -1
  35. package/dist/tokens.d.ts.map +1 -1
  36. package/dist/trails.d.ts +1 -1
  37. package/dist/trails.d.ts.map +1 -1
  38. package/dist/transactions.d.ts +4 -0
  39. package/dist/transactions.d.ts.map +1 -1
  40. package/dist/utils.d.ts +6 -0
  41. package/dist/utils.d.ts.map +1 -1
  42. package/dist/widget/components/DebugScreensDropdown.d.ts.map +1 -1
  43. package/dist/widget/components/ErrorDisplay.d.ts +9 -0
  44. package/dist/widget/components/ErrorDisplay.d.ts.map +1 -0
  45. package/dist/widget/components/FundSendForm.d.ts +1 -1
  46. package/dist/widget/components/FundSendForm.d.ts.map +1 -1
  47. package/dist/widget/components/OriginTransferInformation.d.ts +10 -0
  48. package/dist/widget/components/OriginTransferInformation.d.ts.map +1 -0
  49. package/dist/widget/components/PaySendForm.d.ts +1 -1
  50. package/dist/widget/components/PaySendForm.d.ts.map +1 -1
  51. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  52. package/dist/widget/components/Receipt.d.ts.map +1 -1
  53. package/dist/widget/components/Receive.d.ts +12 -0
  54. package/dist/widget/components/Receive.d.ts.map +1 -0
  55. package/dist/widget/components/Swap.d.ts +5 -1
  56. package/dist/widget/components/Swap.d.ts.map +1 -1
  57. package/dist/widget/components/SwapDisplay.d.ts +9 -0
  58. package/dist/widget/components/SwapDisplay.d.ts.map +1 -0
  59. package/dist/widget/components/TransferPendingVertical.d.ts +2 -0
  60. package/dist/widget/components/TransferPendingVertical.d.ts.map +1 -1
  61. package/dist/widget/components/YellowWarningAnimation.d.ts +2 -0
  62. package/dist/widget/components/YellowWarningAnimation.d.ts.map +1 -0
  63. package/dist/widget/hooks/useDebugScreens.d.ts +22 -0
  64. package/dist/widget/hooks/useDebugScreens.d.ts.map +1 -0
  65. package/dist/widget/hooks/useSendForm.d.ts +7 -3
  66. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  67. package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
  68. package/dist/widget/index.js +1 -1
  69. package/dist/widget/widget.d.ts.map +1 -1
  70. package/package.json +16 -15
  71. package/src/address.ts +3 -0
  72. package/src/analytics.ts +105 -4
  73. package/src/apiClient.ts +1 -1
  74. package/src/cctpqueue.ts +1 -1
  75. package/src/constants.ts +3 -0
  76. package/src/decoders.ts +310 -0
  77. package/src/ens.ts +16 -1
  78. package/src/error.ts +86 -1
  79. package/src/intents.ts +10 -2
  80. package/src/metaTxns.ts +1 -1
  81. package/src/mode.ts +1 -1
  82. package/src/preconditions.ts +1 -1
  83. package/src/prepareSend.ts +432 -114
  84. package/src/prices.ts +5 -1
  85. package/src/proxyCaller.ts +2 -14
  86. package/src/tokenBalances.ts +21 -17
  87. package/src/tokens.ts +62 -3
  88. package/src/trails.ts +2 -2
  89. package/src/transactions.ts +35 -18
  90. package/src/utils.ts +28 -0
  91. package/src/widget/compiled.css +2 -2
  92. package/src/widget/components/DebugScreensDropdown.tsx +3 -0
  93. package/src/widget/components/ErrorDisplay.tsx +150 -0
  94. package/src/widget/components/FundSendForm.tsx +62 -9
  95. package/src/widget/components/OriginTransferInformation.tsx +59 -0
  96. package/src/widget/components/PaySendForm.tsx +65 -12
  97. package/src/widget/components/QRCodeDeposit.tsx +5 -5
  98. package/src/widget/components/QrCode.tsx +1 -1
  99. package/src/widget/components/QuoteDetails.tsx +93 -25
  100. package/src/widget/components/Receipt.tsx +296 -103
  101. package/src/widget/components/Receive.tsx +146 -0
  102. package/src/widget/components/RecentTokens.tsx +1 -1
  103. package/src/widget/components/Swap.tsx +206 -85
  104. package/src/widget/components/SwapDisplay.tsx +68 -0
  105. package/src/widget/components/TokenSelector.tsx +1 -1
  106. package/src/widget/components/TransferPendingVertical.tsx +162 -112
  107. package/src/widget/components/YellowWarningAnimation.tsx +146 -0
  108. package/src/widget/hooks/useDebugScreens.ts +583 -0
  109. package/src/widget/hooks/useSendForm.ts +46 -4
  110. package/src/widget/hooks/useTokenList.ts +8 -27
  111. package/src/widget/widget.tsx +201 -484
@@ -3,7 +3,7 @@ import type {
3
3
  GetIntentCallsPayloadsReturn,
4
4
  IntentPrecondition,
5
5
  SequenceAPIClient,
6
- } from "@0xsequence/api"
6
+ } from "@0xsequence/trails-api"
7
7
  import type { Relayer } from "@0xsequence/wallet-core"
8
8
  import type { Payload } from "@0xsequence/wallet-primitives"
9
9
  import { useQuery } from "@tanstack/react-query"
@@ -111,7 +111,14 @@ import { getTxTimeDiff, getAccountTransactionHistory } from "./transactions.js"
111
111
  import { requestWithTimeout } from "./utils.js"
112
112
  import { InsufficientBalanceError } from "./error.js"
113
113
  import { estimateGasCostUsd } from "./estimate.js"
114
- import { wrapCalldataWithProxyCallerIfNeeded } from "./proxyCaller.js"
114
+ import {
115
+ decodeGuestModuleEvents,
116
+ decodeTrailsTokenSweeperEvents,
117
+ } from "./decoders.js"
118
+ import {
119
+ TRAILS_CONTRACT_PLACEHOLDER_AMOUNT,
120
+ wrapCalldataWithProxyCallerIfNeeded,
121
+ } from "./proxyCaller.js"
115
122
  import {
116
123
  getSequenceUseV3Relayers,
117
124
  getSequenceEnv,
@@ -135,6 +142,7 @@ export type PrepareSendOptions = {
135
142
  destinationTokenAddress: string
136
143
  swapAmount: string
137
144
  tradeType?: TradeType
145
+ originTokenSymbol: string
138
146
  destinationTokenSymbol: string
139
147
  sequenceProjectAccessKey?: string
140
148
  fee: string
@@ -155,7 +163,7 @@ export type PrepareSendOptions = {
155
163
  originNativeTokenPriceUsd?: number | null
156
164
  quoteProvider?: string | null
157
165
  fundMethod?: string
158
- mode?: "pay" | "fund" | "earn" | "swap"
166
+ mode?: "pay" | "fund" | "earn" | "swap" | "receive"
159
167
  checkoutOnHandlers?: CheckoutOnHandlers
160
168
  refundAddress?: string
161
169
  }
@@ -168,7 +176,8 @@ export type PrepareSendFees = {
168
176
  }
169
177
 
170
178
  export type PrepareSendQuote = {
171
- originAddress: string
179
+ originDepositAddress: string
180
+ destinationDepositAddress: string
172
181
  destinationAddress: string
173
182
  destinationCalldata: string
174
183
  originChain: Chain
@@ -340,6 +349,7 @@ export async function prepareSend(
340
349
  destinationTokenAddress,
341
350
  swapAmount,
342
351
  tradeType = TradeType.EXACT_OUTPUT,
352
+ originTokenSymbol,
343
353
  destinationTokenSymbol,
344
354
  fee,
345
355
  client: walletClient,
@@ -363,7 +373,7 @@ export async function prepareSend(
363
373
  checkoutOnHandlers,
364
374
  } = options
365
375
 
366
- const hasCustomCalldata = getIsCustomCalldata(destinationCalldata)
376
+ let hasCustomCalldata = getIsCustomCalldata(destinationCalldata)
367
377
  const mainSignerAddress = account.address
368
378
  const transactionStates: TransactionState[] = []
369
379
 
@@ -377,6 +387,20 @@ export async function prepareSend(
377
387
  let effectiveDestinationAddress = recipient
378
388
  let effectiveDestinationCalldata = destinationCalldata
379
389
 
390
+ if (
391
+ !hasCustomCalldata &&
392
+ quoteProvider === "cctp" &&
393
+ tradeType === TradeType.EXACT_INPUT
394
+ ) {
395
+ // we need to set custom calldata for the cctp transfer in order to have destination intent adddress execution needed for metatxn tracking
396
+ effectiveDestinationCalldata = getERC20TransferData({
397
+ recipient,
398
+ amount: BigInt(TRAILS_CONTRACT_PLACEHOLDER_AMOUNT),
399
+ })
400
+ effectiveDestinationAddress = destinationTokenAddress
401
+ hasCustomCalldata = true
402
+ }
403
+
380
404
  if (
381
405
  hasCustomCalldata &&
382
406
  destinationTokenAddress !== zeroAddress &&
@@ -406,6 +430,7 @@ export async function prepareSend(
406
430
  destinationChainId,
407
431
  originTokenAddress,
408
432
  destinationTokenAddress,
433
+ destinationTokenSymbol,
409
434
  destinationTokenAmount: swapAmount,
410
435
  mode,
411
436
  fundMethod,
@@ -421,6 +446,11 @@ export async function prepareSend(
421
446
  userAddress: account.address,
422
447
  mode,
423
448
  fundMethod,
449
+ originChainId,
450
+ destinationChainId,
451
+ originTokenAddress,
452
+ destinationTokenAddress,
453
+ destinationTokenSymbol,
424
454
  })
425
455
  throw new Error("Wallet client not provided")
426
456
  }
@@ -432,6 +462,11 @@ export async function prepareSend(
432
462
  userAddress: account.address,
433
463
  mode,
434
464
  fundMethod,
465
+ originChainId,
466
+ destinationChainId,
467
+ originTokenAddress,
468
+ destinationTokenAddress,
469
+ destinationTokenSymbol,
435
470
  })
436
471
  throw new Error(`Chain ${originChainId} not found`)
437
472
  }
@@ -543,6 +578,7 @@ export async function prepareSend(
543
578
  destinationChainId,
544
579
  destinationTokenAddress,
545
580
  swapAmount,
581
+ originTokenSymbol,
546
582
  destinationTokenSymbol,
547
583
  recipient: effectiveDestinationAddress,
548
584
  destinationCalldata: effectiveDestinationCalldata,
@@ -581,6 +617,7 @@ async function sendHandlerForDifferentChainDifferentToken({
581
617
  destinationChainId,
582
618
  destinationTokenAddress,
583
619
  swapAmount,
620
+ originTokenSymbol,
584
621
  destinationTokenSymbol,
585
622
  recipient,
586
623
  destinationCalldata,
@@ -616,6 +653,7 @@ async function sendHandlerForDifferentChainDifferentToken({
616
653
  destinationChainId: number
617
654
  destinationTokenAddress: string
618
655
  swapAmount: string
656
+ originTokenSymbol: string
619
657
  destinationTokenSymbol: string
620
658
  recipient: string
621
659
  destinationCalldata?: string
@@ -641,7 +679,7 @@ async function sendHandlerForDifferentChainDifferentToken({
641
679
  originNativeTokenPriceUsd?: number | null
642
680
  quoteProvider?: string | null
643
681
  fundMethod?: string
644
- mode?: "pay" | "fund" | "earn" | "swap"
682
+ mode?: "pay" | "fund" | "earn" | "swap" | "receive"
645
683
  checkoutOnHandlers?: CheckoutOnHandlers
646
684
  }): Promise<PrepareSendReturn> {
647
685
  const testnet = isTestnetDebugMode()
@@ -872,7 +910,15 @@ async function sendHandlerForDifferentChainDifferentToken({
872
910
 
873
911
  console.log("[trails-sdk] Creating intent with args:", intentArgs)
874
912
 
875
- const intent = await getIntentCallsPayloadsFromIntents(apiClient, intentArgs)
913
+ const intent = await getIntentCallsPayloadsFromIntents(
914
+ apiClient,
915
+ intentArgs,
916
+ {
917
+ originTokenSymbol,
918
+ destinationTokenSymbol,
919
+ feeTokenSymbol: originTokenSymbol,
920
+ },
921
+ )
876
922
  console.log("[trails-sdk] Got intent:", intent)
877
923
 
878
924
  if (!intent.preconditions?.length || !intent.calls?.length) {
@@ -887,6 +933,10 @@ async function sendHandlerForDifferentChainDifferentToken({
887
933
  mainSignerAddress,
888
934
  intent.calls,
889
935
  intent.preconditions,
936
+ {
937
+ originTokenSymbol,
938
+ destinationTokenSymbol,
939
+ },
890
940
  )
891
941
 
892
942
  const firstPrecondition = findFirstPreconditionForChainId(
@@ -960,7 +1010,8 @@ async function sendHandlerForDifferentChainDifferentToken({
960
1010
  }
961
1011
 
962
1012
  const quote = await getNormalizedQuoteObject({
963
- originAddress: intentAddress,
1013
+ originDepositAddress: intentAddress,
1014
+ destinationDepositAddress: intent.destinationIntentAddress,
964
1015
  destinationAddress: recipient,
965
1016
  destinationCalldata,
966
1017
  originAmount: depositAmount,
@@ -1049,13 +1100,38 @@ async function sendHandlerForDifferentChainDifferentToken({
1049
1100
 
1050
1101
  console.log("[trails-sdk] testnet", testnet)
1051
1102
 
1103
+ const destinationPublicClient = createPublicClient({
1104
+ chain: getChainInfo(destinationChainId)!,
1105
+ transport: http(),
1106
+ })
1107
+
1052
1108
  const depositPromise = async () => {
1109
+ console.log(
1110
+ "[trails-sdk] depositPromise called - starting deposit transaction",
1111
+ )
1112
+ console.log("[trails-sdk] fundMethod value:", fundMethod)
1113
+
1053
1114
  // Skip wallet deposit if fund method is qr-code
1054
1115
  if (fundMethod === "qr-code" || fundMethod === "exchange") {
1055
- console.log("[trails-sdk] Skipping wallet deposit for QR code mode")
1116
+ console.log(
1117
+ "[trails-sdk] Skipping wallet deposit for QR code mode - fundMethod is:",
1118
+ fundMethod,
1119
+ )
1056
1120
  return
1057
1121
  }
1058
1122
 
1123
+ console.log("[trails-sdk] Calling attemptUserDepositTx with params:", {
1124
+ originTokenAddress: effectiveOriginTokenAddress,
1125
+ gasless,
1126
+ paymasterUrl,
1127
+ chain: effectiveOriginChain.id,
1128
+ account: account.address,
1129
+ firstPreconditionMin,
1130
+ intentAddress,
1131
+ fee,
1132
+ dryMode,
1133
+ })
1134
+
1059
1135
  originUserTxReceipt = await attemptUserDepositTx({
1060
1136
  originTokenAddress: effectiveOriginTokenAddress,
1061
1137
  gasless,
@@ -1078,6 +1154,8 @@ async function sendHandlerForDifferentChainDifferentToken({
1078
1154
  onTransactionStateChange,
1079
1155
  transactionStates,
1080
1156
  fundMethod,
1157
+ originTokenSymbol,
1158
+ destinationTokenSymbol,
1081
1159
  })
1082
1160
 
1083
1161
  if (!originUserTxReceipt) {
@@ -1142,84 +1220,147 @@ async function sendHandlerForDifferentChainDifferentToken({
1142
1220
  }
1143
1221
  }
1144
1222
 
1145
- const originMetaTxnPromise = async () => {
1223
+ const _checkForDestinationDepositTx: () => Promise<TransactionReceipt | null> =
1224
+ async () => {
1225
+ while (true) {
1226
+ try {
1227
+ const response = await getAccountTransactionHistory({
1228
+ chainId: destinationChainId,
1229
+ accountAddress:
1230
+ intent.destinationIntentAddress as `0x${string}`,
1231
+ })
1232
+ console.log(
1233
+ "[trails-sdk] getAccountTransactionHistory response",
1234
+ response,
1235
+ )
1236
+ if (response.transactions.length > 0) {
1237
+ const tx = response.transactions[0]
1238
+ if (!tx?.txnHash) {
1239
+ await new Promise((resolve) => setTimeout(resolve, 1000))
1240
+ continue
1241
+ }
1242
+ // const isReceive = tx.transfers.some(
1243
+ // (transfer) => transfer.transferType === "RECEIVE",
1244
+ // )
1245
+ // if (!isReceive) {
1246
+ // await new Promise((resolve) => setTimeout(resolve, 1000))
1247
+ // continue
1248
+ // }
1249
+ const destinationDepositTxReceipt =
1250
+ await destinationPublicClient.getTransactionReceipt({
1251
+ hash: tx.txnHash as `0x${string}`,
1252
+ })
1253
+
1254
+ transactionStates[2] = getTransactionStateFromReceipt(
1255
+ destinationDepositTxReceipt,
1256
+ destinationChainId,
1257
+ transactionStates[2]?.label,
1258
+ )
1259
+ onTransactionStateChange(transactionStates)
1260
+
1261
+ return destinationDepositTxReceipt
1262
+ }
1263
+ } catch (error) {
1264
+ console.error("Error checking for deposit tx", error)
1265
+ }
1266
+ await new Promise((resolve) => setTimeout(resolve, 1000))
1267
+ }
1268
+ }
1269
+
1270
+ // Variables to store the waitForReceipt functions
1271
+ let originMetaTxnReceiptPromise:
1272
+ | (() => Promise<MetaTxnReceipt | null>)
1273
+ | null = null
1274
+ let destinationMetaTxnReceiptPromise:
1275
+ | (() => Promise<MetaTxnReceipt | null>)
1276
+ | (() => Promise<TransactionReceipt | null>)
1277
+ | null = null
1278
+
1279
+ // First phase: Send meta transactions and queue CCTP
1280
+ const originSendMetaTxnPromise = async () => {
1146
1281
  if (intent.metaTxns[0] && intent.preconditions[0]) {
1147
- originMetaTxnReceipt = await sendMetaTxAndWaitForReceipt({
1282
+ const { waitForReceipt } = await sendMetaTxAndWaitForReceipt({
1148
1283
  metaTx: intent.metaTxns[0] as MetaTxn,
1149
1284
  relayer: originRelayer,
1150
1285
  precondition: intent.preconditions[0] as IntentPrecondition,
1151
1286
  })
1152
1287
 
1153
- if (originMetaTxnReceipt && transactionStates[1]) {
1154
- transactionStates[1] = getTransactionStateFromReceipt(
1155
- originMetaTxnReceipt,
1156
- originChainId,
1157
- transactionStates[1]?.label,
1158
- )
1159
- onTransactionStateChange(transactionStates)
1160
- }
1288
+ // Store the waitForReceipt function for later use
1289
+ originMetaTxnReceiptPromise = waitForReceipt
1161
1290
  }
1162
1291
  }
1163
1292
 
1164
- const destinationMetaTxnPromise = async () => {
1293
+ const destinationSendMetaTxnPromise = async () => {
1165
1294
  if (
1166
1295
  intent.quote.quoteProvider === "relay" &&
1167
1296
  intent.quote.quoteProviderRequestId &&
1168
1297
  !intent.preconditions[1] &&
1169
1298
  !intent.metaTxns[1]
1170
1299
  ) {
1171
- console.log("[trails-sdk] waitForRelayDestinationTx")
1172
- try {
1173
- const txHash = await waitForRelayDestinationTx(
1174
- intent.quote.quoteProviderRequestId,
1175
- )
1176
- console.log("[trails-sdk] waitForRelayDestinationTx txHash", txHash)
1177
- if (txHash) {
1178
- const destinationPublicClient = createPublicClient({
1179
- chain: getChainInfo(destinationChainId)!,
1180
- transport: http(),
1181
- })
1182
- const destinationTxnReceipt =
1183
- await destinationPublicClient.getTransactionReceipt({
1184
- hash: txHash as `0x${string}`,
1185
- })
1300
+ console.log("[trails-sdk] Setting up relay destination promise")
1301
+ // For relay, we'll wait for the receipt in the wait phase
1302
+ // Just store the requestId for later use
1303
+ destinationMetaTxnReceiptPromise = async () => {
1304
+ console.log("[trails-sdk] waitForRelayDestinationTx")
1305
+ try {
1306
+ const txHash = await waitForRelayDestinationTx(
1307
+ intent.quote.quoteProviderRequestId,
1308
+ )
1186
1309
  console.log(
1187
- "[trails-sdk] relay destinationTxnReceipt",
1188
- destinationTxnReceipt,
1310
+ "[trails-sdk] waitForRelayDestinationTx txHash",
1311
+ txHash,
1189
1312
  )
1190
- if (transactionStates[2]) {
1191
- transactionStates[2] = getTransactionStateFromReceipt(
1313
+ if (txHash) {
1314
+ const destinationTxnReceipt =
1315
+ await destinationPublicClient.getTransactionReceipt({
1316
+ hash: txHash as `0x${string}`,
1317
+ })
1318
+ console.log(
1319
+ "[trails-sdk] relay destinationTxnReceipt",
1192
1320
  destinationTxnReceipt,
1193
- destinationChainId,
1194
- transactionStates[2]?.label,
1195
1321
  )
1322
+ if (transactionStates[2]) {
1323
+ transactionStates[2] = getTransactionStateFromReceipt(
1324
+ destinationTxnReceipt,
1325
+ destinationChainId,
1326
+ transactionStates[2]?.label,
1327
+ )
1328
+ }
1329
+ onTransactionStateChange(transactionStates)
1330
+ return destinationTxnReceipt
1196
1331
  }
1197
- onTransactionStateChange(transactionStates)
1198
- }
1199
- } catch (error: unknown) {
1200
- console.error("Error waiting for relay destination tx", error)
1201
- if (transactionStates?.[2]) {
1202
- transactionStates[2].state = "failed"
1203
- onTransactionStateChange(transactionStates)
1332
+ } catch (error: unknown) {
1333
+ console.error("Error waiting for relay destination tx", error)
1334
+ if (transactionStates?.[2]) {
1335
+ transactionStates[2].state = "failed"
1336
+ onTransactionStateChange(transactionStates)
1337
+ }
1338
+ throw error
1204
1339
  }
1340
+ return null
1205
1341
  }
1206
1342
  } else {
1343
+ console.log(
1344
+ "[trails-sdk] Setting up destination meta transaction promise",
1345
+ )
1346
+ console.log("[trails-sdk] intent.metaTxns[1]", intent.metaTxns[1])
1347
+ console.log(
1348
+ "[trails-sdk] intent.preconditions[1]",
1349
+ intent.preconditions[1],
1350
+ )
1207
1351
  if (intent.metaTxns[1] && intent.preconditions[1]) {
1208
- destinationMetaTxnReceipt = await sendMetaTxAndWaitForReceipt({
1352
+ const { waitForReceipt } = await sendMetaTxAndWaitForReceipt({
1209
1353
  metaTx: intent.metaTxns[1] as MetaTxn,
1210
1354
  relayer: destinationRelayer,
1211
1355
  precondition: intent.preconditions[1] as IntentPrecondition,
1212
1356
  })
1213
1357
 
1214
- if (destinationMetaTxnReceipt && transactionStates[2]) {
1215
- transactionStates[2] = getTransactionStateFromReceipt(
1216
- destinationMetaTxnReceipt,
1217
- destinationChainId,
1218
- transactionStates?.[2]?.label,
1219
- )
1220
- onTransactionStateChange(transactionStates)
1221
- }
1358
+ // Store the waitForReceipt function for later use
1359
+ destinationMetaTxnReceiptPromise = waitForReceipt
1222
1360
  }
1361
+ // } else if (intent.destinationIntentAddress) {
1362
+ // destinationMetaTxnReceiptPromise = checkForDestinationDepositTx
1363
+ // }
1223
1364
  }
1224
1365
  }
1225
1366
 
@@ -1250,12 +1391,129 @@ async function sendHandlerForDifferentChainDifferentToken({
1250
1391
  console.error("Error checking for deposit tx", error)
1251
1392
  })
1252
1393
 
1394
+ // Phase 1: Send meta transactions and queue CCTP
1395
+ console.log(
1396
+ "[trails-sdk] Starting Phase 1: Sending meta transactions and queuing CCTP",
1397
+ )
1398
+
1399
+ await Promise.all([
1400
+ originSendMetaTxnPromise(),
1401
+ destinationSendMetaTxnPromise(),
1402
+ ])
1403
+
1404
+ console.log("[trails-sdk] Phase 1 completed successfully")
1405
+
1406
+ // Phase 2: Wait for receipts and execute deposit
1407
+ console.log(
1408
+ "[trails-sdk] Starting Phase 2: Waiting for receipts and executing deposit",
1409
+ )
1410
+
1411
+ const waitForOriginMetaTxnReceiptPromise = async () => {
1412
+ console.log("[trails-sdk] Waiting for origin meta transaction receipt")
1413
+ if (originMetaTxnReceiptPromise) {
1414
+ try {
1415
+ originMetaTxnReceipt = await originMetaTxnReceiptPromise()
1416
+
1417
+ if (originMetaTxnReceipt && transactionStates[1]) {
1418
+ transactionStates[1] = getTransactionStateFromReceipt(
1419
+ originMetaTxnReceipt,
1420
+ originChainId,
1421
+ transactionStates[1]?.label,
1422
+ )
1423
+ onTransactionStateChange(transactionStates)
1424
+
1425
+ try {
1426
+ const receipt = await publicClient.getTransactionReceipt({
1427
+ hash: originMetaTxnReceipt.txnHash as `0x${string}`,
1428
+ })
1429
+ transactionStates[1].decodedTrailsTokenSweeperEvents =
1430
+ decodeTrailsTokenSweeperEvents(receipt)
1431
+ transactionStates[1].decodedGuestModuleEvents =
1432
+ decodeGuestModuleEvents(receipt)
1433
+ } catch (error) {
1434
+ console.error("Error decoding origin tx events", error)
1435
+ }
1436
+ }
1437
+ } catch (error) {
1438
+ console.error(
1439
+ "[trails-sdk] Error waiting for origin receipt:",
1440
+ error,
1441
+ )
1442
+ }
1443
+ } else {
1444
+ console.log(
1445
+ "[trails-sdk] No origin meta transaction receipt promise to wait for",
1446
+ )
1447
+ }
1448
+ }
1449
+
1450
+ const waitForDestinationMetaTxnReceiptPromise = async () => {
1451
+ console.log(
1452
+ "[trails-sdk] Waiting for destination meta transaction receipt",
1453
+ )
1454
+ if (destinationMetaTxnReceiptPromise) {
1455
+ try {
1456
+ destinationMetaTxnReceipt =
1457
+ (await destinationMetaTxnReceiptPromise()) as MetaTxnReceipt
1458
+
1459
+ console.log(
1460
+ "[trails-sdk] destinationMetaTxnReceipt",
1461
+ destinationMetaTxnReceipt,
1462
+ )
1463
+
1464
+ if (destinationMetaTxnReceipt && transactionStates[2]) {
1465
+ transactionStates[2] = getTransactionStateFromReceipt(
1466
+ destinationMetaTxnReceipt,
1467
+ destinationChainId,
1468
+ transactionStates[2]?.label,
1469
+ )
1470
+ onTransactionStateChange(transactionStates)
1471
+
1472
+ try {
1473
+ const receipt =
1474
+ await destinationPublicClient.getTransactionReceipt({
1475
+ hash: destinationMetaTxnReceipt.txnHash as `0x${string}`,
1476
+ })
1477
+ transactionStates[2].decodedTrailsTokenSweeperEvents =
1478
+ decodeTrailsTokenSweeperEvents(receipt)
1479
+ transactionStates[2].decodedGuestModuleEvents =
1480
+ decodeGuestModuleEvents(receipt)
1481
+ } catch (error) {
1482
+ console.error("Error decoding destination tx events", error)
1483
+ }
1484
+ }
1485
+ } catch (error) {
1486
+ console.error(
1487
+ "[trails-sdk] Error waiting for destination receipt:",
1488
+ error,
1489
+ )
1490
+ // For relay transactions, this might be expected if still waiting
1491
+ if (intent.quote.quoteProvider === "relay") {
1492
+ console.log(
1493
+ "[trails-sdk] Relay transaction still waiting, this is normal",
1494
+ )
1495
+ }
1496
+ }
1497
+ } else {
1498
+ console.log(
1499
+ "[trails-sdk] No destination meta transaction receipt promise to wait for",
1500
+ )
1501
+ }
1502
+ }
1503
+
1504
+ console.log("[trails-sdk] Executing Phase 2 Promise.all with deposit")
1505
+ console.log(
1506
+ "[trails-sdk] About to call depositPromise - fundMethod is:",
1507
+ fundMethod,
1508
+ )
1509
+
1253
1510
  await Promise.all([
1254
1511
  depositPromise(),
1512
+ waitForOriginMetaTxnReceiptPromise(),
1513
+ waitForDestinationMetaTxnReceiptPromise(),
1255
1514
  queueCctpPromise(),
1256
- originMetaTxnPromise(),
1257
- destinationMetaTxnPromise(),
1258
1515
  ])
1516
+ console.log("[trails-sdk] Phase 2 completed successfully")
1259
1517
 
1260
1518
  // Track payment completion
1261
1519
  if (originUserTxReceipt && destinationMetaTxnReceipt) {
@@ -1270,6 +1528,10 @@ async function sendHandlerForDifferentChainDifferentToken({
1270
1528
  destinationChainId,
1271
1529
  mode,
1272
1530
  fundMethod,
1531
+ originTokenSymbol,
1532
+ originTokenAddress,
1533
+ destinationTokenAddress,
1534
+ destinationTokenSymbol,
1273
1535
  })
1274
1536
 
1275
1537
  // Call onCheckoutComplete callback if provided
@@ -1284,6 +1546,12 @@ async function sendHandlerForDifferentChainDifferentToken({
1284
1546
  intentAddress,
1285
1547
  mode,
1286
1548
  fundMethod,
1549
+ originChainId,
1550
+ destinationChainId,
1551
+ originTokenSymbol,
1552
+ originTokenAddress,
1553
+ destinationTokenAddress,
1554
+ destinationTokenSymbol,
1287
1555
  })
1288
1556
 
1289
1557
  // Call onCheckoutError callback if provided
@@ -1343,7 +1611,7 @@ async function sendHandlerForSameChainSameToken({
1343
1611
  originNativeTokenPriceUsd?: number | null
1344
1612
  slippageTolerance?: string
1345
1613
  checkoutOnHandlers?: CheckoutOnHandlers
1346
- mode?: "pay" | "fund" | "earn" | "swap"
1614
+ mode?: "pay" | "fund" | "earn" | "swap" | "receive"
1347
1615
  fundMethod?: string
1348
1616
  }): Promise<PrepareSendReturn> {
1349
1617
  console.log("[trails-sdk] isToSameToken && isToSameChain")
@@ -1373,7 +1641,8 @@ async function sendHandlerForSameChainSameToken({
1373
1641
  }
1374
1642
 
1375
1643
  const quote = await getNormalizedQuoteObject({
1376
- originAddress: recipient,
1644
+ originDepositAddress: recipient,
1645
+ destinationDepositAddress: recipient,
1377
1646
  destinationAddress: recipient,
1378
1647
  destinationCalldata,
1379
1648
  originAmount: swapAmount, // fromAmount is same as toAmount for same chain same token
@@ -1510,6 +1779,7 @@ async function sendHandlerForSameChainSameToken({
1510
1779
  chainId: effectiveOriginChainId,
1511
1780
  userAddress: account.address,
1512
1781
  blockNumber: Number(receipt.blockNumber),
1782
+ originTokenAddress,
1513
1783
  })
1514
1784
 
1515
1785
  onTransactionStateChange([
@@ -1528,6 +1798,7 @@ async function sendHandlerForSameChainSameToken({
1528
1798
  originChainId: effectiveOriginChainId, // Same chain
1529
1799
  mode,
1530
1800
  fundMethod,
1801
+ originTokenAddress,
1531
1802
  })
1532
1803
  } else if (originUserTxReceipt) {
1533
1804
  trackPaymentError({
@@ -1535,6 +1806,7 @@ async function sendHandlerForSameChainSameToken({
1535
1806
  userAddress: account.address,
1536
1807
  mode,
1537
1808
  fundMethod,
1809
+ originTokenAddress,
1538
1810
  })
1539
1811
  }
1540
1812
  }
@@ -1569,6 +1841,8 @@ async function _sendHandlerForSameChainDifferentToken({
1569
1841
  originNativeTokenPriceUsd,
1570
1842
  mode,
1571
1843
  fundMethod,
1844
+ originTokenSymbol,
1845
+ destinationTokenSymbol,
1572
1846
  }: {
1573
1847
  originTokenAddress: string
1574
1848
  swapAmount: string
@@ -1586,8 +1860,10 @@ async function _sendHandlerForSameChainDifferentToken({
1586
1860
  sourceTokenPriceUsd?: number | null
1587
1861
  destinationTokenPriceUsd?: number | null
1588
1862
  originNativeTokenPriceUsd?: number | null
1589
- mode?: "pay" | "fund" | "earn" | "swap"
1863
+ mode?: "pay" | "fund" | "earn" | "swap" | "receive"
1590
1864
  fundMethod?: string
1865
+ originTokenSymbol: string
1866
+ destinationTokenSymbol: string
1591
1867
  }): Promise<PrepareSendReturn> {
1592
1868
  const destinationTxs: { to: string; value: string; data: string }[] = []
1593
1869
  const hasCustomCalldata = getIsCustomCalldata(destinationCalldata)
@@ -1675,7 +1951,8 @@ async function _sendHandlerForSameChainDifferentToken({
1675
1951
  }
1676
1952
 
1677
1953
  const normalizedQuote = await getNormalizedQuoteObject({
1678
- originAddress: depositOriginAddress,
1954
+ originDepositAddress: depositOriginAddress,
1955
+ destinationDepositAddress: recipient,
1679
1956
  destinationAddress: recipient,
1680
1957
  destinationCalldata,
1681
1958
  originAmount: depositAmount,
@@ -1743,6 +2020,10 @@ async function _sendHandlerForSameChainDifferentToken({
1743
2020
  destinationChainId: originChainId, // Same chain
1744
2021
  mode,
1745
2022
  fundMethod,
2023
+ originTokenAddress,
2024
+ originTokenSymbol,
2025
+ destinationTokenAddress,
2026
+ destinationTokenSymbol,
1746
2027
  })
1747
2028
  } else if (originUserTxReceipt) {
1748
2029
  trackPaymentError({
@@ -1750,6 +2031,10 @@ async function _sendHandlerForSameChainDifferentToken({
1750
2031
  userAddress: account.address,
1751
2032
  mode,
1752
2033
  fundMethod,
2034
+ originTokenAddress,
2035
+ originTokenSymbol,
2036
+ destinationTokenAddress,
2037
+ destinationTokenSymbol,
1753
2038
  })
1754
2039
  }
1755
2040
 
@@ -1965,6 +2250,8 @@ export async function attemptNonGaslessUserDeposit({
1965
2250
  intentAddress,
1966
2251
  onTransactionStateChange,
1967
2252
  transactionStates,
2253
+ originTokenSymbol,
2254
+ destinationTokenSymbol,
1968
2255
  }: {
1969
2256
  originTokenAddress: string
1970
2257
  firstPreconditionMin: string
@@ -1984,6 +2271,8 @@ export async function attemptNonGaslessUserDeposit({
1984
2271
  intentAddress: string
1985
2272
  onTransactionStateChange: (transactionStates: TransactionState[]) => void
1986
2273
  transactionStates: TransactionState[]
2274
+ originTokenSymbol: string
2275
+ destinationTokenSymbol: string
1987
2276
  }): Promise<TransactionReceipt | null> {
1988
2277
  let originUserTxReceipt: TransactionReceipt | null = null
1989
2278
  const usingLIfi = false
@@ -2193,6 +2482,8 @@ export async function attemptNonGaslessUserDeposit({
2193
2482
  userAddress: account.address,
2194
2483
  blockNumber: Number(receipt.blockNumber),
2195
2484
  intentAddress,
2485
+ originTokenSymbol,
2486
+ destinationTokenSymbol,
2196
2487
  })
2197
2488
 
2198
2489
  console.log("[trails-sdk] receipt", receipt)
@@ -2225,6 +2516,8 @@ async function attemptUserDepositTx({
2225
2516
  onTransactionStateChange,
2226
2517
  transactionStates,
2227
2518
  fundMethod,
2519
+ originTokenSymbol,
2520
+ destinationTokenSymbol,
2228
2521
  }: {
2229
2522
  originTokenAddress: string
2230
2523
  gasless: boolean
@@ -2247,13 +2540,23 @@ async function attemptUserDepositTx({
2247
2540
  onTransactionStateChange: (transactionStates: TransactionState[]) => void
2248
2541
  transactionStates: TransactionState[]
2249
2542
  fundMethod?: string
2543
+ originTokenSymbol: string
2544
+ destinationTokenSymbol: string
2250
2545
  }): Promise<TransactionReceipt | null> {
2251
2546
  let originUserTxReceipt: TransactionReceipt | null = null
2252
2547
  const originChainId = chain.id
2253
2548
 
2549
+ console.log(
2550
+ "[trails-sdk] attemptUserDepositTx called with fundMethod:",
2551
+ fundMethod,
2552
+ )
2553
+
2254
2554
  // Skip wallet deposit if fund method is qr-code
2255
2555
  if (fundMethod === "qr-code" || fundMethod === "exchange") {
2256
- console.log("[trails-sdk] Skipping wallet deposit for QR code mode")
2556
+ console.log(
2557
+ "[trails-sdk] Skipping wallet deposit for QR code mode - fundMethod is:",
2558
+ fundMethod,
2559
+ )
2257
2560
  return null
2258
2561
  }
2259
2562
 
@@ -2298,6 +2601,8 @@ async function attemptUserDepositTx({
2298
2601
  sourceTokenDecimals,
2299
2602
  onTransactionStateChange,
2300
2603
  transactionStates,
2604
+ originTokenSymbol,
2605
+ destinationTokenSymbol,
2301
2606
  })
2302
2607
  }
2303
2608
 
@@ -2350,9 +2655,10 @@ async function sendMetaTxAndWaitForReceipt({
2350
2655
  metaTx: MetaTxn
2351
2656
  relayer: Relayer.Standard.Rpc.RpcRelayer
2352
2657
  precondition: IntentPrecondition
2353
- }): Promise<MetaTxnReceipt | null> {
2658
+ }): Promise<{
2659
+ waitForReceipt: () => Promise<MetaTxnReceipt | null>
2660
+ }> {
2354
2661
  try {
2355
- let originMetaTxnReceipt: MetaTxnReceipt | null = null
2356
2662
  console.log("[trails-sdk] metaTx", metaTx)
2357
2663
  trackRelayerCallStarted({
2358
2664
  walletAddress: metaTx.walletAddress as `0x${string}`,
@@ -2367,31 +2673,6 @@ async function sendMetaTxAndWaitForReceipt({
2367
2673
  contractAddress: metaTx.contract as `0x${string}`,
2368
2674
  chainId: Number(metaTx.chainId),
2369
2675
  })
2370
-
2371
- // eslint-disable-next-line no-constant-condition
2372
- while (true) {
2373
- console.log(
2374
- "[trails-sdk] polling status",
2375
- metaTx.id as `0x${string}`,
2376
- BigInt(metaTx.chainId),
2377
- )
2378
- const receipt: any = await getMetaTxStatus(
2379
- relayer,
2380
- metaTx.id,
2381
- Number(metaTx.chainId),
2382
- )
2383
- console.log("[trails-sdk] status", receipt)
2384
- if (receipt?.transactionHash) {
2385
- originMetaTxnReceipt = receipt.data?.receipt
2386
- if (!originMetaTxnReceipt) {
2387
- throw new Error("No meta txn receipt found")
2388
- }
2389
- break
2390
- }
2391
- await new Promise((resolve) => setTimeout(resolve, 1000))
2392
- }
2393
-
2394
- return originMetaTxnReceipt
2395
2676
  } catch (error) {
2396
2677
  trackRelayerCallError({
2397
2678
  walletAddress: metaTx.walletAddress as `0x${string}`,
@@ -2401,6 +2682,37 @@ async function sendMetaTxAndWaitForReceipt({
2401
2682
  })
2402
2683
  throw error
2403
2684
  }
2685
+
2686
+ return {
2687
+ waitForReceipt: async () => {
2688
+ let originMetaTxnReceipt: MetaTxnReceipt | null = null
2689
+
2690
+ // eslint-disable-next-line no-constant-condition
2691
+ while (true) {
2692
+ console.log(
2693
+ "[trails-sdk] polling status",
2694
+ metaTx.id as `0x${string}`,
2695
+ BigInt(metaTx.chainId),
2696
+ )
2697
+ const receipt: any = await getMetaTxStatus(
2698
+ relayer,
2699
+ metaTx.id,
2700
+ Number(metaTx.chainId),
2701
+ )
2702
+ console.log("[trails-sdk] status", receipt)
2703
+ if (receipt?.transactionHash) {
2704
+ originMetaTxnReceipt = receipt.data?.receipt
2705
+ if (!originMetaTxnReceipt) {
2706
+ throw new Error("No meta txn receipt found")
2707
+ }
2708
+ break
2709
+ }
2710
+ await new Promise((resolve) => setTimeout(resolve, 1000))
2711
+ }
2712
+
2713
+ return originMetaTxnReceipt
2714
+ },
2715
+ }
2404
2716
  }
2405
2717
 
2406
2718
  /**
@@ -2586,25 +2898,27 @@ export type QuoteProviderInfo = {
2586
2898
  url: string
2587
2899
  }
2588
2900
 
2901
+ export type Quote = {
2902
+ fromAmount: string
2903
+ fromAmountMin: string
2904
+ toAmount: string
2905
+ toAmountMin: string
2906
+ originToken: SupportedToken
2907
+ destinationToken: SupportedToken
2908
+ originChain: Chain
2909
+ destinationChain: Chain
2910
+ fees: PrepareSendFees
2911
+ slippageTolerance: string
2912
+ priceImpact: string
2913
+ completionEstimateSeconds: number
2914
+ transactionStates?: TransactionState[]
2915
+ originTokenRate?: string
2916
+ quoteProvider?: QuoteProviderInfo | null
2917
+ destinationTokenRate?: string
2918
+ }
2919
+
2589
2920
  export type UseQuoteReturn = {
2590
- quote: {
2591
- fromAmount: string
2592
- fromAmountMin: string
2593
- toAmount: string
2594
- toAmountMin: string
2595
- originToken: SupportedToken
2596
- destinationToken: SupportedToken
2597
- originChain: Chain
2598
- destinationChain: Chain
2599
- fees: PrepareSendFees
2600
- slippageTolerance: string
2601
- priceImpact: string
2602
- completionEstimateSeconds: number
2603
- transactionStates?: TransactionState[]
2604
- originTokenRate?: string
2605
- quoteProvider?: QuoteProviderInfo | null
2606
- destinationTokenRate?: string
2607
- } | null
2921
+ quote: Quote | null
2608
2922
  swap: (() => Promise<SwapReturn | null>) | null
2609
2923
  isLoadingQuote: boolean
2610
2924
  quoteError: unknown
@@ -2704,6 +3018,7 @@ export function useQuote({
2704
3018
  const sourceTokenDecimals = originToken?.decimals ?? 18
2705
3019
  const destinationTokenDecimals = destinationToken?.decimals ?? 18
2706
3020
  const destinationTokenSymbol = destinationToken?.symbol ?? ""
3021
+ const originTokenSymbol = originToken?.symbol ?? ""
2707
3022
 
2708
3023
  const options = {
2709
3024
  account: walletClient.account!,
@@ -2715,6 +3030,7 @@ export function useQuote({
2715
3030
  destinationTokenAddress: toTokenAddress,
2716
3031
  swapAmount: swapAmount.toString(),
2717
3032
  tradeType: tradeType ?? TradeType.EXACT_OUTPUT,
3033
+ originTokenSymbol: originTokenSymbol,
2718
3034
  destinationTokenSymbol: destinationTokenSymbol,
2719
3035
  client: walletClient,
2720
3036
  apiClient,
@@ -2822,8 +3138,7 @@ export function getFeesFromIntent(
2822
3138
  toAmountUsd,
2823
3139
  }: { tradeType: TradeType; fromAmountUsd: number; toAmountUsd: number },
2824
3140
  ): PrepareSendFees {
2825
- // TODO: remove this once API types are regenerated
2826
- const totalFeeAmountUsd = (intent?.quote as any)?.quoteProviderFeeUsd ?? 0
3141
+ const totalFeeAmountUsd = intent?.trailsFee?.totalFeeUSD ?? 0
2827
3142
  const totalFeeAmountUsdDisplay = formatUsdAmountDisplay(totalFeeAmountUsd)
2828
3143
 
2829
3144
  console.log("[trails-sdk] getFeesFromIntent", {
@@ -2927,7 +3242,8 @@ export function getIsCustomCalldata(calldata: string | undefined): boolean {
2927
3242
  }
2928
3243
 
2929
3244
  export async function getNormalizedQuoteObject({
2930
- originAddress,
3245
+ originDepositAddress,
3246
+ destinationDepositAddress,
2931
3247
  destinationAddress,
2932
3248
  destinationCalldata,
2933
3249
  originChainId,
@@ -2950,7 +3266,8 @@ export async function getNormalizedQuoteObject({
2950
3266
  noSufficientBalance,
2951
3267
  minimumNotMet,
2952
3268
  }: {
2953
- originAddress?: string
3269
+ originDepositAddress?: string
3270
+ destinationDepositAddress?: string
2954
3271
  destinationAddress?: string
2955
3272
  destinationCalldata?: string
2956
3273
  originChainId: number
@@ -3126,7 +3443,8 @@ export async function getNormalizedQuoteObject({
3126
3443
  const priceImpactUsdDisplay = formatUsdAmountDisplay(priceImpactUsd)
3127
3444
 
3128
3445
  return {
3129
- originAddress: originAddress || "",
3446
+ originDepositAddress: originDepositAddress || "",
3447
+ destinationDepositAddress: destinationDepositAddress || "",
3130
3448
  destinationAddress: destinationAddress || "",
3131
3449
  destinationCalldata: hasCustomCalldata ? destinationCalldata || "" : "",
3132
3450
  originAmount: originAmount || "0",