0xtrails 0.4.3 → 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 (207) hide show
  1. package/dist/analytics.d.ts +8 -3
  2. package/dist/analytics.d.ts.map +1 -1
  3. package/dist/{ccip-C9CA3tIi.js → ccip-Dw5AN7oU.js} +27 -27
  4. package/dist/cctp.d.ts +0 -149
  5. package/dist/cctp.d.ts.map +1 -1
  6. package/dist/chains.d.ts +31 -4
  7. package/dist/chains.d.ts.map +1 -1
  8. package/dist/config.d.ts +28 -52
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/constants.d.ts +1 -7
  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-CNB57v0n.js → index-BtVUTbEZ.js} +32255 -40547
  18. package/dist/index.d.ts +13 -11
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +320 -944
  21. package/dist/indexerClient.d.ts +9 -6
  22. package/dist/indexerClient.d.ts.map +1 -1
  23. package/dist/intents.d.ts +0 -18
  24. package/dist/intents.d.ts.map +1 -1
  25. package/dist/mutations.d.ts.map +1 -1
  26. package/dist/paymasterSend.d.ts.map +1 -1
  27. package/dist/prepareSend.d.ts +1 -1
  28. package/dist/prepareSend.d.ts.map +1 -1
  29. package/dist/sendUserOp.d.ts +0 -18
  30. package/dist/sendUserOp.d.ts.map +1 -1
  31. package/dist/sequenceWallet.d.ts +1 -1
  32. package/dist/sequenceWallet.d.ts.map +1 -1
  33. package/dist/tokenBalances.d.ts.map +1 -1
  34. package/dist/tokens.d.ts +10 -8
  35. package/dist/tokens.d.ts.map +1 -1
  36. package/dist/trailsClient.d.ts +3 -3
  37. package/dist/trailsClient.d.ts.map +1 -1
  38. package/dist/transactionIntent/deposits/depositOrchestrator.d.ts +4 -5
  39. package/dist/transactionIntent/deposits/depositOrchestrator.d.ts.map +1 -1
  40. package/dist/transactionIntent/deposits/gaslessDeposit.d.ts +4 -5
  41. package/dist/transactionIntent/deposits/gaslessDeposit.d.ts.map +1 -1
  42. package/dist/transactionIntent/deposits/standardDeposit.d.ts +2 -2
  43. package/dist/transactionIntent/deposits/standardDeposit.d.ts.map +1 -1
  44. package/dist/transactionIntent/execution/transactionState.d.ts +2 -2
  45. package/dist/transactionIntent/execution/transactionState.d.ts.map +1 -1
  46. package/dist/transactionIntent/handlers/crossChain.d.ts +6 -4
  47. package/dist/transactionIntent/handlers/crossChain.d.ts.map +1 -1
  48. package/dist/transactionIntent/handlers/index.d.ts +0 -1
  49. package/dist/transactionIntent/handlers/index.d.ts.map +1 -1
  50. package/dist/transactionIntent/handlers/sameChainSameToken.d.ts +4 -34
  51. package/dist/transactionIntent/handlers/sameChainSameToken.d.ts.map +1 -1
  52. package/dist/transactionIntent/quote/normalizeQuote.d.ts.map +1 -1
  53. package/dist/transactionIntent/quote/quoteHelpers.d.ts +2 -1
  54. package/dist/transactionIntent/quote/quoteHelpers.d.ts.map +1 -1
  55. package/dist/transactionIntent/types.d.ts +7 -19
  56. package/dist/transactionIntent/types.d.ts.map +1 -1
  57. package/dist/transactionIntent/utils/index.d.ts +0 -1
  58. package/dist/transactionIntent/utils/index.d.ts.map +1 -1
  59. package/dist/transactions.d.ts +6 -18
  60. package/dist/transactions.d.ts.map +1 -1
  61. package/dist/utils.d.ts +8 -2
  62. package/dist/utils.d.ts.map +1 -1
  63. package/dist/walletUtils.d.ts +21 -0
  64. package/dist/walletUtils.d.ts.map +1 -0
  65. package/dist/wallets.d.ts +33 -240
  66. package/dist/wallets.d.ts.map +1 -1
  67. package/dist/widget/components/AccountIntentTransactionHistory.d.ts.map +1 -1
  68. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  69. package/dist/widget/components/ConfigDisplay.d.ts.map +1 -1
  70. package/dist/widget/components/FeeOption.d.ts +8 -13
  71. package/dist/widget/components/FeeOption.d.ts.map +1 -1
  72. package/dist/widget/components/FeeOptions.d.ts +11 -5
  73. package/dist/widget/components/FeeOptions.d.ts.map +1 -1
  74. package/dist/widget/components/NativeGasOption.d.ts.map +1 -1
  75. package/dist/widget/components/Pay.d.ts.map +1 -1
  76. package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
  77. package/dist/widget/components/QRCodeDeposit.d.ts +5 -0
  78. package/dist/widget/components/QRCodeDeposit.d.ts.map +1 -1
  79. package/dist/widget/components/QRCodeWalletSelect.d.ts +13 -0
  80. package/dist/widget/components/QRCodeWalletSelect.d.ts.map +1 -0
  81. package/dist/widget/components/QrCode.d.ts.map +1 -1
  82. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  83. package/dist/widget/components/Receipt.d.ts.map +1 -1
  84. package/dist/widget/components/ScreenHeader.d.ts +1 -1
  85. package/dist/widget/components/ScreenHeader.d.ts.map +1 -1
  86. package/dist/widget/components/SlippageToleranceSettings.d.ts +1 -0
  87. package/dist/widget/components/SlippageToleranceSettings.d.ts.map +1 -1
  88. package/dist/widget/components/Toast.d.ts.map +1 -1
  89. package/dist/widget/components/TokenImage.d.ts.map +1 -1
  90. package/dist/widget/components/WalletConnect.d.ts.map +1 -1
  91. package/dist/widget/css/compiled.css +1 -1
  92. package/dist/widget/hooks/useCheckout.d.ts +15 -1
  93. package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
  94. package/dist/widget/hooks/useCurrentScreen.d.ts +1 -1
  95. package/dist/widget/hooks/useCurrentScreen.d.ts.map +1 -1
  96. package/dist/widget/hooks/useDebugScreens.d.ts +1 -1
  97. package/dist/widget/hooks/useDebugScreens.d.ts.map +1 -1
  98. package/dist/widget/hooks/useIntentTransactionHistory.d.ts.map +1 -1
  99. package/dist/widget/hooks/useIsConnectedWalletSmartContract.d.ts +7 -0
  100. package/dist/widget/hooks/useIsConnectedWalletSmartContract.d.ts.map +1 -0
  101. package/dist/widget/hooks/useIsSequenceWallet.d.ts +6 -0
  102. package/dist/widget/hooks/useIsSequenceWallet.d.ts.map +1 -0
  103. package/dist/widget/hooks/useQuote.d.ts +6 -11
  104. package/dist/widget/hooks/useQuote.d.ts.map +1 -1
  105. package/dist/widget/hooks/useRecentTokens.d.ts.map +1 -1
  106. package/dist/widget/hooks/useSelectedFeeOption.d.ts +30 -0
  107. package/dist/widget/hooks/useSelectedFeeOption.d.ts.map +1 -0
  108. package/dist/widget/hooks/useSendForm.d.ts +6 -15
  109. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  110. package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
  111. package/dist/widget/index.d.ts +1 -0
  112. package/dist/widget/index.d.ts.map +1 -1
  113. package/dist/widget/index.js +4 -2
  114. package/dist/widget/providers/TrailsProvider.d.ts +29 -0
  115. package/dist/widget/providers/TrailsProvider.d.ts.map +1 -0
  116. package/dist/widget/widget.d.ts +14 -3
  117. package/dist/widget/widget.d.ts.map +1 -1
  118. package/package.json +9 -8
  119. package/src/analytics.ts +53 -21
  120. package/src/cctp.ts +0 -1016
  121. package/src/chains.ts +155 -68
  122. package/src/config.ts +51 -207
  123. package/src/constants.ts +1 -13
  124. package/src/contractUtils.ts +6 -6
  125. package/src/estimate.ts +3 -6
  126. package/src/fees.ts +5 -10
  127. package/src/gasless.ts +45 -0
  128. package/src/index.ts +18 -40
  129. package/src/indexerClient.ts +39 -48
  130. package/src/intents.ts +51 -226
  131. package/src/mutations.ts +3 -2
  132. package/src/paymasterSend.ts +2 -5
  133. package/src/prepareSend.ts +21 -14
  134. package/src/sendUserOp.ts +3 -64
  135. package/src/sequenceWallet.ts +1 -2
  136. package/src/tokenBalances.ts +2 -1
  137. package/src/tokens.ts +62 -133
  138. package/src/trailsClient.ts +17 -12
  139. package/src/transactionIntent/deposits/depositOrchestrator.ts +14 -15
  140. package/src/transactionIntent/deposits/gaslessDeposit.ts +70 -100
  141. package/src/transactionIntent/deposits/standardDeposit.ts +22 -28
  142. package/src/transactionIntent/execution/transactionState.ts +2 -2
  143. package/src/transactionIntent/handlers/crossChain.ts +179 -381
  144. package/src/transactionIntent/handlers/index.ts +0 -1
  145. package/src/transactionIntent/handlers/sameChainSameToken.ts +228 -94
  146. package/src/transactionIntent/quote/normalizeQuote.ts +4 -6
  147. package/src/transactionIntent/quote/quoteHelpers.ts +35 -3
  148. package/src/transactionIntent/types.ts +7 -27
  149. package/src/transactionIntent/utils/index.ts +0 -1
  150. package/src/transactions.ts +19 -118
  151. package/src/umd.tsx +1 -3
  152. package/src/utils.ts +28 -8
  153. package/src/walletUtils.ts +42 -0
  154. package/src/wallets.ts +365 -208
  155. package/src/widget/compiled.css +1 -1
  156. package/src/widget/components/AccountIntentTransactionHistory.tsx +78 -4
  157. package/src/widget/components/AccountSettings.tsx +17 -17
  158. package/src/widget/components/ChainList.tsx +3 -3
  159. package/src/widget/components/ClassicSwap.tsx +19 -10
  160. package/src/widget/components/ConfigDisplay.tsx +19 -59
  161. package/src/widget/components/FeeOption.tsx +63 -20
  162. package/src/widget/components/FeeOptions.tsx +54 -123
  163. package/src/widget/components/NativeGasOption.tsx +3 -1
  164. package/src/widget/components/Pay.tsx +18 -11
  165. package/src/widget/components/PoolDeposit.tsx +23 -10
  166. package/src/widget/components/QRCodeDeposit.tsx +50 -30
  167. package/src/widget/components/QRCodeWalletSelect.tsx +77 -0
  168. package/src/widget/components/QrCode.tsx +188 -233
  169. package/src/widget/components/QuoteDetails.tsx +48 -2
  170. package/src/widget/components/Receipt.tsx +5 -2
  171. package/src/widget/components/ScreenHeader.tsx +10 -8
  172. package/src/widget/components/SlippageToleranceSettings.tsx +63 -14
  173. package/src/widget/components/Toast.tsx +10 -0
  174. package/src/widget/components/TokenImage.tsx +56 -13
  175. package/src/widget/components/WalletConnect.tsx +37 -99
  176. package/src/widget/hooks/useCheckout.ts +71 -0
  177. package/src/widget/hooks/useCurrentScreen.tsx +1 -0
  178. package/src/widget/hooks/useDebugScreens.ts +5 -0
  179. package/src/widget/hooks/useIntentTransactionHistory.ts +790 -412
  180. package/src/widget/hooks/useIsConnectedWalletSmartContract.ts +43 -0
  181. package/src/widget/hooks/useIsSequenceWallet.ts +17 -0
  182. package/src/widget/hooks/useQuote.ts +27 -34
  183. package/src/widget/hooks/useRecentTokens.ts +2 -1
  184. package/src/widget/hooks/useSelectedFeeOption.tsx +257 -0
  185. package/src/widget/hooks/useSendForm.ts +175 -46
  186. package/src/widget/hooks/useTokenList.ts +15 -2
  187. package/src/widget/index.tsx +8 -0
  188. package/src/widget/providers/TrailsProvider.tsx +123 -0
  189. package/src/widget/widget.tsx +165 -143
  190. package/dist/cctpqueue.d.ts +0 -18
  191. package/dist/cctpqueue.d.ts.map +0 -1
  192. package/dist/preconditions.d.ts +0 -12
  193. package/dist/preconditions.d.ts.map +0 -1
  194. package/dist/trails.d.ts +0 -110
  195. package/dist/trails.d.ts.map +0 -1
  196. package/dist/transactionIntent/handlers/sameChainDifferentToken.d.ts +0 -62
  197. package/dist/transactionIntent/handlers/sameChainDifferentToken.d.ts.map +0 -1
  198. package/dist/transactionIntent/utils/lifiHelpers.d.ts +0 -10
  199. package/dist/transactionIntent/utils/lifiHelpers.d.ts.map +0 -1
  200. package/dist/widget/hooks/useSelectedFeeToken.d.ts +0 -33
  201. package/dist/widget/hooks/useSelectedFeeToken.d.ts.map +0 -1
  202. package/src/cctpqueue.ts +0 -66
  203. package/src/preconditions.ts +0 -47
  204. package/src/trails.ts +0 -1303
  205. package/src/transactionIntent/handlers/sameChainDifferentToken.ts +0 -323
  206. package/src/transactionIntent/utils/lifiHelpers.ts +0 -68
  207. package/src/widget/hooks/useSelectedFeeToken.tsx +0 -288
@@ -1,13 +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"
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
+ }
11
706
 
12
707
  export type UseIntentTransactionHistoryParams = {
13
708
  accountAddress?: string
@@ -34,12 +729,80 @@ export function useIntentTransactionHistory({
34
729
  pageSize = 10,
35
730
  enabled = true,
36
731
  }: UseIntentTransactionHistoryParams): UseIntentTransactionHistoryReturn {
732
+ const trailsConfig = useTrails()
37
733
  const [transactions, setTransactions] = useState<IntentTransaction[]>([])
38
734
  const [loading, setLoading] = useState(false)
39
735
  const [error, setError] = useState<string | null>(null)
40
736
  const [page, setPage] = useState(0)
41
737
  const [hasMore, setHasMore] = useState(false)
42
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
+
43
806
  const fetchTransactions = useCallback(
44
807
  async (pageNum: number = 0, append: boolean = false) => {
45
808
  if (!accountAddress || !enabled) return
@@ -48,417 +811,26 @@ export function useIntentTransactionHistory({
48
811
  setError(null)
49
812
 
50
813
  try {
51
- const result: IntentTransactionHistoryResponse =
52
- await getIntentTransactionHistory({
53
- accountAddress,
54
- pageSize,
55
- page: pageNum,
56
- })
57
-
58
- // Enrich transactions with token information
59
- const enrichedTransactions = await Promise.all(
60
- (result.transactions || []).map(async (transaction) => {
61
- let originToken:
62
- | {
63
- symbol: string
64
- name: string
65
- decimals: number
66
- imageUrl: string
67
- chainId: number
68
- }
69
- | undefined
70
- let destinationToken:
71
- | {
72
- symbol: string
73
- name: string
74
- decimals: number
75
- imageUrl: string
76
- chainId: number
77
- }
78
- | undefined
79
-
80
- // Get supported tokens once for lookup
81
- const supportedTokens = await getSupportedTokens()
82
-
83
- // Get origin token info
84
- if (transaction.originTokenAddress && transaction.originChainId) {
85
- const isNativeOrigin =
86
- transaction.originTokenAddress ===
87
- "0x0000000000000000000000000000000000000000"
88
-
89
- if (isNativeOrigin) {
90
- // For native tokens, find the matching native token by chain ID and address
91
- const nativeToken = supportedTokens.find(
92
- (token) =>
93
- token.contractAddress.toLowerCase() ===
94
- transaction.originTokenAddress!.toLowerCase() &&
95
- token.chainId === transaction.originChainId,
96
- )
97
-
98
- if (nativeToken) {
99
- originToken = {
100
- symbol: nativeToken.symbol,
101
- name: nativeToken.name,
102
- decimals: nativeToken.decimals,
103
- imageUrl: nativeToken.imageUrl,
104
- chainId: nativeToken.chainId,
105
- }
106
- } else {
107
- // Fallback for native token - use chain-specific default
108
- originToken = {
109
- symbol: "ETH", // Could be improved to detect chain-specific native tokens
110
- name: "Native Token",
111
- decimals: 18,
112
- imageUrl:
113
- "https://assets.sequence.info/images/tokens/large/1/0x0000000000000000000000000000000000000000.webp",
114
- chainId: transaction.originChainId,
115
- }
116
- }
117
- } else {
118
- // For ERC20 tokens, first check supported tokens by address and chain
119
- const supportedToken = supportedTokens.find(
120
- (token) =>
121
- token.contractAddress.toLowerCase() ===
122
- transaction.originTokenAddress!.toLowerCase() &&
123
- token.chainId === transaction.originChainId,
124
- )
125
-
126
- if (supportedToken) {
127
- originToken = {
128
- symbol: supportedToken.symbol,
129
- name: supportedToken.name,
130
- decimals: supportedToken.decimals,
131
- imageUrl: supportedToken.imageUrl,
132
- chainId: supportedToken.chainId,
133
- }
134
- } else {
135
- // Fallback to getTokenInfo for on-chain lookup with the specific chain ID
136
- try {
137
- const tokenInfo = await getTokenInfo(
138
- transaction.originChainId,
139
- transaction.originTokenAddress,
140
- )
141
- if (tokenInfo) {
142
- originToken = {
143
- symbol: tokenInfo.symbol,
144
- name: tokenInfo.name,
145
- decimals: tokenInfo.decimals,
146
- imageUrl: tokenInfo.imageUrl,
147
- chainId: tokenInfo.chainId,
148
- }
149
- }
150
- } catch (error) {
151
- logger.console.warn(
152
- "[trails-sdk] Failed to get origin token info for:",
153
- transaction.originTokenAddress,
154
- "on chain",
155
- transaction.originChainId,
156
- error,
157
- )
158
- }
159
- }
160
- }
161
- }
162
-
163
- // Get destination token info
164
- if (
165
- transaction.destinationTokenAddress &&
166
- transaction.destinationChainId
167
- ) {
168
- const isNativeDestination =
169
- transaction.destinationTokenAddress ===
170
- "0x0000000000000000000000000000000000000000"
171
-
172
- if (isNativeDestination) {
173
- // For native tokens, find the matching native token by chain ID and address
174
- const nativeToken = supportedTokens.find(
175
- (token) =>
176
- token.contractAddress.toLowerCase() ===
177
- transaction.destinationTokenAddress!.toLowerCase() &&
178
- token.chainId === transaction.destinationChainId,
179
- )
180
-
181
- if (nativeToken) {
182
- destinationToken = {
183
- symbol: nativeToken.symbol,
184
- name: nativeToken.name,
185
- decimals: nativeToken.decimals,
186
- imageUrl: nativeToken.imageUrl,
187
- chainId: nativeToken.chainId,
188
- }
189
- } else {
190
- // Fallback for native token - use chain-specific default
191
- destinationToken = {
192
- symbol: "ETH", // Could be improved to detect chain-specific native tokens
193
- name: "Native Token",
194
- decimals: 18,
195
- imageUrl:
196
- "https://assets.sequence.info/images/tokens/large/1/0x0000000000000000000000000000000000000000.webp",
197
- chainId: transaction.destinationChainId,
198
- }
199
- }
200
- } else {
201
- // For ERC20 tokens, first check supported tokens by address and chain
202
- const supportedToken = supportedTokens.find(
203
- (token) =>
204
- token.contractAddress.toLowerCase() ===
205
- transaction.destinationTokenAddress!.toLowerCase() &&
206
- token.chainId === transaction.destinationChainId,
207
- )
208
-
209
- if (supportedToken) {
210
- destinationToken = {
211
- symbol: supportedToken.symbol,
212
- name: supportedToken.name,
213
- decimals: supportedToken.decimals,
214
- imageUrl: supportedToken.imageUrl,
215
- chainId: supportedToken.chainId,
216
- }
217
- } else {
218
- // Fallback to getTokenInfo for on-chain lookup with the specific chain ID
219
- try {
220
- const tokenInfo = await getTokenInfo(
221
- transaction.destinationChainId,
222
- transaction.destinationTokenAddress,
223
- )
224
- if (tokenInfo) {
225
- destinationToken = {
226
- symbol: tokenInfo.symbol,
227
- name: tokenInfo.name,
228
- decimals: tokenInfo.decimals,
229
- imageUrl: tokenInfo.imageUrl,
230
- chainId: tokenInfo.chainId,
231
- }
232
- }
233
- } catch (error) {
234
- logger.console.warn(
235
- "[trails-sdk] Failed to get destination token info for:",
236
- transaction.destinationTokenAddress,
237
- "on chain",
238
- transaction.destinationChainId,
239
- error,
240
- )
241
- }
242
- }
243
- }
244
- }
245
-
246
- // Check if origin and destination are the same
247
- const isSameIntentAddress =
248
- transaction.originIntentAddress?.toLowerCase() ===
249
- transaction.destinationIntentAddress?.toLowerCase() &&
250
- transaction.originChainId === transaction.destinationChainId
251
-
252
- // Fetch intent wallet transaction hashes
253
- let originIntentTxHash: string | undefined
254
- let destinationIntentTxHash: string | undefined
255
- let originIntentTxExplorerUrl: string | undefined
256
- let destinationIntentTxExplorerUrl: string | undefined
257
- let originIntentDepositTxHash: string | undefined
258
- let destinationIntentDepositTxHash: string | undefined
259
- let originIntentDepositTxExplorerUrl: string | undefined
260
- let destinationIntentDepositTxExplorerUrl: string | undefined
261
-
262
- // Get origin intent transaction hashes (both deposit and action)
263
- if (transaction.originIntentAddress && transaction.originChainId) {
264
- try {
265
- const originHistory = await getAccountTransactionHistory({
266
- chainId: transaction.originChainId,
267
- accountAddress: transaction.originIntentAddress,
268
- pageSize: 10, // Fetch more to find both deposit and action txs
269
- page: 0,
270
- includeMetadata: true, // Need metadata to check transfer direction
271
- })
272
- if (
273
- originHistory?.transactions &&
274
- originHistory.transactions.length > 0
275
- ) {
276
- const intentAddress =
277
- transaction.originIntentAddress.toLowerCase()
278
-
279
- // Find first deposit tx (transfer TO intent address)
280
- const depositTx = originHistory.transactions.find((tx) =>
281
- tx.transfers?.some(
282
- (transfer) => transfer.to.toLowerCase() === intentAddress,
283
- ),
284
- )
285
- if (depositTx) {
286
- originIntentDepositTxHash = depositTx.txnHash
287
- originIntentDepositTxExplorerUrl = getExplorerUrl({
288
- txHash: depositTx.txnHash,
289
- chainId: transaction.originChainId,
290
- })
291
- }
292
-
293
- // Find first action tx (transfer FROM intent address)
294
- const actionTx = originHistory.transactions.find((tx) =>
295
- tx.transfers?.some(
296
- (transfer) =>
297
- transfer.from.toLowerCase() === intentAddress,
298
- ),
299
- )
300
- if (actionTx) {
301
- originIntentTxHash = actionTx.txnHash
302
- originIntentTxExplorerUrl = getExplorerUrl({
303
- txHash: actionTx.txnHash,
304
- chainId: transaction.originChainId,
305
- })
306
- }
307
- }
308
- } catch (error) {
309
- logger.console.warn(
310
- "[trails-sdk] Failed to get origin intent transaction hashes for:",
311
- transaction.originIntentAddress,
312
- "on chain",
313
- transaction.originChainId,
314
- error,
315
- )
316
- }
317
- }
318
-
319
- // Get destination intent transaction hashes (only if different from origin)
320
- if (
321
- !isSameIntentAddress &&
322
- transaction.destinationIntentAddress &&
323
- transaction.destinationChainId
324
- ) {
325
- try {
326
- const destinationHistory = await getAccountTransactionHistory({
327
- chainId: transaction.destinationChainId,
328
- accountAddress: transaction.destinationIntentAddress,
329
- pageSize: 10, // Fetch more to find both deposit and action txs
330
- page: 0,
331
- includeMetadata: true, // Need metadata to check transfer direction
332
- })
333
- if (
334
- destinationHistory?.transactions &&
335
- destinationHistory.transactions.length > 0
336
- ) {
337
- const intentAddress =
338
- transaction.destinationIntentAddress.toLowerCase()
339
-
340
- // Find first deposit tx (transfer TO intent address)
341
- const depositTx = destinationHistory.transactions.find((tx) =>
342
- tx.transfers?.some(
343
- (transfer) => transfer.to.toLowerCase() === intentAddress,
344
- ),
345
- )
346
- if (depositTx) {
347
- destinationIntentDepositTxHash = depositTx.txnHash
348
- destinationIntentDepositTxExplorerUrl = getExplorerUrl({
349
- txHash: depositTx.txnHash,
350
- chainId: transaction.destinationChainId,
351
- })
352
- }
353
-
354
- // Find first action tx (transfer FROM intent address)
355
- const actionTx = destinationHistory.transactions.find((tx) =>
356
- tx.transfers?.some(
357
- (transfer) =>
358
- transfer.from.toLowerCase() === intentAddress,
359
- ),
360
- )
361
- if (actionTx) {
362
- destinationIntentTxHash = actionTx.txnHash
363
- destinationIntentTxExplorerUrl = getExplorerUrl({
364
- txHash: actionTx.txnHash,
365
- chainId: transaction.destinationChainId,
366
- })
367
- }
368
- }
369
- } catch (error) {
370
- logger.console.warn(
371
- "[trails-sdk] Failed to get destination intent transaction hashes for:",
372
- transaction.destinationIntentAddress,
373
- "on chain",
374
- transaction.destinationChainId,
375
- error,
376
- )
377
- }
378
- }
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
+ )
379
825
 
380
- // Calculate effective execution status
381
- const getEffectiveStatus = (): string => {
382
- const {
383
- executionStatus,
384
- originChainId,
385
- destinationChainId,
386
- originIntentAddress,
387
- destinationIntentAddress,
388
- createdAt,
389
- } = transaction
390
-
391
- // Check if this is a same-chain transaction (same address and chain)
392
- const isSameChain =
393
- originIntentAddress?.toLowerCase() ===
394
- destinationIntentAddress?.toLowerCase() &&
395
- originChainId === destinationChainId
396
-
397
- // Check if there's no destination (same-chain or no destination intent)
398
- const hasNoDestination =
399
- !destinationIntentAddress || !destinationChainId || isSameChain
400
-
401
- // Check if it's been over 5 minutes since creation
402
- const isOver5Minutes = createdAt
403
- ? Date.now() - new Date(createdAt).getTime() > 5 * 60 * 1000
404
- : false
405
-
406
- // Rule 1: If there's a destination intent tx, it's completed
407
- if (destinationIntentTxHash) {
408
- return "completed"
409
- }
410
-
411
- // Rule 2: Only origin tx hash exists and no real destination (same-chain case)
412
- if (originIntentTxHash && originChainId && hasNoDestination) {
413
- return "completed"
414
- }
415
-
416
- // Rule 3: If there's a deposit but no action yet, status is pending
417
- if (originIntentDepositTxHash && !originIntentTxHash) {
418
- return "pending"
419
- }
420
-
421
- // Rule 4: Cross-chain with origin tx but no destination tx
422
- // If over 5 minutes, consider it failed; otherwise pending
423
- if (
424
- originIntentTxHash &&
425
- !destinationIntentTxHash &&
426
- originChainId &&
427
- destinationChainId &&
428
- !isSameChain &&
429
- destinationIntentAddress
430
- ) {
431
- return isOver5Minutes ? "failed" : "pending"
432
- }
433
-
434
- // Otherwise, use the original execution status
435
- return executionStatus || "unknown"
436
- }
826
+ // Get supported tokens once for all transactions
827
+ const supportedTokens = await getSupportedTokens()
437
828
 
438
- const effectiveExecutionStatus = getEffectiveStatus()
439
-
440
- // If same intent address, omit destination address/chain to avoid duplication
441
- return {
442
- ...transaction,
443
- ...(isSameIntentAddress
444
- ? {
445
- destinationIntentAddress: "",
446
- destinationChainId: undefined,
447
- }
448
- : {}),
449
- originToken,
450
- destinationToken,
451
- originIntentTxHash,
452
- destinationIntentTxHash,
453
- originIntentTxExplorerUrl,
454
- destinationIntentTxExplorerUrl,
455
- originIntentDepositTxHash,
456
- destinationIntentDepositTxHash,
457
- originIntentDepositTxExplorerUrl,
458
- destinationIntentDepositTxExplorerUrl,
459
- executionStatus: effectiveExecutionStatus,
460
- }
461
- }),
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
+ ),
462
834
  )
463
835
 
464
836
  if (append) {
@@ -483,7 +855,13 @@ export function useIntentTransactionHistory({
483
855
  setLoading(false)
484
856
  }
485
857
  },
486
- [accountAddress, enabled, pageSize],
858
+ [
859
+ accountAddress,
860
+ enabled,
861
+ pageSize,
862
+ trailsConfig,
863
+ fetchIntentTransactionHistoryFromApi,
864
+ ],
487
865
  )
488
866
 
489
867
  const refetch = () => {