0xtrails 0.8.2 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aave.d.ts.map +1 -1
- package/dist/{ccip-ru_Yzdas.js → ccip-Bs-QcZXm.js} +13 -13
- package/dist/constants.d.ts +2 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/fees.d.ts +11 -17
- package/dist/fees.d.ts.map +1 -1
- package/dist/{index-Si7cO9V7.js → index-C_EsqqSn.js} +20320 -20063
- package/dist/index.js +425 -847
- package/dist/intents.d.ts +1 -2
- package/dist/intents.d.ts.map +1 -1
- package/dist/prepareSend.d.ts.map +1 -1
- package/dist/recover.d.ts +8 -9
- package/dist/recover.d.ts.map +1 -1
- package/dist/tokenBalances.d.ts +51 -0
- package/dist/tokenBalances.d.ts.map +1 -1
- package/dist/trailsRouter.d.ts +15 -0
- package/dist/trailsRouter.d.ts.map +1 -1
- package/dist/transactionIntent/deposits/depositOrchestrator.d.ts +1 -3
- package/dist/transactionIntent/deposits/depositOrchestrator.d.ts.map +1 -1
- package/dist/transactionIntent/deposits/standardDeposit.d.ts +1 -3
- package/dist/transactionIntent/deposits/standardDeposit.d.ts.map +1 -1
- package/dist/transactionIntent/handlers/crossChain.d.ts +2 -4
- package/dist/transactionIntent/handlers/crossChain.d.ts.map +1 -1
- package/dist/transactionIntent/handlers/sameChainSameToken.d.ts +5 -4
- package/dist/transactionIntent/handlers/sameChainSameToken.d.ts.map +1 -1
- package/dist/transactionIntent/quote/normalizeQuote.d.ts +1 -1
- package/dist/transactionIntent/quote/normalizeQuote.d.ts.map +1 -1
- package/dist/transactionIntent/quote/quoteHelpers.d.ts +1 -1
- package/dist/transactionIntent/quote/quoteHelpers.d.ts.map +1 -1
- package/dist/transactionIntent/types.d.ts +11 -18
- package/dist/transactionIntent/types.d.ts.map +1 -1
- package/dist/widget/components/AccountIntentTransactionHistory.d.ts.map +1 -1
- package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
- package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
- package/dist/widget/components/SlippageToleranceSettings.d.ts +2 -1
- package/dist/widget/components/SlippageToleranceSettings.d.ts.map +1 -1
- package/dist/widget/css/compiled.css +1 -1
- package/dist/widget/hooks/useQuote.d.ts +94 -35
- package/dist/widget/hooks/useQuote.d.ts.map +1 -1
- package/dist/widget/hooks/useSendForm.d.ts +2 -2
- package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
- package/dist/widget/hooks/useTrailsSendTransaction.d.ts.map +1 -1
- package/dist/widget/index.js +1 -1
- package/package.json +2 -2
- package/src/aave.ts +4 -0
- package/src/constants.ts +4 -0
- package/src/fees.ts +47 -72
- package/src/intents.ts +1 -3
- package/src/morpho.ts +1 -1
- package/src/prepareSend.ts +42 -6
- package/src/recover.ts +116 -172
- package/src/tokenBalances.ts +301 -1
- package/src/trailsRouter.ts +77 -0
- package/src/transactionIntent/deposits/depositOrchestrator.ts +0 -6
- package/src/transactionIntent/deposits/standardDeposit.ts +167 -184
- package/src/transactionIntent/handlers/crossChain.ts +8 -11
- package/src/transactionIntent/handlers/sameChainSameToken.ts +619 -608
- package/src/transactionIntent/quote/normalizeQuote.ts +32 -46
- package/src/transactionIntent/quote/quoteHelpers.ts +4 -2
- package/src/transactionIntent/types.ts +11 -18
- package/src/widget/compiled.css +1 -1
- package/src/widget/components/AccountIntentTransactionHistory.tsx +50 -18
- package/src/widget/components/ClassicSwap.tsx +25 -30
- package/src/widget/components/QuoteDetails.tsx +18 -27
- package/src/widget/components/SlippageToleranceSettings.tsx +55 -25
- package/src/widget/hooks/useQuote.ts +317 -79
- package/src/widget/hooks/useSendForm.ts +123 -764
- package/src/widget/hooks/useTrailsSendTransaction.ts +0 -2
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { useQuery } from "@tanstack/react-query"
|
|
2
|
-
import { useRef } from "react"
|
|
2
|
+
import { useRef, useMemo, useState, useEffect } from "react"
|
|
3
3
|
import type { TransactionReceipt } from "viem"
|
|
4
|
-
import { zeroAddress, erc20Abi } from "viem"
|
|
4
|
+
import { zeroAddress, erc20Abi, parseUnits } from "viem"
|
|
5
|
+
import { etherlink } from "viem/chains"
|
|
5
6
|
import { useIndexerGatewayClient } from "../../indexerClient.js"
|
|
6
7
|
import { getTokenBalancesWithPrices } from "../../tokenBalances.js"
|
|
7
8
|
import { logger } from "../../logger.js"
|
|
@@ -14,12 +15,14 @@ import { prepareSend } from "../../prepareSend.js"
|
|
|
14
15
|
import { abortControllerRegistry } from "../../abortController.js"
|
|
15
16
|
import { TradeType } from "../../prepareSend.js"
|
|
16
17
|
import { getChainInfo, useChainRpcClient } from "../../chains.js"
|
|
18
|
+
import { getIsContract } from "../../contractUtils.js"
|
|
17
19
|
import type { IntentTransaction } from "@0xtrails/api"
|
|
18
20
|
import type { PrepareSendOptions } from "../../transactionIntent/types.js"
|
|
19
21
|
import type { Chain } from "../../chains.js"
|
|
20
22
|
import type { Token } from "../../tokens.js"
|
|
21
23
|
import type { TransactionState } from "../../transactions.js"
|
|
22
24
|
import type { PrepareSendFees } from "../../prepareSend.js"
|
|
25
|
+
import type { TrailsFeeBreakdown } from "../../fees.js"
|
|
23
26
|
import { getTokenPrice } from "../../prices.js"
|
|
24
27
|
import { useCommitIntent, useExecuteIntent } from "../../mutations.js"
|
|
25
28
|
import type { CheckoutOnHandlers } from "./useCheckout.js"
|
|
@@ -30,7 +33,7 @@ import type { FeeOption, RouteProvider } from "@0xtrails/api"
|
|
|
30
33
|
*
|
|
31
34
|
* All parameters are optional, but the hook will not fetch a quote until all required
|
|
32
35
|
* parameters are provided (walletClient, fromTokenAddress, fromChainId, toTokenAddress,
|
|
33
|
-
* toChainId, swapAmount,
|
|
36
|
+
* toChainId, swapAmount, toAddress).
|
|
34
37
|
*/
|
|
35
38
|
export type UseQuoteProps = {
|
|
36
39
|
/** The wallet client from wagmi (e.g., from `useWalletClient()`). Required for quote fetching. */
|
|
@@ -48,6 +51,10 @@ export type UseQuoteProps = {
|
|
|
48
51
|
/** The amount to swap, as a string or bigint in the token's smallest unit (wei/smallest unit). For example, 1 USDC (6 decimals) = '1000000'. */
|
|
49
52
|
swapAmount?: string | bigint
|
|
50
53
|
/** The recipient address for the destination token. */
|
|
54
|
+
toAddress?: string | null
|
|
55
|
+
/**
|
|
56
|
+
* @deprecated Use `toAddress` instead. This will be removed in a future version.
|
|
57
|
+
*/
|
|
51
58
|
toRecipient?: string | null
|
|
52
59
|
/** The trade type: `TradeType.EXACT_INPUT` (you specify input amount) or `TradeType.EXACT_OUTPUT` (you specify output amount). Defaults to `EXACT_OUTPUT`. */
|
|
53
60
|
tradeType?: TradeType | null
|
|
@@ -100,6 +107,8 @@ export type UseQuoteProps = {
|
|
|
100
107
|
nodeGatewayEnv?: "prod" | "dev" | "local" | "cors-anywhere"
|
|
101
108
|
/** Optional flag to indicate if the wallet is a smart wallet. */
|
|
102
109
|
isSmartWallet?: boolean | null
|
|
110
|
+
/** Optional fund method (e.g., 'wallet', 'qr-code', 'exchange'). Used to determine default balance for external deposit flows. */
|
|
111
|
+
fundMethod?: string | null
|
|
103
112
|
}
|
|
104
113
|
|
|
105
114
|
/**
|
|
@@ -138,10 +147,31 @@ export type RouteProviderInfo = {
|
|
|
138
147
|
* and other relevant information for displaying to the user.
|
|
139
148
|
*/
|
|
140
149
|
export type Quote = {
|
|
150
|
+
// Kept for backward compatibility (use origin*/destination* versions)
|
|
141
151
|
fromAmount: string
|
|
142
152
|
fromAmountMin: string
|
|
143
153
|
toAmount: string
|
|
144
154
|
toAmountMin: string
|
|
155
|
+
fromAmountUsdDisplay: string
|
|
156
|
+
toAmountUsdDisplay: string
|
|
157
|
+
// PrepareSendQuote compatible names
|
|
158
|
+
originAmount: string
|
|
159
|
+
originAmountMin: string
|
|
160
|
+
destinationAmount: string
|
|
161
|
+
destinationAmountMin: string
|
|
162
|
+
originAmountUsdDisplay: string
|
|
163
|
+
destinationAmountUsdDisplay: string
|
|
164
|
+
originAmountDisplay: string
|
|
165
|
+
destinationAmountDisplay: string
|
|
166
|
+
originAmountMinDisplay: string
|
|
167
|
+
originAmountMinUsdFormatted: string
|
|
168
|
+
originAmountMinUsdDisplay: string
|
|
169
|
+
destinationAmountMinDisplay: string
|
|
170
|
+
destinationAmountMinUsdFormatted: string
|
|
171
|
+
destinationAmountMinUsdDisplay: string
|
|
172
|
+
originAmountUsdFormatted: string
|
|
173
|
+
destinationAmountUsdFormatted: string
|
|
174
|
+
// Common fields
|
|
145
175
|
originToken: Token
|
|
146
176
|
destinationToken: Token
|
|
147
177
|
originChain: Chain
|
|
@@ -149,38 +179,65 @@ export type Quote = {
|
|
|
149
179
|
fees: PrepareSendFees
|
|
150
180
|
slippageTolerance: string
|
|
151
181
|
priceImpact: string
|
|
152
|
-
priceImpactUsd
|
|
153
|
-
priceImpactUsdDisplay
|
|
182
|
+
priceImpactUsd: string | null
|
|
183
|
+
priceImpactUsdDisplay: string
|
|
154
184
|
completionEstimateSeconds: number
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
185
|
+
completionEstimateDisplay: string
|
|
186
|
+
transactionStates: TransactionState[]
|
|
187
|
+
originTokenRate: string
|
|
188
|
+
routeProviders: RouteProviderInfo[]
|
|
189
|
+
destinationTokenRate: string
|
|
190
|
+
gasCostUsd: number | null
|
|
191
|
+
gasCostUsdDisplay: string
|
|
192
|
+
gasCost: string
|
|
193
|
+
gasCostFormatted: string
|
|
194
|
+
totalFeesUsd: number | null
|
|
195
|
+
totalFeesUsdDisplay: string
|
|
196
|
+
totalGasUsd: number | null
|
|
197
|
+
totalGasUsdDisplay: string
|
|
198
|
+
intentId: string | null
|
|
199
|
+
originAmountFormatted: string
|
|
200
|
+
destinationAmountFormatted: string
|
|
201
|
+
originDepositAddress: string
|
|
202
|
+
destinationDepositAddress: string
|
|
203
|
+
destinationAddress: string
|
|
204
|
+
destinationCalldata: string
|
|
205
|
+
trailsFeeBreakdown: TrailsFeeBreakdown | null
|
|
206
|
+
originGasUsd: number | null
|
|
207
|
+
originGasUsdDisplay: string
|
|
208
|
+
destinationGasUsd: number | null
|
|
209
|
+
destinationGasUsdDisplay: string
|
|
210
|
+
providerFeeUsd: number | null
|
|
211
|
+
providerFeeUsdDisplay: string
|
|
212
|
+
trailsFeeUsd: number | null
|
|
213
|
+
trailsFeeUsdDisplay: string
|
|
214
|
+
totalProviderFeesUsd: number | null
|
|
215
|
+
totalProviderFeesUsdDisplay: string
|
|
216
|
+
noSufficientBalance: boolean
|
|
171
217
|
}
|
|
172
218
|
|
|
173
219
|
/**
|
|
174
220
|
* Return type for the {@link useQuote} hook.
|
|
175
221
|
*
|
|
176
|
-
* Provides the quote data,
|
|
222
|
+
* Provides the quote data, send function, loading state, error information,
|
|
177
223
|
* and utility functions for managing the quote lifecycle.
|
|
178
224
|
*/
|
|
225
|
+
export type SendOptions = {
|
|
226
|
+
/** Override the fee option for this transaction (e.g., pay gas in USDC instead of ETH). */
|
|
227
|
+
selectedFeeOption?: FeeOption | null
|
|
228
|
+
/** Callback called when the wallet signature is confirmed and transaction is submitted. */
|
|
229
|
+
onOriginSend?: () => void
|
|
230
|
+
}
|
|
231
|
+
|
|
179
232
|
export type UseQuoteReturn = {
|
|
180
|
-
/** The quote object with
|
|
233
|
+
/** The quote object with transaction details, or `null` if not available. */
|
|
181
234
|
quote: Quote | null
|
|
182
|
-
/** A function to execute the
|
|
183
|
-
|
|
235
|
+
/** A function to execute the transaction, or `null` if not available. Returns a Promise that resolves to transaction result with receipts. Accepts optional SendOptions to override fee option. */
|
|
236
|
+
send: ((options?: SendOptions) => Promise<SwapReturn | null>) | null
|
|
237
|
+
/**
|
|
238
|
+
* @deprecated Use `send` instead. This will be removed in a future version.
|
|
239
|
+
*/
|
|
240
|
+
swap: ((options?: SendOptions) => Promise<SwapReturn | null>) | null
|
|
184
241
|
/** Boolean indicating if a quote is currently being fetched. */
|
|
185
242
|
isLoadingQuote: boolean
|
|
186
243
|
/** Any error that occurred while fetching the quote, or `null`. */
|
|
@@ -191,6 +248,14 @@ export type UseQuoteReturn = {
|
|
|
191
248
|
refetchQuote: () => void
|
|
192
249
|
/** Function to abort any in-flight quote requests. */
|
|
193
250
|
abort: () => void
|
|
251
|
+
/** Fee options available for this quote (e.g., pay gas in different tokens). */
|
|
252
|
+
feeOptions: FeeOption[]
|
|
253
|
+
/** Whether the recipient address is a contract (null if not yet checked). */
|
|
254
|
+
isRecipientContract: boolean | null
|
|
255
|
+
/** Whether the sender address is a contract on the origin chain (null if not yet checked). */
|
|
256
|
+
isSenderContractOnOrigin: boolean | null
|
|
257
|
+
/** Whether the sender address is a contract on the destination chain (null if not yet checked). */
|
|
258
|
+
isSenderContractOnDestination: boolean | null
|
|
194
259
|
}
|
|
195
260
|
|
|
196
261
|
// TODO: consolidate useSendForm, prepareSend, and useQuote hooks into a single hook
|
|
@@ -213,7 +278,7 @@ export type UseQuoteReturn = {
|
|
|
213
278
|
* const { data: walletClient } = useWalletClient()
|
|
214
279
|
* const { address } = useAccount()
|
|
215
280
|
*
|
|
216
|
-
* const { quote,
|
|
281
|
+
* const { quote, send, isLoadingQuote, quoteError, refetchQuote } = useQuote({
|
|
217
282
|
* walletClient,
|
|
218
283
|
* fromTokenAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC
|
|
219
284
|
* fromChainId: 1, // Ethereum
|
|
@@ -221,7 +286,7 @@ export type UseQuoteReturn = {
|
|
|
221
286
|
* toChainId: 8453, // Base
|
|
222
287
|
* swapAmount: '1000000', // 1 USDC (6 decimals)
|
|
223
288
|
* tradeType: TradeType.EXACT_INPUT,
|
|
224
|
-
*
|
|
289
|
+
* toAddress: address,
|
|
225
290
|
* slippageTolerance: '0.005', // 0.5%
|
|
226
291
|
* onStatusUpdate: (states) => {
|
|
227
292
|
* console.log('Transaction states:', states)
|
|
@@ -236,14 +301,14 @@ export type UseQuoteReturn = {
|
|
|
236
301
|
* return () => clearInterval(id)
|
|
237
302
|
* }, [refetchQuote])
|
|
238
303
|
*
|
|
239
|
-
* const
|
|
240
|
-
* if (!
|
|
304
|
+
* const handleSend = async () => {
|
|
305
|
+
* if (!send) return
|
|
241
306
|
*
|
|
242
307
|
* try {
|
|
243
|
-
* const result = await
|
|
244
|
-
* console.log('
|
|
308
|
+
* const result = await send()
|
|
309
|
+
* console.log('Transaction completed:', result)
|
|
245
310
|
* } catch (error) {
|
|
246
|
-
* console.error('
|
|
311
|
+
* console.error('Transaction failed:', error)
|
|
247
312
|
* }
|
|
248
313
|
* }
|
|
249
314
|
*
|
|
@@ -255,7 +320,7 @@ export type UseQuoteReturn = {
|
|
|
255
320
|
* <div>
|
|
256
321
|
* <div>From: {quote.fromAmount} {quote.originToken.symbol}</div>
|
|
257
322
|
* <div>To: {quote.toAmount} {quote.destinationToken.symbol}</div>
|
|
258
|
-
* <button onClick={
|
|
323
|
+
* <button onClick={handleSend}>Send</button>
|
|
259
324
|
* </div>
|
|
260
325
|
* )
|
|
261
326
|
* }
|
|
@@ -269,7 +334,7 @@ export type UseQuoteReturn = {
|
|
|
269
334
|
* @param props.toChainId - The chain ID of the destination chain
|
|
270
335
|
* @param props.swapAmount - The amount to swap, as a string or bigint in the token's smallest unit (wei/smallest unit). For example, 1 USDC (6 decimals) = '1000000'
|
|
271
336
|
* @param props.tradeType - The trade type: `TradeType.EXACT_INPUT` (you specify input amount) or `TradeType.EXACT_OUTPUT` (you specify output amount). Defaults to `EXACT_OUTPUT`
|
|
272
|
-
* @param props.
|
|
337
|
+
* @param props.toAddress - The recipient address for the destination token.
|
|
273
338
|
* @param props.slippageTolerance - Slippage tolerance as a decimal string or number (e.g., '0.005' for 0.5%, '0.01' for 1%). Defaults to a reasonable value if not provided
|
|
274
339
|
* @param props.toCalldata - Optional custom calldata for the destination chain. Used for executing arbitrary contract calls (e.g., swap and mint an NFT on destination chain)
|
|
275
340
|
* @param props.onStatusUpdate - Optional callback function that receives transaction state updates during swap execution
|
|
@@ -284,8 +349,8 @@ export type UseQuoteReturn = {
|
|
|
284
349
|
* @param props.isSmartWallet - Optional flag to indicate if the wallet is a smart wallet
|
|
285
350
|
*
|
|
286
351
|
* @returns An object containing:
|
|
287
|
-
* - `quote` - The quote object with
|
|
288
|
-
* - `
|
|
352
|
+
* - `quote` - The quote object with transaction details, or `null` if not available
|
|
353
|
+
* - `send` - A function to execute the transaction, or `null` if not available. Returns a Promise that resolves to transaction result with receipts
|
|
289
354
|
* - `isLoadingQuote` - Boolean indicating if a quote is currently being fetched
|
|
290
355
|
* - `quoteError` - Any error that occurred while fetching the quote, or `null`
|
|
291
356
|
* - `quoteErrorPrettified` - A user-friendly error message string
|
|
@@ -312,6 +377,7 @@ export function useQuote({
|
|
|
312
377
|
toChainId,
|
|
313
378
|
swapAmount,
|
|
314
379
|
tradeType,
|
|
380
|
+
toAddress,
|
|
315
381
|
toRecipient,
|
|
316
382
|
toCalldata,
|
|
317
383
|
slippageTolerance,
|
|
@@ -325,7 +391,15 @@ export function useQuote({
|
|
|
325
391
|
abortSignal: externalAbortSignal,
|
|
326
392
|
apiKey,
|
|
327
393
|
isSmartWallet,
|
|
394
|
+
fundMethod,
|
|
328
395
|
}: Partial<UseQuoteProps> = {}): UseQuoteReturn {
|
|
396
|
+
// Handle deprecated toRecipient prop - prefer toAddress
|
|
397
|
+
if (toRecipient && !toAddress) {
|
|
398
|
+
logger.console.warn(
|
|
399
|
+
"[trails-sdk] [useQuote] Warning: 'toRecipient' is deprecated, use 'toAddress' instead",
|
|
400
|
+
)
|
|
401
|
+
}
|
|
402
|
+
const effectiveToAddress = toAddress ?? toRecipient
|
|
329
403
|
// Set node gateway environment override for this quote session
|
|
330
404
|
if (nodeGatewayEnv) {
|
|
331
405
|
;(globalThis as any).__testNodeGatewayEnv = nodeGatewayEnv
|
|
@@ -354,6 +428,15 @@ export function useQuote({
|
|
|
354
428
|
})()
|
|
355
429
|
: abortControllerRef.current.signal
|
|
356
430
|
|
|
431
|
+
// Debounce swapAmount to prevent excessive API calls while user is typing
|
|
432
|
+
const [debouncedSwapAmount, setDebouncedSwapAmount] = useState(swapAmount)
|
|
433
|
+
useEffect(() => {
|
|
434
|
+
const timer = setTimeout(() => {
|
|
435
|
+
setDebouncedSwapAmount(swapAmount)
|
|
436
|
+
}, 500)
|
|
437
|
+
return () => clearTimeout(timer)
|
|
438
|
+
}, [swapAmount])
|
|
439
|
+
|
|
357
440
|
const { trailsApiKey, trailsApiUrl, sequenceIndexerUrl } = useTrails()
|
|
358
441
|
// trailsApiKey is the same as sequenceProjectAccessKey
|
|
359
442
|
const sequenceProjectAccessKey = trailsApiKey
|
|
@@ -374,6 +457,74 @@ export function useQuote({
|
|
|
374
457
|
const commitIntentMutation = useCommitIntent()
|
|
375
458
|
const executeIntentMutation = useExecuteIntent()
|
|
376
459
|
|
|
460
|
+
// Auto-detect swap/bridge provider for etherlink chains
|
|
461
|
+
const effectiveSwapProvider = useMemo((): RouteProvider | null => {
|
|
462
|
+
// Force lifi for etherlink chains when no provider specified
|
|
463
|
+
if (!swapProvider) {
|
|
464
|
+
if (fromChainId === etherlink.id || toChainId === etherlink.id) {
|
|
465
|
+
return "lifi" as RouteProvider
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
return swapProvider ?? null
|
|
469
|
+
}, [swapProvider, fromChainId, toChainId])
|
|
470
|
+
|
|
471
|
+
const effectiveBridgeProvider = useMemo((): RouteProvider | null => {
|
|
472
|
+
// Force lifi for etherlink chains when no provider specified
|
|
473
|
+
if (!bridgeProvider) {
|
|
474
|
+
if (fromChainId === etherlink.id || toChainId === etherlink.id) {
|
|
475
|
+
return "lifi" as RouteProvider
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return bridgeProvider ?? null
|
|
479
|
+
}, [bridgeProvider, fromChainId, toChainId])
|
|
480
|
+
|
|
481
|
+
// Contract detection queries
|
|
482
|
+
const { data: isRecipientContract } = useQuery({
|
|
483
|
+
queryKey: ["isContract", "recipient", effectiveToAddress, toChainId],
|
|
484
|
+
queryFn: async () => {
|
|
485
|
+
if (!effectiveToAddress || !toChainId) return null
|
|
486
|
+
return getIsContract(effectiveToAddress as `0x${string}`, toChainId)
|
|
487
|
+
},
|
|
488
|
+
enabled: !!effectiveToAddress && !!toChainId,
|
|
489
|
+
staleTime: 60 * 1000, // Cache for 1 minute
|
|
490
|
+
})
|
|
491
|
+
|
|
492
|
+
const { data: isSenderContractOnOrigin } = useQuery({
|
|
493
|
+
queryKey: [
|
|
494
|
+
"isContract",
|
|
495
|
+
"senderOrigin",
|
|
496
|
+
walletClient?.account?.address,
|
|
497
|
+
fromChainId,
|
|
498
|
+
],
|
|
499
|
+
queryFn: async () => {
|
|
500
|
+
if (!walletClient?.account?.address || !fromChainId) return null
|
|
501
|
+
return getIsContract(
|
|
502
|
+
walletClient.account.address as `0x${string}`,
|
|
503
|
+
fromChainId,
|
|
504
|
+
)
|
|
505
|
+
},
|
|
506
|
+
enabled: !!walletClient?.account?.address && !!fromChainId,
|
|
507
|
+
staleTime: 60 * 1000,
|
|
508
|
+
})
|
|
509
|
+
|
|
510
|
+
const { data: isSenderContractOnDestination } = useQuery({
|
|
511
|
+
queryKey: [
|
|
512
|
+
"isContract",
|
|
513
|
+
"senderDestination",
|
|
514
|
+
walletClient?.account?.address,
|
|
515
|
+
toChainId,
|
|
516
|
+
],
|
|
517
|
+
queryFn: async () => {
|
|
518
|
+
if (!walletClient?.account?.address || !toChainId) return null
|
|
519
|
+
return getIsContract(
|
|
520
|
+
walletClient.account.address as `0x${string}`,
|
|
521
|
+
toChainId,
|
|
522
|
+
)
|
|
523
|
+
},
|
|
524
|
+
enabled: !!walletClient?.account?.address && !!toChainId,
|
|
525
|
+
staleTime: 60 * 1000,
|
|
526
|
+
})
|
|
527
|
+
|
|
377
528
|
const { data, isLoading, error, refetch } = useQuery({
|
|
378
529
|
queryKey: [
|
|
379
530
|
"prepareSend",
|
|
@@ -381,8 +532,8 @@ export function useQuote({
|
|
|
381
532
|
fromChainId,
|
|
382
533
|
toTokenAddress,
|
|
383
534
|
toChainId,
|
|
384
|
-
|
|
385
|
-
|
|
535
|
+
debouncedSwapAmount?.toString(),
|
|
536
|
+
effectiveToAddress,
|
|
386
537
|
toCalldata,
|
|
387
538
|
tradeType,
|
|
388
539
|
slippageTolerance,
|
|
@@ -390,6 +541,7 @@ export function useQuote({
|
|
|
390
541
|
bridgeProvider,
|
|
391
542
|
apiKey,
|
|
392
543
|
isSmartWallet,
|
|
544
|
+
fundMethod,
|
|
393
545
|
],
|
|
394
546
|
queryFn: async () => {
|
|
395
547
|
try {
|
|
@@ -406,8 +558,8 @@ export function useQuote({
|
|
|
406
558
|
!trailsClient ||
|
|
407
559
|
!fromTokenAddress ||
|
|
408
560
|
!toTokenAddress ||
|
|
409
|
-
!
|
|
410
|
-
!
|
|
561
|
+
!debouncedSwapAmount ||
|
|
562
|
+
!effectiveToAddress ||
|
|
411
563
|
!fromChainId ||
|
|
412
564
|
!toChainId ||
|
|
413
565
|
!indexerGatewayClient
|
|
@@ -612,15 +764,22 @@ export function useQuote({
|
|
|
612
764
|
const destinationTokenSymbol = destinationToken?.symbol ?? ""
|
|
613
765
|
const originTokenSymbol = originToken?.symbol ?? ""
|
|
614
766
|
|
|
767
|
+
// For qr-code and exchange fund methods, use a default amount of 100 tokens
|
|
768
|
+
// since the user will deposit from an external wallet (their connected wallet balance may be 0)
|
|
769
|
+
const effectiveOriginTokenBalance =
|
|
770
|
+
fundMethod === "qr-code" || fundMethod === "exchange"
|
|
771
|
+
? parseUnits("100", sourceTokenDecimals).toString()
|
|
772
|
+
: originTokenBalanceAmount
|
|
773
|
+
|
|
615
774
|
const options: PrepareSendOptions = {
|
|
616
775
|
account: walletClient.account!,
|
|
617
776
|
originTokenAddress: fromTokenAddress,
|
|
618
777
|
originChainId: fromChainId,
|
|
619
|
-
originTokenBalance:
|
|
778
|
+
originTokenBalance: effectiveOriginTokenBalance,
|
|
620
779
|
destinationChainId: toChainId,
|
|
621
|
-
recipient:
|
|
780
|
+
recipient: effectiveToAddress,
|
|
622
781
|
destinationTokenAddress: toTokenAddress,
|
|
623
|
-
swapAmount:
|
|
782
|
+
swapAmount: debouncedSwapAmount.toString(),
|
|
624
783
|
tradeType: tradeType ?? TradeType.EXACT_OUTPUT,
|
|
625
784
|
originTokenSymbol: originTokenSymbol,
|
|
626
785
|
destinationTokenSymbol: destinationTokenSymbol,
|
|
@@ -629,12 +788,10 @@ export function useQuote({
|
|
|
629
788
|
trailsClient,
|
|
630
789
|
sourceTokenDecimals,
|
|
631
790
|
destinationTokenDecimals,
|
|
632
|
-
fee: "0",
|
|
633
|
-
dryMode: false,
|
|
634
791
|
onTransactionStateChange: onStatusUpdate ?? (() => {}),
|
|
635
792
|
slippageTolerance: slippageTolerance?.toString(),
|
|
636
|
-
swapProvider:
|
|
637
|
-
bridgeProvider:
|
|
793
|
+
swapProvider: effectiveSwapProvider,
|
|
794
|
+
bridgeProvider: effectiveBridgeProvider,
|
|
638
795
|
paymasterUrl: paymasterUrl,
|
|
639
796
|
selectedFeeOption: selectedFeeOption ?? null,
|
|
640
797
|
abortSignal: combinedAbortSignal,
|
|
@@ -649,57 +806,119 @@ export function useQuote({
|
|
|
649
806
|
isSmartWallet: isSmartWallet ?? undefined,
|
|
650
807
|
trailsApiKey,
|
|
651
808
|
trailsApiUrl,
|
|
809
|
+
fundMethod: fundMethod ?? undefined,
|
|
652
810
|
}
|
|
653
811
|
|
|
654
812
|
logger.console.log("[trails-sdk] options", options)
|
|
655
813
|
|
|
656
|
-
const {
|
|
814
|
+
const {
|
|
815
|
+
quote: prepareSendQuote,
|
|
816
|
+
send: prepareSendSend,
|
|
817
|
+
feeOptions: prepareSendFeeOptions,
|
|
818
|
+
} = await prepareSend(options)
|
|
657
819
|
|
|
658
|
-
const quote = {
|
|
820
|
+
const quote: Quote = {
|
|
821
|
+
// Backward compatible fields
|
|
659
822
|
fromAmount: prepareSendQuote.originAmount,
|
|
660
823
|
toAmount: prepareSendQuote.destinationAmount,
|
|
661
824
|
fromAmountMin: prepareSendQuote.originAmountMin,
|
|
662
825
|
toAmountMin: prepareSendQuote.destinationAmountMin,
|
|
826
|
+
fromAmountUsdDisplay: prepareSendQuote.originAmountUsdDisplay ?? "",
|
|
827
|
+
toAmountUsdDisplay:
|
|
828
|
+
prepareSendQuote.destinationAmountUsdDisplay ?? "",
|
|
829
|
+
// PrepareSendQuote compatible fields
|
|
830
|
+
originAmount: prepareSendQuote.originAmount,
|
|
831
|
+
originAmountMin: prepareSendQuote.originAmountMin,
|
|
832
|
+
destinationAmount: prepareSendQuote.destinationAmount,
|
|
833
|
+
destinationAmountMin: prepareSendQuote.destinationAmountMin,
|
|
834
|
+
originAmountUsdDisplay: prepareSendQuote.originAmountUsdDisplay ?? "",
|
|
835
|
+
destinationAmountUsdDisplay:
|
|
836
|
+
prepareSendQuote.destinationAmountUsdDisplay ?? "",
|
|
837
|
+
originAmountDisplay: prepareSendQuote.originAmountDisplay ?? "",
|
|
838
|
+
destinationAmountDisplay:
|
|
839
|
+
prepareSendQuote.destinationAmountDisplay ?? "",
|
|
840
|
+
originAmountMinDisplay: prepareSendQuote.originAmountMinDisplay ?? "",
|
|
841
|
+
originAmountMinUsdFormatted:
|
|
842
|
+
prepareSendQuote.originAmountMinUsdFormatted ?? "",
|
|
843
|
+
originAmountMinUsdDisplay:
|
|
844
|
+
prepareSendQuote.originAmountMinUsdDisplay ?? "",
|
|
845
|
+
destinationAmountMinDisplay:
|
|
846
|
+
prepareSendQuote.destinationAmountMinDisplay ?? "",
|
|
847
|
+
destinationAmountMinUsdFormatted:
|
|
848
|
+
prepareSendQuote.destinationAmountMinUsdFormatted ?? "",
|
|
849
|
+
destinationAmountMinUsdDisplay:
|
|
850
|
+
prepareSendQuote.destinationAmountMinUsdDisplay ?? "",
|
|
851
|
+
originAmountUsdFormatted:
|
|
852
|
+
prepareSendQuote.originAmountUsdFormatted ?? "",
|
|
853
|
+
destinationAmountUsdFormatted:
|
|
854
|
+
prepareSendQuote.destinationAmountUsdFormatted ?? "",
|
|
855
|
+
// Common fields
|
|
663
856
|
originToken: prepareSendQuote.originToken,
|
|
664
857
|
destinationToken: prepareSendQuote.destinationToken,
|
|
665
858
|
originChain: prepareSendQuote.originChain,
|
|
666
859
|
destinationChain: prepareSendQuote.destinationChain,
|
|
667
860
|
fees: prepareSendQuote.fees,
|
|
668
861
|
priceImpact: prepareSendQuote.priceImpact,
|
|
669
|
-
priceImpactUsd: prepareSendQuote.priceImpactUsd ??
|
|
670
|
-
priceImpactUsdDisplay:
|
|
671
|
-
prepareSendQuote.priceImpactUsdDisplay ?? undefined,
|
|
862
|
+
priceImpactUsd: prepareSendQuote.priceImpactUsd ?? null,
|
|
863
|
+
priceImpactUsdDisplay: prepareSendQuote.priceImpactUsdDisplay ?? "",
|
|
672
864
|
completionEstimateSeconds: prepareSendQuote.completionEstimateSeconds,
|
|
865
|
+
completionEstimateDisplay:
|
|
866
|
+
prepareSendQuote.completionEstimateDisplay ?? "",
|
|
673
867
|
slippageTolerance: prepareSendQuote.slippageTolerance,
|
|
674
|
-
transactionStates: prepareSendQuote.transactionStates,
|
|
675
|
-
originTokenRate: prepareSendQuote.originTokenRate,
|
|
676
|
-
destinationTokenRate: prepareSendQuote.destinationTokenRate,
|
|
677
|
-
routeProviders: prepareSendQuote.routeProviders,
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
868
|
+
transactionStates: prepareSendQuote.transactionStates ?? [],
|
|
869
|
+
originTokenRate: prepareSendQuote.originTokenRate ?? "",
|
|
870
|
+
destinationTokenRate: prepareSendQuote.destinationTokenRate ?? "",
|
|
871
|
+
routeProviders: prepareSendQuote.routeProviders ?? [],
|
|
872
|
+
gasCostUsd: prepareSendQuote.gasCostUsd ?? null,
|
|
873
|
+
gasCostUsdDisplay: prepareSendQuote.gasCostUsdDisplay ?? "",
|
|
874
|
+
gasCost: prepareSendQuote.gasCost ?? "",
|
|
875
|
+
gasCostFormatted: prepareSendQuote.gasCostFormatted ?? "",
|
|
876
|
+
totalFeesUsd: prepareSendQuote.totalFeesUsd ?? null,
|
|
877
|
+
totalFeesUsdDisplay: prepareSendQuote.totalFeesUsdDisplay ?? "",
|
|
878
|
+
totalGasUsd: prepareSendQuote.totalGasUsd ?? null,
|
|
879
|
+
totalGasUsdDisplay: prepareSendQuote.totalGasUsdDisplay ?? "",
|
|
880
|
+
intentId: prepareSendQuote.intentId ?? null,
|
|
881
|
+
originAmountFormatted: prepareSendQuote.originAmountFormatted ?? "",
|
|
882
|
+
destinationAmountFormatted:
|
|
883
|
+
prepareSendQuote.destinationAmountFormatted ?? "",
|
|
884
|
+
originDepositAddress: prepareSendQuote.originDepositAddress ?? "",
|
|
885
|
+
destinationDepositAddress:
|
|
886
|
+
prepareSendQuote.destinationDepositAddress ?? "",
|
|
887
|
+
destinationAddress: prepareSendQuote.destinationAddress ?? "",
|
|
888
|
+
destinationCalldata: prepareSendQuote.destinationCalldata ?? "",
|
|
889
|
+
// Fee breakdown fields
|
|
890
|
+
trailsFeeBreakdown: prepareSendQuote.trailsFeeBreakdown ?? null,
|
|
891
|
+
originGasUsd: prepareSendQuote.originGasUsd ?? null,
|
|
892
|
+
originGasUsdDisplay: prepareSendQuote.originGasUsdDisplay ?? "",
|
|
893
|
+
destinationGasUsd: prepareSendQuote.destinationGasUsd ?? null,
|
|
894
|
+
destinationGasUsdDisplay:
|
|
895
|
+
prepareSendQuote.destinationGasUsdDisplay ?? "",
|
|
896
|
+
providerFeeUsd: prepareSendQuote.providerFeeUsd ?? null,
|
|
897
|
+
providerFeeUsdDisplay: prepareSendQuote.providerFeeUsdDisplay ?? "",
|
|
898
|
+
trailsFeeUsd: prepareSendQuote.trailsFeeUsd ?? null,
|
|
899
|
+
trailsFeeUsdDisplay: prepareSendQuote.trailsFeeUsdDisplay ?? "",
|
|
900
|
+
totalProviderFeesUsd: prepareSendQuote.totalProviderFeesUsd ?? null,
|
|
901
|
+
totalProviderFeesUsdDisplay:
|
|
902
|
+
prepareSendQuote.totalProviderFeesUsdDisplay ?? "",
|
|
903
|
+
noSufficientBalance: prepareSendQuote.noSufficientBalance ?? false,
|
|
694
904
|
}
|
|
695
905
|
|
|
696
|
-
const
|
|
906
|
+
const sendTransaction = async (
|
|
907
|
+
options?: SendOptions,
|
|
908
|
+
): Promise<SwapReturn> => {
|
|
909
|
+
// Use fee option from send() call if provided, otherwise fall back to prop value
|
|
910
|
+
const effectiveFeeOption =
|
|
911
|
+
options?.selectedFeeOption !== undefined
|
|
912
|
+
? options.selectedFeeOption
|
|
913
|
+
: (selectedFeeOption ?? null)
|
|
914
|
+
|
|
697
915
|
const {
|
|
698
916
|
depositUserTxnReceipt,
|
|
699
917
|
destinationIntentTransaction,
|
|
700
918
|
totalCompletionSeconds,
|
|
701
|
-
} = await
|
|
702
|
-
selectedFeeOption:
|
|
919
|
+
} = await prepareSendSend({
|
|
920
|
+
selectedFeeOption: effectiveFeeOption,
|
|
921
|
+
onOriginSend: options?.onOriginSend,
|
|
703
922
|
})
|
|
704
923
|
|
|
705
924
|
return {
|
|
@@ -725,7 +944,8 @@ export function useQuote({
|
|
|
725
944
|
|
|
726
945
|
return {
|
|
727
946
|
quote,
|
|
728
|
-
|
|
947
|
+
send: sendTransaction,
|
|
948
|
+
feeOptions: prepareSendFeeOptions?.feeOptions ?? [],
|
|
729
949
|
}
|
|
730
950
|
} catch (error) {
|
|
731
951
|
logger.console.error(
|
|
@@ -741,8 +961,8 @@ export function useQuote({
|
|
|
741
961
|
trailsClient &&
|
|
742
962
|
fromTokenAddress &&
|
|
743
963
|
toTokenAddress &&
|
|
744
|
-
|
|
745
|
-
|
|
964
|
+
debouncedSwapAmount &&
|
|
965
|
+
effectiveToAddress &&
|
|
746
966
|
fromChainId &&
|
|
747
967
|
toChainId &&
|
|
748
968
|
indexerGatewayClient &&
|
|
@@ -758,9 +978,22 @@ export function useQuote({
|
|
|
758
978
|
refetchOnReconnect: true, // Refetch when network reconnects
|
|
759
979
|
})
|
|
760
980
|
|
|
981
|
+
const sendFn = data?.send || null
|
|
982
|
+
|
|
983
|
+
// Create deprecated swap wrapper
|
|
984
|
+
const swapFn = sendFn
|
|
985
|
+
? async (options?: SendOptions) => {
|
|
986
|
+
logger.console.warn(
|
|
987
|
+
"[trails-sdk] [useQuote] Warning: 'swap' is deprecated, use 'send' instead",
|
|
988
|
+
)
|
|
989
|
+
return sendFn(options)
|
|
990
|
+
}
|
|
991
|
+
: null
|
|
992
|
+
|
|
761
993
|
return {
|
|
762
994
|
quote: data?.quote || null,
|
|
763
|
-
|
|
995
|
+
send: sendFn,
|
|
996
|
+
swap: swapFn,
|
|
764
997
|
isLoadingQuote: isLoading,
|
|
765
998
|
quoteError: error,
|
|
766
999
|
quoteErrorPrettified: getPrettifiedErrorMessage(error),
|
|
@@ -783,5 +1016,10 @@ export function useQuote({
|
|
|
783
1016
|
abortControllerRegistry.getAll(),
|
|
784
1017
|
)
|
|
785
1018
|
},
|
|
1019
|
+
feeOptions: data?.feeOptions || [],
|
|
1020
|
+
// Contract detection results
|
|
1021
|
+
isRecipientContract: isRecipientContract ?? null,
|
|
1022
|
+
isSenderContractOnOrigin: isSenderContractOnOrigin ?? null,
|
|
1023
|
+
isSenderContractOnDestination: isSenderContractOnDestination ?? null,
|
|
786
1024
|
}
|
|
787
1025
|
}
|