0xtrails 0.2.4 → 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/aave.d.ts +8 -0
- package/dist/aave.d.ts.map +1 -1
- package/dist/abortController.d.ts +8 -0
- package/dist/abortController.d.ts.map +1 -0
- package/dist/{ccip-BlV1Mry3.js → ccip-Xjh9d1gb.js} +7 -7
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/constants.d.ts +3 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/error.d.ts +1 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/estimate.d.ts +52 -0
- package/dist/estimate.d.ts.map +1 -1
- package/dist/fees.d.ts +19 -0
- package/dist/fees.d.ts.map +1 -0
- package/dist/{index-BNWCIGfQ.js → index-BnhdZ8Ho.js} +76406 -75798
- package/dist/index.js +726 -520
- package/dist/intents.d.ts +40 -0
- package/dist/intents.d.ts.map +1 -1
- package/dist/metaTxnMonitor.d.ts +3 -3
- package/dist/metaTxnMonitor.d.ts.map +1 -1
- package/dist/metaTxns.d.ts +3 -3
- package/dist/metaTxns.d.ts.map +1 -1
- package/dist/morpho.d.ts +8 -0
- package/dist/morpho.d.ts.map +1 -1
- package/dist/prepareSend.d.ts +19 -75
- package/dist/prepareSend.d.ts.map +1 -1
- package/dist/queryParams.d.ts.map +1 -1
- package/dist/relayer.d.ts +6 -6
- package/dist/relayer.d.ts.map +1 -1
- package/dist/sequenceWallet.d.ts +2 -2
- package/dist/sequenceWallet.d.ts.map +1 -1
- package/dist/tokens.d.ts.map +1 -1
- package/dist/transactions.d.ts +4 -2
- package/dist/transactions.d.ts.map +1 -1
- package/dist/wallets.d.ts.map +1 -1
- package/dist/widget/components/AccountActionsDropdown.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 +4 -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/ConnectedWallets.d.ts +4 -0
- package/dist/widget/components/ConnectedWallets.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/FundMethods.d.ts.map +1 -1
- package/dist/widget/components/{FundSendForm.d.ts → FundSwap.d.ts} +13 -7
- package/dist/widget/components/FundSwap.d.ts.map +1 -0
- package/dist/widget/components/FundingMethodSelectorButton.d.ts +4 -0
- package/dist/widget/components/FundingMethodSelectorButton.d.ts.map +1 -0
- 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/PercentageMaxButtons.d.ts +12 -0
- package/dist/widget/components/PercentageMaxButtons.d.ts.map +1 -0
- package/dist/widget/components/{PaySendForm.d.ts → PoolDeposit.d.ts} +14 -36
- package/dist/widget/components/PoolDeposit.d.ts.map +1 -0
- package/dist/widget/components/{SimpleSwap.d.ts → PoolWithdraw.d.ts} +19 -10
- package/dist/widget/components/PoolWithdraw.d.ts.map +1 -0
- 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/Receive.d.ts.map +1 -1
- package/dist/widget/components/RecipientSelectorButton.d.ts +4 -0
- package/dist/widget/components/RecipientSelectorButton.d.ts.map +1 -0
- package/dist/widget/components/Recipients.d.ts.map +1 -1
- package/dist/widget/components/RequiredPropsError.d.ts +8 -0
- package/dist/widget/components/RequiredPropsError.d.ts.map +1 -0
- package/dist/widget/components/ScreenHeader.d.ts.map +1 -1
- package/dist/widget/components/SlippageToleranceSettings.d.ts.map +1 -1
- package/dist/widget/components/Swap.d.ts +3 -2
- package/dist/widget/components/Swap.d.ts.map +1 -1
- package/dist/widget/components/SwapSettings.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/TokenImage.d.ts +1 -0
- package/dist/widget/components/TokenImage.d.ts.map +1 -1
- package/dist/widget/components/TokenList.d.ts.map +1 -1
- package/dist/widget/components/TokenSelector.d.ts.map +1 -1
- package/dist/widget/components/TokenSelectorButton.d.ts +16 -0
- package/dist/widget/components/TokenSelectorButton.d.ts.map +1 -0
- package/dist/widget/components/Tooltip.d.ts +9 -0
- package/dist/widget/components/Tooltip.d.ts.map +1 -0
- package/dist/widget/components/UserPreferences.d.ts.map +1 -1
- package/dist/widget/components/WaasFeeOptions.d.ts +9 -0
- package/dist/widget/components/WaasFeeOptions.d.ts.map +1 -0
- package/dist/widget/components/WalletConfirmation.d.ts.map +1 -1
- package/dist/widget/components/WalletConnect.d.ts.map +1 -1
- package/dist/widget/components/WalletList.d.ts.map +1 -1
- package/dist/widget/css/compiled.css +2 -0
- package/dist/widget/css/index.css +554 -0
- package/dist/widget/hooks/useBack.d.ts +1 -0
- package/dist/widget/hooks/useBack.d.ts.map +1 -1
- package/dist/widget/hooks/useCheckout.d.ts +1 -1
- package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
- package/dist/widget/hooks/useCurrentScreen.d.ts +1 -1
- package/dist/widget/hooks/useCurrentScreen.d.ts.map +1 -1
- package/dist/widget/hooks/useDefaultTokenSelection.d.ts +3 -3
- package/dist/widget/hooks/useDefaultTokenSelection.d.ts.map +1 -1
- package/dist/widget/hooks/usePayMessage.d.ts.map +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/useSelectedFundMethod.d.ts +12 -0
- package/dist/widget/hooks/useSelectedFundMethod.d.ts.map +1 -0
- package/dist/widget/hooks/useSelectedRecipient.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/index.js +2 -2
- package/dist/widget/widget.d.ts +9 -4
- package/dist/widget/widget.d.ts.map +1 -1
- package/package.json +18 -12
- package/src/aave.ts +32 -0
- package/src/abortController.ts +35 -0
- package/src/config.ts +12 -4
- package/src/constants.ts +5 -0
- package/src/error.ts +19 -1
- package/src/estimate.ts +416 -5
- package/src/fees.ts +199 -0
- package/src/intents.ts +161 -11
- package/src/metaTxnMonitor.ts +3 -3
- package/src/metaTxns.ts +3 -5
- package/src/morpho.ts +32 -0
- package/src/prepareSend.ts +714 -550
- package/src/queryParams.ts +2 -1
- package/src/relayer.ts +11 -11
- package/src/sequenceWallet.ts +2 -2
- package/src/tokens.ts +7 -1
- package/src/trails.ts +3 -3
- package/src/transactions.ts +62 -18
- package/src/wallets.ts +8 -0
- package/src/widget/compiled.css +2 -2
- package/src/widget/components/AccountActionsDropdown.tsx +3 -13
- package/src/widget/components/AccountIntentTransactionHistoryButton.tsx +22 -0
- package/src/widget/components/AccountSettings.tsx +48 -54
- package/src/widget/components/ChainFilterDropdown.tsx +24 -3
- package/src/widget/components/ClassicSwap.tsx +131 -213
- package/src/widget/components/ConnectWallet.tsx +8 -38
- package/src/widget/components/ConnectedWallets.tsx +132 -77
- package/src/widget/components/DynamicInputStyles.tsx +76 -0
- package/src/widget/components/Earn.tsx +82 -593
- package/src/widget/components/ErrorAnimationIcon.tsx +130 -0
- package/src/widget/components/FeeBreakdown.tsx +155 -0
- package/src/widget/components/Fund.tsx +41 -108
- package/src/widget/components/FundMethods.tsx +82 -159
- package/src/widget/components/FundSwap.tsx +52 -0
- package/src/widget/components/FundingMethodSelectorButton.tsx +70 -0
- package/src/widget/components/Identicon.tsx +164 -95
- package/src/widget/components/MeshConnectExchanges.tsx +2 -15
- package/src/widget/components/Modal.tsx +0 -8
- package/src/widget/components/Pay.tsx +214 -237
- package/src/widget/components/PercentageMaxButtons.tsx +77 -0
- package/src/widget/components/PoolDeposit.tsx +569 -0
- package/src/widget/components/PoolWithdraw.tsx +884 -0
- package/src/widget/components/PriceImpactWarning.tsx +1 -1
- package/src/widget/components/QuoteDetails.tsx +43 -12
- package/src/widget/components/Receipt.tsx +16 -2
- package/src/widget/components/Receive.tsx +0 -2
- package/src/widget/components/RecipientSelectorButton.tsx +44 -0
- package/src/widget/components/Recipients.tsx +63 -157
- package/src/widget/components/RequiredPropsError.tsx +33 -0
- package/src/widget/components/ScreenHeader.tsx +62 -34
- package/src/widget/components/SlippageToleranceSettings.tsx +2 -1
- package/src/widget/components/Swap.tsx +4 -45
- package/src/widget/components/SwapSettings.tsx +2 -14
- package/src/widget/components/ThemeProvider.tsx +2 -1
- package/src/widget/components/TokenDisplayNonSelectable.tsx +40 -0
- package/src/widget/components/TokenImage.tsx +22 -5
- package/src/widget/components/TokenList.tsx +0 -1
- package/src/widget/components/TokenSelector.tsx +63 -53
- package/src/widget/components/TokenSelectorButton.tsx +98 -0
- package/src/widget/components/Tooltip.tsx +51 -0
- package/src/widget/components/TransferPendingVertical.tsx +1 -1
- package/src/widget/components/UserPreferences.tsx +6 -24
- package/src/widget/components/WaasFeeOptions.tsx +450 -0
- package/src/widget/components/WalletConfirmation.tsx +76 -14
- package/src/widget/components/WalletConnect.tsx +93 -29
- package/src/widget/components/WalletList.tsx +4 -2
- package/src/widget/hooks/useBack.tsx +2 -0
- package/src/widget/hooks/useCheckout.ts +36 -20
- package/src/widget/hooks/useCurrentScreen.tsx +1 -0
- package/src/widget/hooks/useDefaultTokenSelection.tsx +104 -28
- package/src/widget/hooks/usePayMessage.tsx +86 -11
- package/src/widget/hooks/useQuote.ts +413 -0
- package/src/widget/hooks/useSelectedFundMethod.tsx +41 -0
- package/src/widget/hooks/useSelectedRecipient.tsx +10 -0
- package/src/widget/hooks/useSendForm.ts +32 -6
- package/src/widget/index.css +27 -0
- package/src/widget/widget.tsx +326 -283
- package/dist/widget/components/FundSendForm.d.ts.map +0 -1
- package/dist/widget/components/PaySendForm.d.ts.map +0 -1
- package/dist/widget/components/SimpleSwap.d.ts.map +0 -1
- package/dist/widget/hooks/useSwapSettings.d.ts +0 -16
- package/dist/widget/hooks/useSwapSettings.d.ts.map +0 -1
- package/src/widget/components/FundSendForm.tsx +0 -903
- package/src/widget/components/PaySendForm.tsx +0 -869
- package/src/widget/components/SimpleSwap.tsx +0 -983
- package/src/widget/hooks/useSwapSettings.tsx +0 -100
|
@@ -8,6 +8,7 @@ import { truncateAddress } from "../../utils.js"
|
|
|
8
8
|
import { getExplorerUrlForAddress } from "../../explorer.js"
|
|
9
9
|
import ChainImage from "../components/ChainImage.js"
|
|
10
10
|
import TokenImage from "../components/TokenImage.js"
|
|
11
|
+
import { Identicon } from "../components/Identicon.js"
|
|
11
12
|
import { logger } from "../../logger.js"
|
|
12
13
|
|
|
13
14
|
export interface PayMessagePart {
|
|
@@ -85,25 +86,23 @@ export function usePayMessage(): UsePayMessageReturn {
|
|
|
85
86
|
})
|
|
86
87
|
|
|
87
88
|
const parsed = useMemo(() => {
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const template = payMessage || defaultMessage
|
|
89
|
+
// If no payMessage provided, return empty parts (default layout handled separately)
|
|
90
|
+
if (!payMessage) {
|
|
91
|
+
return []
|
|
92
|
+
}
|
|
94
93
|
|
|
95
94
|
// Parse the template into parts
|
|
96
95
|
const parts: PayMessagePart[] = []
|
|
97
96
|
const regex = /\{([^}]+)\}/g
|
|
98
97
|
let lastIndex = 0
|
|
99
|
-
let match: RegExpExecArray | null = regex.exec(
|
|
98
|
+
let match: RegExpExecArray | null = regex.exec(payMessage)
|
|
100
99
|
|
|
101
100
|
while (match !== null) {
|
|
102
101
|
// Add text before the placeholder
|
|
103
102
|
if (match.index > lastIndex) {
|
|
104
103
|
parts.push({
|
|
105
104
|
type: "text",
|
|
106
|
-
value:
|
|
105
|
+
value: payMessage.substring(lastIndex, match.index),
|
|
107
106
|
})
|
|
108
107
|
}
|
|
109
108
|
|
|
@@ -176,14 +175,14 @@ export function usePayMessage(): UsePayMessageReturn {
|
|
|
176
175
|
}
|
|
177
176
|
|
|
178
177
|
lastIndex = regex.lastIndex
|
|
179
|
-
match = regex.exec(
|
|
178
|
+
match = regex.exec(payMessage)
|
|
180
179
|
}
|
|
181
180
|
|
|
182
181
|
// Add remaining text
|
|
183
|
-
if (lastIndex <
|
|
182
|
+
if (lastIndex < payMessage.length) {
|
|
184
183
|
parts.push({
|
|
185
184
|
type: "text",
|
|
186
|
-
value:
|
|
185
|
+
value: payMessage.substring(lastIndex),
|
|
187
186
|
})
|
|
188
187
|
}
|
|
189
188
|
|
|
@@ -206,6 +205,78 @@ export function usePayMessage(): UsePayMessageReturn {
|
|
|
206
205
|
|
|
207
206
|
// Render the message
|
|
208
207
|
const message = useMemo(() => {
|
|
208
|
+
// If no payMessage provided, render default layout
|
|
209
|
+
if (!payMessage) {
|
|
210
|
+
return (
|
|
211
|
+
<div className="flex flex-col items-center justify-center px-8 py-4 space-y-4 min-w-full">
|
|
212
|
+
{/* App image above the amount if it exists */}
|
|
213
|
+
{appImageUrl && (
|
|
214
|
+
<img
|
|
215
|
+
src={appImageUrl}
|
|
216
|
+
alt={appName || "App"}
|
|
217
|
+
className="w-12 h-12 rounded-lg"
|
|
218
|
+
onError={(e) => {
|
|
219
|
+
logger.console.error(
|
|
220
|
+
`Error loading app image: ${e}, appImageUrl: ${appImageUrl}`,
|
|
221
|
+
)
|
|
222
|
+
// Hide image on error
|
|
223
|
+
e.currentTarget.style.display = "none"
|
|
224
|
+
}}
|
|
225
|
+
/>
|
|
226
|
+
)}
|
|
227
|
+
|
|
228
|
+
{/* Large amount in center */}
|
|
229
|
+
<div className="font-bold text-center">
|
|
230
|
+
{(() => {
|
|
231
|
+
const amountToShow = targetAmountUsdFormatted || toAmount || "0"
|
|
232
|
+
const tokenSymbol = tokenInfo?.symbol || ""
|
|
233
|
+
const displayText = targetAmountUsdFormatted
|
|
234
|
+
? amountToShow
|
|
235
|
+
: `${amountToShow} ${tokenSymbol}`.trim()
|
|
236
|
+
|
|
237
|
+
// Dynamic font size based on text length
|
|
238
|
+
const textLength = displayText.length
|
|
239
|
+
let fontSize = "text-6xl" // Default large size
|
|
240
|
+
|
|
241
|
+
if (textLength > 12) {
|
|
242
|
+
fontSize = "text-2xl"
|
|
243
|
+
} else if (textLength > 10) {
|
|
244
|
+
fontSize = "text-3xl"
|
|
245
|
+
} else if (textLength > 8) {
|
|
246
|
+
fontSize = "text-4xl"
|
|
247
|
+
} else if (textLength > 6) {
|
|
248
|
+
fontSize = "text-5xl"
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return <div className={fontSize}>{displayText}</div>
|
|
252
|
+
})()}
|
|
253
|
+
</div>
|
|
254
|
+
|
|
255
|
+
{/* Recipient info below */}
|
|
256
|
+
{toAddress && (
|
|
257
|
+
<div className="flex items-center space-x-2 text-center">
|
|
258
|
+
<span className="text-sm text-gray-600 dark:text-gray-400">
|
|
259
|
+
to
|
|
260
|
+
</span>
|
|
261
|
+
<Identicon value={toAddress} size={20} />
|
|
262
|
+
<a
|
|
263
|
+
href={getExplorerUrlForAddress({
|
|
264
|
+
address: toAddress,
|
|
265
|
+
chainId: toChainId ? Number(toChainId) : 1,
|
|
266
|
+
})}
|
|
267
|
+
target="_blank"
|
|
268
|
+
rel="noopener noreferrer"
|
|
269
|
+
className="text-sm hover:underline cursor-pointer text-blue-600 dark:text-blue-400"
|
|
270
|
+
>
|
|
271
|
+
{truncateAddress(toAddress)}
|
|
272
|
+
</a>
|
|
273
|
+
</div>
|
|
274
|
+
)}
|
|
275
|
+
</div>
|
|
276
|
+
)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Render user-provided message with handlebar interpolation
|
|
209
280
|
return (
|
|
210
281
|
<>
|
|
211
282
|
{parsed.map((part) => {
|
|
@@ -354,6 +425,7 @@ export function usePayMessage(): UsePayMessageReturn {
|
|
|
354
425
|
</>
|
|
355
426
|
)
|
|
356
427
|
}, [
|
|
428
|
+
payMessage,
|
|
357
429
|
parsed,
|
|
358
430
|
appName,
|
|
359
431
|
appUrl,
|
|
@@ -361,6 +433,9 @@ export function usePayMessage(): UsePayMessageReturn {
|
|
|
361
433
|
appDescription,
|
|
362
434
|
tokenInfo,
|
|
363
435
|
toChainId,
|
|
436
|
+
toAddress,
|
|
437
|
+
targetAmountUsdFormatted,
|
|
438
|
+
toAmount,
|
|
364
439
|
])
|
|
365
440
|
|
|
366
441
|
return {
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { createContext, useContext, useState, type ReactNode } from "react"
|
|
2
|
+
|
|
3
|
+
interface SelectedFundMethodContextType {
|
|
4
|
+
selectedFundMethod: string
|
|
5
|
+
setSelectedFundMethod: (method: string) => void
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const SelectedFundMethodContext = createContext<
|
|
9
|
+
SelectedFundMethodContextType | undefined
|
|
10
|
+
>(undefined)
|
|
11
|
+
|
|
12
|
+
interface SelectedFundMethodProviderProps {
|
|
13
|
+
children: ReactNode
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const SelectedFundMethodProvider = ({
|
|
17
|
+
children,
|
|
18
|
+
}: SelectedFundMethodProviderProps) => {
|
|
19
|
+
const [selectedFundMethod, setSelectedFundMethod] = useState<string>("wallet")
|
|
20
|
+
|
|
21
|
+
const value: SelectedFundMethodContextType = {
|
|
22
|
+
selectedFundMethod,
|
|
23
|
+
setSelectedFundMethod,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<SelectedFundMethodContext.Provider value={value}>
|
|
28
|
+
{children}
|
|
29
|
+
</SelectedFundMethodContext.Provider>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const useSelectedFundMethod = (): SelectedFundMethodContextType => {
|
|
34
|
+
const context = useContext(SelectedFundMethodContext)
|
|
35
|
+
if (context === undefined) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
"useSelectedFundMethod must be used within a SelectedFundMethodProvider",
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
return context
|
|
41
|
+
}
|
|
@@ -2,8 +2,10 @@ import React, {
|
|
|
2
2
|
createContext,
|
|
3
3
|
useContext,
|
|
4
4
|
useState,
|
|
5
|
+
useEffect,
|
|
5
6
|
type ReactNode,
|
|
6
7
|
} from "react"
|
|
8
|
+
import { useAccount } from "wagmi"
|
|
7
9
|
|
|
8
10
|
interface SelectedRecipientContextType {
|
|
9
11
|
selectedRecipient: string | null
|
|
@@ -21,10 +23,18 @@ interface SelectedRecipientProviderProps {
|
|
|
21
23
|
export const SelectedRecipientProvider: React.FC<
|
|
22
24
|
SelectedRecipientProviderProps
|
|
23
25
|
> = ({ children }) => {
|
|
26
|
+
const { address } = useAccount()
|
|
24
27
|
const [selectedRecipient, setSelectedRecipient] = useState<string | null>(
|
|
25
28
|
null,
|
|
26
29
|
)
|
|
27
30
|
|
|
31
|
+
// Set recipient to address only if address exists and no recipient is selected
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (address && !selectedRecipient) {
|
|
34
|
+
setSelectedRecipient(address)
|
|
35
|
+
}
|
|
36
|
+
}, [address, selectedRecipient])
|
|
37
|
+
|
|
28
38
|
return (
|
|
29
39
|
<SelectedRecipientContext.Provider
|
|
30
40
|
value={{
|