0xtrails 0.4.2 → 0.5.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.
- package/dist/{ccip-Dl3umoGg.js → ccip-DhEkQ6QC.js} +27 -27
- package/dist/cctpqueue.d.ts.map +1 -1
- package/dist/chains.d.ts +3 -1
- package/dist/chains.d.ts.map +1 -1
- package/dist/config.d.ts +17 -52
- package/dist/config.d.ts.map +1 -1
- package/dist/constants.d.ts +0 -6
- package/dist/constants.d.ts.map +1 -1
- package/dist/{index-sMS_ge1R.js → index-MhD2DA7_.js} +23613 -23893
- package/dist/index.d.ts +7 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +319 -944
- package/dist/indexerClient.d.ts +9 -6
- package/dist/indexerClient.d.ts.map +1 -1
- package/dist/intents.d.ts +0 -1
- package/dist/intents.d.ts.map +1 -1
- package/dist/prepareSend.d.ts.map +1 -1
- package/dist/sequenceWallet.d.ts +1 -1
- package/dist/sequenceWallet.d.ts.map +1 -1
- package/dist/trailsClient.d.ts +3 -3
- package/dist/trailsClient.d.ts.map +1 -1
- package/dist/transactionIntent/deposits/gaslessDeposit.d.ts.map +1 -1
- package/dist/transactionIntent/deposits/standardDeposit.d.ts.map +1 -1
- package/dist/transactionIntent/handlers/crossChain.d.ts +3 -1
- package/dist/transactionIntent/handlers/crossChain.d.ts.map +1 -1
- package/dist/transactionIntent/handlers/sameChainSameToken.d.ts.map +1 -1
- package/dist/transactionIntent/types.d.ts +1 -0
- package/dist/transactionIntent/types.d.ts.map +1 -1
- package/dist/transactions.d.ts +8 -2
- package/dist/transactions.d.ts.map +1 -1
- package/dist/wallets.d.ts.map +1 -1
- package/dist/widget/components/AccountIntentTransactionHistory.d.ts.map +1 -1
- package/dist/widget/components/ConfigDisplay.d.ts.map +1 -1
- package/dist/widget/components/FeeOptions.d.ts.map +1 -1
- package/dist/widget/components/SlippageToleranceSettings.d.ts +1 -0
- package/dist/widget/components/SlippageToleranceSettings.d.ts.map +1 -1
- package/dist/widget/components/WalletConnect.d.ts.map +1 -1
- package/dist/widget/hooks/useIntentTransactionHistory.d.ts.map +1 -1
- package/dist/widget/hooks/useQuote.d.ts +2 -4
- package/dist/widget/hooks/useQuote.d.ts.map +1 -1
- package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
- package/dist/widget/index.d.ts +1 -0
- package/dist/widget/index.d.ts.map +1 -1
- package/dist/widget/index.js +4 -2
- package/dist/widget/providers/TrailsProvider.d.ts +18 -0
- package/dist/widget/providers/TrailsProvider.d.ts.map +1 -0
- package/dist/widget/widget.d.ts +3 -3
- package/dist/widget/widget.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/analytics.ts +2 -2
- package/src/cctpqueue.ts +6 -3
- package/src/chains.ts +62 -29
- package/src/config.ts +36 -210
- package/src/constants.ts +0 -9
- package/src/index.ts +12 -35
- package/src/indexerClient.ts +39 -48
- package/src/intents.ts +0 -21
- package/src/prepareSend.ts +16 -2
- package/src/sequenceWallet.ts +1 -2
- package/src/trailsClient.ts +17 -12
- package/src/transactionIntent/deposits/gaslessDeposit.ts +88 -43
- package/src/transactionIntent/deposits/standardDeposit.ts +91 -53
- package/src/transactionIntent/handlers/crossChain.ts +88 -0
- package/src/transactionIntent/handlers/sameChainSameToken.ts +22 -0
- package/src/transactionIntent/types.ts +1 -0
- package/src/transactions.ts +122 -24
- package/src/wallets.ts +5 -6
- package/src/widget/components/AccountIntentTransactionHistory.tsx +5 -0
- package/src/widget/components/ConfigDisplay.tsx +19 -59
- package/src/widget/components/FeeOptions.tsx +1 -1
- package/src/widget/components/SlippageToleranceSettings.tsx +63 -14
- package/src/widget/components/WalletConnect.tsx +37 -99
- package/src/widget/hooks/useIntentTransactionHistory.ts +9 -1
- package/src/widget/hooks/useQuote.ts +11 -17
- package/src/widget/hooks/useSendForm.ts +4 -0
- package/src/widget/index.tsx +8 -0
- package/src/widget/providers/TrailsProvider.tsx +95 -0
- package/src/widget/widget.tsx +49 -98
- package/dist/trails.d.ts +0 -110
- package/dist/trails.d.ts.map +0 -1
- package/src/trails.ts +0 -1303
package/src/trails.ts
DELETED
|
@@ -1,1303 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
TransactionPrecondition,
|
|
3
|
-
Intent,
|
|
4
|
-
QuoteIntentRequest,
|
|
5
|
-
MetaTxn,
|
|
6
|
-
IntentCalls,
|
|
7
|
-
} from "@0xsequence/trails-api"
|
|
8
|
-
import type { TrailsClient } from "./trailsClient.js"
|
|
9
|
-
import { useMutation, useQuery } from "@tanstack/react-query"
|
|
10
|
-
import { Address } from "ox"
|
|
11
|
-
import { useCallback, useEffect, useState } from "react"
|
|
12
|
-
import type { Hex } from "viem"
|
|
13
|
-
import {
|
|
14
|
-
createWalletClient,
|
|
15
|
-
custom,
|
|
16
|
-
ethAddress,
|
|
17
|
-
isAddress,
|
|
18
|
-
isAddressEqual,
|
|
19
|
-
zeroAddress,
|
|
20
|
-
} from "viem"
|
|
21
|
-
import type { Connector } from "wagmi"
|
|
22
|
-
import {
|
|
23
|
-
useEstimateGas,
|
|
24
|
-
useSendTransaction,
|
|
25
|
-
useSwitchChain,
|
|
26
|
-
useWaitForTransactionReceipt,
|
|
27
|
-
} from "wagmi"
|
|
28
|
-
import { useTrailsClient } from "./trailsClient.js"
|
|
29
|
-
import { attemptSwitchChain } from "./chainSwitch.js"
|
|
30
|
-
import { getChainInfo } from "./chains.js"
|
|
31
|
-
import { getERC20TransferData } from "./encoders.js"
|
|
32
|
-
import type {
|
|
33
|
-
OriginCallParams,
|
|
34
|
-
TrailsFee as LocalTrailsFee,
|
|
35
|
-
} from "./intents.js"
|
|
36
|
-
import {
|
|
37
|
-
calculateIntentAddress,
|
|
38
|
-
calculateOriginAndDestinationIntentAddresses,
|
|
39
|
-
quoteIntent as quoteIntentFromIntents,
|
|
40
|
-
} from "./intents.js"
|
|
41
|
-
import {
|
|
42
|
-
useIntentReceiptMonitor,
|
|
43
|
-
type IntentReceiptStatus,
|
|
44
|
-
} from "./intentReceiptMonitor.js"
|
|
45
|
-
import { findPreconditionAddresses } from "./preconditions.js"
|
|
46
|
-
import { queueCCTPTransfer } from "./cctpqueue.js"
|
|
47
|
-
import { logger } from "./logger.js"
|
|
48
|
-
import { useCommitIntent, useExecuteIntent } from "./mutations.js"
|
|
49
|
-
// Removed: import { SEQUENCE_V3_CONTRACT_ADDRESSES_OVERRIDES } from "./constants.js"
|
|
50
|
-
|
|
51
|
-
export type WagmiAccount = {
|
|
52
|
-
address: `0x${string}`
|
|
53
|
-
isConnected: boolean
|
|
54
|
-
chainId: number
|
|
55
|
-
connector?: Connector
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export type UseTrailsConfig = {
|
|
59
|
-
account: WagmiAccount
|
|
60
|
-
disableAutoExecute?: boolean
|
|
61
|
-
sequenceProjectAccessKey?: string
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export type UseTrailsReturn = {
|
|
65
|
-
trailsClient: TrailsClient
|
|
66
|
-
metaTxns: MetaTxn[] | null
|
|
67
|
-
intentCalls: IntentCalls[] | null
|
|
68
|
-
intentPreconditions: TransactionPrecondition[] | null
|
|
69
|
-
trailsFee: LocalTrailsFee | null
|
|
70
|
-
txnHash: Hex | undefined
|
|
71
|
-
committedOriginIntentAddress: string | null
|
|
72
|
-
committedDestinationIntentAddress: string | null
|
|
73
|
-
verificationStatus: {
|
|
74
|
-
success: boolean
|
|
75
|
-
calculatedOriginAddress?: string
|
|
76
|
-
calculatedDestinationAddress?: string
|
|
77
|
-
receivedOriginAddress?: string
|
|
78
|
-
receivedDestinationAddress?: string
|
|
79
|
-
} | null
|
|
80
|
-
estimatedGas: bigint | undefined
|
|
81
|
-
isEstimateError: boolean
|
|
82
|
-
estimateError: Error | null
|
|
83
|
-
calculateIntentAddress: typeof calculateIntentAddress
|
|
84
|
-
calculateOriginAndDestinationIntentAddresses: typeof calculateOriginAndDestinationIntentAddresses
|
|
85
|
-
committedIntent: Intent | undefined
|
|
86
|
-
isLoadingCommittedIntent: boolean
|
|
87
|
-
committedConfigError: Error | null
|
|
88
|
-
commitIntent: (intent: Intent) => void
|
|
89
|
-
commitIntentPending: boolean
|
|
90
|
-
commitIntentSuccess: boolean
|
|
91
|
-
commitIntentError: Error | null
|
|
92
|
-
commitIntentArgs: Intent | undefined
|
|
93
|
-
executeIntent: (args: {
|
|
94
|
-
intentId: string
|
|
95
|
-
depositTransactionHash: string
|
|
96
|
-
}) => void
|
|
97
|
-
executeIntentPending: boolean
|
|
98
|
-
executeIntentSuccess: boolean
|
|
99
|
-
executeIntentError: Error | null
|
|
100
|
-
getIntentFromQuoteIntent: (args: QuoteIntentRequest) => Promise<Intent>
|
|
101
|
-
operationHashes: { [key: string]: Hex }
|
|
102
|
-
sendOriginTransaction: () => Promise<void>
|
|
103
|
-
switchChain: any // TODO: Add proper type
|
|
104
|
-
isSwitchingChain: boolean
|
|
105
|
-
switchChainError: Error | null
|
|
106
|
-
isTransactionInProgress: boolean
|
|
107
|
-
isChainSwitchRequired: boolean
|
|
108
|
-
sendTransaction: any // TODO: Add proper type
|
|
109
|
-
isSendingTransaction: boolean
|
|
110
|
-
originCallStatus: {
|
|
111
|
-
txnHash?: string
|
|
112
|
-
status?: string
|
|
113
|
-
revertReason?: string | null
|
|
114
|
-
gasUsed?: number
|
|
115
|
-
effectiveGasPrice?: string
|
|
116
|
-
} | null
|
|
117
|
-
updateOriginCallStatus: (
|
|
118
|
-
hash: Hex | undefined,
|
|
119
|
-
status: "success" | "reverted" | "pending" | "sending",
|
|
120
|
-
gasUsed?: bigint,
|
|
121
|
-
effectiveGasPrice?: bigint,
|
|
122
|
-
revertReason?: string | null,
|
|
123
|
-
) => void
|
|
124
|
-
isEstimatingGas: boolean
|
|
125
|
-
isAutoExecute: boolean
|
|
126
|
-
updateAutoExecute: (enabled: boolean) => void
|
|
127
|
-
receipt: any // TODO: Add proper type
|
|
128
|
-
isWaitingForReceipt: boolean
|
|
129
|
-
receiptIsSuccess: boolean
|
|
130
|
-
receiptIsError: boolean
|
|
131
|
-
receiptError: Error | null
|
|
132
|
-
hasAutoExecuted: boolean
|
|
133
|
-
originCallSuccess: boolean
|
|
134
|
-
sentMetaTxns: { [key: string]: number }
|
|
135
|
-
clearIntent: () => void
|
|
136
|
-
intentReceiptStatus: IntentReceiptStatus | null
|
|
137
|
-
quoteIntent: (args: QuoteIntentRequest) => void
|
|
138
|
-
quoteIntentPending: boolean
|
|
139
|
-
quoteIntentSuccess: boolean
|
|
140
|
-
quoteIntentError: Error | null
|
|
141
|
-
quoteIntentArgs: QuoteIntentRequest | undefined
|
|
142
|
-
originCallParams: OriginCallParams | null
|
|
143
|
-
updateOriginCallParams: (
|
|
144
|
-
args: { originChainId: number; tokenAddress: string } | null,
|
|
145
|
-
) => void
|
|
146
|
-
originBlockTimestamp: number | null
|
|
147
|
-
metaTxnBlockTimestamps: {
|
|
148
|
-
[key: string]: { timestamp: number | null; error?: string }
|
|
149
|
-
}
|
|
150
|
-
originIntentAddress: string | null
|
|
151
|
-
destinationIntentAddress: string | null
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export function useTrails(config: UseTrailsConfig): UseTrailsReturn {
|
|
155
|
-
const {
|
|
156
|
-
account,
|
|
157
|
-
disableAutoExecute = false,
|
|
158
|
-
sequenceProjectAccessKey,
|
|
159
|
-
} = config
|
|
160
|
-
const trailsClient = useTrailsClient({
|
|
161
|
-
apiKey: sequenceProjectAccessKey,
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
const [isAutoExecute, setIsAutoExecute] = useState(!disableAutoExecute)
|
|
165
|
-
const [hasAutoExecuted, setHasAutoExecuted] = useState(false)
|
|
166
|
-
|
|
167
|
-
// Track timestamps of when each meta-transaction was last sent
|
|
168
|
-
const [sentMetaTxns, setSentMetaTxns] = useState<{ [key: string]: number }>(
|
|
169
|
-
{},
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
// State declarations
|
|
173
|
-
const [metaTxns, setMetaTxns] = useState<MetaTxn[] | null>(null)
|
|
174
|
-
const [intentCalls, setIntentCalls] = useState<IntentCalls[] | null>(null)
|
|
175
|
-
const [intentPreconditions, setIntentPreconditions] = useState<
|
|
176
|
-
TransactionPrecondition[] | null
|
|
177
|
-
>(null)
|
|
178
|
-
const [trailsFee, setTrailsFee] = useState<LocalTrailsFee | null>(null)
|
|
179
|
-
const [intent, setIntent] = useState<Intent | null>(null)
|
|
180
|
-
const [txnHash, setTxnHash] = useState<Hex | undefined>()
|
|
181
|
-
const [committedOriginIntentAddress, setCommittedOriginIntentAddress] =
|
|
182
|
-
useState<string | null>(null)
|
|
183
|
-
const [
|
|
184
|
-
committedDestinationIntentAddress,
|
|
185
|
-
setCommittedDestinationIntentAddress,
|
|
186
|
-
] = useState<string | null>(null)
|
|
187
|
-
const [originIntentAddress, setOriginIntentAddress] = useState<string | null>(
|
|
188
|
-
null,
|
|
189
|
-
)
|
|
190
|
-
const [destinationIntentAddress, setDestinationIntentAddress] = useState<
|
|
191
|
-
string | null
|
|
192
|
-
>(null)
|
|
193
|
-
// const [preconditionStatuses, setPreconditionStatuses] = useState<boolean[]>([])
|
|
194
|
-
|
|
195
|
-
const [originCallParams, setOriginCallParams] =
|
|
196
|
-
useState<OriginCallParams | null>(null)
|
|
197
|
-
|
|
198
|
-
const [operationHashes, setOperationHashes] = useState<{
|
|
199
|
-
[key: string]: Hex
|
|
200
|
-
}>({})
|
|
201
|
-
const [isTransactionInProgress, setIsTransactionInProgress] = useState(false)
|
|
202
|
-
const [isChainSwitchRequired, setIsChainSwitchRequired] = useState(false)
|
|
203
|
-
const {
|
|
204
|
-
switchChain,
|
|
205
|
-
isPending: isSwitchingChain,
|
|
206
|
-
error: switchChainError,
|
|
207
|
-
} = useSwitchChain()
|
|
208
|
-
|
|
209
|
-
const sendOriginTxn = useSendTransaction()
|
|
210
|
-
|
|
211
|
-
const [isEstimatingGas, setIsEstimatingGas] = useState(false)
|
|
212
|
-
const [originCallStatus, setOriginCallStatus] = useState<{
|
|
213
|
-
txnHash?: string
|
|
214
|
-
status?: string
|
|
215
|
-
revertReason?: string | null
|
|
216
|
-
gasUsed?: number
|
|
217
|
-
effectiveGasPrice?: string
|
|
218
|
-
} | null>(null)
|
|
219
|
-
|
|
220
|
-
const [originBlockTimestamp, setOriginBlockTimestamp] = useState<
|
|
221
|
-
number | null
|
|
222
|
-
>(null)
|
|
223
|
-
const [metaTxnBlockTimestamps, setMetaTxnBlockTimestamps] = useState<{
|
|
224
|
-
[key: string]: { timestamp: number | null; error?: string }
|
|
225
|
-
}>({})
|
|
226
|
-
|
|
227
|
-
const [verificationStatus, setVerificationStatus] = useState<{
|
|
228
|
-
success: boolean
|
|
229
|
-
calculatedOriginAddress?: string
|
|
230
|
-
calculatedDestinationAddress?: string
|
|
231
|
-
receivedOriginAddress?: string
|
|
232
|
-
receivedDestinationAddress?: string
|
|
233
|
-
} | null>(null)
|
|
234
|
-
|
|
235
|
-
// Add gas estimation hook with proper types
|
|
236
|
-
const {
|
|
237
|
-
data: estimatedGas,
|
|
238
|
-
isError: isEstimateError,
|
|
239
|
-
error: estimateError,
|
|
240
|
-
} = useEstimateGas(
|
|
241
|
-
originCallParams?.to && originCallParams?.chainId && !originCallParams.error
|
|
242
|
-
? {
|
|
243
|
-
to: originCallParams.to || undefined,
|
|
244
|
-
data: originCallParams.data || undefined,
|
|
245
|
-
value: originCallParams.value || undefined,
|
|
246
|
-
chainId: originCallParams.chainId || undefined,
|
|
247
|
-
}
|
|
248
|
-
: undefined,
|
|
249
|
-
)
|
|
250
|
-
|
|
251
|
-
// Commit intent mutation with verification logic wrapper
|
|
252
|
-
const baseCommitIntentMutation = useCommitIntent()
|
|
253
|
-
|
|
254
|
-
const commitIntentMutation = useMutation({
|
|
255
|
-
mutationFn: async (intent: Intent) => {
|
|
256
|
-
logger.console.log(
|
|
257
|
-
"[useTrails] commitIntentMutation started with intent:",
|
|
258
|
-
intent,
|
|
259
|
-
)
|
|
260
|
-
|
|
261
|
-
try {
|
|
262
|
-
const originChainId = quoteIntentMutation.variables?.originChainId
|
|
263
|
-
const destinationChainId =
|
|
264
|
-
quoteIntentMutation.variables?.destinationChainId
|
|
265
|
-
|
|
266
|
-
if (!originChainId || !destinationChainId) {
|
|
267
|
-
logger.console.error(
|
|
268
|
-
"[useTrails] Could not determine origin/destination chain IDs for verification.",
|
|
269
|
-
)
|
|
270
|
-
throw new Error(
|
|
271
|
-
"Could not determine origin/destination chain IDs for verification.",
|
|
272
|
-
)
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
logger.console.log("[useTrails] Calculating intent address...")
|
|
276
|
-
logger.console.log("[useTrails] Main signer:", intent.ownerAddress)
|
|
277
|
-
logger.console.log("[useTrails] Calls:", intent.calls)
|
|
278
|
-
|
|
279
|
-
const { originIntentAddress, destinationIntentAddress } =
|
|
280
|
-
calculateOriginAndDestinationIntentAddresses(
|
|
281
|
-
intent.ownerAddress,
|
|
282
|
-
intent.calls,
|
|
283
|
-
)
|
|
284
|
-
|
|
285
|
-
const {
|
|
286
|
-
originAddress: originPreconditionAddress,
|
|
287
|
-
destinationAddress: destinationPreconditionAddress,
|
|
288
|
-
} = findPreconditionAddresses(
|
|
289
|
-
intent.preconditions,
|
|
290
|
-
originChainId,
|
|
291
|
-
destinationChainId,
|
|
292
|
-
)
|
|
293
|
-
|
|
294
|
-
logger.console.log("[useTrails] Verification addresses:", {
|
|
295
|
-
calculatedOrigin: originIntentAddress.toString(),
|
|
296
|
-
calculatedDestination: destinationIntentAddress.toString(),
|
|
297
|
-
preconditionOrigin: originPreconditionAddress,
|
|
298
|
-
preconditionDestination: destinationPreconditionAddress,
|
|
299
|
-
})
|
|
300
|
-
|
|
301
|
-
const isOriginVerified =
|
|
302
|
-
!!originPreconditionAddress &&
|
|
303
|
-
isAddressEqual(
|
|
304
|
-
Address.from(originPreconditionAddress),
|
|
305
|
-
originIntentAddress,
|
|
306
|
-
)
|
|
307
|
-
|
|
308
|
-
logger.console.log("[useTrails] Origin verified:", isOriginVerified)
|
|
309
|
-
|
|
310
|
-
// For single chain, destination address may not be in preconditions,
|
|
311
|
-
// but the destination intent address should be the zero address.
|
|
312
|
-
const isDestinationVerified =
|
|
313
|
-
(destinationPreconditionAddress &&
|
|
314
|
-
isAddressEqual(
|
|
315
|
-
Address.from(destinationPreconditionAddress),
|
|
316
|
-
destinationIntentAddress,
|
|
317
|
-
)) ||
|
|
318
|
-
(!destinationPreconditionAddress &&
|
|
319
|
-
originChainId === destinationChainId &&
|
|
320
|
-
isAddressEqual(destinationIntentAddress, zeroAddress))
|
|
321
|
-
|
|
322
|
-
logger.console.log(
|
|
323
|
-
"[useTrails] Destination verified:",
|
|
324
|
-
isDestinationVerified,
|
|
325
|
-
)
|
|
326
|
-
|
|
327
|
-
const isVerified = isOriginVerified && isDestinationVerified
|
|
328
|
-
|
|
329
|
-
setVerificationStatus({
|
|
330
|
-
success: isVerified,
|
|
331
|
-
receivedOriginAddress: originPreconditionAddress,
|
|
332
|
-
receivedDestinationAddress: destinationPreconditionAddress,
|
|
333
|
-
calculatedOriginAddress: originIntentAddress.toString(),
|
|
334
|
-
calculatedDestinationAddress: destinationIntentAddress.toString(),
|
|
335
|
-
})
|
|
336
|
-
|
|
337
|
-
if (!isVerified) {
|
|
338
|
-
logger.console.error("[useTrails] Address verification failed.", {
|
|
339
|
-
isOriginVerified,
|
|
340
|
-
isDestinationVerified,
|
|
341
|
-
})
|
|
342
|
-
throw new Error(
|
|
343
|
-
`Address verification failed. Origin verified: ${isOriginVerified}, Destination verified: ${isDestinationVerified}`,
|
|
344
|
-
)
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// Commit the intent that was returned from intent
|
|
348
|
-
logger.console.log("[useTrails] Committing intent to API...")
|
|
349
|
-
const response = await baseCommitIntentMutation.mutateAsync(intent)
|
|
350
|
-
logger.console.log("[useTrails] API Commit Response:", response)
|
|
351
|
-
return {
|
|
352
|
-
originIntentAddress: originIntentAddress.toString(),
|
|
353
|
-
destinationIntentAddress: destinationIntentAddress.toString(),
|
|
354
|
-
response,
|
|
355
|
-
}
|
|
356
|
-
} catch (error) {
|
|
357
|
-
console.error("[useTrails] Error during commit intent mutation:", error)
|
|
358
|
-
throw error
|
|
359
|
-
}
|
|
360
|
-
},
|
|
361
|
-
onSuccess: (data) => {
|
|
362
|
-
logger.console.log(
|
|
363
|
-
"[useTrails] Intent config committed successfully. Data:",
|
|
364
|
-
data,
|
|
365
|
-
)
|
|
366
|
-
logger.console.log(
|
|
367
|
-
"[useTrails] Setting committedOriginIntentAddress:",
|
|
368
|
-
data.originIntentAddress,
|
|
369
|
-
)
|
|
370
|
-
setCommittedOriginIntentAddress(data.originIntentAddress)
|
|
371
|
-
setCommittedDestinationIntentAddress(data.destinationIntentAddress)
|
|
372
|
-
},
|
|
373
|
-
onError: (error) => {
|
|
374
|
-
logger.console.error("[useTrails] Failed to commit intent config:", error)
|
|
375
|
-
setCommittedOriginIntentAddress(null)
|
|
376
|
-
setCommittedDestinationIntentAddress(null)
|
|
377
|
-
},
|
|
378
|
-
})
|
|
379
|
-
|
|
380
|
-
// Execute intent mutation
|
|
381
|
-
const executeIntentMutation = useExecuteIntent()
|
|
382
|
-
|
|
383
|
-
// New Query to fetch committed intent config
|
|
384
|
-
const {
|
|
385
|
-
data: committedIntent,
|
|
386
|
-
isLoading: isLoadingCommittedIntent,
|
|
387
|
-
error: committedConfigError,
|
|
388
|
-
} = useQuery<Intent, Error>({
|
|
389
|
-
queryKey: ["getIntent", committedOriginIntentAddress],
|
|
390
|
-
queryFn: async () => {
|
|
391
|
-
if (!trailsClient || !committedOriginIntentAddress) {
|
|
392
|
-
throw new Error("API client or committed intent address not available")
|
|
393
|
-
}
|
|
394
|
-
logger.console.log(
|
|
395
|
-
"Fetching intent config for address:",
|
|
396
|
-
committedOriginIntentAddress,
|
|
397
|
-
)
|
|
398
|
-
const response = await trailsClient.getIntent({
|
|
399
|
-
intentId: committedOriginIntentAddress,
|
|
400
|
-
})
|
|
401
|
-
return response.intent
|
|
402
|
-
},
|
|
403
|
-
enabled:
|
|
404
|
-
!!committedOriginIntentAddress &&
|
|
405
|
-
!!trailsClient &&
|
|
406
|
-
commitIntentMutation.isSuccess,
|
|
407
|
-
staleTime: 1000 * 60 * 5, // 5 minutes
|
|
408
|
-
retry: 1,
|
|
409
|
-
})
|
|
410
|
-
|
|
411
|
-
async function getIntentFromQuoteIntent(args: QuoteIntentRequest) {
|
|
412
|
-
const { intent } = await quoteIntentFromIntents(trailsClient, args)
|
|
413
|
-
return intent
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// TODO: Add type for args
|
|
417
|
-
const quoteIntentMutation = useMutation<Intent, Error, QuoteIntentRequest>({
|
|
418
|
-
mutationFn: async (args: QuoteIntentRequest) => {
|
|
419
|
-
if (
|
|
420
|
-
args.originChainId === args.destinationChainId &&
|
|
421
|
-
isAddressEqual(
|
|
422
|
-
Address.from(args.originTokenAddress),
|
|
423
|
-
Address.from(args.destinationTokenAddress),
|
|
424
|
-
)
|
|
425
|
-
) {
|
|
426
|
-
throw new Error(
|
|
427
|
-
"The same token cannot be used as both the source and destination token.",
|
|
428
|
-
)
|
|
429
|
-
}
|
|
430
|
-
if (!account.address) {
|
|
431
|
-
throw new Error("Missing selected token or account address")
|
|
432
|
-
}
|
|
433
|
-
// Reset commit state when generating a new intent
|
|
434
|
-
setCommittedOriginIntentAddress(null)
|
|
435
|
-
setCommittedDestinationIntentAddress(null)
|
|
436
|
-
setVerificationStatus(null)
|
|
437
|
-
setTrailsFee(null)
|
|
438
|
-
setMetaTxns(null)
|
|
439
|
-
setIntentCalls(null)
|
|
440
|
-
setIntentPreconditions(null)
|
|
441
|
-
setOriginIntentAddress(null)
|
|
442
|
-
setDestinationIntentAddress(null)
|
|
443
|
-
|
|
444
|
-
const { intent: data } = await quoteIntentFromIntents(trailsClient, args)
|
|
445
|
-
|
|
446
|
-
setMetaTxns(data.metaTxns)
|
|
447
|
-
setIntentCalls(data.calls)
|
|
448
|
-
setIntentPreconditions(data.preconditions)
|
|
449
|
-
setTrailsFee(data.fees as any)
|
|
450
|
-
setOriginIntentAddress(data.originIntentAddress)
|
|
451
|
-
setDestinationIntentAddress(data.destinationIntentAddress)
|
|
452
|
-
setIntent(data)
|
|
453
|
-
setCommittedOriginIntentAddress(null)
|
|
454
|
-
setCommittedDestinationIntentAddress(null)
|
|
455
|
-
|
|
456
|
-
setVerificationStatus(null)
|
|
457
|
-
return data
|
|
458
|
-
},
|
|
459
|
-
onSuccess: (data) => {
|
|
460
|
-
logger.console.log("Intent Config Success:", data)
|
|
461
|
-
|
|
462
|
-
setTrailsFee((data.fees || null) as any)
|
|
463
|
-
setOriginIntentAddress(data.originIntentAddress)
|
|
464
|
-
setDestinationIntentAddress(data.destinationIntentAddress)
|
|
465
|
-
|
|
466
|
-
if (
|
|
467
|
-
data?.calls &&
|
|
468
|
-
data.calls.length > 0 &&
|
|
469
|
-
data.preconditions &&
|
|
470
|
-
data.preconditions.length > 0 &&
|
|
471
|
-
data.metaTxns &&
|
|
472
|
-
data.metaTxns.length > 0
|
|
473
|
-
) {
|
|
474
|
-
setIntentCalls(data.calls)
|
|
475
|
-
setIntentPreconditions(data.preconditions)
|
|
476
|
-
setMetaTxns(data.metaTxns)
|
|
477
|
-
} else {
|
|
478
|
-
logger.console.warn("API returned success but no operations found.")
|
|
479
|
-
setIntentCalls(null)
|
|
480
|
-
setIntentPreconditions(null)
|
|
481
|
-
setMetaTxns(null)
|
|
482
|
-
}
|
|
483
|
-
},
|
|
484
|
-
onError: (error) => {
|
|
485
|
-
console.error("Intent Config Error:", error)
|
|
486
|
-
setIntentCalls(null)
|
|
487
|
-
setIntentPreconditions(null)
|
|
488
|
-
setMetaTxns(null)
|
|
489
|
-
setTrailsFee(null)
|
|
490
|
-
setOriginIntentAddress(null)
|
|
491
|
-
setDestinationIntentAddress(null)
|
|
492
|
-
},
|
|
493
|
-
})
|
|
494
|
-
|
|
495
|
-
const clearIntent = useCallback(() => {
|
|
496
|
-
logger.console.log("[Trails] Clearing intent state")
|
|
497
|
-
setIntentCalls(null)
|
|
498
|
-
setIntentPreconditions(null)
|
|
499
|
-
setMetaTxns(null)
|
|
500
|
-
setTrailsFee(null)
|
|
501
|
-
setCommittedOriginIntentAddress(null)
|
|
502
|
-
setCommittedDestinationIntentAddress(null)
|
|
503
|
-
setVerificationStatus(null)
|
|
504
|
-
setOriginIntentAddress(null)
|
|
505
|
-
setDestinationIntentAddress(null)
|
|
506
|
-
setOperationHashes({})
|
|
507
|
-
setHasAutoExecuted(false)
|
|
508
|
-
setMetaTxnBlockTimestamps({})
|
|
509
|
-
}, []) // Empty deps array since these setters are stable
|
|
510
|
-
|
|
511
|
-
const updateOriginCallStatus = useCallback(
|
|
512
|
-
(
|
|
513
|
-
hash: Hex | undefined,
|
|
514
|
-
status: "success" | "reverted" | "pending" | "sending",
|
|
515
|
-
gasUsed?: bigint,
|
|
516
|
-
effectiveGasPrice?: bigint,
|
|
517
|
-
revertReason?: string | null,
|
|
518
|
-
) => {
|
|
519
|
-
setOriginCallStatus({
|
|
520
|
-
txnHash: hash,
|
|
521
|
-
status:
|
|
522
|
-
status === "success"
|
|
523
|
-
? "Success"
|
|
524
|
-
: status === "reverted"
|
|
525
|
-
? "Failed"
|
|
526
|
-
: status === "sending"
|
|
527
|
-
? "Sending..."
|
|
528
|
-
: "Pending",
|
|
529
|
-
revertReason:
|
|
530
|
-
status === "reverted"
|
|
531
|
-
? revertReason || "Transaction reverted"
|
|
532
|
-
: undefined,
|
|
533
|
-
gasUsed: gasUsed ? Number(gasUsed) : undefined,
|
|
534
|
-
effectiveGasPrice: effectiveGasPrice?.toString(),
|
|
535
|
-
})
|
|
536
|
-
},
|
|
537
|
-
[],
|
|
538
|
-
)
|
|
539
|
-
|
|
540
|
-
const sendOriginTransaction = async () => {
|
|
541
|
-
logger.console.log("Sending origin transaction...")
|
|
542
|
-
logger.console.log(
|
|
543
|
-
isTransactionInProgress,
|
|
544
|
-
originCallParams,
|
|
545
|
-
originCallParams?.error,
|
|
546
|
-
originCallParams?.to,
|
|
547
|
-
originCallParams?.data,
|
|
548
|
-
originCallParams?.value,
|
|
549
|
-
originCallParams?.chainId,
|
|
550
|
-
)
|
|
551
|
-
if (
|
|
552
|
-
isTransactionInProgress || // Prevent duplicate transactions
|
|
553
|
-
!originCallParams ||
|
|
554
|
-
originCallParams.error ||
|
|
555
|
-
!originCallParams.to ||
|
|
556
|
-
originCallParams.data === null ||
|
|
557
|
-
originCallParams.value === null ||
|
|
558
|
-
originCallParams.chainId === null
|
|
559
|
-
) {
|
|
560
|
-
logger.console.error(
|
|
561
|
-
"Origin call parameters not available or invalid:",
|
|
562
|
-
originCallParams,
|
|
563
|
-
)
|
|
564
|
-
updateOriginCallStatus(
|
|
565
|
-
undefined,
|
|
566
|
-
"reverted",
|
|
567
|
-
undefined,
|
|
568
|
-
undefined,
|
|
569
|
-
"Origin call parameters not ready",
|
|
570
|
-
)
|
|
571
|
-
return
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
// Check if we need to switch chains
|
|
575
|
-
if (account.chainId !== originCallParams.chainId) {
|
|
576
|
-
setIsChainSwitchRequired(true)
|
|
577
|
-
updateOriginCallStatus(
|
|
578
|
-
undefined,
|
|
579
|
-
"pending",
|
|
580
|
-
undefined,
|
|
581
|
-
undefined,
|
|
582
|
-
`Switching to chain ${originCallParams.chainId}...`,
|
|
583
|
-
)
|
|
584
|
-
|
|
585
|
-
const walletClient = createWalletClient({
|
|
586
|
-
chain: getChainInfo(originCallParams.chainId)!,
|
|
587
|
-
transport: custom((await account.connector!.getProvider()) as any), // TODO: Add proper type
|
|
588
|
-
})
|
|
589
|
-
|
|
590
|
-
try {
|
|
591
|
-
await attemptSwitchChain({
|
|
592
|
-
walletClient,
|
|
593
|
-
desiredChainId: originCallParams.chainId,
|
|
594
|
-
})
|
|
595
|
-
setIsChainSwitchRequired(false)
|
|
596
|
-
} catch (error: unknown) {
|
|
597
|
-
logger.console.error("Chain switch failed:", error)
|
|
598
|
-
if (error instanceof Error && error.message.includes("User rejected")) {
|
|
599
|
-
setIsAutoExecute(false)
|
|
600
|
-
}
|
|
601
|
-
updateOriginCallStatus(
|
|
602
|
-
undefined,
|
|
603
|
-
"reverted",
|
|
604
|
-
undefined,
|
|
605
|
-
undefined,
|
|
606
|
-
error instanceof Error
|
|
607
|
-
? error.message
|
|
608
|
-
: "Unknown error switching chain",
|
|
609
|
-
)
|
|
610
|
-
setIsChainSwitchRequired(false)
|
|
611
|
-
return // Stop execution on switch failure
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
// Ensure only one transaction is sent at a time
|
|
616
|
-
if (!isTransactionInProgress) {
|
|
617
|
-
setIsTransactionInProgress(true) // Mark transaction as in progress
|
|
618
|
-
setTxnHash(undefined)
|
|
619
|
-
updateOriginCallStatus(undefined, "sending")
|
|
620
|
-
|
|
621
|
-
if (!estimatedGas && !isEstimateError) {
|
|
622
|
-
setIsEstimatingGas(true)
|
|
623
|
-
return // Wait for gas estimation
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
if (isEstimateError) {
|
|
627
|
-
logger.console.error("Gas estimation failed:", estimateError)
|
|
628
|
-
updateOriginCallStatus(
|
|
629
|
-
undefined,
|
|
630
|
-
"reverted",
|
|
631
|
-
undefined,
|
|
632
|
-
undefined,
|
|
633
|
-
`Gas estimation failed: ${estimateError?.message}`,
|
|
634
|
-
)
|
|
635
|
-
setIsTransactionInProgress(false)
|
|
636
|
-
return
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
// Add 20% buffer to estimated gas
|
|
640
|
-
const gasLimit = estimatedGas
|
|
641
|
-
? BigInt(Math.floor(Number(estimatedGas) * 1.2))
|
|
642
|
-
: undefined
|
|
643
|
-
|
|
644
|
-
sendOriginTxn.sendTransaction(
|
|
645
|
-
{
|
|
646
|
-
to: originCallParams.to,
|
|
647
|
-
data: originCallParams.data,
|
|
648
|
-
value: originCallParams.value,
|
|
649
|
-
chainId: originCallParams.chainId,
|
|
650
|
-
gas: gasLimit,
|
|
651
|
-
},
|
|
652
|
-
{
|
|
653
|
-
onSuccess: (hash: Hex) => {
|
|
654
|
-
logger.console.log("Transaction sent, hash:", hash)
|
|
655
|
-
setTxnHash(hash)
|
|
656
|
-
setIsTransactionInProgress(false) // Reset transaction state
|
|
657
|
-
},
|
|
658
|
-
onError: (error: unknown) => {
|
|
659
|
-
logger.console.error("Transaction failed:", error)
|
|
660
|
-
if (
|
|
661
|
-
error instanceof Error &&
|
|
662
|
-
(error.message.includes("User rejected") ||
|
|
663
|
-
error.message.includes("user rejected"))
|
|
664
|
-
) {
|
|
665
|
-
setIsAutoExecute(false)
|
|
666
|
-
}
|
|
667
|
-
updateOriginCallStatus(
|
|
668
|
-
undefined,
|
|
669
|
-
"reverted",
|
|
670
|
-
undefined,
|
|
671
|
-
undefined,
|
|
672
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
673
|
-
)
|
|
674
|
-
setIsTransactionInProgress(false)
|
|
675
|
-
},
|
|
676
|
-
},
|
|
677
|
-
)
|
|
678
|
-
} else {
|
|
679
|
-
logger.console.warn(
|
|
680
|
-
"Transaction already in progress. Skipping duplicate request.",
|
|
681
|
-
)
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
// Remove the chain change effect that might be resetting state
|
|
686
|
-
useEffect(() => {
|
|
687
|
-
if (switchChainError) {
|
|
688
|
-
logger.console.error("Chain switch error:", switchChainError)
|
|
689
|
-
updateOriginCallStatus(
|
|
690
|
-
undefined,
|
|
691
|
-
"reverted",
|
|
692
|
-
undefined,
|
|
693
|
-
undefined,
|
|
694
|
-
`Chain switch failed: ${switchChainError.message || "Unknown error"}`,
|
|
695
|
-
)
|
|
696
|
-
setIsChainSwitchRequired(false)
|
|
697
|
-
}
|
|
698
|
-
}, [switchChainError, updateOriginCallStatus])
|
|
699
|
-
|
|
700
|
-
// Reset gas estimation state when parameters change
|
|
701
|
-
useEffect(() => {
|
|
702
|
-
setIsEstimatingGas(false)
|
|
703
|
-
}, [])
|
|
704
|
-
|
|
705
|
-
// Only update chain switch required state when needed
|
|
706
|
-
useEffect(() => {
|
|
707
|
-
if (
|
|
708
|
-
originCallParams?.chainId &&
|
|
709
|
-
account.chainId === originCallParams.chainId
|
|
710
|
-
) {
|
|
711
|
-
logger.console.log("No chain switch required")
|
|
712
|
-
setIsChainSwitchRequired(false)
|
|
713
|
-
}
|
|
714
|
-
}, [account.chainId, originCallParams?.chainId])
|
|
715
|
-
|
|
716
|
-
// Effect to handle chain switching
|
|
717
|
-
useEffect(() => {
|
|
718
|
-
if (
|
|
719
|
-
originCallParams?.chainId &&
|
|
720
|
-
account.chainId !== originCallParams.chainId
|
|
721
|
-
) {
|
|
722
|
-
async function check() {
|
|
723
|
-
try {
|
|
724
|
-
const chainId = originCallParams!.chainId!
|
|
725
|
-
const walletClient = createWalletClient({
|
|
726
|
-
chain: getChainInfo(chainId)!,
|
|
727
|
-
transport: custom((await account.connector!.getProvider()) as any), // TODO: Add proper type
|
|
728
|
-
})
|
|
729
|
-
await attemptSwitchChain({
|
|
730
|
-
walletClient,
|
|
731
|
-
desiredChainId: chainId,
|
|
732
|
-
})
|
|
733
|
-
} catch (error) {
|
|
734
|
-
logger.console.error("Chain switch failed:", error)
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
check().catch(logger.console.error)
|
|
738
|
-
}
|
|
739
|
-
}, [account, originCallParams])
|
|
740
|
-
|
|
741
|
-
// Hook to wait for transaction receipt
|
|
742
|
-
const {
|
|
743
|
-
data: receipt,
|
|
744
|
-
isLoading: isWaitingForReceipt,
|
|
745
|
-
isSuccess: receiptIsSuccess,
|
|
746
|
-
isError: receiptIsError,
|
|
747
|
-
error: receiptError,
|
|
748
|
-
} = useWaitForTransactionReceipt({
|
|
749
|
-
hash: txnHash,
|
|
750
|
-
confirmations: 1,
|
|
751
|
-
query: {
|
|
752
|
-
enabled: !!txnHash,
|
|
753
|
-
},
|
|
754
|
-
})
|
|
755
|
-
|
|
756
|
-
// Modify the effect that watches for transaction status
|
|
757
|
-
useEffect(() => {
|
|
758
|
-
if (!txnHash) {
|
|
759
|
-
// Only reset these when txnHash is cleared
|
|
760
|
-
if (originCallStatus?.txnHash) {
|
|
761
|
-
setOriginCallStatus(null)
|
|
762
|
-
}
|
|
763
|
-
setOriginBlockTimestamp(null)
|
|
764
|
-
if (Object.keys(sentMetaTxns).length > 0) {
|
|
765
|
-
setSentMetaTxns({})
|
|
766
|
-
}
|
|
767
|
-
return
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
if (
|
|
771
|
-
originCallStatus?.txnHash === txnHash &&
|
|
772
|
-
(originCallStatus?.status === "Success" ||
|
|
773
|
-
originCallStatus?.status === "Failed") &&
|
|
774
|
-
!isWaitingForReceipt
|
|
775
|
-
) {
|
|
776
|
-
return
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
if (isWaitingForReceipt) {
|
|
780
|
-
setOriginCallStatus((prevStatus) => ({
|
|
781
|
-
...(prevStatus?.txnHash === txnHash
|
|
782
|
-
? prevStatus
|
|
783
|
-
: {
|
|
784
|
-
gasUsed: undefined,
|
|
785
|
-
effectiveGasPrice: undefined,
|
|
786
|
-
revertReason: undefined,
|
|
787
|
-
}),
|
|
788
|
-
txnHash,
|
|
789
|
-
status: "Pending",
|
|
790
|
-
}))
|
|
791
|
-
return
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
if (receiptIsSuccess && receipt) {
|
|
795
|
-
const newStatus = receipt.status === "success" ? "Success" : "Failed"
|
|
796
|
-
setOriginCallStatus({
|
|
797
|
-
txnHash: receipt.transactionHash,
|
|
798
|
-
status: newStatus,
|
|
799
|
-
gasUsed: receipt.gasUsed ? Number(receipt.gasUsed) : undefined,
|
|
800
|
-
effectiveGasPrice: receipt.effectiveGasPrice?.toString(),
|
|
801
|
-
revertReason:
|
|
802
|
-
receipt.status === "reverted"
|
|
803
|
-
? ((receiptError as any)?.message as string | undefined) ||
|
|
804
|
-
"Transaction reverted by receipt"
|
|
805
|
-
: undefined,
|
|
806
|
-
})
|
|
807
|
-
|
|
808
|
-
// Timestamp is now available from transaction receipts via the API (txnMinedAt)
|
|
809
|
-
if (newStatus !== "Success") {
|
|
810
|
-
setOriginBlockTimestamp(null)
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
if (
|
|
814
|
-
newStatus === "Success" &&
|
|
815
|
-
metaTxns &&
|
|
816
|
-
metaTxns.length > 0 &&
|
|
817
|
-
isAutoExecute &&
|
|
818
|
-
!metaTxns.some((tx: MetaTxn) => sentMetaTxns[`${tx.chainId}-${tx.id}`])
|
|
819
|
-
) {
|
|
820
|
-
logger.console.log(
|
|
821
|
-
"Origin transaction successful, calling executeIntent and auto-sending all meta transactions...",
|
|
822
|
-
)
|
|
823
|
-
|
|
824
|
-
// Call executeIntent with intentId and depositTransactionHash
|
|
825
|
-
if (committedOriginIntentAddress && txnHash) {
|
|
826
|
-
executeIntentMutation.mutate({
|
|
827
|
-
intentId: committedOriginIntentAddress,
|
|
828
|
-
depositTransactionHash: txnHash,
|
|
829
|
-
})
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
} else if (receiptIsError) {
|
|
833
|
-
setOriginCallStatus({
|
|
834
|
-
txnHash,
|
|
835
|
-
status: "Failed",
|
|
836
|
-
revertReason:
|
|
837
|
-
((receiptError as any)?.message as string | undefined) ||
|
|
838
|
-
"Failed to get receipt",
|
|
839
|
-
gasUsed: undefined,
|
|
840
|
-
effectiveGasPrice: undefined,
|
|
841
|
-
})
|
|
842
|
-
setOriginBlockTimestamp(null)
|
|
843
|
-
}
|
|
844
|
-
}, [
|
|
845
|
-
txnHash,
|
|
846
|
-
isWaitingForReceipt,
|
|
847
|
-
receiptIsSuccess,
|
|
848
|
-
receiptIsError,
|
|
849
|
-
receipt,
|
|
850
|
-
receiptError,
|
|
851
|
-
metaTxns,
|
|
852
|
-
sentMetaTxns,
|
|
853
|
-
isAutoExecute,
|
|
854
|
-
originCallStatus?.status,
|
|
855
|
-
originCallStatus?.txnHash,
|
|
856
|
-
committedOriginIntentAddress,
|
|
857
|
-
executeIntentMutation,
|
|
858
|
-
])
|
|
859
|
-
|
|
860
|
-
// Modify the auto-execute effect
|
|
861
|
-
useEffect(() => {
|
|
862
|
-
const shouldAutoSend =
|
|
863
|
-
isAutoExecute &&
|
|
864
|
-
commitIntentMutation.isSuccess &&
|
|
865
|
-
originCallParams?.chainId &&
|
|
866
|
-
account.chainId === originCallParams.chainId &&
|
|
867
|
-
!originCallParams.error &&
|
|
868
|
-
originCallParams.to &&
|
|
869
|
-
originCallParams.data !== null &&
|
|
870
|
-
originCallParams.value !== null &&
|
|
871
|
-
!sendOriginTxn.isPending &&
|
|
872
|
-
!isWaitingForReceipt &&
|
|
873
|
-
!txnHash &&
|
|
874
|
-
!isChainSwitchRequired &&
|
|
875
|
-
!originCallStatus &&
|
|
876
|
-
!hasAutoExecuted
|
|
877
|
-
|
|
878
|
-
if (shouldAutoSend) {
|
|
879
|
-
logger.console.log("Auto-executing transaction: All conditions met.")
|
|
880
|
-
setHasAutoExecuted(true)
|
|
881
|
-
|
|
882
|
-
// Set initial status
|
|
883
|
-
setOriginCallStatus({
|
|
884
|
-
status: "Sending...",
|
|
885
|
-
})
|
|
886
|
-
|
|
887
|
-
sendOriginTxn.sendTransaction(
|
|
888
|
-
{
|
|
889
|
-
to: originCallParams.to!,
|
|
890
|
-
data: originCallParams.data!,
|
|
891
|
-
value: originCallParams.value!,
|
|
892
|
-
chainId: originCallParams.chainId!,
|
|
893
|
-
},
|
|
894
|
-
{
|
|
895
|
-
onSuccess: (hash: Hex) => {
|
|
896
|
-
logger.console.log("Auto-executed transaction sent, hash:", hash)
|
|
897
|
-
setTxnHash(hash)
|
|
898
|
-
},
|
|
899
|
-
onError: (error: unknown) => {
|
|
900
|
-
logger.console.error("Auto-executed transaction failed:", error)
|
|
901
|
-
if (
|
|
902
|
-
error instanceof Error &&
|
|
903
|
-
(error.message.includes("User rejected") ||
|
|
904
|
-
error.message.includes("user rejected"))
|
|
905
|
-
) {
|
|
906
|
-
setIsAutoExecute(false)
|
|
907
|
-
}
|
|
908
|
-
setOriginCallStatus({
|
|
909
|
-
status: "Failed",
|
|
910
|
-
revertReason:
|
|
911
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
912
|
-
})
|
|
913
|
-
setHasAutoExecuted(false)
|
|
914
|
-
},
|
|
915
|
-
},
|
|
916
|
-
)
|
|
917
|
-
}
|
|
918
|
-
}, [
|
|
919
|
-
isAutoExecute,
|
|
920
|
-
commitIntentMutation.isSuccess,
|
|
921
|
-
originCallParams,
|
|
922
|
-
account.chainId,
|
|
923
|
-
sendOriginTxn.isPending,
|
|
924
|
-
isWaitingForReceipt,
|
|
925
|
-
txnHash,
|
|
926
|
-
isChainSwitchRequired,
|
|
927
|
-
originCallStatus,
|
|
928
|
-
hasAutoExecuted,
|
|
929
|
-
sendOriginTxn,
|
|
930
|
-
])
|
|
931
|
-
|
|
932
|
-
// Effect to auto-commit when intent calls payloads are ready
|
|
933
|
-
useEffect(() => {
|
|
934
|
-
if (
|
|
935
|
-
isAutoExecute &&
|
|
936
|
-
intentCalls &&
|
|
937
|
-
intentPreconditions &&
|
|
938
|
-
trailsFee &&
|
|
939
|
-
account.address &&
|
|
940
|
-
originIntentAddress &&
|
|
941
|
-
!commitIntentMutation.isPending &&
|
|
942
|
-
!commitIntentMutation.isSuccess
|
|
943
|
-
) {
|
|
944
|
-
logger.console.log("Auto-committing intent configuration...")
|
|
945
|
-
if (intent) {
|
|
946
|
-
commitIntentMutation.mutate(intent)
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
}, [
|
|
950
|
-
isAutoExecute,
|
|
951
|
-
intent,
|
|
952
|
-
intentCalls,
|
|
953
|
-
intentPreconditions,
|
|
954
|
-
trailsFee,
|
|
955
|
-
account.address,
|
|
956
|
-
originIntentAddress,
|
|
957
|
-
commitIntentMutation,
|
|
958
|
-
])
|
|
959
|
-
|
|
960
|
-
const [tokenAddress, setTokenAddress] = useState<string | null>(null)
|
|
961
|
-
const [originChainId, setOriginChainId] = useState<number | null>(null)
|
|
962
|
-
|
|
963
|
-
useEffect(() => {
|
|
964
|
-
if (
|
|
965
|
-
!originIntentAddress ||
|
|
966
|
-
!intentCalls?.[0]?.chainId ||
|
|
967
|
-
!tokenAddress ||
|
|
968
|
-
!originChainId ||
|
|
969
|
-
!intentPreconditions ||
|
|
970
|
-
!account.address
|
|
971
|
-
) {
|
|
972
|
-
setOriginCallParams(null)
|
|
973
|
-
return
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
try {
|
|
977
|
-
const intentAddressString = originIntentAddress as Address.Address
|
|
978
|
-
|
|
979
|
-
if (!intentAddressString || !isAddress(intentAddressString)) {
|
|
980
|
-
setOriginCallParams(null)
|
|
981
|
-
return
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
let calcTo: Address.Address
|
|
985
|
-
let calcData: Hex = "0x"
|
|
986
|
-
let calcValue: bigint = 0n
|
|
987
|
-
|
|
988
|
-
const recipientAddress = intentAddressString
|
|
989
|
-
|
|
990
|
-
const isNative =
|
|
991
|
-
tokenAddress === zeroAddress || tokenAddress === ethAddress
|
|
992
|
-
|
|
993
|
-
if (isNative) {
|
|
994
|
-
const nativePrecondition = intentPreconditions.find(
|
|
995
|
-
(p: TransactionPrecondition) =>
|
|
996
|
-
(p.type === "transfer-native" || p.type === "native-balance") &&
|
|
997
|
-
p.chainId === originChainId,
|
|
998
|
-
)
|
|
999
|
-
const nativeMinAmount = nativePrecondition?.minAmount?.toString()
|
|
1000
|
-
if (nativeMinAmount === undefined) {
|
|
1001
|
-
throw new Error("Could not find precondition or min amount")
|
|
1002
|
-
}
|
|
1003
|
-
calcValue = BigInt(nativeMinAmount)
|
|
1004
|
-
calcTo = recipientAddress
|
|
1005
|
-
} else {
|
|
1006
|
-
const erc20Precondition = intentPreconditions.find(
|
|
1007
|
-
(p: TransactionPrecondition) =>
|
|
1008
|
-
p.type === "erc20-balance" &&
|
|
1009
|
-
p.chainId === originChainId &&
|
|
1010
|
-
p.tokenAddress &&
|
|
1011
|
-
isAddressEqual(
|
|
1012
|
-
Address.from(p.tokenAddress),
|
|
1013
|
-
Address.from(tokenAddress),
|
|
1014
|
-
),
|
|
1015
|
-
)
|
|
1016
|
-
|
|
1017
|
-
const erc20MinAmount = erc20Precondition?.minAmount?.toString()
|
|
1018
|
-
if (erc20MinAmount === undefined) {
|
|
1019
|
-
throw new Error("Could not find precondition or min amount")
|
|
1020
|
-
}
|
|
1021
|
-
calcData = getERC20TransferData({
|
|
1022
|
-
recipient: recipientAddress,
|
|
1023
|
-
amount: BigInt(erc20MinAmount),
|
|
1024
|
-
})
|
|
1025
|
-
calcTo = tokenAddress as Address.Address
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
setOriginCallParams({
|
|
1029
|
-
to: calcTo,
|
|
1030
|
-
data: calcData,
|
|
1031
|
-
value: calcValue,
|
|
1032
|
-
chainId: originChainId,
|
|
1033
|
-
error: undefined,
|
|
1034
|
-
})
|
|
1035
|
-
} catch (error: unknown) {
|
|
1036
|
-
logger.console.error(
|
|
1037
|
-
"[trails-sdk] Failed to calculate origin call params for UI:",
|
|
1038
|
-
error,
|
|
1039
|
-
)
|
|
1040
|
-
setOriginCallParams({
|
|
1041
|
-
to: null,
|
|
1042
|
-
data: null,
|
|
1043
|
-
value: null,
|
|
1044
|
-
chainId: null,
|
|
1045
|
-
error: error instanceof Error ? error.message : "Unknown error",
|
|
1046
|
-
})
|
|
1047
|
-
}
|
|
1048
|
-
}, [
|
|
1049
|
-
intentCalls,
|
|
1050
|
-
tokenAddress,
|
|
1051
|
-
originChainId,
|
|
1052
|
-
intentPreconditions,
|
|
1053
|
-
account.address,
|
|
1054
|
-
originIntentAddress,
|
|
1055
|
-
])
|
|
1056
|
-
|
|
1057
|
-
// const checkPreconditionStatuses = useCallback(async () => {
|
|
1058
|
-
// if (!intentPreconditions) return
|
|
1059
|
-
|
|
1060
|
-
// const statuses = await Promise.all(
|
|
1061
|
-
// intentPreconditions.map(async (precondition) => {
|
|
1062
|
-
// try {
|
|
1063
|
-
// const chainIdString = precondition.chainId
|
|
1064
|
-
// if (!chainIdString) {
|
|
1065
|
-
// console.warn('[trails-sdk] Precondition missing chainId:', precondition)
|
|
1066
|
-
// return false
|
|
1067
|
-
// }
|
|
1068
|
-
// const chainId = parseInt(chainIdString)
|
|
1069
|
-
// if (isNaN(chainId) || chainId <= 0) {
|
|
1070
|
-
// console.warn('[trails-sdk] Precondition has invalid chainId:', chainIdString, precondition)
|
|
1071
|
-
// return false
|
|
1072
|
-
// }
|
|
1073
|
-
|
|
1074
|
-
// const chainRelayer = getRelayer(chainId)
|
|
1075
|
-
// if (!chainRelayer) {
|
|
1076
|
-
// console.error(`[trails-sdk] No relayer found for chainId: ${chainId}`)
|
|
1077
|
-
// return false
|
|
1078
|
-
// }
|
|
1079
|
-
|
|
1080
|
-
// return await chainRelayer.checkPrecondition(precondition)
|
|
1081
|
-
// } catch (error) {
|
|
1082
|
-
// console.error('[trails-sdk] Error checking precondition:', error, 'Precondition:', precondition)
|
|
1083
|
-
// return false
|
|
1084
|
-
// }
|
|
1085
|
-
// }),
|
|
1086
|
-
// )
|
|
1087
|
-
|
|
1088
|
-
// setPreconditionStatuses(statuses)
|
|
1089
|
-
// }, [intentPreconditions, getRelayer])
|
|
1090
|
-
|
|
1091
|
-
// useEffect(() => {
|
|
1092
|
-
// // TODO: Remove this once we have a way to check precondition statuses
|
|
1093
|
-
// if (false) {
|
|
1094
|
-
// checkPreconditionStatuses()
|
|
1095
|
-
// }
|
|
1096
|
-
// }, [intentPreconditions, checkPreconditionStatuses])
|
|
1097
|
-
|
|
1098
|
-
// Use the new WaitIntentReceipt API to monitor intent execution
|
|
1099
|
-
// This replaces the old per-meta-transaction polling approach
|
|
1100
|
-
const intentReceiptStatus = useIntentReceiptMonitor(
|
|
1101
|
-
committedOriginIntentAddress ?? undefined,
|
|
1102
|
-
trailsClient,
|
|
1103
|
-
)
|
|
1104
|
-
|
|
1105
|
-
// Simplified block timestamp tracking using IntentReceipt API
|
|
1106
|
-
// The IntentReceipt provides unified status for both origin and destination transactions
|
|
1107
|
-
useEffect(() => {
|
|
1108
|
-
logger.console.log("[trails-sdk] Intent receipt status update:", {
|
|
1109
|
-
intentId: committedOriginIntentAddress,
|
|
1110
|
-
done: intentReceiptStatus.done,
|
|
1111
|
-
hasReceipt: !!intentReceiptStatus.intentReceipt,
|
|
1112
|
-
})
|
|
1113
|
-
|
|
1114
|
-
if (!intentReceiptStatus.intentReceipt) {
|
|
1115
|
-
logger.console.log("[trails-sdk] No intent receipt yet, waiting...")
|
|
1116
|
-
return
|
|
1117
|
-
}
|
|
1118
|
-
|
|
1119
|
-
// Update block timestamps from intent receipt transactions
|
|
1120
|
-
const newTimestamps: typeof metaTxnBlockTimestamps = {}
|
|
1121
|
-
|
|
1122
|
-
if (intentReceiptStatus.intentReceipt.originTransaction) {
|
|
1123
|
-
const originTx = intentReceiptStatus.intentReceipt.originTransaction
|
|
1124
|
-
const key = `${originTx.chainId}-${originTx.metaTxnId}`
|
|
1125
|
-
newTimestamps[key] = { timestamp: null } // TODO: fetch actual block timestamp
|
|
1126
|
-
}
|
|
1127
|
-
|
|
1128
|
-
if (intentReceiptStatus.intentReceipt.destinationTransaction) {
|
|
1129
|
-
const destTx = intentReceiptStatus.intentReceipt.destinationTransaction
|
|
1130
|
-
const key = `${destTx.chainId}-${destTx.metaTxnId}`
|
|
1131
|
-
newTimestamps[key] = { timestamp: null } // TODO: fetch actual block timestamp
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
|
-
setMetaTxnBlockTimestamps(newTimestamps)
|
|
1135
|
-
}, [
|
|
1136
|
-
intentReceiptStatus.intentReceipt,
|
|
1137
|
-
intentReceiptStatus.done,
|
|
1138
|
-
committedOriginIntentAddress,
|
|
1139
|
-
])
|
|
1140
|
-
|
|
1141
|
-
// Effect to handle completed intent
|
|
1142
|
-
useEffect(() => {
|
|
1143
|
-
if (intentReceiptStatus.done && intentReceiptStatus.intentReceipt) {
|
|
1144
|
-
logger.console.log("[trails-sdk] Intent execution completed", {
|
|
1145
|
-
intentId: committedOriginIntentAddress,
|
|
1146
|
-
status: intentReceiptStatus.intentReceipt.status,
|
|
1147
|
-
})
|
|
1148
|
-
|
|
1149
|
-
// Handle CCTP transfers if needed
|
|
1150
|
-
if (intentReceiptStatus.intentReceipt.originTransaction?.txnHash) {
|
|
1151
|
-
const providerFromQuote = trailsFee?.quoteProvider
|
|
1152
|
-
? String(trailsFee.quoteProvider).toLowerCase()
|
|
1153
|
-
: undefined
|
|
1154
|
-
const providerFromArgs = quoteIntentMutation.variables?.options
|
|
1155
|
-
?.quoteProvider
|
|
1156
|
-
? String(
|
|
1157
|
-
quoteIntentMutation.variables.options.quoteProvider,
|
|
1158
|
-
).toLowerCase()
|
|
1159
|
-
: undefined
|
|
1160
|
-
const isCctp =
|
|
1161
|
-
providerFromQuote === "cctp" || providerFromArgs === "cctp"
|
|
1162
|
-
|
|
1163
|
-
if (isCctp && quoteIntentMutation.variables) {
|
|
1164
|
-
try {
|
|
1165
|
-
queueCCTPTransfer({
|
|
1166
|
-
sourceTxHash: intentReceiptStatus.intentReceipt.originTransaction
|
|
1167
|
-
.txnHash as `0x${string}`,
|
|
1168
|
-
sourceChainId: quoteIntentMutation.variables.originChainId,
|
|
1169
|
-
destinationChainId:
|
|
1170
|
-
quoteIntentMutation.variables.destinationChainId,
|
|
1171
|
-
trailsClient,
|
|
1172
|
-
})
|
|
1173
|
-
} catch (error) {
|
|
1174
|
-
logger.console.error("[Trails] queueCCTPTransfer error", error)
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
}, [
|
|
1180
|
-
intentReceiptStatus.done,
|
|
1181
|
-
intentReceiptStatus.intentReceipt,
|
|
1182
|
-
committedOriginIntentAddress,
|
|
1183
|
-
trailsFee,
|
|
1184
|
-
quoteIntentMutation.variables,
|
|
1185
|
-
trailsClient,
|
|
1186
|
-
])
|
|
1187
|
-
|
|
1188
|
-
const updateAutoExecute = (enabled: boolean) => {
|
|
1189
|
-
setIsAutoExecute(enabled)
|
|
1190
|
-
}
|
|
1191
|
-
|
|
1192
|
-
function quoteIntent(args: QuoteIntentRequest) {
|
|
1193
|
-
quoteIntentMutation.mutate(args)
|
|
1194
|
-
}
|
|
1195
|
-
|
|
1196
|
-
function commitIntent(intent: Intent) {
|
|
1197
|
-
logger.console.log("[trails-sdk] commitIntent", intent)
|
|
1198
|
-
commitIntentMutation.mutate(intent)
|
|
1199
|
-
}
|
|
1200
|
-
|
|
1201
|
-
function updateOriginCallParams(
|
|
1202
|
-
args: { originChainId: number; tokenAddress: string } | null,
|
|
1203
|
-
) {
|
|
1204
|
-
if (!args) {
|
|
1205
|
-
setOriginCallParams(null)
|
|
1206
|
-
return
|
|
1207
|
-
}
|
|
1208
|
-
const { originChainId, tokenAddress } = args
|
|
1209
|
-
setOriginChainId(originChainId)
|
|
1210
|
-
setTokenAddress(tokenAddress)
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
|
-
const { chainId } = account
|
|
1214
|
-
|
|
1215
|
-
const originChainIdFromParams = originCallParams?.chainId
|
|
1216
|
-
|
|
1217
|
-
const quoteIntentPending = quoteIntentMutation.isPending
|
|
1218
|
-
const quoteIntentSuccess = quoteIntentMutation.isSuccess
|
|
1219
|
-
const quoteIntentError = quoteIntentMutation.error
|
|
1220
|
-
const quoteIntentArgs = quoteIntentMutation.variables
|
|
1221
|
-
|
|
1222
|
-
const commitIntentPending = commitIntentMutation.isPending
|
|
1223
|
-
const commitIntentSuccess = commitIntentMutation.isSuccess
|
|
1224
|
-
const commitIntentError = commitIntentMutation.error
|
|
1225
|
-
const commitIntentArgs = commitIntentMutation.variables
|
|
1226
|
-
|
|
1227
|
-
const executeIntentPending = executeIntentMutation.isPending
|
|
1228
|
-
const executeIntentSuccess = executeIntentMutation.isSuccess
|
|
1229
|
-
const executeIntentError = executeIntentMutation.error
|
|
1230
|
-
|
|
1231
|
-
function executeIntent(args: {
|
|
1232
|
-
intentId: string
|
|
1233
|
-
depositTransactionHash: string
|
|
1234
|
-
}) {
|
|
1235
|
-
executeIntentMutation.mutate(args)
|
|
1236
|
-
}
|
|
1237
|
-
|
|
1238
|
-
return {
|
|
1239
|
-
trailsClient,
|
|
1240
|
-
metaTxns,
|
|
1241
|
-
intentCalls,
|
|
1242
|
-
intentPreconditions,
|
|
1243
|
-
trailsFee,
|
|
1244
|
-
txnHash,
|
|
1245
|
-
committedOriginIntentAddress,
|
|
1246
|
-
committedDestinationIntentAddress,
|
|
1247
|
-
verificationStatus,
|
|
1248
|
-
estimatedGas,
|
|
1249
|
-
isEstimateError,
|
|
1250
|
-
estimateError,
|
|
1251
|
-
calculateIntentAddress,
|
|
1252
|
-
calculateOriginAndDestinationIntentAddresses,
|
|
1253
|
-
committedIntent,
|
|
1254
|
-
isLoadingCommittedIntent,
|
|
1255
|
-
committedConfigError,
|
|
1256
|
-
commitIntent,
|
|
1257
|
-
commitIntentPending,
|
|
1258
|
-
commitIntentSuccess,
|
|
1259
|
-
commitIntentError,
|
|
1260
|
-
commitIntentArgs,
|
|
1261
|
-
executeIntent,
|
|
1262
|
-
executeIntentPending,
|
|
1263
|
-
executeIntentSuccess,
|
|
1264
|
-
executeIntentError,
|
|
1265
|
-
getIntentFromQuoteIntent,
|
|
1266
|
-
operationHashes,
|
|
1267
|
-
sendOriginTransaction,
|
|
1268
|
-
switchChain,
|
|
1269
|
-
isSwitchingChain,
|
|
1270
|
-
switchChainError,
|
|
1271
|
-
isTransactionInProgress,
|
|
1272
|
-
isChainSwitchRequired:
|
|
1273
|
-
chainId !== originChainIdFromParams && !!originChainIdFromParams,
|
|
1274
|
-
sendTransaction: sendOriginTxn.sendTransaction,
|
|
1275
|
-
isSendingTransaction: sendOriginTxn.isPending,
|
|
1276
|
-
originCallStatus,
|
|
1277
|
-
updateOriginCallStatus,
|
|
1278
|
-
isEstimatingGas,
|
|
1279
|
-
isAutoExecute,
|
|
1280
|
-
updateAutoExecute,
|
|
1281
|
-
receipt,
|
|
1282
|
-
isWaitingForReceipt,
|
|
1283
|
-
receiptIsSuccess,
|
|
1284
|
-
receiptIsError,
|
|
1285
|
-
receiptError,
|
|
1286
|
-
hasAutoExecuted,
|
|
1287
|
-
originCallSuccess: sendOriginTxn.isSuccess,
|
|
1288
|
-
sentMetaTxns,
|
|
1289
|
-
clearIntent,
|
|
1290
|
-
intentReceiptStatus,
|
|
1291
|
-
quoteIntent,
|
|
1292
|
-
quoteIntentPending,
|
|
1293
|
-
quoteIntentSuccess,
|
|
1294
|
-
quoteIntentError,
|
|
1295
|
-
quoteIntentArgs,
|
|
1296
|
-
originCallParams,
|
|
1297
|
-
updateOriginCallParams,
|
|
1298
|
-
originBlockTimestamp,
|
|
1299
|
-
metaTxnBlockTimestamps,
|
|
1300
|
-
originIntentAddress,
|
|
1301
|
-
destinationIntentAddress,
|
|
1302
|
-
}
|
|
1303
|
-
}
|