0xtrails 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/dist/analytics.d.ts +8 -3
  2. package/dist/analytics.d.ts.map +1 -1
  3. package/dist/{ccip-DhEkQ6QC.js → ccip-Dw5AN7oU.js} +1 -1
  4. package/dist/cctp.d.ts +0 -149
  5. package/dist/cctp.d.ts.map +1 -1
  6. package/dist/chains.d.ts +28 -3
  7. package/dist/chains.d.ts.map +1 -1
  8. package/dist/config.d.ts +11 -0
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/constants.d.ts +1 -1
  11. package/dist/constants.d.ts.map +1 -1
  12. package/dist/contractUtils.d.ts.map +1 -1
  13. package/dist/estimate.d.ts.map +1 -1
  14. package/dist/fees.d.ts.map +1 -1
  15. package/dist/gasless.d.ts +12 -0
  16. package/dist/gasless.d.ts.map +1 -1
  17. package/dist/{index-MhD2DA7_.js → index-BtVUTbEZ.js} +30984 -38945
  18. package/dist/index.d.ts +7 -5
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +108 -107
  21. package/dist/indexerClient.d.ts +2 -2
  22. package/dist/intents.d.ts +0 -17
  23. package/dist/intents.d.ts.map +1 -1
  24. package/dist/mutations.d.ts.map +1 -1
  25. package/dist/paymasterSend.d.ts.map +1 -1
  26. package/dist/prepareSend.d.ts +1 -1
  27. package/dist/prepareSend.d.ts.map +1 -1
  28. package/dist/sendUserOp.d.ts +0 -18
  29. package/dist/sendUserOp.d.ts.map +1 -1
  30. package/dist/tokenBalances.d.ts.map +1 -1
  31. package/dist/tokens.d.ts +10 -8
  32. package/dist/tokens.d.ts.map +1 -1
  33. package/dist/transactionIntent/deposits/depositOrchestrator.d.ts +4 -5
  34. package/dist/transactionIntent/deposits/depositOrchestrator.d.ts.map +1 -1
  35. package/dist/transactionIntent/deposits/gaslessDeposit.d.ts +4 -5
  36. package/dist/transactionIntent/deposits/gaslessDeposit.d.ts.map +1 -1
  37. package/dist/transactionIntent/deposits/standardDeposit.d.ts +2 -2
  38. package/dist/transactionIntent/deposits/standardDeposit.d.ts.map +1 -1
  39. package/dist/transactionIntent/execution/transactionState.d.ts +2 -2
  40. package/dist/transactionIntent/execution/transactionState.d.ts.map +1 -1
  41. package/dist/transactionIntent/handlers/crossChain.d.ts +4 -4
  42. package/dist/transactionIntent/handlers/crossChain.d.ts.map +1 -1
  43. package/dist/transactionIntent/handlers/index.d.ts +0 -1
  44. package/dist/transactionIntent/handlers/index.d.ts.map +1 -1
  45. package/dist/transactionIntent/handlers/sameChainSameToken.d.ts +4 -34
  46. package/dist/transactionIntent/handlers/sameChainSameToken.d.ts.map +1 -1
  47. package/dist/transactionIntent/quote/normalizeQuote.d.ts.map +1 -1
  48. package/dist/transactionIntent/quote/quoteHelpers.d.ts +2 -1
  49. package/dist/transactionIntent/quote/quoteHelpers.d.ts.map +1 -1
  50. package/dist/transactionIntent/types.d.ts +6 -19
  51. package/dist/transactionIntent/types.d.ts.map +1 -1
  52. package/dist/transactionIntent/utils/index.d.ts +0 -1
  53. package/dist/transactionIntent/utils/index.d.ts.map +1 -1
  54. package/dist/transactions.d.ts +2 -20
  55. package/dist/transactions.d.ts.map +1 -1
  56. package/dist/utils.d.ts +8 -2
  57. package/dist/utils.d.ts.map +1 -1
  58. package/dist/walletUtils.d.ts +21 -0
  59. package/dist/walletUtils.d.ts.map +1 -0
  60. package/dist/wallets.d.ts +33 -240
  61. package/dist/wallets.d.ts.map +1 -1
  62. package/dist/widget/components/AccountIntentTransactionHistory.d.ts.map +1 -1
  63. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  64. package/dist/widget/components/FeeOption.d.ts +8 -13
  65. package/dist/widget/components/FeeOption.d.ts.map +1 -1
  66. package/dist/widget/components/FeeOptions.d.ts +11 -5
  67. package/dist/widget/components/FeeOptions.d.ts.map +1 -1
  68. package/dist/widget/components/NativeGasOption.d.ts.map +1 -1
  69. package/dist/widget/components/Pay.d.ts.map +1 -1
  70. package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
  71. package/dist/widget/components/QRCodeDeposit.d.ts +5 -0
  72. package/dist/widget/components/QRCodeDeposit.d.ts.map +1 -1
  73. package/dist/widget/components/QRCodeWalletSelect.d.ts +13 -0
  74. package/dist/widget/components/QRCodeWalletSelect.d.ts.map +1 -0
  75. package/dist/widget/components/QrCode.d.ts.map +1 -1
  76. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  77. package/dist/widget/components/Receipt.d.ts.map +1 -1
  78. package/dist/widget/components/ScreenHeader.d.ts +1 -1
  79. package/dist/widget/components/ScreenHeader.d.ts.map +1 -1
  80. package/dist/widget/components/Toast.d.ts.map +1 -1
  81. package/dist/widget/components/TokenImage.d.ts.map +1 -1
  82. package/dist/widget/css/compiled.css +1 -1
  83. package/dist/widget/hooks/useCheckout.d.ts +15 -1
  84. package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
  85. package/dist/widget/hooks/useCurrentScreen.d.ts +1 -1
  86. package/dist/widget/hooks/useCurrentScreen.d.ts.map +1 -1
  87. package/dist/widget/hooks/useDebugScreens.d.ts +1 -1
  88. package/dist/widget/hooks/useDebugScreens.d.ts.map +1 -1
  89. package/dist/widget/hooks/useIntentTransactionHistory.d.ts.map +1 -1
  90. package/dist/widget/hooks/useIsConnectedWalletSmartContract.d.ts +7 -0
  91. package/dist/widget/hooks/useIsConnectedWalletSmartContract.d.ts.map +1 -0
  92. package/dist/widget/hooks/useIsSequenceWallet.d.ts +6 -0
  93. package/dist/widget/hooks/useIsSequenceWallet.d.ts.map +1 -0
  94. package/dist/widget/hooks/useQuote.d.ts +5 -8
  95. package/dist/widget/hooks/useQuote.d.ts.map +1 -1
  96. package/dist/widget/hooks/useRecentTokens.d.ts.map +1 -1
  97. package/dist/widget/hooks/useSelectedFeeOption.d.ts +30 -0
  98. package/dist/widget/hooks/useSelectedFeeOption.d.ts.map +1 -0
  99. package/dist/widget/hooks/useSendForm.d.ts +6 -15
  100. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  101. package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
  102. package/dist/widget/index.js +1 -1
  103. package/dist/widget/providers/TrailsProvider.d.ts +23 -12
  104. package/dist/widget/providers/TrailsProvider.d.ts.map +1 -1
  105. package/dist/widget/widget.d.ts +11 -0
  106. package/dist/widget/widget.d.ts.map +1 -1
  107. package/package.json +8 -8
  108. package/src/analytics.ts +53 -21
  109. package/src/cctp.ts +0 -1016
  110. package/src/chains.ts +93 -39
  111. package/src/config.ts +24 -6
  112. package/src/constants.ts +1 -4
  113. package/src/contractUtils.ts +6 -6
  114. package/src/estimate.ts +3 -6
  115. package/src/fees.ts +5 -10
  116. package/src/gasless.ts +45 -0
  117. package/src/index.ts +7 -6
  118. package/src/indexerClient.ts +2 -2
  119. package/src/intents.ts +52 -206
  120. package/src/mutations.ts +3 -2
  121. package/src/paymasterSend.ts +2 -5
  122. package/src/prepareSend.ts +9 -12
  123. package/src/sendUserOp.ts +3 -64
  124. package/src/tokenBalances.ts +2 -1
  125. package/src/tokens.ts +62 -133
  126. package/src/trailsClient.ts +1 -1
  127. package/src/transactionIntent/deposits/depositOrchestrator.ts +14 -15
  128. package/src/transactionIntent/deposits/gaslessDeposit.ts +70 -100
  129. package/src/transactionIntent/deposits/standardDeposit.ts +22 -28
  130. package/src/transactionIntent/execution/transactionState.ts +2 -2
  131. package/src/transactionIntent/handlers/crossChain.ts +165 -385
  132. package/src/transactionIntent/handlers/index.ts +0 -1
  133. package/src/transactionIntent/handlers/sameChainSameToken.ts +228 -94
  134. package/src/transactionIntent/quote/normalizeQuote.ts +4 -6
  135. package/src/transactionIntent/quote/quoteHelpers.ts +35 -3
  136. package/src/transactionIntent/types.ts +6 -27
  137. package/src/transactionIntent/utils/index.ts +0 -1
  138. package/src/transactions.ts +6 -203
  139. package/src/umd.tsx +1 -3
  140. package/src/utils.ts +28 -8
  141. package/src/walletUtils.ts +42 -0
  142. package/src/wallets.ts +361 -203
  143. package/src/widget/compiled.css +1 -1
  144. package/src/widget/components/AccountIntentTransactionHistory.tsx +73 -4
  145. package/src/widget/components/AccountSettings.tsx +17 -17
  146. package/src/widget/components/ChainList.tsx +3 -3
  147. package/src/widget/components/ClassicSwap.tsx +19 -10
  148. package/src/widget/components/ConfigDisplay.tsx +1 -1
  149. package/src/widget/components/FeeOption.tsx +63 -20
  150. package/src/widget/components/FeeOptions.tsx +54 -123
  151. package/src/widget/components/NativeGasOption.tsx +3 -1
  152. package/src/widget/components/Pay.tsx +18 -11
  153. package/src/widget/components/PoolDeposit.tsx +23 -10
  154. package/src/widget/components/QRCodeDeposit.tsx +50 -30
  155. package/src/widget/components/QRCodeWalletSelect.tsx +77 -0
  156. package/src/widget/components/QrCode.tsx +188 -233
  157. package/src/widget/components/QuoteDetails.tsx +48 -2
  158. package/src/widget/components/Receipt.tsx +5 -2
  159. package/src/widget/components/ScreenHeader.tsx +10 -8
  160. package/src/widget/components/Toast.tsx +10 -0
  161. package/src/widget/components/TokenImage.tsx +56 -13
  162. package/src/widget/hooks/useCheckout.ts +71 -0
  163. package/src/widget/hooks/useCurrentScreen.tsx +1 -0
  164. package/src/widget/hooks/useDebugScreens.ts +5 -0
  165. package/src/widget/hooks/useIntentTransactionHistory.ts +788 -418
  166. package/src/widget/hooks/useIsConnectedWalletSmartContract.ts +43 -0
  167. package/src/widget/hooks/useIsSequenceWallet.ts +17 -0
  168. package/src/widget/hooks/useQuote.ts +16 -17
  169. package/src/widget/hooks/useRecentTokens.ts +2 -1
  170. package/src/widget/hooks/useSelectedFeeOption.tsx +257 -0
  171. package/src/widget/hooks/useSendForm.ts +172 -47
  172. package/src/widget/hooks/useTokenList.ts +15 -2
  173. package/src/widget/providers/TrailsProvider.tsx +53 -25
  174. package/src/widget/widget.tsx +119 -48
  175. package/dist/cctpqueue.d.ts +0 -18
  176. package/dist/cctpqueue.d.ts.map +0 -1
  177. package/dist/preconditions.d.ts +0 -12
  178. package/dist/preconditions.d.ts.map +0 -1
  179. package/dist/transactionIntent/handlers/sameChainDifferentToken.d.ts +0 -62
  180. package/dist/transactionIntent/handlers/sameChainDifferentToken.d.ts.map +0 -1
  181. package/dist/transactionIntent/utils/lifiHelpers.d.ts +0 -10
  182. package/dist/transactionIntent/utils/lifiHelpers.d.ts.map +0 -1
  183. package/dist/widget/hooks/useSelectedFeeToken.d.ts +0 -33
  184. package/dist/widget/hooks/useSelectedFeeToken.d.ts.map +0 -1
  185. package/src/cctpqueue.ts +0 -69
  186. package/src/preconditions.ts +0 -47
  187. package/src/transactionIntent/handlers/sameChainDifferentToken.ts +0 -323
  188. package/src/transactionIntent/utils/lifiHelpers.ts +0 -68
  189. package/src/widget/hooks/useSelectedFeeToken.tsx +0 -288
@@ -1,14 +1,708 @@
1
1
  import { useState, useEffect, useCallback } from "react"
2
2
  import {
3
- getIntentTransactionHistory,
4
3
  getAccountTransactionHistory,
5
4
  type IntentTransaction,
6
- type IntentTransactionHistoryResponse,
7
5
  } from "../../transactions.js"
8
6
  import { getTokenInfo, getSupportedTokens } from "../../tokens.js"
9
7
  import { getExplorerUrl } from "../../explorer.js"
10
8
  import { logger } from "../../logger.js"
11
9
  import { useTrails } from "../providers/TrailsProvider.js"
10
+ import { commonTokenImages } from "../../tokens.js"
11
+ import { getTrailsClient } from "../../trailsClient.js"
12
+ import { SortOrder, type IntentSummary } from "@0xsequence/trails-api"
13
+ import {
14
+ decodeTrailsTokenSweeperEvents,
15
+ decodeGuestModuleEvents,
16
+ } from "../../decoders.js"
17
+ import { getPublicRpcClient } from "../../chains.js"
18
+ import { getChainInfo } from "../../chains.js"
19
+ import { isNativeToken } from "../../utils.js"
20
+
21
+ // ============================================================================
22
+ // Helper Functions
23
+ // ============================================================================
24
+
25
+ /**
26
+ * Maps API status to UI status format
27
+ */
28
+ function mapApiStatusToUiStatus(apiStatus: string): string {
29
+ const status = apiStatus?.toUpperCase() || "UNKNOWN"
30
+ switch (status) {
31
+ case "SUCCEEDED":
32
+ return "completed"
33
+ case "EXECUTING":
34
+ return "processing"
35
+ case "PENDING":
36
+ return "pending"
37
+ case "FAILED":
38
+ return "failed"
39
+ case "CREATED":
40
+ return "created"
41
+ default:
42
+ return apiStatus?.toLowerCase() || "unknown"
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Maps IntentSummary from API to IntentTransaction
48
+ */
49
+ function mapIntentSummaryToTransaction(
50
+ intent: IntentSummary,
51
+ ): IntentTransaction {
52
+ return {
53
+ originIntentAddress: intent.originIntentAddress,
54
+ destinationIntentAddress: intent.destinationIntentAddress,
55
+ mainSigner: intent.ownerAddress,
56
+ metaTxnId: intent.intentId,
57
+ txnHash:
58
+ intent.originTransactionHash ||
59
+ intent.destinationTransactionHash ||
60
+ undefined,
61
+ executionStatus: mapApiStatusToUiStatus(intent.status),
62
+ originChainId: intent.originChainId,
63
+ destinationChainId: intent.destinationChainId,
64
+ originTokenAddress: intent.originTokenAddress,
65
+ originTokenAmount: intent.originTokenAmount.toString(),
66
+ destinationTokenAddress: intent.destinationTokenAddress,
67
+ destinationTokenAmount: intent.destinationTokenAmount.toString(),
68
+ destinationToAddress: intent.ownerAddress,
69
+ createdAt: intent.createdAt,
70
+ originTransactionHash: intent.originTransactionHash,
71
+ destinationTransactionHash: intent.destinationTransactionHash,
72
+ originToken: intent.originTokenMetadata
73
+ ? ({
74
+ symbol: intent.originTokenMetadata.symbol || "",
75
+ name: intent.originTokenMetadata.name || "",
76
+ decimals: intent.originTokenMetadata.decimals ?? 18,
77
+ imageUrl: intent.originTokenMetadata.logoUri ?? "",
78
+ chainId: intent.originTokenMetadata.chainId,
79
+ } as {
80
+ symbol: string
81
+ name: string
82
+ decimals: number
83
+ imageUrl: string
84
+ chainId: number
85
+ })
86
+ : undefined,
87
+ destinationToken: intent.destinationTokenMetadata
88
+ ? ({
89
+ symbol: intent.destinationTokenMetadata.symbol || "",
90
+ name: intent.destinationTokenMetadata.name || "",
91
+ decimals: intent.destinationTokenMetadata.decimals ?? 18,
92
+ imageUrl: intent.destinationTokenMetadata.logoUri ?? "",
93
+ chainId: intent.destinationTokenMetadata.chainId,
94
+ } as {
95
+ symbol: string
96
+ name: string
97
+ decimals: number
98
+ imageUrl: string
99
+ chainId: number
100
+ })
101
+ : undefined,
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Gets token info for a given token address and chain ID
107
+ */
108
+ async function getTokenInfoForTransaction(
109
+ tokenAddress: string,
110
+ chainId: number,
111
+ supportedTokens: Awaited<ReturnType<typeof getSupportedTokens>>,
112
+ ): Promise<
113
+ | {
114
+ symbol: string
115
+ name: string
116
+ decimals: number
117
+ imageUrl: string
118
+ chainId: number
119
+ }
120
+ | undefined
121
+ > {
122
+ if (isNativeToken(tokenAddress)) {
123
+ const nativeToken = supportedTokens.find(
124
+ (token) =>
125
+ token.contractAddress.toLowerCase() === tokenAddress.toLowerCase() &&
126
+ token.chainId === chainId,
127
+ )
128
+
129
+ if (nativeToken) {
130
+ return {
131
+ symbol: nativeToken.symbol,
132
+ name: nativeToken.name,
133
+ decimals: nativeToken.decimals,
134
+ imageUrl: nativeToken.imageUrl,
135
+ chainId: nativeToken.chainId,
136
+ }
137
+ }
138
+
139
+ // Fallback for native token
140
+ return {
141
+ symbol: "ETH",
142
+ name: "Native Token",
143
+ decimals: 18,
144
+ imageUrl: commonTokenImages.ETH as string,
145
+ chainId,
146
+ }
147
+ }
148
+
149
+ // For ERC20 tokens, check supported tokens first
150
+ const supportedToken = supportedTokens.find(
151
+ (token) =>
152
+ token.contractAddress.toLowerCase() === tokenAddress.toLowerCase() &&
153
+ token.chainId === chainId,
154
+ )
155
+
156
+ if (supportedToken) {
157
+ return {
158
+ symbol: supportedToken.symbol,
159
+ name: supportedToken.name,
160
+ decimals: supportedToken.decimals,
161
+ imageUrl: supportedToken.imageUrl,
162
+ chainId: supportedToken.chainId,
163
+ }
164
+ }
165
+
166
+ // Fallback to on-chain lookup
167
+ try {
168
+ const tokenInfo = await getTokenInfo(chainId, tokenAddress)
169
+ if (tokenInfo) {
170
+ return {
171
+ symbol: tokenInfo.symbol,
172
+ name: tokenInfo.name,
173
+ decimals: tokenInfo.decimals,
174
+ imageUrl: tokenInfo.imageUrl,
175
+ chainId: tokenInfo.chainId,
176
+ }
177
+ }
178
+ } catch (error) {
179
+ logger.console.warn(
180
+ "[trails-sdk] Failed to get token info for:",
181
+ tokenAddress,
182
+ "on chain",
183
+ chainId,
184
+ error,
185
+ )
186
+ }
187
+
188
+ return undefined
189
+ }
190
+
191
+ /**
192
+ * Gets origin intent transaction hashes from account history
193
+ */
194
+ async function getOriginIntentTransactionHashes(
195
+ transaction: IntentTransaction,
196
+ trailsConfig: ReturnType<typeof useTrails>,
197
+ ): Promise<{
198
+ originIntentTxHash?: string
199
+ originIntentTxExplorerUrl?: string
200
+ originIntentDepositTxHash?: string
201
+ originIntentDepositTxExplorerUrl?: string
202
+ }> {
203
+ if (
204
+ !transaction.originIntentAddress ||
205
+ !transaction.originChainId ||
206
+ transaction.originTransactionHash
207
+ ) {
208
+ return {}
209
+ }
210
+
211
+ try {
212
+ const originHistory = await getAccountTransactionHistory({
213
+ chainId: transaction.originChainId,
214
+ accountAddress: transaction.originIntentAddress,
215
+ pageSize: 10,
216
+ page: 0,
217
+ includeMetadata: true,
218
+ apiKey: trailsConfig.trailsApiKey || "",
219
+ indexerUrl: trailsConfig.sequenceIndexerUrl!,
220
+ })
221
+
222
+ if (
223
+ !originHistory?.transactions ||
224
+ originHistory.transactions.length === 0
225
+ ) {
226
+ return {}
227
+ }
228
+
229
+ const intentAddress = transaction.originIntentAddress.toLowerCase()
230
+ const result: {
231
+ originIntentTxHash?: string
232
+ originIntentTxExplorerUrl?: string
233
+ originIntentDepositTxHash?: string
234
+ originIntentDepositTxExplorerUrl?: string
235
+ } = {}
236
+
237
+ // Find first deposit tx (transfer TO intent address)
238
+ const depositTx = originHistory.transactions.find((tx) =>
239
+ tx.transfers?.some(
240
+ (transfer) => transfer.to.toLowerCase() === intentAddress,
241
+ ),
242
+ )
243
+ if (depositTx) {
244
+ result.originIntentDepositTxHash = depositTx.txnHash
245
+ result.originIntentDepositTxExplorerUrl = getExplorerUrl({
246
+ txHash: depositTx.txnHash,
247
+ chainId: transaction.originChainId,
248
+ })
249
+ }
250
+
251
+ // Find first action tx (transfer FROM intent address)
252
+ const actionTx = originHistory.transactions.find((tx) =>
253
+ tx.transfers?.some(
254
+ (transfer) => transfer.from.toLowerCase() === intentAddress,
255
+ ),
256
+ )
257
+ if (actionTx) {
258
+ result.originIntentTxHash = actionTx.txnHash
259
+ result.originIntentTxExplorerUrl = getExplorerUrl({
260
+ txHash: actionTx.txnHash,
261
+ chainId: transaction.originChainId,
262
+ })
263
+ }
264
+
265
+ return result
266
+ } catch (error) {
267
+ logger.console.warn(
268
+ "[trails-sdk] Failed to get origin intent transaction hashes for:",
269
+ transaction.originIntentAddress,
270
+ "on chain",
271
+ transaction.originChainId,
272
+ error,
273
+ )
274
+ return {}
275
+ }
276
+ }
277
+
278
+ /**
279
+ * Gets destination intent transaction hashes from account history
280
+ */
281
+ async function getDestinationIntentTransactionHashes(
282
+ transaction: IntentTransaction,
283
+ isSameIntentAddress: boolean,
284
+ trailsConfig: ReturnType<typeof useTrails>,
285
+ ): Promise<{
286
+ destinationIntentTxHash?: string
287
+ destinationIntentTxExplorerUrl?: string
288
+ destinationIntentDepositTxHash?: string
289
+ destinationIntentDepositTxExplorerUrl?: string
290
+ }> {
291
+ if (
292
+ transaction.destinationTransactionHash ||
293
+ isSameIntentAddress ||
294
+ !transaction.destinationIntentAddress ||
295
+ !transaction.destinationChainId
296
+ ) {
297
+ return {}
298
+ }
299
+
300
+ try {
301
+ const destinationHistory = await getAccountTransactionHistory({
302
+ chainId: transaction.destinationChainId,
303
+ accountAddress: transaction.destinationIntentAddress,
304
+ pageSize: 10,
305
+ page: 0,
306
+ includeMetadata: true,
307
+ apiKey: trailsConfig.trailsApiKey || "",
308
+ indexerUrl: trailsConfig.sequenceIndexerUrl!,
309
+ })
310
+
311
+ if (
312
+ !destinationHistory?.transactions ||
313
+ destinationHistory.transactions.length === 0
314
+ ) {
315
+ return {}
316
+ }
317
+
318
+ const intentAddress = transaction.destinationIntentAddress.toLowerCase()
319
+ const result: {
320
+ destinationIntentTxHash?: string
321
+ destinationIntentTxExplorerUrl?: string
322
+ destinationIntentDepositTxHash?: string
323
+ destinationIntentDepositTxExplorerUrl?: string
324
+ } = {}
325
+
326
+ // Find first deposit tx (transfer TO intent address)
327
+ const depositTx = destinationHistory.transactions.find((tx) =>
328
+ tx.transfers?.some(
329
+ (transfer) => transfer.to.toLowerCase() === intentAddress,
330
+ ),
331
+ )
332
+ if (depositTx) {
333
+ result.destinationIntentDepositTxHash = depositTx.txnHash
334
+ result.destinationIntentDepositTxExplorerUrl = getExplorerUrl({
335
+ txHash: depositTx.txnHash,
336
+ chainId: transaction.destinationChainId,
337
+ })
338
+ }
339
+
340
+ // Find first action tx (transfer FROM intent address)
341
+ const actionTx = destinationHistory.transactions.find((tx) =>
342
+ tx.transfers?.some(
343
+ (transfer) => transfer.from.toLowerCase() === intentAddress,
344
+ ),
345
+ )
346
+ if (actionTx) {
347
+ result.destinationIntentTxHash = actionTx.txnHash
348
+ result.destinationIntentTxExplorerUrl = getExplorerUrl({
349
+ txHash: actionTx.txnHash,
350
+ chainId: transaction.destinationChainId,
351
+ })
352
+ }
353
+
354
+ return result
355
+ } catch (error) {
356
+ logger.console.warn(
357
+ "[trails-sdk] Failed to get destination intent transaction hashes for:",
358
+ transaction.destinationIntentAddress,
359
+ "on chain",
360
+ transaction.destinationChainId,
361
+ error,
362
+ )
363
+ return {}
364
+ }
365
+ }
366
+
367
+ /**
368
+ * Decodes transaction events to detect refunds or failures
369
+ */
370
+ async function decodeTransactionEvents(
371
+ transaction: IntentTransaction,
372
+ txHashes: {
373
+ originIntentTxHash?: string
374
+ destinationIntentTxHash?: string
375
+ originIntentDepositTxHash?: string
376
+ destinationIntentDepositTxHash?: string
377
+ },
378
+ ): Promise<{ refunded: boolean; failed: boolean }> {
379
+ const isExecuting =
380
+ transaction.executionStatus === "executing" ||
381
+ transaction.executionStatus === "processing"
382
+ const createdAtTime = transaction.createdAt
383
+ ? new Date(transaction.createdAt).getTime()
384
+ : null
385
+ const isExecutingOver1Minute =
386
+ isExecuting && createdAtTime && Date.now() - createdAtTime > 60 * 1000
387
+
388
+ if (!isExecutingOver1Minute) {
389
+ return { refunded: false, failed: false }
390
+ }
391
+
392
+ // Collect all transaction hashes to check
393
+ const txHashesToCheck: Array<{ hash: string; chainId: number }> = []
394
+
395
+ if (txHashes.originIntentTxHash && transaction.originChainId) {
396
+ txHashesToCheck.push({
397
+ hash: txHashes.originIntentTxHash,
398
+ chainId: transaction.originChainId,
399
+ })
400
+ }
401
+ if (txHashes.originIntentDepositTxHash && transaction.originChainId) {
402
+ txHashesToCheck.push({
403
+ hash: txHashes.originIntentDepositTxHash,
404
+ chainId: transaction.originChainId,
405
+ })
406
+ }
407
+ if (txHashes.destinationIntentTxHash && transaction.destinationChainId) {
408
+ txHashesToCheck.push({
409
+ hash: txHashes.destinationIntentTxHash,
410
+ chainId: transaction.destinationChainId,
411
+ })
412
+ }
413
+ if (
414
+ txHashes.destinationIntentDepositTxHash &&
415
+ transaction.destinationChainId
416
+ ) {
417
+ txHashesToCheck.push({
418
+ hash: txHashes.destinationIntentDepositTxHash,
419
+ chainId: transaction.destinationChainId,
420
+ })
421
+ }
422
+ if (transaction.originTransactionHash && transaction.originChainId) {
423
+ txHashesToCheck.push({
424
+ hash: transaction.originTransactionHash,
425
+ chainId: transaction.originChainId,
426
+ })
427
+ }
428
+ if (
429
+ transaction.destinationTransactionHash &&
430
+ transaction.destinationChainId
431
+ ) {
432
+ txHashesToCheck.push({
433
+ hash: transaction.destinationTransactionHash,
434
+ chainId: transaction.destinationChainId,
435
+ })
436
+ }
437
+
438
+ // Decode events from all known transaction hashes
439
+ for (const { hash, chainId } of txHashesToCheck) {
440
+ try {
441
+ const chainInfo = getChainInfo(chainId)
442
+ if (!chainInfo) {
443
+ continue
444
+ }
445
+
446
+ const publicClient = getPublicRpcClient(chainInfo)
447
+ const receipt = await publicClient.getTransactionReceipt({
448
+ hash: hash as `0x${string}`,
449
+ })
450
+
451
+ // Decode Trails Token Sweeper events (refunds)
452
+ const sweeperEvents = decodeTrailsTokenSweeperEvents(receipt)
453
+ const hasRefund = sweeperEvents.some(
454
+ (event) => event.type === "Refund" || event.type === "RefundAndSweep",
455
+ )
456
+
457
+ // Decode Guest Module events (failures)
458
+ const guestModuleEvents = decodeGuestModuleEvents(receipt)
459
+ const hasCallFailed = guestModuleEvents.some(
460
+ (event) => event.type === "CallFailed",
461
+ )
462
+
463
+ if (hasRefund) {
464
+ logger.console.log("[trails-sdk] Found refund event in transaction", {
465
+ hash,
466
+ chainId,
467
+ })
468
+ return { refunded: true, failed: false }
469
+ }
470
+
471
+ if (hasCallFailed) {
472
+ logger.console.log(
473
+ "[trails-sdk] Found CallFailed event in transaction",
474
+ { hash, chainId },
475
+ )
476
+ return { refunded: false, failed: true }
477
+ }
478
+ } catch (error) {
479
+ logger.console.warn(
480
+ "[trails-sdk] Failed to decode events for transaction",
481
+ { hash, chainId, error },
482
+ )
483
+ // Continue checking other transactions
484
+ }
485
+ }
486
+
487
+ return { refunded: false, failed: false }
488
+ }
489
+
490
+ /**
491
+ * Calculates effective execution status based on transaction state and decoded events
492
+ */
493
+ function getEffectiveExecutionStatus(
494
+ transaction: IntentTransaction,
495
+ txHashes: {
496
+ originIntentTxHash?: string
497
+ destinationIntentTxHash?: string
498
+ originIntentDepositTxHash?: string
499
+ destinationIntentDepositTxHash?: string
500
+ },
501
+ decodedEvents: { refunded: boolean; failed: boolean },
502
+ ): string {
503
+ const {
504
+ executionStatus,
505
+ originChainId,
506
+ destinationChainId,
507
+ originIntentAddress,
508
+ destinationIntentAddress,
509
+ createdAt,
510
+ } = transaction
511
+
512
+ // If we decoded a refund, return "refunded"
513
+ if (decodedEvents.refunded) {
514
+ return "refunded"
515
+ }
516
+
517
+ // If we decoded a failure, return "failed"
518
+ if (decodedEvents.failed) {
519
+ return "failed"
520
+ }
521
+
522
+ // If API provided a status, use it
523
+ if (executionStatus && executionStatus !== "unknown") {
524
+ return executionStatus
525
+ }
526
+
527
+ // Check if this is a same-chain transaction
528
+ const isSameChain =
529
+ originIntentAddress?.toLowerCase() ===
530
+ destinationIntentAddress?.toLowerCase() &&
531
+ originChainId === destinationChainId
532
+
533
+ // Check if there's no destination
534
+ const hasNoDestination =
535
+ !destinationIntentAddress || !destinationChainId || isSameChain
536
+
537
+ // Check if it's been over 5 minutes since creation
538
+ const isOver5Minutes = createdAt
539
+ ? Date.now() - new Date(createdAt).getTime() > 5 * 60 * 1000
540
+ : false
541
+
542
+ // Rule 1: If there's a destination intent tx, it's completed
543
+ if (txHashes.destinationIntentTxHash) {
544
+ return "completed"
545
+ }
546
+
547
+ // Rule 2: Only origin tx hash exists and no real destination (same-chain case)
548
+ if (txHashes.originIntentTxHash && originChainId && hasNoDestination) {
549
+ return "completed"
550
+ }
551
+
552
+ // Rule 3: If there's a deposit but no action yet, status is pending
553
+ if (txHashes.originIntentDepositTxHash && !txHashes.originIntentTxHash) {
554
+ return "pending"
555
+ }
556
+
557
+ // Rule 4: Cross-chain with origin tx but no destination tx
558
+ if (
559
+ txHashes.originIntentTxHash &&
560
+ !txHashes.destinationIntentTxHash &&
561
+ originChainId &&
562
+ destinationChainId &&
563
+ !isSameChain &&
564
+ destinationIntentAddress
565
+ ) {
566
+ return isOver5Minutes ? "failed" : "pending"
567
+ }
568
+
569
+ // Otherwise, use the original execution status or default to unknown
570
+ return executionStatus || "unknown"
571
+ }
572
+
573
+ /**
574
+ * Enriches a single transaction with token info, transaction hashes, and status
575
+ */
576
+ async function enrichTransaction(
577
+ transaction: IntentTransaction,
578
+ supportedTokens: Awaited<ReturnType<typeof getSupportedTokens>>,
579
+ trailsConfig: ReturnType<typeof useTrails>,
580
+ ): Promise<IntentTransaction> {
581
+ // Get token info
582
+ const originToken =
583
+ transaction.originTokenAddress && transaction.originChainId
584
+ ? await getTokenInfoForTransaction(
585
+ transaction.originTokenAddress,
586
+ transaction.originChainId,
587
+ supportedTokens,
588
+ )
589
+ : undefined
590
+
591
+ const destinationToken =
592
+ transaction.destinationTokenAddress && transaction.destinationChainId
593
+ ? await getTokenInfoForTransaction(
594
+ transaction.destinationTokenAddress,
595
+ transaction.destinationChainId,
596
+ supportedTokens,
597
+ )
598
+ : undefined
599
+
600
+ // Check if origin and destination are the same
601
+ const isSameIntentAddress =
602
+ transaction.originIntentAddress?.toLowerCase() ===
603
+ transaction.destinationIntentAddress?.toLowerCase() &&
604
+ transaction.originChainId === transaction.destinationChainId
605
+
606
+ // Get transaction hashes from API or fetch from account history
607
+ let originIntentTxHash: string | undefined = transaction.originTransactionHash
608
+ let destinationIntentTxHash: string | undefined =
609
+ transaction.destinationTransactionHash
610
+ let originIntentTxExplorerUrl: string | undefined
611
+ let destinationIntentTxExplorerUrl: string | undefined
612
+ let originIntentDepositTxHash: string | undefined
613
+ let destinationIntentDepositTxHash: string | undefined
614
+ let originIntentDepositTxExplorerUrl: string | undefined
615
+ let destinationIntentDepositTxExplorerUrl: string | undefined
616
+
617
+ // Generate explorer URLs for API-provided transaction hashes
618
+ if (originIntentTxHash && transaction.originChainId) {
619
+ originIntentTxExplorerUrl = getExplorerUrl({
620
+ txHash: originIntentTxHash,
621
+ chainId: transaction.originChainId,
622
+ })
623
+ }
624
+ if (destinationIntentTxHash && transaction.destinationChainId) {
625
+ destinationIntentTxExplorerUrl = getExplorerUrl({
626
+ txHash: destinationIntentTxHash,
627
+ chainId: transaction.destinationChainId,
628
+ })
629
+ }
630
+
631
+ // Get origin intent transaction hashes if not provided by API
632
+ const originHashes = await getOriginIntentTransactionHashes(
633
+ transaction,
634
+ trailsConfig,
635
+ )
636
+ if (originHashes.originIntentTxHash) {
637
+ originIntentTxHash = originHashes.originIntentTxHash
638
+ originIntentTxExplorerUrl = originHashes.originIntentTxExplorerUrl
639
+ }
640
+ if (originHashes.originIntentDepositTxHash) {
641
+ originIntentDepositTxHash = originHashes.originIntentDepositTxHash
642
+ originIntentDepositTxExplorerUrl =
643
+ originHashes.originIntentDepositTxExplorerUrl
644
+ }
645
+
646
+ // Get destination intent transaction hashes if not provided by API
647
+ const destinationHashes = await getDestinationIntentTransactionHashes(
648
+ transaction,
649
+ isSameIntentAddress,
650
+ trailsConfig,
651
+ )
652
+ if (destinationHashes.destinationIntentTxHash) {
653
+ destinationIntentTxHash = destinationHashes.destinationIntentTxHash
654
+ destinationIntentTxExplorerUrl =
655
+ destinationHashes.destinationIntentTxExplorerUrl
656
+ }
657
+ if (destinationHashes.destinationIntentDepositTxHash) {
658
+ destinationIntentDepositTxHash =
659
+ destinationHashes.destinationIntentDepositTxHash
660
+ destinationIntentDepositTxExplorerUrl =
661
+ destinationHashes.destinationIntentDepositTxExplorerUrl
662
+ }
663
+
664
+ // Decode events for executing transactions
665
+ const decodedEvents = await decodeTransactionEvents(transaction, {
666
+ originIntentTxHash,
667
+ destinationIntentTxHash,
668
+ originIntentDepositTxHash,
669
+ destinationIntentDepositTxHash,
670
+ })
671
+
672
+ // Calculate effective execution status
673
+ const effectiveExecutionStatus = getEffectiveExecutionStatus(
674
+ transaction,
675
+ {
676
+ originIntentTxHash,
677
+ destinationIntentTxHash,
678
+ originIntentDepositTxHash,
679
+ destinationIntentDepositTxHash,
680
+ },
681
+ decodedEvents,
682
+ )
683
+
684
+ // Return enriched transaction
685
+ return {
686
+ ...transaction,
687
+ ...(isSameIntentAddress
688
+ ? {
689
+ destinationIntentAddress: "",
690
+ destinationChainId: undefined,
691
+ }
692
+ : {}),
693
+ originToken,
694
+ destinationToken,
695
+ originIntentTxHash,
696
+ destinationIntentTxHash,
697
+ originIntentTxExplorerUrl,
698
+ destinationIntentTxExplorerUrl,
699
+ originIntentDepositTxHash,
700
+ destinationIntentDepositTxHash,
701
+ originIntentDepositTxExplorerUrl,
702
+ destinationIntentDepositTxExplorerUrl,
703
+ executionStatus: effectiveExecutionStatus,
704
+ }
705
+ }
12
706
 
13
707
  export type UseIntentTransactionHistoryParams = {
14
708
  accountAddress?: string
@@ -42,6 +736,73 @@ export function useIntentTransactionHistory({
42
736
  const [page, setPage] = useState(0)
43
737
  const [hasMore, setHasMore] = useState(false)
44
738
 
739
+ /**
740
+ * Fetches intent transaction history from the API
741
+ */
742
+ const fetchIntentTransactionHistoryFromApi = useCallback(
743
+ async (
744
+ accountAddress: string,
745
+ pageSize: number,
746
+ pageNum: number,
747
+ apiKey: string,
748
+ apiUrl: string,
749
+ ) => {
750
+ if (!apiKey) {
751
+ throw new Error("Trails api key is required")
752
+ }
753
+
754
+ const trailsClient = getTrailsClient({ apiKey, hostname: apiUrl })
755
+
756
+ const requestParams = {
757
+ byOwnerAddress: accountAddress,
758
+ page: {
759
+ pageSize,
760
+ sort: [
761
+ {
762
+ column: "created_at",
763
+ order: SortOrder.DESC,
764
+ },
765
+ ],
766
+ },
767
+ }
768
+
769
+ logger.console.log(
770
+ "[trails-sdk] getIntentTransactionHistory request:",
771
+ requestParams,
772
+ )
773
+
774
+ const apiResult =
775
+ await trailsClient.getIntentTransactionHistory(requestParams)
776
+
777
+ logger.console.log("[trails-sdk] getIntentTransactionHistory response:", {
778
+ intentCount: apiResult.intents?.length || 0,
779
+ hasNextPage: !!apiResult.nextPage,
780
+ firstIntent: apiResult.intents?.[0],
781
+ })
782
+
783
+ // Map API response to IntentTransaction
784
+ const mappedTransactions: IntentTransaction[] = (
785
+ apiResult.intents || []
786
+ ).map(mapIntentSummaryToTransaction)
787
+
788
+ return {
789
+ transactions: mappedTransactions,
790
+ page: apiResult.nextPage
791
+ ? {
792
+ page: pageNum,
793
+ pageSize,
794
+ more: apiResult.nextPage.more || false,
795
+ }
796
+ : {
797
+ page: pageNum,
798
+ pageSize,
799
+ more: false,
800
+ },
801
+ }
802
+ },
803
+ [],
804
+ )
805
+
45
806
  const fetchTransactions = useCallback(
46
807
  async (pageNum: number = 0, append: boolean = false) => {
47
808
  if (!accountAddress || !enabled) return
@@ -50,423 +811,26 @@ export function useIntentTransactionHistory({
50
811
  setError(null)
51
812
 
52
813
  try {
53
- const result: IntentTransactionHistoryResponse =
54
- await getIntentTransactionHistory({
55
- accountAddress,
56
- pageSize,
57
- page: pageNum,
58
- apiKey: trailsConfig.trailsApiKey,
59
- apiUrl: trailsConfig.trailsApiUrl!,
60
- })
61
-
62
- // Enrich transactions with token information
63
- const enrichedTransactions = await Promise.all(
64
- (result.transactions || []).map(async (transaction) => {
65
- let originToken:
66
- | {
67
- symbol: string
68
- name: string
69
- decimals: number
70
- imageUrl: string
71
- chainId: number
72
- }
73
- | undefined
74
- let destinationToken:
75
- | {
76
- symbol: string
77
- name: string
78
- decimals: number
79
- imageUrl: string
80
- chainId: number
81
- }
82
- | undefined
83
-
84
- // Get supported tokens once for lookup
85
- const supportedTokens = await getSupportedTokens()
86
-
87
- // Get origin token info
88
- if (transaction.originTokenAddress && transaction.originChainId) {
89
- const isNativeOrigin =
90
- transaction.originTokenAddress ===
91
- "0x0000000000000000000000000000000000000000"
92
-
93
- if (isNativeOrigin) {
94
- // For native tokens, find the matching native token by chain ID and address
95
- const nativeToken = supportedTokens.find(
96
- (token) =>
97
- token.contractAddress.toLowerCase() ===
98
- transaction.originTokenAddress!.toLowerCase() &&
99
- token.chainId === transaction.originChainId,
100
- )
101
-
102
- if (nativeToken) {
103
- originToken = {
104
- symbol: nativeToken.symbol,
105
- name: nativeToken.name,
106
- decimals: nativeToken.decimals,
107
- imageUrl: nativeToken.imageUrl,
108
- chainId: nativeToken.chainId,
109
- }
110
- } else {
111
- // Fallback for native token - use chain-specific default
112
- originToken = {
113
- symbol: "ETH", // Could be improved to detect chain-specific native tokens
114
- name: "Native Token",
115
- decimals: 18,
116
- imageUrl:
117
- "https://assets.sequence.info/images/tokens/large/1/0x0000000000000000000000000000000000000000.webp",
118
- chainId: transaction.originChainId,
119
- }
120
- }
121
- } else {
122
- // For ERC20 tokens, first check supported tokens by address and chain
123
- const supportedToken = supportedTokens.find(
124
- (token) =>
125
- token.contractAddress.toLowerCase() ===
126
- transaction.originTokenAddress!.toLowerCase() &&
127
- token.chainId === transaction.originChainId,
128
- )
129
-
130
- if (supportedToken) {
131
- originToken = {
132
- symbol: supportedToken.symbol,
133
- name: supportedToken.name,
134
- decimals: supportedToken.decimals,
135
- imageUrl: supportedToken.imageUrl,
136
- chainId: supportedToken.chainId,
137
- }
138
- } else {
139
- // Fallback to getTokenInfo for on-chain lookup with the specific chain ID
140
- try {
141
- const tokenInfo = await getTokenInfo(
142
- transaction.originChainId,
143
- transaction.originTokenAddress,
144
- )
145
- if (tokenInfo) {
146
- originToken = {
147
- symbol: tokenInfo.symbol,
148
- name: tokenInfo.name,
149
- decimals: tokenInfo.decimals,
150
- imageUrl: tokenInfo.imageUrl,
151
- chainId: tokenInfo.chainId,
152
- }
153
- }
154
- } catch (error) {
155
- logger.console.warn(
156
- "[trails-sdk] Failed to get origin token info for:",
157
- transaction.originTokenAddress,
158
- "on chain",
159
- transaction.originChainId,
160
- error,
161
- )
162
- }
163
- }
164
- }
165
- }
166
-
167
- // Get destination token info
168
- if (
169
- transaction.destinationTokenAddress &&
170
- transaction.destinationChainId
171
- ) {
172
- const isNativeDestination =
173
- transaction.destinationTokenAddress ===
174
- "0x0000000000000000000000000000000000000000"
175
-
176
- if (isNativeDestination) {
177
- // For native tokens, find the matching native token by chain ID and address
178
- const nativeToken = supportedTokens.find(
179
- (token) =>
180
- token.contractAddress.toLowerCase() ===
181
- transaction.destinationTokenAddress!.toLowerCase() &&
182
- token.chainId === transaction.destinationChainId,
183
- )
184
-
185
- if (nativeToken) {
186
- destinationToken = {
187
- symbol: nativeToken.symbol,
188
- name: nativeToken.name,
189
- decimals: nativeToken.decimals,
190
- imageUrl: nativeToken.imageUrl,
191
- chainId: nativeToken.chainId,
192
- }
193
- } else {
194
- // Fallback for native token - use chain-specific default
195
- destinationToken = {
196
- symbol: "ETH", // Could be improved to detect chain-specific native tokens
197
- name: "Native Token",
198
- decimals: 18,
199
- imageUrl:
200
- "https://assets.sequence.info/images/tokens/large/1/0x0000000000000000000000000000000000000000.webp",
201
- chainId: transaction.destinationChainId,
202
- }
203
- }
204
- } else {
205
- // For ERC20 tokens, first check supported tokens by address and chain
206
- const supportedToken = supportedTokens.find(
207
- (token) =>
208
- token.contractAddress.toLowerCase() ===
209
- transaction.destinationTokenAddress!.toLowerCase() &&
210
- token.chainId === transaction.destinationChainId,
211
- )
212
-
213
- if (supportedToken) {
214
- destinationToken = {
215
- symbol: supportedToken.symbol,
216
- name: supportedToken.name,
217
- decimals: supportedToken.decimals,
218
- imageUrl: supportedToken.imageUrl,
219
- chainId: supportedToken.chainId,
220
- }
221
- } else {
222
- // Fallback to getTokenInfo for on-chain lookup with the specific chain ID
223
- try {
224
- const tokenInfo = await getTokenInfo(
225
- transaction.destinationChainId,
226
- transaction.destinationTokenAddress,
227
- )
228
- if (tokenInfo) {
229
- destinationToken = {
230
- symbol: tokenInfo.symbol,
231
- name: tokenInfo.name,
232
- decimals: tokenInfo.decimals,
233
- imageUrl: tokenInfo.imageUrl,
234
- chainId: tokenInfo.chainId,
235
- }
236
- }
237
- } catch (error) {
238
- logger.console.warn(
239
- "[trails-sdk] Failed to get destination token info for:",
240
- transaction.destinationTokenAddress,
241
- "on chain",
242
- transaction.destinationChainId,
243
- error,
244
- )
245
- }
246
- }
247
- }
248
- }
249
-
250
- // Check if origin and destination are the same
251
- const isSameIntentAddress =
252
- transaction.originIntentAddress?.toLowerCase() ===
253
- transaction.destinationIntentAddress?.toLowerCase() &&
254
- transaction.originChainId === transaction.destinationChainId
255
-
256
- // Fetch intent wallet transaction hashes
257
- let originIntentTxHash: string | undefined
258
- let destinationIntentTxHash: string | undefined
259
- let originIntentTxExplorerUrl: string | undefined
260
- let destinationIntentTxExplorerUrl: string | undefined
261
- let originIntentDepositTxHash: string | undefined
262
- let destinationIntentDepositTxHash: string | undefined
263
- let originIntentDepositTxExplorerUrl: string | undefined
264
- let destinationIntentDepositTxExplorerUrl: string | undefined
265
-
266
- // Get origin intent transaction hashes (both deposit and action)
267
- if (transaction.originIntentAddress && transaction.originChainId) {
268
- try {
269
- const originHistory = await getAccountTransactionHistory({
270
- chainId: transaction.originChainId,
271
- accountAddress: transaction.originIntentAddress,
272
- pageSize: 10, // Fetch more to find both deposit and action txs
273
- page: 0,
274
- includeMetadata: true, // Need metadata to check transfer direction
275
- apiKey: trailsConfig.trailsApiKey,
276
- indexerUrl: trailsConfig.sequenceIndexerUrl!,
277
- })
278
- if (
279
- originHistory?.transactions &&
280
- originHistory.transactions.length > 0
281
- ) {
282
- const intentAddress =
283
- transaction.originIntentAddress.toLowerCase()
284
-
285
- // Find first deposit tx (transfer TO intent address)
286
- const depositTx = originHistory.transactions.find((tx) =>
287
- tx.transfers?.some(
288
- (transfer) => transfer.to.toLowerCase() === intentAddress,
289
- ),
290
- )
291
- if (depositTx) {
292
- originIntentDepositTxHash = depositTx.txnHash
293
- originIntentDepositTxExplorerUrl = getExplorerUrl({
294
- txHash: depositTx.txnHash,
295
- chainId: transaction.originChainId,
296
- })
297
- }
298
-
299
- // Find first action tx (transfer FROM intent address)
300
- const actionTx = originHistory.transactions.find((tx) =>
301
- tx.transfers?.some(
302
- (transfer) =>
303
- transfer.from.toLowerCase() === intentAddress,
304
- ),
305
- )
306
- if (actionTx) {
307
- originIntentTxHash = actionTx.txnHash
308
- originIntentTxExplorerUrl = getExplorerUrl({
309
- txHash: actionTx.txnHash,
310
- chainId: transaction.originChainId,
311
- })
312
- }
313
- }
314
- } catch (error) {
315
- logger.console.warn(
316
- "[trails-sdk] Failed to get origin intent transaction hashes for:",
317
- transaction.originIntentAddress,
318
- "on chain",
319
- transaction.originChainId,
320
- error,
321
- )
322
- }
323
- }
324
-
325
- // Get destination intent transaction hashes (only if different from origin)
326
- if (
327
- !isSameIntentAddress &&
328
- transaction.destinationIntentAddress &&
329
- transaction.destinationChainId
330
- ) {
331
- try {
332
- const destinationHistory = await getAccountTransactionHistory({
333
- chainId: transaction.destinationChainId,
334
- accountAddress: transaction.destinationIntentAddress,
335
- pageSize: 10, // Fetch more to find both deposit and action txs
336
- page: 0,
337
- includeMetadata: true, // Need metadata to check transfer direction
338
- apiKey: trailsConfig.trailsApiKey,
339
- indexerUrl: trailsConfig.sequenceIndexerUrl!,
340
- })
341
- if (
342
- destinationHistory?.transactions &&
343
- destinationHistory.transactions.length > 0
344
- ) {
345
- const intentAddress =
346
- transaction.destinationIntentAddress.toLowerCase()
347
-
348
- // Find first deposit tx (transfer TO intent address)
349
- const depositTx = destinationHistory.transactions.find((tx) =>
350
- tx.transfers?.some(
351
- (transfer) => transfer.to.toLowerCase() === intentAddress,
352
- ),
353
- )
354
- if (depositTx) {
355
- destinationIntentDepositTxHash = depositTx.txnHash
356
- destinationIntentDepositTxExplorerUrl = getExplorerUrl({
357
- txHash: depositTx.txnHash,
358
- chainId: transaction.destinationChainId,
359
- })
360
- }
361
-
362
- // Find first action tx (transfer FROM intent address)
363
- const actionTx = destinationHistory.transactions.find((tx) =>
364
- tx.transfers?.some(
365
- (transfer) =>
366
- transfer.from.toLowerCase() === intentAddress,
367
- ),
368
- )
369
- if (actionTx) {
370
- destinationIntentTxHash = actionTx.txnHash
371
- destinationIntentTxExplorerUrl = getExplorerUrl({
372
- txHash: actionTx.txnHash,
373
- chainId: transaction.destinationChainId,
374
- })
375
- }
376
- }
377
- } catch (error) {
378
- logger.console.warn(
379
- "[trails-sdk] Failed to get destination intent transaction hashes for:",
380
- transaction.destinationIntentAddress,
381
- "on chain",
382
- transaction.destinationChainId,
383
- error,
384
- )
385
- }
386
- }
814
+ const apiKey = trailsConfig.trailsApiKey || ""
815
+ const apiUrl = trailsConfig.trailsApiUrl!
816
+
817
+ // Fetch intent transaction history from API
818
+ const result = await fetchIntentTransactionHistoryFromApi(
819
+ accountAddress,
820
+ pageSize,
821
+ pageNum,
822
+ apiKey,
823
+ apiUrl,
824
+ )
387
825
 
388
- // Calculate effective execution status
389
- const getEffectiveStatus = (): string => {
390
- const {
391
- executionStatus,
392
- originChainId,
393
- destinationChainId,
394
- originIntentAddress,
395
- destinationIntentAddress,
396
- createdAt,
397
- } = transaction
398
-
399
- // Check if this is a same-chain transaction (same address and chain)
400
- const isSameChain =
401
- originIntentAddress?.toLowerCase() ===
402
- destinationIntentAddress?.toLowerCase() &&
403
- originChainId === destinationChainId
404
-
405
- // Check if there's no destination (same-chain or no destination intent)
406
- const hasNoDestination =
407
- !destinationIntentAddress || !destinationChainId || isSameChain
408
-
409
- // Check if it's been over 5 minutes since creation
410
- const isOver5Minutes = createdAt
411
- ? Date.now() - new Date(createdAt).getTime() > 5 * 60 * 1000
412
- : false
413
-
414
- // Rule 1: If there's a destination intent tx, it's completed
415
- if (destinationIntentTxHash) {
416
- return "completed"
417
- }
418
-
419
- // Rule 2: Only origin tx hash exists and no real destination (same-chain case)
420
- if (originIntentTxHash && originChainId && hasNoDestination) {
421
- return "completed"
422
- }
423
-
424
- // Rule 3: If there's a deposit but no action yet, status is pending
425
- if (originIntentDepositTxHash && !originIntentTxHash) {
426
- return "pending"
427
- }
428
-
429
- // Rule 4: Cross-chain with origin tx but no destination tx
430
- // If over 5 minutes, consider it failed; otherwise pending
431
- if (
432
- originIntentTxHash &&
433
- !destinationIntentTxHash &&
434
- originChainId &&
435
- destinationChainId &&
436
- !isSameChain &&
437
- destinationIntentAddress
438
- ) {
439
- return isOver5Minutes ? "failed" : "pending"
440
- }
441
-
442
- // Otherwise, use the original execution status
443
- return executionStatus || "unknown"
444
- }
826
+ // Get supported tokens once for all transactions
827
+ const supportedTokens = await getSupportedTokens()
445
828
 
446
- const effectiveExecutionStatus = getEffectiveStatus()
447
-
448
- // If same intent address, omit destination address/chain to avoid duplication
449
- return {
450
- ...transaction,
451
- ...(isSameIntentAddress
452
- ? {
453
- destinationIntentAddress: "",
454
- destinationChainId: undefined,
455
- }
456
- : {}),
457
- originToken,
458
- destinationToken,
459
- originIntentTxHash,
460
- destinationIntentTxHash,
461
- originIntentTxExplorerUrl,
462
- destinationIntentTxExplorerUrl,
463
- originIntentDepositTxHash,
464
- destinationIntentDepositTxHash,
465
- originIntentDepositTxExplorerUrl,
466
- destinationIntentDepositTxExplorerUrl,
467
- executionStatus: effectiveExecutionStatus,
468
- }
469
- }),
829
+ // Enrich transactions with token information, transaction hashes, and status
830
+ const enrichedTransactions = await Promise.all(
831
+ (result.transactions || []).map(async (transaction) =>
832
+ enrichTransaction(transaction, supportedTokens, trailsConfig),
833
+ ),
470
834
  )
471
835
 
472
836
  if (append) {
@@ -491,7 +855,13 @@ export function useIntentTransactionHistory({
491
855
  setLoading(false)
492
856
  }
493
857
  },
494
- [accountAddress, enabled, pageSize, trailsConfig],
858
+ [
859
+ accountAddress,
860
+ enabled,
861
+ pageSize,
862
+ trailsConfig,
863
+ fetchIntentTransactionHistoryFromApi,
864
+ ],
495
865
  )
496
866
 
497
867
  const refetch = () => {