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
|
@@ -11,7 +11,7 @@ import { SearchInputField } from "./SearchInputField.js"
|
|
|
11
11
|
import { Identicon } from "./Identicon.js"
|
|
12
12
|
import { truncateAddress } from "../../utils.js"
|
|
13
13
|
import { useConnections, useAccount, useWalletClient } from "wagmi"
|
|
14
|
-
import { ChevronDown } from "lucide-react"
|
|
14
|
+
import { ChevronDown, Info } from "lucide-react"
|
|
15
15
|
import { getExplorerUrl } from "../../explorer.js"
|
|
16
16
|
import { ErrorDisplay } from "./ErrorDisplay.js"
|
|
17
17
|
import { logger } from "../../logger.js"
|
|
@@ -79,6 +79,7 @@ const TransactionItem: React.FC<{
|
|
|
79
79
|
intentError,
|
|
80
80
|
balancesError,
|
|
81
81
|
hasIntentBalance,
|
|
82
|
+
recoverToken,
|
|
82
83
|
refetchIntent,
|
|
83
84
|
} = useIntentRecover({
|
|
84
85
|
intentId: transaction.intentId && !refundTxHash ? transaction.intentId : "",
|
|
@@ -341,23 +342,54 @@ const TransactionItem: React.FC<{
|
|
|
341
342
|
</svg>
|
|
342
343
|
</a>
|
|
343
344
|
) : canRecover ? (
|
|
344
|
-
<
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
345
|
+
<div className="flex items-center gap-1">
|
|
346
|
+
<button
|
|
347
|
+
type="button"
|
|
348
|
+
onClick={handleRecover}
|
|
349
|
+
disabled={
|
|
350
|
+
isRecovering ||
|
|
351
|
+
isLoadingIntent ||
|
|
352
|
+
isLoadingBalances ||
|
|
353
|
+
!!intentError ||
|
|
354
|
+
!!balancesError
|
|
355
|
+
}
|
|
356
|
+
className="flex items-center justify-center gap-1 py-1 px-3 rounded-md transition-colors cursor-pointer text-xs font-medium text-white bg-orange-600 hover:bg-orange-700 disabled:bg-gray-400 disabled:cursor-not-allowed"
|
|
357
|
+
aria-label="Recover intent"
|
|
358
|
+
>
|
|
359
|
+
{isRecovering || isLoadingIntent || isLoadingBalances
|
|
360
|
+
? "Recovering..."
|
|
361
|
+
: "Recover"}
|
|
362
|
+
</button>
|
|
363
|
+
{/* Info tooltip showing recoverable token details */}
|
|
364
|
+
{recoverToken &&
|
|
365
|
+
!isRecovering &&
|
|
366
|
+
!isLoadingIntent &&
|
|
367
|
+
!isLoadingBalances && (
|
|
368
|
+
<div className="relative group">
|
|
369
|
+
<Info
|
|
370
|
+
className="w-4 h-4 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 cursor-help"
|
|
371
|
+
aria-label="Token balance info"
|
|
372
|
+
/>
|
|
373
|
+
<div className="absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-3 py-2 bg-gray-900 dark:bg-gray-700 text-white text-xs rounded-lg shadow-lg opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 whitespace-nowrap z-50 pointer-events-none">
|
|
374
|
+
<div className="flex flex-col gap-0.5">
|
|
375
|
+
<span className="font-medium">
|
|
376
|
+
Recover {recoverToken.symbol}
|
|
377
|
+
</span>
|
|
378
|
+
<span className="text-gray-300">
|
|
379
|
+
{recoverToken.balanceFormatted} {recoverToken.symbol}
|
|
380
|
+
</span>
|
|
381
|
+
{recoverToken.balanceUsdDisplay && (
|
|
382
|
+
<span className="text-gray-400">
|
|
383
|
+
≈ {recoverToken.balanceUsdDisplay}
|
|
384
|
+
</span>
|
|
385
|
+
)}
|
|
386
|
+
</div>
|
|
387
|
+
{/* Tooltip arrow */}
|
|
388
|
+
<div className="absolute top-full left-1/2 -translate-x-1/2 -mt-px border-4 border-transparent border-t-gray-900 dark:border-t-gray-700" />
|
|
389
|
+
</div>
|
|
390
|
+
</div>
|
|
391
|
+
)}
|
|
392
|
+
</div>
|
|
361
393
|
) : null}
|
|
362
394
|
{recoverError && (
|
|
363
395
|
<div className="w-full">
|
|
@@ -249,15 +249,17 @@ export const ClassicSwap: React.FC<ClassicSwapProps> = ({
|
|
|
249
249
|
setLastInputType("buy")
|
|
250
250
|
}, [])
|
|
251
251
|
|
|
252
|
-
// Handle buy input focus -
|
|
252
|
+
// Handle buy input focus - only copy displayed value to state for editing
|
|
253
|
+
// Don't switch trade type or clear sellAmount here - that happens in handleBuyAmountChange
|
|
254
|
+
// This prevents unnecessary requotes when user just focuses without editing
|
|
253
255
|
const handleBuyInputFocus = useCallback(() => {
|
|
254
|
-
// When focusing on buy field
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
256
|
+
// When focusing on buy field in EXACT_INPUT mode, copy the calculated value
|
|
257
|
+
// to buyAmount state so user can edit it. The actual mode switch happens
|
|
258
|
+
// when the user starts typing (in handleBuyAmountChange)
|
|
259
|
+
if (tradeType === TradeType.EXACT_INPUT && toAmountDisplay && !buyAmount) {
|
|
260
|
+
setBuyAmount(toAmountDisplay)
|
|
259
261
|
}
|
|
260
|
-
}, [tradeType, buyAmount])
|
|
262
|
+
}, [tradeType, toAmountDisplay, buyAmount])
|
|
261
263
|
|
|
262
264
|
// Update amounts when quote is received
|
|
263
265
|
useEffect(() => {
|
|
@@ -748,7 +750,7 @@ export const ClassicSwap: React.FC<ClassicSwapProps> = ({
|
|
|
748
750
|
value={
|
|
749
751
|
tradeType === TradeType.EXACT_OUTPUT
|
|
750
752
|
? buyAmount
|
|
751
|
-
: toAmountDisplay || ""
|
|
753
|
+
: buyAmount || toAmountDisplay || ""
|
|
752
754
|
}
|
|
753
755
|
onChange={(e) => handleBuyAmountChange(e.target.value)}
|
|
754
756
|
onFocus={handleBuyInputFocus}
|
|
@@ -780,31 +782,24 @@ export const ClassicSwap: React.FC<ClassicSwapProps> = ({
|
|
|
780
782
|
</div>
|
|
781
783
|
|
|
782
784
|
{/* Bottom Info Row */}
|
|
783
|
-
<div
|
|
784
|
-
className={
|
|
785
|
-
!prepareSendQuote?.destinationAmountUsdDisplay && !isLoadingQuote
|
|
786
|
-
? "flex justify-end mt-4"
|
|
787
|
-
: "mt-4 flex items-center justify-between"
|
|
788
|
-
}
|
|
789
|
-
>
|
|
785
|
+
<div className="mt-4 flex items-center justify-between">
|
|
790
786
|
{/* Destination Amount USD from Quote */}
|
|
791
|
-
|
|
792
|
-
(
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
787
|
+
<div className="text-xs text-gray-500 dark:text-gray-400">
|
|
788
|
+
{(prepareSendQuote?.destinationAmountUsdDisplay ||
|
|
789
|
+
(isLoadingQuote && tradeType === TradeType.EXACT_INPUT)) && (
|
|
790
|
+
<>
|
|
791
|
+
≈{" "}
|
|
792
|
+
{isLoadingQuote && tradeType === TradeType.EXACT_INPUT
|
|
793
|
+
? "$0.00"
|
|
794
|
+
: prepareSendQuote?.destinationAmountUsdDisplay}
|
|
795
|
+
</>
|
|
796
|
+
)}
|
|
797
|
+
</div>
|
|
800
798
|
|
|
801
|
-
{/* Dest Token Balance */}
|
|
799
|
+
{/* Dest Token Balance - always on right */}
|
|
802
800
|
{destinationToken && (
|
|
803
|
-
<div className="
|
|
804
|
-
|
|
805
|
-
Balance:{" "}
|
|
806
|
-
{isBalanceVisible ? destinationBalanceDisplay : "0.00"}
|
|
807
|
-
</div>
|
|
801
|
+
<div className="text-xs text-gray-500 dark:text-gray-400">
|
|
802
|
+
Balance: {isBalanceVisible ? destinationBalanceDisplay : "0.00"}
|
|
808
803
|
</div>
|
|
809
804
|
)}
|
|
810
805
|
</div>
|
|
@@ -280,7 +280,7 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
|
|
|
280
280
|
/>
|
|
281
281
|
</RowLabel>
|
|
282
282
|
<RowValue>
|
|
283
|
-
<span title={`$${quote.
|
|
283
|
+
<span title={`$${quote.destinationGasUsd ?? 0}`}>
|
|
284
284
|
{
|
|
285
285
|
quote.trailsFeeBreakdown.destinationRelayFee
|
|
286
286
|
.usdValue
|
|
@@ -301,37 +301,28 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
|
|
|
301
301
|
if (!quote?.trailsFeeBreakdown) return null
|
|
302
302
|
|
|
303
303
|
// Check if provider fee is non-zero
|
|
304
|
-
const hasProviderFee = (quote.
|
|
304
|
+
const hasProviderFee = (quote.providerFeeUsd ?? 0) > 0
|
|
305
305
|
|
|
306
306
|
// Check if trails fee is non-zero
|
|
307
|
-
const hasTrailsFee = (quote.
|
|
307
|
+
const hasTrailsFee = (quote.trailsFeeUsd ?? 0) > 0
|
|
308
308
|
|
|
309
309
|
if (!hasProviderFee && !hasTrailsFee) return null
|
|
310
310
|
|
|
311
311
|
return (
|
|
312
312
|
<RowGroup>
|
|
313
|
-
{/* All Provider Fees
|
|
314
|
-
{(
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
</RowLabel>
|
|
327
|
-
<RowValue>
|
|
328
|
-
<span title={`$${quote.allProviderFeesUsd ?? 0}`}>
|
|
329
|
-
{quote.allProviderFeesUsdDisplay}
|
|
330
|
-
</span>
|
|
331
|
-
</RowValue>
|
|
332
|
-
</Row>
|
|
333
|
-
)
|
|
334
|
-
})()}
|
|
313
|
+
{/* All Provider Fees (swap spread is shown in priceImpact) */}
|
|
314
|
+
{(quote.totalProviderFeesUsd ?? 0) > 0 && (
|
|
315
|
+
<Row>
|
|
316
|
+
<RowLabel tooltip="Total provider fees for bridge and routing">
|
|
317
|
+
All provider fees
|
|
318
|
+
</RowLabel>
|
|
319
|
+
<RowValue>
|
|
320
|
+
<span title={`$${quote.totalProviderFeesUsd ?? 0}`}>
|
|
321
|
+
{quote.totalProviderFeesUsdDisplay}
|
|
322
|
+
</span>
|
|
323
|
+
</RowValue>
|
|
324
|
+
</Row>
|
|
325
|
+
)}
|
|
335
326
|
|
|
336
327
|
{hasProviderFee &&
|
|
337
328
|
quote.trailsFeeBreakdown.providerFee &&
|
|
@@ -394,7 +385,7 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
|
|
|
394
385
|
)}
|
|
395
386
|
</RowLabel>
|
|
396
387
|
<RowValue>
|
|
397
|
-
<span title={`$${quote.
|
|
388
|
+
<span title={`$${quote.providerFeeUsd ?? 0}`}>
|
|
398
389
|
{providerFee.usdValue}
|
|
399
390
|
</span>
|
|
400
391
|
</RowValue>
|
|
@@ -497,7 +488,7 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
|
|
|
497
488
|
<TriangleAlert className="size-3" />
|
|
498
489
|
</span>
|
|
499
490
|
)}
|
|
500
|
-
{quote.priceImpact}%
|
|
491
|
+
{Number(quote.priceImpact).toFixed(2)}%
|
|
501
492
|
</RowValue>
|
|
502
493
|
</Row>
|
|
503
494
|
{quote.priceImpactUsdDisplay && (
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type React from "react"
|
|
2
2
|
import { useState, useEffect } from "react"
|
|
3
3
|
import { HelpCircle } from "lucide-react"
|
|
4
|
-
import {
|
|
4
|
+
import { SLIPPAGE_AUTO } from "../../constants.js"
|
|
5
5
|
|
|
6
6
|
// Convert decimal format to percentage for display
|
|
7
7
|
// Format: "0.05" (5%) -> "5"
|
|
8
8
|
const decimalToPercentage = (decimal: string): string => {
|
|
9
|
+
if (decimal === SLIPPAGE_AUTO) return SLIPPAGE_AUTO
|
|
9
10
|
const num = parseFloat(decimal)
|
|
10
11
|
return (num * 100).toString()
|
|
11
12
|
}
|
|
@@ -13,6 +14,7 @@ const decimalToPercentage = (decimal: string): string => {
|
|
|
13
14
|
// Convert percentage to decimal format for storage
|
|
14
15
|
// Format: "5" (5%) -> "0.05"
|
|
15
16
|
const percentageToDecimal = (percentage: string): string => {
|
|
17
|
+
if (percentage === SLIPPAGE_AUTO) return SLIPPAGE_AUTO
|
|
16
18
|
const num = parseFloat(percentage)
|
|
17
19
|
return (num / 100).toString()
|
|
18
20
|
}
|
|
@@ -20,11 +22,23 @@ const percentageToDecimal = (percentage: string): string => {
|
|
|
20
22
|
// Local storage key for user's slippage preference
|
|
21
23
|
const SLIPPAGE_STORAGE_KEY = "trails-slippage-tolerance"
|
|
22
24
|
|
|
25
|
+
// Helper function to check if slippage is in AUTO mode
|
|
26
|
+
export const isSlippageAuto = (value: string | null | undefined): boolean => {
|
|
27
|
+
return value === SLIPPAGE_AUTO || value === null || value === undefined
|
|
28
|
+
}
|
|
29
|
+
|
|
23
30
|
// Helper function to get current slippage tolerance (for use in SDK functions)
|
|
24
31
|
// Returns the user's stored preference or falls back to the provided default
|
|
25
|
-
|
|
32
|
+
// Returns null if AUTO mode is selected (backend will calculate optimal slippage)
|
|
33
|
+
export const getSlippageToleranceValue = (
|
|
34
|
+
defaultValue: string,
|
|
35
|
+
): string | null => {
|
|
26
36
|
try {
|
|
27
37
|
const stored = localStorage.getItem(SLIPPAGE_STORAGE_KEY)
|
|
38
|
+
if (stored === SLIPPAGE_AUTO || stored === null) {
|
|
39
|
+
// AUTO mode - return null so backend calculates optimal slippage
|
|
40
|
+
return null
|
|
41
|
+
}
|
|
28
42
|
return stored || defaultValue
|
|
29
43
|
} catch (_error) {
|
|
30
44
|
return defaultValue
|
|
@@ -38,7 +52,6 @@ interface SlippageToleranceSettingsProps {
|
|
|
38
52
|
export const SlippageToleranceSettings: React.FC<
|
|
39
53
|
SlippageToleranceSettingsProps
|
|
40
54
|
> = ({ className = "" }) => {
|
|
41
|
-
const config = useTrails()
|
|
42
55
|
const [displayValue, setDisplayValue] = useState("")
|
|
43
56
|
const [showTooltip, setShowTooltip] = useState(false)
|
|
44
57
|
|
|
@@ -49,14 +62,14 @@ export const SlippageToleranceSettings: React.FC<
|
|
|
49
62
|
if (stored) {
|
|
50
63
|
setDisplayValue(decimalToPercentage(stored))
|
|
51
64
|
} else {
|
|
52
|
-
// Default to
|
|
53
|
-
setDisplayValue(
|
|
65
|
+
// Default to AUTO mode
|
|
66
|
+
setDisplayValue(SLIPPAGE_AUTO)
|
|
54
67
|
}
|
|
55
68
|
} catch (_error) {
|
|
56
|
-
// Fallback to
|
|
57
|
-
setDisplayValue(
|
|
69
|
+
// Fallback to AUTO if localStorage fails
|
|
70
|
+
setDisplayValue(SLIPPAGE_AUTO)
|
|
58
71
|
}
|
|
59
|
-
}, [
|
|
72
|
+
}, [])
|
|
60
73
|
|
|
61
74
|
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
62
75
|
const value = e.target.value
|
|
@@ -73,17 +86,13 @@ export const SlippageToleranceSettings: React.FC<
|
|
|
73
86
|
}
|
|
74
87
|
|
|
75
88
|
const handleInputBlur = () => {
|
|
76
|
-
if (displayValue === "") {
|
|
77
|
-
// Reset to
|
|
89
|
+
if (displayValue === "" || displayValue === SLIPPAGE_AUTO) {
|
|
90
|
+
// Reset to AUTO mode
|
|
91
|
+
setDisplayValue(SLIPPAGE_AUTO)
|
|
78
92
|
try {
|
|
79
|
-
|
|
80
|
-
if (stored) {
|
|
81
|
-
setDisplayValue(decimalToPercentage(stored))
|
|
82
|
-
} else {
|
|
83
|
-
setDisplayValue(decimalToPercentage(String(config.slippageTolerance)))
|
|
84
|
-
}
|
|
93
|
+
localStorage.setItem(SLIPPAGE_STORAGE_KEY, SLIPPAGE_AUTO)
|
|
85
94
|
} catch (_error) {
|
|
86
|
-
|
|
95
|
+
// Ignore localStorage errors
|
|
87
96
|
}
|
|
88
97
|
return
|
|
89
98
|
}
|
|
@@ -91,17 +100,17 @@ export const SlippageToleranceSettings: React.FC<
|
|
|
91
100
|
const percentage = parseFloat(displayValue)
|
|
92
101
|
|
|
93
102
|
// Validate range (0.01% to 50%)
|
|
94
|
-
if (percentage < 0.01 || percentage > 50) {
|
|
95
|
-
// Reset to stored value or
|
|
103
|
+
if (Number.isNaN(percentage) || percentage < 0.01 || percentage > 50) {
|
|
104
|
+
// Reset to stored value or AUTO default
|
|
96
105
|
try {
|
|
97
106
|
const stored = localStorage.getItem(SLIPPAGE_STORAGE_KEY)
|
|
98
|
-
if (stored) {
|
|
107
|
+
if (stored && stored !== SLIPPAGE_AUTO) {
|
|
99
108
|
setDisplayValue(decimalToPercentage(stored))
|
|
100
109
|
} else {
|
|
101
|
-
setDisplayValue(
|
|
110
|
+
setDisplayValue(SLIPPAGE_AUTO)
|
|
102
111
|
}
|
|
103
112
|
} catch (_error) {
|
|
104
|
-
setDisplayValue(
|
|
113
|
+
setDisplayValue(SLIPPAGE_AUTO)
|
|
105
114
|
}
|
|
106
115
|
return
|
|
107
116
|
}
|
|
@@ -124,6 +133,15 @@ export const SlippageToleranceSettings: React.FC<
|
|
|
124
133
|
}
|
|
125
134
|
}
|
|
126
135
|
|
|
136
|
+
const handleAutoClick = () => {
|
|
137
|
+
setDisplayValue(SLIPPAGE_AUTO)
|
|
138
|
+
try {
|
|
139
|
+
localStorage.setItem(SLIPPAGE_STORAGE_KEY, SLIPPAGE_AUTO)
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.warn("Failed to save slippage tolerance to localStorage:", error)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
127
145
|
const handlePresetClick = (percentage: number) => {
|
|
128
146
|
const percentageStr = percentage.toString()
|
|
129
147
|
setDisplayValue(percentageStr)
|
|
@@ -152,8 +170,9 @@ export const SlippageToleranceSettings: React.FC<
|
|
|
152
170
|
</button>
|
|
153
171
|
{showTooltip && (
|
|
154
172
|
<div className="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 w-64 p-2 text-xs text-white bg-gray-900 dark:bg-gray-700 rounded-md shadow-lg z-10">
|
|
155
|
-
|
|
156
|
-
|
|
173
|
+
Auto mode calculates optimal slippage based on transaction size
|
|
174
|
+
and routing. Higher slippage allows for larger price movements but
|
|
175
|
+
may result in less favorable rates.
|
|
157
176
|
<div className="absolute top-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-l-4 border-r-4 border-t-4 border-transparent border-t-gray-900 dark:border-t-gray-700"></div>
|
|
158
177
|
</div>
|
|
159
178
|
)}
|
|
@@ -162,7 +181,18 @@ export const SlippageToleranceSettings: React.FC<
|
|
|
162
181
|
|
|
163
182
|
{/* Preset buttons */}
|
|
164
183
|
<div className="flex items-center space-x-2">
|
|
165
|
-
|
|
184
|
+
<button
|
|
185
|
+
type="button"
|
|
186
|
+
onClick={handleAutoClick}
|
|
187
|
+
className={`px-2 py-1 text-xs font-medium trails-border-radius-container border border-solid transition-colors cursor-pointer ${
|
|
188
|
+
displayValue === SLIPPAGE_AUTO
|
|
189
|
+
? "bg-blue-500 text-white border-blue-500"
|
|
190
|
+
: "border-gray-300 text-gray-600 dark:border-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 hover:trails-hover-bg hover:border-gray-400 dark:hover:border-gray-500"
|
|
191
|
+
}`}
|
|
192
|
+
>
|
|
193
|
+
Auto
|
|
194
|
+
</button>
|
|
195
|
+
{[0.5, 1, 3, 5].map((preset) => (
|
|
166
196
|
<button
|
|
167
197
|
key={preset}
|
|
168
198
|
type="button"
|