0xtrails 0.2.5 → 0.2.6
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/abortController.d.ts +8 -0
- package/dist/abortController.d.ts.map +1 -0
- package/dist/{ccip-CXlshvBY.js → ccip-Xjh9d1gb.js} +7 -7
- package/dist/constants.d.ts +2 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/fees.d.ts +19 -0
- package/dist/fees.d.ts.map +1 -0
- package/dist/{index-_QuyGrjU.js → index-BnhdZ8Ho.js} +34769 -34247
- package/dist/index.js +726 -520
- package/dist/prepareSend.d.ts +11 -77
- package/dist/prepareSend.d.ts.map +1 -1
- package/dist/transactions.d.ts +4 -2
- package/dist/transactions.d.ts.map +1 -1
- package/dist/widget/components/AccountIntentTransactionHistoryButton.d.ts +4 -0
- package/dist/widget/components/AccountIntentTransactionHistoryButton.d.ts.map +1 -0
- package/dist/widget/components/AccountSettings.d.ts.map +1 -1
- package/dist/widget/components/ChainFilterDropdown.d.ts.map +1 -1
- package/dist/widget/components/ClassicSwap.d.ts +2 -2
- package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
- package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
- package/dist/widget/components/DynamicInputStyles.d.ts +18 -0
- package/dist/widget/components/DynamicInputStyles.d.ts.map +1 -0
- package/dist/widget/components/Earn.d.ts +2 -2
- package/dist/widget/components/Earn.d.ts.map +1 -1
- package/dist/widget/components/ErrorAnimationIcon.d.ts +2 -0
- package/dist/widget/components/ErrorAnimationIcon.d.ts.map +1 -0
- package/dist/widget/components/FeeBreakdown.d.ts +9 -0
- package/dist/widget/components/FeeBreakdown.d.ts.map +1 -0
- package/dist/widget/components/Fund.d.ts +2 -2
- package/dist/widget/components/Fund.d.ts.map +1 -1
- package/dist/widget/components/FundSwap.d.ts +2 -2
- package/dist/widget/components/FundSwap.d.ts.map +1 -1
- package/dist/widget/components/FundingMethodSelectorButton.d.ts.map +1 -1
- package/dist/widget/components/Identicon.d.ts.map +1 -1
- package/dist/widget/components/MeshConnectExchanges.d.ts +0 -3
- package/dist/widget/components/MeshConnectExchanges.d.ts.map +1 -1
- package/dist/widget/components/Modal.d.ts.map +1 -1
- package/dist/widget/components/Pay.d.ts +2 -2
- package/dist/widget/components/Pay.d.ts.map +1 -1
- package/dist/widget/components/PoolDeposit.d.ts +3 -2
- package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
- package/dist/widget/components/PoolWithdraw.d.ts +3 -2
- package/dist/widget/components/PoolWithdraw.d.ts.map +1 -1
- package/dist/widget/components/QuoteDetails.d.ts +1 -0
- package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
- package/dist/widget/components/Receipt.d.ts.map +1 -1
- package/dist/widget/components/RecipientSelectorButton.d.ts.map +1 -1
- package/dist/widget/components/ScreenHeader.d.ts.map +1 -1
- package/dist/widget/components/Swap.d.ts +2 -2
- package/dist/widget/components/Swap.d.ts.map +1 -1
- package/dist/widget/components/ThemeProvider.d.ts.map +1 -1
- package/dist/widget/components/TokenDisplayNonSelectable.d.ts +11 -0
- package/dist/widget/components/TokenDisplayNonSelectable.d.ts.map +1 -0
- package/dist/widget/components/TokenSelector.d.ts.map +1 -1
- package/dist/widget/components/TokenSelectorButton.d.ts.map +1 -1
- package/dist/widget/components/Tooltip.d.ts +9 -0
- package/dist/widget/components/Tooltip.d.ts.map +1 -0
- package/dist/widget/components/WaasFeeOptions.d.ts +1 -0
- package/dist/widget/components/WaasFeeOptions.d.ts.map +1 -1
- package/dist/widget/components/WalletConfirmation.d.ts.map +1 -1
- package/dist/widget/components/WalletConnect.d.ts.map +1 -1
- package/dist/widget/css/compiled.css +1 -1
- package/dist/widget/hooks/useQuote.d.ts +83 -0
- package/dist/widget/hooks/useQuote.d.ts.map +1 -0
- package/dist/widget/hooks/useSendForm.d.ts +2 -2
- package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
- package/dist/widget/index.js +2 -2
- package/dist/widget/widget.d.ts +5 -0
- package/dist/widget/widget.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/abortController.ts +35 -0
- package/src/constants.ts +3 -0
- package/src/fees.ts +199 -0
- package/src/prepareSend.ts +225 -398
- package/src/trails.ts +3 -3
- package/src/transactions.ts +62 -18
- package/src/widget/compiled.css +1 -1
- package/src/widget/components/AccountIntentTransactionHistoryButton.tsx +22 -0
- package/src/widget/components/AccountSettings.tsx +48 -36
- package/src/widget/components/ChainFilterDropdown.tsx +24 -3
- package/src/widget/components/ClassicSwap.tsx +24 -62
- package/src/widget/components/ConnectWallet.tsx +4 -1
- package/src/widget/components/ConnectedWallets.tsx +21 -21
- package/src/widget/components/DynamicInputStyles.tsx +76 -0
- package/src/widget/components/Earn.tsx +34 -29
- package/src/widget/components/ErrorAnimationIcon.tsx +130 -0
- package/src/widget/components/FeeBreakdown.tsx +155 -0
- package/src/widget/components/Fund.tsx +10 -26
- package/src/widget/components/FundSwap.tsx +2 -2
- package/src/widget/components/FundingMethodSelectorButton.tsx +24 -14
- package/src/widget/components/Identicon.tsx +164 -95
- package/src/widget/components/MeshConnectExchanges.tsx +2 -15
- package/src/widget/components/Modal.tsx +0 -12
- package/src/widget/components/Pay.tsx +65 -63
- package/src/widget/components/PoolDeposit.tsx +206 -230
- package/src/widget/components/PoolWithdraw.tsx +219 -238
- package/src/widget/components/PriceImpactWarning.tsx +1 -1
- package/src/widget/components/QuoteDetails.tsx +25 -8
- package/src/widget/components/Receipt.tsx +16 -2
- package/src/widget/components/RecipientSelectorButton.tsx +7 -5
- package/src/widget/components/Recipients.tsx +1 -1
- package/src/widget/components/ScreenHeader.tsx +60 -36
- package/src/widget/components/Swap.tsx +2 -2
- package/src/widget/components/ThemeProvider.tsx +2 -1
- package/src/widget/components/TokenDisplayNonSelectable.tsx +40 -0
- package/src/widget/components/TokenImage.tsx +1 -1
- package/src/widget/components/TokenSelector.tsx +62 -53
- package/src/widget/components/TokenSelectorButton.tsx +38 -15
- package/src/widget/components/Tooltip.tsx +51 -0
- package/src/widget/components/TransferPendingVertical.tsx +1 -1
- package/src/widget/components/WaasFeeOptions.tsx +124 -5
- package/src/widget/components/WalletConfirmation.tsx +23 -13
- package/src/widget/components/WalletConnect.tsx +93 -29
- package/src/widget/hooks/useQuote.ts +413 -0
- package/src/widget/hooks/useSendForm.ts +8 -4
- package/src/widget/widget.tsx +175 -190
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
import { useQuery } from "@tanstack/react-query"
|
|
2
|
+
import { useRef } from "react"
|
|
3
|
+
import type { TransactionReceipt } from "viem"
|
|
4
|
+
import { zeroAddress } from "viem"
|
|
5
|
+
import { useAPIClient } from "../../apiClient.js"
|
|
6
|
+
import { useIndexerGatewayClient } from "../../indexerClient.js"
|
|
7
|
+
import { getTokenBalancesWithPrices } from "../../tokenBalances.js"
|
|
8
|
+
import { logger } from "../../logger.js"
|
|
9
|
+
import { getSequenceEnv } from "../../config.js"
|
|
10
|
+
import { useSupportedTokens } from "../../tokens.js"
|
|
11
|
+
import { useTrailsClient } from "../../trailsClient.js"
|
|
12
|
+
import { getExplorerUrl } from "../../explorer.js"
|
|
13
|
+
import { getFullErrorMessage } from "../../error.js"
|
|
14
|
+
import { prepareSend } from "../../prepareSend.js"
|
|
15
|
+
import { abortControllerRegistry } from "../../abortController.js"
|
|
16
|
+
import { useRelayers } from "../../relayer.js"
|
|
17
|
+
import { TradeType } from "../../prepareSend.js"
|
|
18
|
+
import type { Chain } from "../../chains.js"
|
|
19
|
+
import type { SupportedToken } from "../../tokens.js"
|
|
20
|
+
import type { TransactionState } from "../../transactions.js"
|
|
21
|
+
import type { PrepareSendFees } from "../../prepareSend.js"
|
|
22
|
+
import type { MetaTxnReceipt } from "../../relayer.js"
|
|
23
|
+
import type { RelayerEnv } from "../../relayer.js"
|
|
24
|
+
import type { SequenceEnv } from "../../config.js"
|
|
25
|
+
|
|
26
|
+
export type UseQuoteProps = {
|
|
27
|
+
walletClient?: any // TODO: fix this, has to do with viem/wagmi versions
|
|
28
|
+
fromTokenAddress?: string | null
|
|
29
|
+
fromChainId?: number | null
|
|
30
|
+
toTokenAddress?: string | null
|
|
31
|
+
toChainId?: number | null
|
|
32
|
+
toCalldata?: string | null
|
|
33
|
+
swapAmount?: string | bigint
|
|
34
|
+
toRecipient?: string | null
|
|
35
|
+
tradeType?: TradeType | null
|
|
36
|
+
slippageTolerance?: string | number | null
|
|
37
|
+
onStatusUpdate?: ((transactionStates: TransactionState[]) => void) | null
|
|
38
|
+
quoteProvider?: string | null
|
|
39
|
+
gasless?: boolean
|
|
40
|
+
paymasterUrl?: string
|
|
41
|
+
selectedFeeToken?: {
|
|
42
|
+
tokenAddress: string
|
|
43
|
+
tokenSymbol?: string
|
|
44
|
+
} | null
|
|
45
|
+
abortSignal?: AbortSignal
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type SwapReturn = {
|
|
49
|
+
originTransaction: {
|
|
50
|
+
transactionHash?: string | null
|
|
51
|
+
explorerUrl?: string | null
|
|
52
|
+
receipt: TransactionReceipt | MetaTxnReceipt | null
|
|
53
|
+
}
|
|
54
|
+
destinationTransaction: {
|
|
55
|
+
transactionHash?: string | null
|
|
56
|
+
explorerUrl?: string | null
|
|
57
|
+
receipt: MetaTxnReceipt | null
|
|
58
|
+
}
|
|
59
|
+
totalCompletionSeconds?: number
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type QuoteProviderInfo = {
|
|
63
|
+
id: string
|
|
64
|
+
name: string
|
|
65
|
+
url: string
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export type Quote = {
|
|
69
|
+
fromAmount: string
|
|
70
|
+
fromAmountMin: string
|
|
71
|
+
toAmount: string
|
|
72
|
+
toAmountMin: string
|
|
73
|
+
originToken: SupportedToken
|
|
74
|
+
destinationToken: SupportedToken
|
|
75
|
+
originChain: Chain
|
|
76
|
+
destinationChain: Chain
|
|
77
|
+
fees: PrepareSendFees
|
|
78
|
+
slippageTolerance: string
|
|
79
|
+
priceImpact: string
|
|
80
|
+
completionEstimateSeconds: number
|
|
81
|
+
transactionStates?: TransactionState[]
|
|
82
|
+
originTokenRate?: string
|
|
83
|
+
quoteProvider?: QuoteProviderInfo | null
|
|
84
|
+
destinationTokenRate?: string
|
|
85
|
+
fromAmountUsdDisplay?: string
|
|
86
|
+
toAmountUsdDisplay?: string
|
|
87
|
+
gasCostUsd?: number
|
|
88
|
+
gasCostUsdDisplay?: string
|
|
89
|
+
gasCost?: string
|
|
90
|
+
gasCostFormatted?: string
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export type UseQuoteReturn = {
|
|
94
|
+
quote: Quote | null
|
|
95
|
+
swap: (() => Promise<SwapReturn | null>) | null
|
|
96
|
+
isLoadingQuote: boolean
|
|
97
|
+
quoteError: unknown
|
|
98
|
+
refetchQuote: () => void
|
|
99
|
+
abort: () => void
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function useQuote({
|
|
103
|
+
walletClient,
|
|
104
|
+
fromTokenAddress,
|
|
105
|
+
fromChainId,
|
|
106
|
+
toTokenAddress,
|
|
107
|
+
toChainId,
|
|
108
|
+
swapAmount,
|
|
109
|
+
tradeType,
|
|
110
|
+
toRecipient,
|
|
111
|
+
toCalldata,
|
|
112
|
+
slippageTolerance,
|
|
113
|
+
onStatusUpdate,
|
|
114
|
+
quoteProvider,
|
|
115
|
+
gasless,
|
|
116
|
+
paymasterUrl,
|
|
117
|
+
selectedFeeToken,
|
|
118
|
+
relayerEnv,
|
|
119
|
+
nodeGatewayEnv,
|
|
120
|
+
abortSignal: externalAbortSignal,
|
|
121
|
+
}: Partial<
|
|
122
|
+
UseQuoteProps & { relayerEnv?: RelayerEnv; nodeGatewayEnv?: SequenceEnv }
|
|
123
|
+
> = {}): UseQuoteReturn {
|
|
124
|
+
// Set node gateway environment override for this quote session
|
|
125
|
+
if (nodeGatewayEnv) {
|
|
126
|
+
;(globalThis as any).__testNodeGatewayEnv = nodeGatewayEnv
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Create abort controller for this hook instance
|
|
130
|
+
const abortControllerRef = useRef<AbortController>(new AbortController())
|
|
131
|
+
|
|
132
|
+
// Combine external abort signal with internal abort controller
|
|
133
|
+
const combinedAbortSignal = externalAbortSignal
|
|
134
|
+
? (() => {
|
|
135
|
+
const controller = new AbortController()
|
|
136
|
+
|
|
137
|
+
// Listen to external abort signal
|
|
138
|
+
externalAbortSignal.addEventListener("abort", () => {
|
|
139
|
+
controller.abort()
|
|
140
|
+
abortControllerRef.current?.abort()
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
// Listen to internal abort controller
|
|
144
|
+
abortControllerRef.current?.signal.addEventListener("abort", () => {
|
|
145
|
+
controller.abort()
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
return controller.signal
|
|
149
|
+
})()
|
|
150
|
+
: abortControllerRef.current.signal
|
|
151
|
+
|
|
152
|
+
const apiClient = useAPIClient()
|
|
153
|
+
const trailsClient = useTrailsClient()
|
|
154
|
+
const { getRelayer } = useRelayers({
|
|
155
|
+
env: relayerEnv || (getSequenceEnv() as RelayerEnv),
|
|
156
|
+
})
|
|
157
|
+
const indexerGatewayClient = useIndexerGatewayClient()
|
|
158
|
+
|
|
159
|
+
const { supportedTokens } = useSupportedTokens()
|
|
160
|
+
|
|
161
|
+
const { data, isLoading, error, refetch } = useQuery({
|
|
162
|
+
queryKey: [
|
|
163
|
+
"quote",
|
|
164
|
+
fromTokenAddress,
|
|
165
|
+
fromChainId,
|
|
166
|
+
toTokenAddress,
|
|
167
|
+
toChainId,
|
|
168
|
+
swapAmount?.toString(),
|
|
169
|
+
toRecipient,
|
|
170
|
+
toCalldata,
|
|
171
|
+
tradeType,
|
|
172
|
+
slippageTolerance,
|
|
173
|
+
quoteProvider,
|
|
174
|
+
],
|
|
175
|
+
queryFn: async () => {
|
|
176
|
+
try {
|
|
177
|
+
// Reset the abort controller for new queries to ensure fresh state
|
|
178
|
+
if (abortControllerRef.current.signal.aborted) {
|
|
179
|
+
logger.console.log(
|
|
180
|
+
"[trails-sdk] Resetting aborted controller for new query",
|
|
181
|
+
)
|
|
182
|
+
abortControllerRef.current = new AbortController()
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (
|
|
186
|
+
!walletClient ||
|
|
187
|
+
!apiClient ||
|
|
188
|
+
!trailsClient ||
|
|
189
|
+
!fromTokenAddress ||
|
|
190
|
+
!toTokenAddress ||
|
|
191
|
+
!swapAmount ||
|
|
192
|
+
!toRecipient ||
|
|
193
|
+
!fromChainId ||
|
|
194
|
+
!toChainId ||
|
|
195
|
+
!indexerGatewayClient
|
|
196
|
+
) {
|
|
197
|
+
return null
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Get token balance using async method
|
|
201
|
+
const { balances } = await getTokenBalancesWithPrices({
|
|
202
|
+
account: walletClient.account!.address,
|
|
203
|
+
indexerGatewayClient,
|
|
204
|
+
apiClient,
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
const originTokenBalance = balances.find(
|
|
208
|
+
(b) =>
|
|
209
|
+
b.chainId === fromChainId &&
|
|
210
|
+
(b.contractAddress?.toLowerCase() ===
|
|
211
|
+
fromTokenAddress.toLowerCase() ||
|
|
212
|
+
(!b.contractAddress && fromTokenAddress === zeroAddress)),
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
const originTokenBalanceAmount = originTokenBalance?.balance ?? "0"
|
|
216
|
+
const destinationRelayer = getRelayer(toChainId)
|
|
217
|
+
const originRelayer = getRelayer(fromChainId)
|
|
218
|
+
|
|
219
|
+
// Note: Disable this check for now to allow fetching a quote even when the origin balance is zero
|
|
220
|
+
// if (originTokenBalanceAmount === "0") {
|
|
221
|
+
// return null
|
|
222
|
+
// }
|
|
223
|
+
|
|
224
|
+
// logger.console.log("supportedTokens", supportedTokens)
|
|
225
|
+
|
|
226
|
+
const originToken = supportedTokens?.find(
|
|
227
|
+
(token) =>
|
|
228
|
+
token.contractAddress?.toLowerCase() ===
|
|
229
|
+
fromTokenAddress?.toLowerCase() && token.chainId === fromChainId,
|
|
230
|
+
)
|
|
231
|
+
const destinationToken = supportedTokens?.find(
|
|
232
|
+
(token) =>
|
|
233
|
+
token.contractAddress?.toLowerCase() ===
|
|
234
|
+
toTokenAddress?.toLowerCase() && token.chainId === toChainId,
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
const sourceTokenDecimals = originToken?.decimals
|
|
238
|
+
if (!sourceTokenDecimals) {
|
|
239
|
+
logger.console.error(
|
|
240
|
+
"[trails-sdk] [useQuote] Missing source token decimals:",
|
|
241
|
+
{
|
|
242
|
+
originToken,
|
|
243
|
+
fromTokenAddress,
|
|
244
|
+
fromChainId,
|
|
245
|
+
},
|
|
246
|
+
)
|
|
247
|
+
throw new Error("Source token decimals not found")
|
|
248
|
+
}
|
|
249
|
+
const destinationTokenDecimals = destinationToken?.decimals
|
|
250
|
+
if (!destinationTokenDecimals) {
|
|
251
|
+
logger.console.error(
|
|
252
|
+
"[trails-sdk] Missing destination token decimals:",
|
|
253
|
+
{
|
|
254
|
+
destinationToken,
|
|
255
|
+
toTokenAddress,
|
|
256
|
+
toChainId,
|
|
257
|
+
},
|
|
258
|
+
)
|
|
259
|
+
throw new Error("Destination token decimals not found")
|
|
260
|
+
}
|
|
261
|
+
const destinationTokenSymbol = destinationToken?.symbol ?? ""
|
|
262
|
+
const originTokenSymbol = originToken?.symbol ?? ""
|
|
263
|
+
|
|
264
|
+
const options = {
|
|
265
|
+
account: walletClient.account!,
|
|
266
|
+
originTokenAddress: fromTokenAddress,
|
|
267
|
+
originChainId: fromChainId,
|
|
268
|
+
originTokenBalance: originTokenBalanceAmount,
|
|
269
|
+
destinationChainId: toChainId,
|
|
270
|
+
recipient: toRecipient,
|
|
271
|
+
destinationTokenAddress: toTokenAddress,
|
|
272
|
+
swapAmount: swapAmount.toString(),
|
|
273
|
+
tradeType: tradeType ?? TradeType.EXACT_OUTPUT,
|
|
274
|
+
originTokenSymbol: originTokenSymbol,
|
|
275
|
+
destinationTokenSymbol: destinationTokenSymbol,
|
|
276
|
+
destinationCalldata: toCalldata as string,
|
|
277
|
+
client: walletClient,
|
|
278
|
+
apiClient,
|
|
279
|
+
trailsClient,
|
|
280
|
+
originRelayer,
|
|
281
|
+
destinationRelayer,
|
|
282
|
+
sourceTokenDecimals,
|
|
283
|
+
destinationTokenDecimals,
|
|
284
|
+
fee: "0",
|
|
285
|
+
dryMode: false,
|
|
286
|
+
onTransactionStateChange: onStatusUpdate ?? (() => {}),
|
|
287
|
+
slippageTolerance: slippageTolerance?.toString(),
|
|
288
|
+
quoteProvider: quoteProvider,
|
|
289
|
+
gasless: gasless ?? false,
|
|
290
|
+
paymasterUrl: paymasterUrl,
|
|
291
|
+
selectedFeeToken: selectedFeeToken ?? undefined,
|
|
292
|
+
abortSignal: combinedAbortSignal,
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
logger.console.log("[trails-sdk] options", options)
|
|
296
|
+
|
|
297
|
+
const { quote: prepareSendQuote, send } = await prepareSend(options)
|
|
298
|
+
|
|
299
|
+
const quote = {
|
|
300
|
+
fromAmount: prepareSendQuote.originAmount,
|
|
301
|
+
toAmount: prepareSendQuote.destinationAmount,
|
|
302
|
+
fromAmountMin: prepareSendQuote.originAmountMin,
|
|
303
|
+
toAmountMin: prepareSendQuote.destinationAmountMin,
|
|
304
|
+
originToken: prepareSendQuote.originToken,
|
|
305
|
+
destinationToken: prepareSendQuote.destinationToken,
|
|
306
|
+
originChain: prepareSendQuote.originChain,
|
|
307
|
+
destinationChain: prepareSendQuote.destinationChain,
|
|
308
|
+
fees: prepareSendQuote.fees,
|
|
309
|
+
priceImpact: prepareSendQuote.priceImpact,
|
|
310
|
+
completionEstimateSeconds: prepareSendQuote.completionEstimateSeconds,
|
|
311
|
+
slippageTolerance: prepareSendQuote.slippageTolerance,
|
|
312
|
+
transactionStates: prepareSendQuote.transactionStates,
|
|
313
|
+
originTokenRate: prepareSendQuote.originTokenRate,
|
|
314
|
+
destinationTokenRate: prepareSendQuote.destinationTokenRate,
|
|
315
|
+
quoteProvider: prepareSendQuote.quoteProvider,
|
|
316
|
+
fromAmountUsdDisplay:
|
|
317
|
+
prepareSendQuote.originAmountUsdDisplay ?? undefined,
|
|
318
|
+
toAmountUsdDisplay:
|
|
319
|
+
prepareSendQuote.destinationAmountUsdDisplay ?? undefined,
|
|
320
|
+
gasCostUsd: prepareSendQuote.gasCostUsd ?? undefined,
|
|
321
|
+
gasCostUsdDisplay: prepareSendQuote.gasCostUsdDisplay ?? undefined,
|
|
322
|
+
gasCost: prepareSendQuote.gasCost ?? undefined,
|
|
323
|
+
gasCostFormatted: prepareSendQuote.gasCostFormatted ?? undefined,
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const swap = async (): Promise<SwapReturn> => {
|
|
327
|
+
const {
|
|
328
|
+
originUserTxReceipt,
|
|
329
|
+
destinationMetaTxnReceipt,
|
|
330
|
+
totalCompletionSeconds,
|
|
331
|
+
} = await send({
|
|
332
|
+
selectedFeeToken: selectedFeeToken ?? undefined,
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
return {
|
|
336
|
+
originTransaction: {
|
|
337
|
+
transactionHash: originUserTxReceipt?.transactionHash,
|
|
338
|
+
explorerUrl: getExplorerUrl({
|
|
339
|
+
txHash: originUserTxReceipt?.transactionHash as string,
|
|
340
|
+
chainId: fromChainId,
|
|
341
|
+
}),
|
|
342
|
+
receipt: originUserTxReceipt,
|
|
343
|
+
},
|
|
344
|
+
destinationTransaction: {
|
|
345
|
+
transactionHash: destinationMetaTxnReceipt?.txnHash,
|
|
346
|
+
explorerUrl: getExplorerUrl({
|
|
347
|
+
txHash: destinationMetaTxnReceipt?.txnHash as string,
|
|
348
|
+
chainId: toChainId,
|
|
349
|
+
}),
|
|
350
|
+
receipt: destinationMetaTxnReceipt,
|
|
351
|
+
},
|
|
352
|
+
totalCompletionSeconds,
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return {
|
|
357
|
+
quote,
|
|
358
|
+
swap,
|
|
359
|
+
}
|
|
360
|
+
} catch (error) {
|
|
361
|
+
logger.console.error(
|
|
362
|
+
"[trails-sdk] [useQuote] Error getting quote:",
|
|
363
|
+
error,
|
|
364
|
+
)
|
|
365
|
+
throw getFullErrorMessage(error)
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
// Prevent unnecessary refetching
|
|
369
|
+
enabled: Boolean(
|
|
370
|
+
walletClient &&
|
|
371
|
+
apiClient &&
|
|
372
|
+
fromTokenAddress &&
|
|
373
|
+
toTokenAddress &&
|
|
374
|
+
swapAmount &&
|
|
375
|
+
toRecipient &&
|
|
376
|
+
fromChainId &&
|
|
377
|
+
toChainId &&
|
|
378
|
+
indexerGatewayClient,
|
|
379
|
+
),
|
|
380
|
+
staleTime: 30 * 1000, // Consider data fresh for 30 seconds
|
|
381
|
+
refetchOnWindowFocus: false, // Don't refetch when window regains focus
|
|
382
|
+
refetchOnMount: false, // Don't refetch on component remount if data exists
|
|
383
|
+
refetchInterval: false, // Disable automatic polling
|
|
384
|
+
retry: 2, // Limit retry attempts
|
|
385
|
+
refetchOnReconnect: true, // Refetch when network reconnects
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
return {
|
|
389
|
+
quote: data?.quote || null,
|
|
390
|
+
swap: data?.swap || null,
|
|
391
|
+
isLoadingQuote: isLoading,
|
|
392
|
+
quoteError: error,
|
|
393
|
+
refetchQuote: () => refetch(),
|
|
394
|
+
abort: () => {
|
|
395
|
+
logger.console.log("[trails-sdk] useQuote abort() called")
|
|
396
|
+
logger.console.log(
|
|
397
|
+
"[trails-sdk] Active operations before abort:",
|
|
398
|
+
abortControllerRegistry.getAll(),
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
// Abort the internal abort controller
|
|
402
|
+
abortControllerRef.current.abort()
|
|
403
|
+
|
|
404
|
+
// Also abort all active polling operations in prepareSend
|
|
405
|
+
abortControllerRegistry.abortAll()
|
|
406
|
+
|
|
407
|
+
logger.console.log(
|
|
408
|
+
"[trails-sdk] Abort completed, active operations:",
|
|
409
|
+
abortControllerRegistry.getAll(),
|
|
410
|
+
)
|
|
411
|
+
},
|
|
412
|
+
}
|
|
413
|
+
}
|
|
@@ -84,14 +84,14 @@ export type OnCompleteProps = {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
export type UseSendProps = {
|
|
87
|
-
account
|
|
87
|
+
account?: Account
|
|
88
88
|
toAmount?: string
|
|
89
89
|
toRecipient?: string
|
|
90
90
|
toChainId?: number
|
|
91
91
|
toToken?: string
|
|
92
92
|
toCalldata?: string
|
|
93
93
|
refundAddress?: string
|
|
94
|
-
walletClient
|
|
94
|
+
walletClient?: WalletClient
|
|
95
95
|
onTransactionStateChange: (transactionStates: TransactionState[]) => void
|
|
96
96
|
onError: (error: Error | string | null) => void
|
|
97
97
|
onWaitingForWalletConfirm: (quote: PrepareSendQuote) => void
|
|
@@ -687,6 +687,7 @@ export function useSendForm({
|
|
|
687
687
|
const getQuote = useCallback(async () => {
|
|
688
688
|
// Only get quote if all required inputs are present
|
|
689
689
|
if (
|
|
690
|
+
!account ||
|
|
690
691
|
!amount ||
|
|
691
692
|
!destinationTokenAddress ||
|
|
692
693
|
!isValidRecipient ||
|
|
@@ -945,6 +946,7 @@ export function useSendForm({
|
|
|
945
946
|
selectedToken?.chainId,
|
|
946
947
|
selectedToken?.balance,
|
|
947
948
|
selectedToken?.tokenPriceUsd,
|
|
949
|
+
recipient, // Add recipient to trigger quote re-fetch when it changes
|
|
948
950
|
// selectedFeeToken is passed to send() at execution time, not needed here
|
|
949
951
|
])
|
|
950
952
|
|
|
@@ -958,7 +960,8 @@ export function useSendForm({
|
|
|
958
960
|
}, [prepareSendResult, toAmountFormatted])
|
|
959
961
|
|
|
960
962
|
const quotedDestinationAmountDisplay = useMemo(() => {
|
|
961
|
-
|
|
963
|
+
const formattedAmount = formatAmount(quotedDestinationAmount || 0)
|
|
964
|
+
return Number(formattedAmount) === 0 ? "0" : formattedAmount
|
|
962
965
|
}, [quotedDestinationAmount])
|
|
963
966
|
|
|
964
967
|
// Set raw fee options in the hook whenever prepareSendResult changes
|
|
@@ -1158,6 +1161,7 @@ export function useSendForm({
|
|
|
1158
1161
|
|
|
1159
1162
|
// Get button text based on recipient and calldata
|
|
1160
1163
|
const buttonText = useMemo(() => {
|
|
1164
|
+
if (!account?.address) return "Connect your wallet"
|
|
1161
1165
|
if (!selectedToken) return "Select a token"
|
|
1162
1166
|
if (!amount) return "Enter an amount"
|
|
1163
1167
|
if (!selectedDestToken?.symbol) return "Select a token"
|
|
@@ -1251,7 +1255,7 @@ export function useSendForm({
|
|
|
1251
1255
|
amount,
|
|
1252
1256
|
isValidRecipient,
|
|
1253
1257
|
recipient,
|
|
1254
|
-
account
|
|
1258
|
+
account?.address,
|
|
1255
1259
|
selectedDestToken?.symbol,
|
|
1256
1260
|
isWaitingForWalletConfirm,
|
|
1257
1261
|
isSubmitting,
|