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
|
@@ -1,983 +0,0 @@
|
|
|
1
|
-
import type React from "react"
|
|
2
|
-
import { useEffect, useRef, useState, useCallback, useMemo } from "react"
|
|
3
|
-
import type { Account, WalletClient } from "viem"
|
|
4
|
-
import { ChevronRight, ArrowDown, Loader2, Search } from "lucide-react"
|
|
5
|
-
import { useOriginSelectedToken } from "../hooks/useOriginSelectedToken.js"
|
|
6
|
-
import { useDestinationSelectedToken } from "../hooks/useDestinationSelectedToken.js"
|
|
7
|
-
import { useBalanceVisible } from "../hooks/useBalanceVisible.js"
|
|
8
|
-
import { useTokenList } from "../hooks/useTokenList.js"
|
|
9
|
-
import { useRecentTokens } from "../hooks/useRecentTokens.js"
|
|
10
|
-
import { useDefaultTokenSelection } from "../hooks/useDefaultTokenSelection.js" // Context version
|
|
11
|
-
import { ScreenHeader } from "./ScreenHeader.js"
|
|
12
|
-
import { TokenSelector } from "./TokenSelector.js"
|
|
13
|
-
import { ChainList } from "./ChainList.js"
|
|
14
|
-
import { TokenImage } from "./TokenImage.js"
|
|
15
|
-
import { QuoteDetails } from "./QuoteDetails.js"
|
|
16
|
-
import { ErrorDisplay } from "./ErrorDisplay.js"
|
|
17
|
-
import { formatAmount, formatUsdAmountDisplay } from "../../tokenBalances.js"
|
|
18
|
-
import { getChainInfo } from "../../chains.js"
|
|
19
|
-
import { logger } from "../../logger.js"
|
|
20
|
-
import { useSendForm } from "../hooks/useSendForm.js"
|
|
21
|
-
import { TradeType } from "../../prepareSend.js"
|
|
22
|
-
import type { TransactionState } from "../../transactions.js"
|
|
23
|
-
import type { OnCompleteProps } from "../hooks/useSendForm.js"
|
|
24
|
-
import type { CheckoutOnHandlers } from "../hooks/useCheckout.js"
|
|
25
|
-
import { useMode } from "../hooks/useMode.js"
|
|
26
|
-
|
|
27
|
-
interface SimpleSwapProps {
|
|
28
|
-
onBack?: () => void
|
|
29
|
-
account: Account
|
|
30
|
-
walletClient: WalletClient
|
|
31
|
-
onTransactionStateChange: (transactionStates: TransactionState[]) => void
|
|
32
|
-
onError: (error: Error | string | null) => void
|
|
33
|
-
onWaitingForWalletConfirm: (props: any) => void
|
|
34
|
-
onConfirm: () => void
|
|
35
|
-
onComplete: (result: OnCompleteProps) => void
|
|
36
|
-
onSend: (amount: string, recipient: string) => void
|
|
37
|
-
paymasterUrls?: Array<{ chainId: number; url: string }>
|
|
38
|
-
gasless?: boolean
|
|
39
|
-
setWalletConfirmRetryHandler: (handler: () => Promise<void>) => void
|
|
40
|
-
quoteProvider?: string
|
|
41
|
-
fundMethod?: string
|
|
42
|
-
checkoutOnHandlers?: CheckoutOnHandlers
|
|
43
|
-
showHeader?: boolean
|
|
44
|
-
onTokenSelectorVisibilityChange?: (isVisible: boolean) => void
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export const SimpleSwap: React.FC<SimpleSwapProps> = ({
|
|
48
|
-
onBack,
|
|
49
|
-
account,
|
|
50
|
-
walletClient,
|
|
51
|
-
onTransactionStateChange,
|
|
52
|
-
onError,
|
|
53
|
-
onWaitingForWalletConfirm,
|
|
54
|
-
onConfirm,
|
|
55
|
-
onComplete,
|
|
56
|
-
onSend,
|
|
57
|
-
paymasterUrls,
|
|
58
|
-
gasless,
|
|
59
|
-
setWalletConfirmRetryHandler,
|
|
60
|
-
quoteProvider,
|
|
61
|
-
fundMethod,
|
|
62
|
-
checkoutOnHandlers,
|
|
63
|
-
showHeader = true,
|
|
64
|
-
onTokenSelectorVisibilityChange,
|
|
65
|
-
}) => {
|
|
66
|
-
const { mode } = useMode()
|
|
67
|
-
const { selectedToken, setSelectedToken } = useOriginSelectedToken()
|
|
68
|
-
const { setSelectedToken: setDestinationTokenFromHook } =
|
|
69
|
-
useDestinationSelectedToken()
|
|
70
|
-
const { isBalanceVisible } = useBalanceVisible()
|
|
71
|
-
const { recentTokens, addRecentToken } = useRecentTokens(account?.address)
|
|
72
|
-
|
|
73
|
-
// Use new default token selection hook
|
|
74
|
-
const { defaultOriginToken, isLoading: isLoadingDefaults } =
|
|
75
|
-
useDefaultTokenSelection()
|
|
76
|
-
|
|
77
|
-
// Get sorted tokens to auto-select the highest USD value token
|
|
78
|
-
const { filteredTokensFormatted } = useTokenList({
|
|
79
|
-
onContinue: () => {}, // Not used for auto-selection
|
|
80
|
-
onError: () => {}, // Not used for auto-selection
|
|
81
|
-
fundMethod: undefined,
|
|
82
|
-
allSupportedTokens: false,
|
|
83
|
-
})
|
|
84
|
-
const [isInputTypeUsd, setIsInputTypeUsd] = useState(false)
|
|
85
|
-
const [tokenAmountForBackend, setTokenAmountForBackend] = useState("")
|
|
86
|
-
const [inputDisplayValue, setInputDisplayValue] = useState("")
|
|
87
|
-
const [_destinationToken, setDestinationToken] = useState<any>(null)
|
|
88
|
-
const [showSourceTokenSelector, setShowSourceTokenSelector] = useState(false)
|
|
89
|
-
const [showDestinationTokenSelector, setShowDestinationTokenSelector] =
|
|
90
|
-
useState(false)
|
|
91
|
-
const [showSourceChainList, setShowSourceChainList] = useState(false)
|
|
92
|
-
const [showDestinationChainList, setShowDestinationChainList] =
|
|
93
|
-
useState(false)
|
|
94
|
-
const [showPercentageButtons, setShowPercentageButtons] = useState(false)
|
|
95
|
-
const inputRef = useRef<HTMLInputElement>(null)
|
|
96
|
-
|
|
97
|
-
// Use the same useSendForm hook as Swap.tsx for real quote functionality
|
|
98
|
-
const {
|
|
99
|
-
amount,
|
|
100
|
-
amountUsdDisplay,
|
|
101
|
-
balanceFormatted,
|
|
102
|
-
handleSubmit,
|
|
103
|
-
isSubmitting,
|
|
104
|
-
isLoadingQuote,
|
|
105
|
-
selectedDestToken,
|
|
106
|
-
setAmount,
|
|
107
|
-
setSelectedDestToken,
|
|
108
|
-
setSelectedDestinationChain,
|
|
109
|
-
buttonText,
|
|
110
|
-
destinationTokenAddress,
|
|
111
|
-
isValidCustomToken,
|
|
112
|
-
prepareSendQuote,
|
|
113
|
-
quoteError,
|
|
114
|
-
quoteErrorPrettified,
|
|
115
|
-
isSameTokenWithoutCustomCalldata,
|
|
116
|
-
} = useSendForm({
|
|
117
|
-
account,
|
|
118
|
-
toAmount: undefined,
|
|
119
|
-
toRecipient: account.address,
|
|
120
|
-
toChainId: undefined, // Let the hook manage this internally
|
|
121
|
-
toToken: undefined, // Let the hook manage this internally
|
|
122
|
-
toCalldata: undefined,
|
|
123
|
-
walletClient,
|
|
124
|
-
onTransactionStateChange,
|
|
125
|
-
onError,
|
|
126
|
-
onWaitingForWalletConfirm,
|
|
127
|
-
paymasterUrls,
|
|
128
|
-
gasless,
|
|
129
|
-
onConfirm,
|
|
130
|
-
onComplete,
|
|
131
|
-
onSend,
|
|
132
|
-
selectedToken: selectedToken as any,
|
|
133
|
-
setWalletConfirmRetryHandler,
|
|
134
|
-
tradeType: TradeType.EXACT_INPUT,
|
|
135
|
-
quoteProvider,
|
|
136
|
-
fundMethod,
|
|
137
|
-
mode,
|
|
138
|
-
onNavigateToMeshConnect: undefined,
|
|
139
|
-
checkoutOnHandlers,
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
// Get real token prices from useSendForm (no more mock prices)
|
|
143
|
-
// The useSendForm hook provides real amountUsdDisplay
|
|
144
|
-
|
|
145
|
-
// Auto-select source token using new hook
|
|
146
|
-
useEffect(() => {
|
|
147
|
-
if (!selectedToken && !isLoadingDefaults && defaultOriginToken) {
|
|
148
|
-
logger.console.log(
|
|
149
|
-
"[trails-sdk] Auto-selecting source token:",
|
|
150
|
-
defaultOriginToken,
|
|
151
|
-
)
|
|
152
|
-
setSelectedToken(defaultOriginToken as any)
|
|
153
|
-
|
|
154
|
-
// Add to recent tokens
|
|
155
|
-
addRecentToken(defaultOriginToken as any)
|
|
156
|
-
}
|
|
157
|
-
}, [
|
|
158
|
-
selectedToken,
|
|
159
|
-
isLoadingDefaults,
|
|
160
|
-
defaultOriginToken,
|
|
161
|
-
setSelectedToken,
|
|
162
|
-
addRecentToken,
|
|
163
|
-
])
|
|
164
|
-
|
|
165
|
-
// Auto-focus input field when selectedToken is available
|
|
166
|
-
useEffect(() => {
|
|
167
|
-
if (selectedToken && inputRef.current) {
|
|
168
|
-
inputRef.current.focus()
|
|
169
|
-
}
|
|
170
|
-
}, [selectedToken])
|
|
171
|
-
|
|
172
|
-
// Notify parent when token selector visibility changes
|
|
173
|
-
useEffect(() => {
|
|
174
|
-
const isTokenSelectorVisible =
|
|
175
|
-
showSourceTokenSelector ||
|
|
176
|
-
showDestinationTokenSelector ||
|
|
177
|
-
showSourceChainList ||
|
|
178
|
-
showDestinationChainList
|
|
179
|
-
onTokenSelectorVisibilityChange?.(isTokenSelectorVisible)
|
|
180
|
-
}, [
|
|
181
|
-
showSourceTokenSelector,
|
|
182
|
-
showDestinationTokenSelector,
|
|
183
|
-
showSourceChainList,
|
|
184
|
-
showDestinationChainList,
|
|
185
|
-
onTokenSelectorVisibilityChange,
|
|
186
|
-
])
|
|
187
|
-
|
|
188
|
-
// Sync display value with token amount only when mode changes (not during typing)
|
|
189
|
-
const [lastInputMode, setLastInputMode] = useState(isInputTypeUsd)
|
|
190
|
-
|
|
191
|
-
useEffect(() => {
|
|
192
|
-
// Only sync when mode actually changes, not during normal typing
|
|
193
|
-
if (lastInputMode !== isInputTypeUsd && tokenAmountForBackend) {
|
|
194
|
-
const tokenAmount = parseFloat(tokenAmountForBackend) || 0
|
|
195
|
-
if (isInputTypeUsd) {
|
|
196
|
-
// Show USD with max 2 decimals - we'll calculate from amountUsdDisplay if available
|
|
197
|
-
// For now, just show the token amount since we don't have a reliable USD conversion
|
|
198
|
-
setInputDisplayValue(Number(tokenAmount.toFixed(8)).toString())
|
|
199
|
-
} else {
|
|
200
|
-
// Show token with max 8 decimals
|
|
201
|
-
setInputDisplayValue(Number(tokenAmount.toFixed(8)).toString())
|
|
202
|
-
}
|
|
203
|
-
setLastInputMode(isInputTypeUsd)
|
|
204
|
-
}
|
|
205
|
-
}, [isInputTypeUsd, tokenAmountForBackend, lastInputMode])
|
|
206
|
-
|
|
207
|
-
// Handle input amount changes with 8 decimal limit and 16 char total limit
|
|
208
|
-
const handleAmountChange = useCallback(
|
|
209
|
-
(value: string) => {
|
|
210
|
-
// Allow empty string
|
|
211
|
-
if (value === "") {
|
|
212
|
-
setInputDisplayValue("")
|
|
213
|
-
setTokenAmountForBackend("")
|
|
214
|
-
setAmount("")
|
|
215
|
-
return
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Limit total length to 16 characters
|
|
219
|
-
if (value.length > 16) {
|
|
220
|
-
return
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Validate decimal places (max 8 decimals) and allow single decimal point
|
|
224
|
-
const decimalMatch = value.match(/^\d*\.?\d{0,8}$/)
|
|
225
|
-
if (!decimalMatch) {
|
|
226
|
-
return // Don't update if invalid format
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Store the display value
|
|
230
|
-
setInputDisplayValue(value)
|
|
231
|
-
|
|
232
|
-
// Update the token amount for backend and useSendForm
|
|
233
|
-
const sourceTokenPrice = (selectedToken as any)?.tokenPriceUsd || 0
|
|
234
|
-
|
|
235
|
-
if (isInputTypeUsd && sourceTokenPrice > 0) {
|
|
236
|
-
// Convert USD input to token amount
|
|
237
|
-
const usdAmount = parseFloat(value) || 0
|
|
238
|
-
const tokenAmount = usdAmount / sourceTokenPrice
|
|
239
|
-
setTokenAmountForBackend(tokenAmount.toString())
|
|
240
|
-
setAmount(tokenAmount.toString())
|
|
241
|
-
} else {
|
|
242
|
-
// Direct token amount input
|
|
243
|
-
setTokenAmountForBackend(value)
|
|
244
|
-
setAmount(value)
|
|
245
|
-
}
|
|
246
|
-
},
|
|
247
|
-
[setAmount, isInputTypeUsd, selectedToken],
|
|
248
|
-
)
|
|
249
|
-
|
|
250
|
-
// Get display values based on input type
|
|
251
|
-
const displayAmount = useMemo(() => {
|
|
252
|
-
return inputDisplayValue
|
|
253
|
-
}, [inputDisplayValue])
|
|
254
|
-
|
|
255
|
-
const displayUsdValue = useMemo(() => {
|
|
256
|
-
const sourceTokenPrice = (selectedToken as any)?.tokenPriceUsd || 0
|
|
257
|
-
|
|
258
|
-
if (isInputTypeUsd && sourceTokenPrice > 0) {
|
|
259
|
-
// Show token amount when in USD mode
|
|
260
|
-
const tokenAmount = parseFloat(tokenAmountForBackend) || 0
|
|
261
|
-
return `${formatAmount(tokenAmount)} ${selectedToken?.symbol || "TOKEN"}`
|
|
262
|
-
}
|
|
263
|
-
// Show USD amount when in token mode
|
|
264
|
-
if (sourceTokenPrice > 0) {
|
|
265
|
-
const tokenAmount = parseFloat(tokenAmountForBackend) || 0
|
|
266
|
-
const usdAmount = tokenAmount * sourceTokenPrice
|
|
267
|
-
return formatUsdAmountDisplay(usdAmount)
|
|
268
|
-
}
|
|
269
|
-
return amountUsdDisplay || "$0.00"
|
|
270
|
-
}, [tokenAmountForBackend, isInputTypeUsd, selectedToken, amountUsdDisplay])
|
|
271
|
-
|
|
272
|
-
// Handle percentage clicks for quick amounts
|
|
273
|
-
const handlePercentageClick = useCallback(
|
|
274
|
-
(percentage: number) => {
|
|
275
|
-
if (!balanceFormatted) {
|
|
276
|
-
return
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const balance = parseFloat(balanceFormatted)
|
|
280
|
-
if (Number.isNaN(balance)) return
|
|
281
|
-
|
|
282
|
-
const amount = (balance * percentage) / 100
|
|
283
|
-
// Cap decimals to 8 places
|
|
284
|
-
const cappedAmount = parseFloat(amount.toFixed(8))
|
|
285
|
-
const tokenAmountStr = cappedAmount.toString()
|
|
286
|
-
|
|
287
|
-
// Update all states consistently
|
|
288
|
-
setTokenAmountForBackend(tokenAmountStr)
|
|
289
|
-
setAmount(tokenAmountStr)
|
|
290
|
-
|
|
291
|
-
// Update display - always show token amount
|
|
292
|
-
setInputDisplayValue(tokenAmountStr)
|
|
293
|
-
},
|
|
294
|
-
[balanceFormatted, setAmount],
|
|
295
|
-
)
|
|
296
|
-
|
|
297
|
-
const handleMaxClick = useCallback(() => {
|
|
298
|
-
handlePercentageClick(100)
|
|
299
|
-
}, [handlePercentageClick])
|
|
300
|
-
|
|
301
|
-
// Handle source token selection from full-screen selector
|
|
302
|
-
const handleSourceTokenSelectorSelect = useCallback(
|
|
303
|
-
(token: any) => {
|
|
304
|
-
const formattedToken = {
|
|
305
|
-
...token,
|
|
306
|
-
decimals: token.contractInfo?.decimals || token.decimals,
|
|
307
|
-
contractInfo: {
|
|
308
|
-
decimals: token.contractInfo?.decimals || token.decimals,
|
|
309
|
-
contractAddress: token.contractAddress,
|
|
310
|
-
symbol: token.symbol,
|
|
311
|
-
name: token.name,
|
|
312
|
-
},
|
|
313
|
-
}
|
|
314
|
-
setSelectedToken(formattedToken)
|
|
315
|
-
setShowSourceTokenSelector(false)
|
|
316
|
-
|
|
317
|
-
// Add to recent tokens
|
|
318
|
-
addRecentToken(formattedToken)
|
|
319
|
-
|
|
320
|
-
logger.console.log(
|
|
321
|
-
"[trails-sdk] selected source token from selector in SimpleSwap",
|
|
322
|
-
token,
|
|
323
|
-
)
|
|
324
|
-
},
|
|
325
|
-
[setSelectedToken, addRecentToken],
|
|
326
|
-
)
|
|
327
|
-
|
|
328
|
-
// Handle destination token selection from full-screen selector
|
|
329
|
-
const handleDestinationTokenSelectorSelect = useCallback(
|
|
330
|
-
(token: any) => {
|
|
331
|
-
const formattedToken = {
|
|
332
|
-
...token,
|
|
333
|
-
decimals: token.contractInfo?.decimals || token.decimals,
|
|
334
|
-
contractInfo: {
|
|
335
|
-
decimals: token.contractInfo?.decimals || token.decimals,
|
|
336
|
-
contractAddress: token.contractAddress,
|
|
337
|
-
symbol: token.symbol,
|
|
338
|
-
name: token.name,
|
|
339
|
-
},
|
|
340
|
-
}
|
|
341
|
-
setDestinationToken(formattedToken)
|
|
342
|
-
setDestinationTokenFromHook(formattedToken as any)
|
|
343
|
-
setSelectedDestToken(formattedToken as any)
|
|
344
|
-
|
|
345
|
-
// Update destination chain to match the selected token's chain
|
|
346
|
-
// This is crucial for proper same-token detection in useSendForm
|
|
347
|
-
if (setSelectedDestinationChain && token.chainId) {
|
|
348
|
-
const chainInfo = getChainInfo(token.chainId)
|
|
349
|
-
if (chainInfo) {
|
|
350
|
-
setSelectedDestinationChain(chainInfo)
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
setShowDestinationTokenSelector(false)
|
|
355
|
-
|
|
356
|
-
// Add to recent tokens
|
|
357
|
-
addRecentToken(formattedToken)
|
|
358
|
-
|
|
359
|
-
logger.console.log(
|
|
360
|
-
"[trails-sdk] selected destination token from selector in SimpleSwap",
|
|
361
|
-
token,
|
|
362
|
-
)
|
|
363
|
-
},
|
|
364
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
365
|
-
[
|
|
366
|
-
setSelectedDestToken,
|
|
367
|
-
setDestinationTokenFromHook,
|
|
368
|
-
setSelectedDestinationChain,
|
|
369
|
-
addRecentToken,
|
|
370
|
-
],
|
|
371
|
-
)
|
|
372
|
-
|
|
373
|
-
// Handle token swap (reverse from/to tokens)
|
|
374
|
-
const handleTokenSwap = useCallback(() => {
|
|
375
|
-
if (selectedToken && selectedDestToken) {
|
|
376
|
-
const tempToken = selectedToken
|
|
377
|
-
|
|
378
|
-
// Find the destination token in the user's token list to get balance info
|
|
379
|
-
const destTokenWithBalance = filteredTokensFormatted?.find(
|
|
380
|
-
(token) =>
|
|
381
|
-
token.contractAddress ===
|
|
382
|
-
(selectedDestToken as any).contractAddress &&
|
|
383
|
-
token.chainId === (selectedDestToken as any).chainId,
|
|
384
|
-
)
|
|
385
|
-
|
|
386
|
-
// Create new source token (from destination) - use balance info if available
|
|
387
|
-
const newSourceToken = {
|
|
388
|
-
...selectedDestToken,
|
|
389
|
-
// Preserve all important properties
|
|
390
|
-
imageUrl: (selectedDestToken as any).imageUrl,
|
|
391
|
-
chainId: (selectedDestToken as any).chainId,
|
|
392
|
-
contractAddress: (selectedDestToken as any).contractAddress,
|
|
393
|
-
contractInfo: (selectedDestToken as any).contractInfo,
|
|
394
|
-
// Use balance info from user's token list if available, otherwise keep existing
|
|
395
|
-
balanceUsdFormatted:
|
|
396
|
-
destTokenWithBalance?.balanceUsdFormatted ||
|
|
397
|
-
(selectedDestToken as any).balanceUsdFormatted ||
|
|
398
|
-
"$0.00",
|
|
399
|
-
balance:
|
|
400
|
-
destTokenWithBalance?.balance ||
|
|
401
|
-
(selectedDestToken as any).balance ||
|
|
402
|
-
"0",
|
|
403
|
-
tokenPriceUsd:
|
|
404
|
-
destTokenWithBalance?.tokenPriceUsd ||
|
|
405
|
-
(selectedDestToken as any).tokenPriceUsd ||
|
|
406
|
-
0,
|
|
407
|
-
balanceUsd:
|
|
408
|
-
destTokenWithBalance?.balanceUsd ||
|
|
409
|
-
(selectedDestToken as any).balanceUsd ||
|
|
410
|
-
"0",
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
const newDestToken = {
|
|
414
|
-
...tempToken,
|
|
415
|
-
// Preserve all important properties that might be lost
|
|
416
|
-
imageUrl: (tempToken as any).imageUrl,
|
|
417
|
-
balanceUsdFormatted: (tempToken as any).balanceUsdFormatted,
|
|
418
|
-
balance: (tempToken as any).balance,
|
|
419
|
-
tokenPriceUsd: (tempToken as any).tokenPriceUsd,
|
|
420
|
-
balanceUsd: (tempToken as any).balanceUsd,
|
|
421
|
-
chainId: (tempToken as any).chainId,
|
|
422
|
-
contractAddress: (tempToken as any).contractAddress,
|
|
423
|
-
contractInfo: (tempToken as any).contractInfo,
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
setSelectedToken(newSourceToken as any)
|
|
427
|
-
setDestinationToken(newDestToken)
|
|
428
|
-
setDestinationTokenFromHook(newDestToken as any)
|
|
429
|
-
setSelectedDestToken(newDestToken as any)
|
|
430
|
-
|
|
431
|
-
// Update destination chain to match the swapped token's chain
|
|
432
|
-
if (setSelectedDestinationChain && newDestToken.chainId) {
|
|
433
|
-
const chainInfo = getChainInfo(newDestToken.chainId)
|
|
434
|
-
if (chainInfo) {
|
|
435
|
-
setSelectedDestinationChain(chainInfo)
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
// Add both tokens to recent tokens when swapping
|
|
440
|
-
addRecentToken(newSourceToken as any)
|
|
441
|
-
addRecentToken(newDestToken as any)
|
|
442
|
-
logger.console.log("[trails-sdk] swapped tokens in SimpleSwap", {
|
|
443
|
-
originalSource: tempToken,
|
|
444
|
-
originalDest: selectedDestToken,
|
|
445
|
-
destTokenWithBalance,
|
|
446
|
-
newSourceToken,
|
|
447
|
-
newDestToken,
|
|
448
|
-
})
|
|
449
|
-
}
|
|
450
|
-
}, [
|
|
451
|
-
selectedToken,
|
|
452
|
-
selectedDestToken,
|
|
453
|
-
setSelectedToken,
|
|
454
|
-
setSelectedDestToken,
|
|
455
|
-
setDestinationTokenFromHook,
|
|
456
|
-
filteredTokensFormatted,
|
|
457
|
-
setSelectedDestinationChain,
|
|
458
|
-
addRecentToken,
|
|
459
|
-
])
|
|
460
|
-
|
|
461
|
-
// Handle recent token selection
|
|
462
|
-
const handleRecentTokenSelect = useCallback(
|
|
463
|
-
(token: any) => {
|
|
464
|
-
// For source token selection
|
|
465
|
-
if (showSourceTokenSelector) {
|
|
466
|
-
handleSourceTokenSelectorSelect(token)
|
|
467
|
-
}
|
|
468
|
-
// For destination token selection
|
|
469
|
-
else if (showDestinationTokenSelector) {
|
|
470
|
-
handleDestinationTokenSelectorSelect(token)
|
|
471
|
-
}
|
|
472
|
-
},
|
|
473
|
-
[
|
|
474
|
-
showSourceTokenSelector,
|
|
475
|
-
showDestinationTokenSelector,
|
|
476
|
-
handleSourceTokenSelectorSelect,
|
|
477
|
-
handleDestinationTokenSelectorSelect,
|
|
478
|
-
],
|
|
479
|
-
)
|
|
480
|
-
|
|
481
|
-
// Handle input type toggle (USD vs Token)
|
|
482
|
-
const handleInputTypeToggle = useCallback(() => {
|
|
483
|
-
// Use tokenAmountForBackend as the source of truth for conversion
|
|
484
|
-
const currentTokenAmount = parseFloat(tokenAmountForBackend) || 0
|
|
485
|
-
const sourceTokenPrice = (selectedToken as any)?.tokenPriceUsd || 0
|
|
486
|
-
|
|
487
|
-
if (isInputTypeUsd && sourceTokenPrice > 0) {
|
|
488
|
-
// Switching from USD to token mode
|
|
489
|
-
// Display the token amount (limit to 8 decimals)
|
|
490
|
-
const tokenAmountStr = Number(currentTokenAmount.toFixed(8)).toString()
|
|
491
|
-
setInputDisplayValue(tokenAmountStr)
|
|
492
|
-
} else if (!isInputTypeUsd && sourceTokenPrice > 0) {
|
|
493
|
-
// Switching from token to USD mode
|
|
494
|
-
// Display USD amount (limit to 2 decimals)
|
|
495
|
-
const usdAmount = currentTokenAmount * sourceTokenPrice
|
|
496
|
-
const usdAmountStr = Number(usdAmount.toFixed(2)).toString()
|
|
497
|
-
setInputDisplayValue(usdAmountStr)
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
// Toggle the mode
|
|
501
|
-
setIsInputTypeUsd(!isInputTypeUsd)
|
|
502
|
-
|
|
503
|
-
// Focus the input field after toggling
|
|
504
|
-
setTimeout(() => {
|
|
505
|
-
if (inputRef.current) {
|
|
506
|
-
inputRef.current.focus()
|
|
507
|
-
// Select all text for easy replacement
|
|
508
|
-
inputRef.current.select()
|
|
509
|
-
}
|
|
510
|
-
}, 0)
|
|
511
|
-
}, [isInputTypeUsd, tokenAmountForBackend, selectedToken])
|
|
512
|
-
|
|
513
|
-
// Dynamic font size based on input length - matching Earn.tsx
|
|
514
|
-
const inputStyles = useMemo(() => {
|
|
515
|
-
const inputLength = displayAmount.length
|
|
516
|
-
let fontSize: string
|
|
517
|
-
|
|
518
|
-
if (inputLength > 12) {
|
|
519
|
-
fontSize = "1rem"
|
|
520
|
-
} else if (inputLength > 9) {
|
|
521
|
-
fontSize = "1.25rem"
|
|
522
|
-
} else if (inputLength > 6) {
|
|
523
|
-
fontSize = "1.5rem"
|
|
524
|
-
} else if (inputLength > 3) {
|
|
525
|
-
fontSize = "1.75rem"
|
|
526
|
-
} else {
|
|
527
|
-
fontSize = "2rem"
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
return {
|
|
531
|
-
fontSize,
|
|
532
|
-
transition: "all 0.1s ease-in-out",
|
|
533
|
-
}
|
|
534
|
-
}, [displayAmount.length])
|
|
535
|
-
|
|
536
|
-
logger.console.log("[trails-sdk] SimpleSwap", {
|
|
537
|
-
tokenAmountForBackend,
|
|
538
|
-
displayAmount,
|
|
539
|
-
isInputTypeUsd,
|
|
540
|
-
amountUsdDisplay,
|
|
541
|
-
selectedToken,
|
|
542
|
-
})
|
|
543
|
-
|
|
544
|
-
// Debug swap button disabled state
|
|
545
|
-
logger.console.log("[trails-sdk] SimpleSwap button state", {
|
|
546
|
-
amount,
|
|
547
|
-
isSubmitting,
|
|
548
|
-
destinationTokenAddress,
|
|
549
|
-
isValidCustomToken,
|
|
550
|
-
isLoadingQuote,
|
|
551
|
-
prepareSendQuote: !!prepareSendQuote,
|
|
552
|
-
noSufficientBalance: prepareSendQuote?.noSufficientBalance,
|
|
553
|
-
isSameTokenWithoutCustomCalldata,
|
|
554
|
-
selectedDestToken: !!selectedDestToken,
|
|
555
|
-
buttonDisabled:
|
|
556
|
-
!amount ||
|
|
557
|
-
isSubmitting ||
|
|
558
|
-
!destinationTokenAddress ||
|
|
559
|
-
!isValidCustomToken ||
|
|
560
|
-
isLoadingQuote ||
|
|
561
|
-
!prepareSendQuote ||
|
|
562
|
-
prepareSendQuote?.noSufficientBalance ||
|
|
563
|
-
isSameTokenWithoutCustomCalldata,
|
|
564
|
-
})
|
|
565
|
-
|
|
566
|
-
// Show source chain list screen
|
|
567
|
-
if (showSourceChainList) {
|
|
568
|
-
return (
|
|
569
|
-
<ChainList
|
|
570
|
-
onBack={() => {
|
|
571
|
-
setShowSourceChainList(false)
|
|
572
|
-
setShowSourceTokenSelector(true)
|
|
573
|
-
}}
|
|
574
|
-
/>
|
|
575
|
-
)
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
// Show source token selector screen
|
|
579
|
-
if (showSourceTokenSelector) {
|
|
580
|
-
return (
|
|
581
|
-
<div className="space-y-2">
|
|
582
|
-
{showHeader && (
|
|
583
|
-
<ScreenHeader
|
|
584
|
-
onBack={() => setShowSourceTokenSelector(false)}
|
|
585
|
-
headerContent="Select From Token"
|
|
586
|
-
headerContentAlign="left"
|
|
587
|
-
/>
|
|
588
|
-
)}
|
|
589
|
-
<TokenSelector
|
|
590
|
-
onTokenSelect={handleSourceTokenSelectorSelect}
|
|
591
|
-
onError={onError}
|
|
592
|
-
showContinueButton={false}
|
|
593
|
-
compactMode={false}
|
|
594
|
-
allSupportedTokens={false}
|
|
595
|
-
chainListScreen={true}
|
|
596
|
-
onNavigateToChainList={() => {
|
|
597
|
-
setShowSourceTokenSelector(false)
|
|
598
|
-
setShowSourceChainList(true)
|
|
599
|
-
}}
|
|
600
|
-
recentTokens={recentTokens}
|
|
601
|
-
onRecentTokenSelect={handleRecentTokenSelect}
|
|
602
|
-
/>
|
|
603
|
-
</div>
|
|
604
|
-
)
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
// Show destination chain list screen
|
|
608
|
-
if (showDestinationChainList) {
|
|
609
|
-
return (
|
|
610
|
-
<ChainList
|
|
611
|
-
onBack={() => {
|
|
612
|
-
setShowDestinationChainList(false)
|
|
613
|
-
setShowDestinationTokenSelector(true)
|
|
614
|
-
}}
|
|
615
|
-
/>
|
|
616
|
-
)
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
// Show destination token selector screen
|
|
620
|
-
if (showDestinationTokenSelector) {
|
|
621
|
-
return (
|
|
622
|
-
<div className="space-y-2">
|
|
623
|
-
{showHeader && (
|
|
624
|
-
<ScreenHeader
|
|
625
|
-
onBack={() => setShowDestinationTokenSelector(false)}
|
|
626
|
-
headerContent="Select To Token"
|
|
627
|
-
headerContentAlign="left"
|
|
628
|
-
/>
|
|
629
|
-
)}
|
|
630
|
-
<TokenSelector
|
|
631
|
-
onTokenSelect={handleDestinationTokenSelectorSelect}
|
|
632
|
-
onError={onError}
|
|
633
|
-
showContinueButton={false}
|
|
634
|
-
compactMode={false}
|
|
635
|
-
allSupportedTokens={true}
|
|
636
|
-
chainListScreen={true}
|
|
637
|
-
onNavigateToChainList={() => {
|
|
638
|
-
setShowDestinationTokenSelector(false)
|
|
639
|
-
setShowDestinationChainList(true)
|
|
640
|
-
}}
|
|
641
|
-
recentTokens={recentTokens}
|
|
642
|
-
onRecentTokenSelect={handleRecentTokenSelect}
|
|
643
|
-
/>
|
|
644
|
-
</div>
|
|
645
|
-
)
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
if (!selectedToken) {
|
|
649
|
-
return (
|
|
650
|
-
<div className="space-y-2">
|
|
651
|
-
{showHeader && (
|
|
652
|
-
<ScreenHeader
|
|
653
|
-
onBack={onBack}
|
|
654
|
-
headerContent="Swap"
|
|
655
|
-
headerContentAlign="left"
|
|
656
|
-
/>
|
|
657
|
-
)}
|
|
658
|
-
|
|
659
|
-
<div className="space-y-2">
|
|
660
|
-
{/* Token Selector */}
|
|
661
|
-
<div className="relative">
|
|
662
|
-
<button
|
|
663
|
-
type="button"
|
|
664
|
-
onClick={() => setShowSourceTokenSelector(true)}
|
|
665
|
-
className="w-full flex items-center justify-between space-x-3 trails-list-item trails-border-radius-list-button px-4 py-2 transition-all duration-200 cursor-pointer"
|
|
666
|
-
>
|
|
667
|
-
<div className="flex items-center space-x-3 flex-1">
|
|
668
|
-
<div className="w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
|
|
669
|
-
<Search className="w-4 h-4 text-gray-400" />
|
|
670
|
-
</div>
|
|
671
|
-
<div className="text-left flex-1">
|
|
672
|
-
<div className="font-medium text-gray-900 dark:text-white text-sm">
|
|
673
|
-
From token
|
|
674
|
-
</div>
|
|
675
|
-
<div className="text-xs trails-text-muted">Select token</div>
|
|
676
|
-
</div>
|
|
677
|
-
</div>
|
|
678
|
-
<ChevronRight className="w-4 h-4 trails-text-muted flex-shrink-0" />
|
|
679
|
-
</button>
|
|
680
|
-
</div>
|
|
681
|
-
</div>
|
|
682
|
-
</div>
|
|
683
|
-
)
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
return (
|
|
687
|
-
<div className="space-y-2">
|
|
688
|
-
{showHeader && (
|
|
689
|
-
<ScreenHeader
|
|
690
|
-
onBack={onBack}
|
|
691
|
-
headerContent="Swap"
|
|
692
|
-
headerContentAlign="left"
|
|
693
|
-
/>
|
|
694
|
-
)}
|
|
695
|
-
|
|
696
|
-
<div className="space-y-1">
|
|
697
|
-
{/* Amount Input Section */}
|
|
698
|
-
<div className="space-y-1">
|
|
699
|
-
{/* Amount Input with Max Button */}
|
|
700
|
-
<div className="flex items-center justify-between relative px-4 py-2">
|
|
701
|
-
<div className="flex items-center flex-1">
|
|
702
|
-
<input
|
|
703
|
-
ref={inputRef}
|
|
704
|
-
type="text"
|
|
705
|
-
value={displayAmount}
|
|
706
|
-
onChange={(e) => handleAmountChange(e.target.value)}
|
|
707
|
-
placeholder="0"
|
|
708
|
-
className="bg-transparent border-none outline-none font-bold text-left trails-text-primary placeholder-trails-text-primary"
|
|
709
|
-
style={{
|
|
710
|
-
fontSize: inputStyles.fontSize,
|
|
711
|
-
width: `${Math.max((displayAmount || "0").length, 1)}ch`,
|
|
712
|
-
minWidth: "1ch",
|
|
713
|
-
maxWidth: "270px",
|
|
714
|
-
padding: "0",
|
|
715
|
-
margin: "0",
|
|
716
|
-
transition: "all 0.1s ease-in-out",
|
|
717
|
-
}}
|
|
718
|
-
inputMode="decimal"
|
|
719
|
-
/>
|
|
720
|
-
<span
|
|
721
|
-
className="font-bold text-gray-400 dark:text-gray-500"
|
|
722
|
-
style={{
|
|
723
|
-
fontSize: inputStyles.fontSize,
|
|
724
|
-
marginLeft:
|
|
725
|
-
displayAmount && displayAmount !== "0" ? "0.2em" : "0.3em",
|
|
726
|
-
padding: "0",
|
|
727
|
-
transition: "all 0.2s ease-in-out",
|
|
728
|
-
}}
|
|
729
|
-
>
|
|
730
|
-
{isInputTypeUsd ? "USD" : selectedToken.symbol.slice(0, 4)}
|
|
731
|
-
</span>
|
|
732
|
-
</div>
|
|
733
|
-
|
|
734
|
-
{/* Percentage Buttons */}
|
|
735
|
-
<div
|
|
736
|
-
className="flex items-center gap-1.5"
|
|
737
|
-
onMouseEnter={() => setShowPercentageButtons(true)}
|
|
738
|
-
onMouseLeave={() => setShowPercentageButtons(false)}
|
|
739
|
-
>
|
|
740
|
-
{showPercentageButtons && (
|
|
741
|
-
<>
|
|
742
|
-
<button
|
|
743
|
-
type="button"
|
|
744
|
-
onClick={() => handlePercentageClick(25)}
|
|
745
|
-
className="flex h-6 px-2 py-0.5 justify-center items-center rounded-full bg-gray-200 dark:bg-gray-600 text-gray-700 dark:text-gray-300 text-xs font-medium transition-all duration-200 hover:bg-gray-300 dark:hover:bg-gray-500 cursor-pointer animate-in slide-in-from-right-2 fade-in"
|
|
746
|
-
>
|
|
747
|
-
25%
|
|
748
|
-
</button>
|
|
749
|
-
<button
|
|
750
|
-
type="button"
|
|
751
|
-
onClick={() => handlePercentageClick(50)}
|
|
752
|
-
className="flex h-6 px-2 py-0.5 justify-center items-center rounded-full bg-gray-200 dark:bg-gray-600 text-gray-700 dark:text-gray-300 text-xs font-medium transition-all duration-200 hover:bg-gray-300 dark:hover:bg-gray-500 cursor-pointer animate-in slide-in-from-right-2 fade-in"
|
|
753
|
-
>
|
|
754
|
-
50%
|
|
755
|
-
</button>
|
|
756
|
-
</>
|
|
757
|
-
)}
|
|
758
|
-
<button
|
|
759
|
-
type="button"
|
|
760
|
-
onClick={handleMaxClick}
|
|
761
|
-
className="flex h-6 px-2 py-0.5 justify-center items-center rounded-full bg-gray-200 dark:bg-gray-600 text-gray-700 dark:text-gray-300 text-xs font-medium transition-all duration-200 hover:bg-gray-300 dark:hover:bg-gray-500 cursor-pointer"
|
|
762
|
-
>
|
|
763
|
-
MAX
|
|
764
|
-
</button>
|
|
765
|
-
</div>
|
|
766
|
-
</div>
|
|
767
|
-
|
|
768
|
-
{/* USD Value Toggle */}
|
|
769
|
-
<div className="flex items-center justify-start">
|
|
770
|
-
<button
|
|
771
|
-
type="button"
|
|
772
|
-
onClick={handleInputTypeToggle}
|
|
773
|
-
className="flex items-center justify-start gap-2 px-3 py-1.5 rounded-md transition-colors cursor-pointer text-blue-600 hover:bg-gray-50 dark:hover:bg-gray-700 hover:text-blue-700"
|
|
774
|
-
style={{ color: "#155DFC" }}
|
|
775
|
-
>
|
|
776
|
-
<svg
|
|
777
|
-
width="20"
|
|
778
|
-
height="21"
|
|
779
|
-
viewBox="0 0 20 21"
|
|
780
|
-
fill="none"
|
|
781
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
782
|
-
>
|
|
783
|
-
<path
|
|
784
|
-
d="M11.3879 14.7175V10.4181C11.3879 10.2209 11.4547 10.0554 11.5884 9.9217C11.7216 9.78848 11.8868 9.72187 12.0841 9.72187C12.2814 9.72187 12.4469 9.78848 12.5805 9.9217C12.7138 10.0554 12.7804 10.2209 12.7804 10.4181V14.7175L14.0859 13.412C14.2135 13.2844 14.3729 13.2175 14.5642 13.2115C14.7559 13.2059 14.9214 13.2728 15.0606 13.412C15.1999 13.5397 15.2725 13.6991 15.2785 13.8904C15.2841 14.0821 15.2173 14.2475 15.078 14.3868L12.5715 16.8933C12.5019 16.963 12.4264 17.0122 12.3452 17.0409C12.264 17.0702 12.1769 17.0848 12.0841 17.0848C11.9913 17.0848 11.9042 17.0702 11.823 17.0409C11.7418 17.0122 11.6664 16.963 11.5967 16.8933L9.09021 14.3868C8.96256 14.2592 8.89874 14.0997 8.89874 13.9085C8.89874 13.7168 8.96836 13.5513 9.10761 13.412C9.24687 13.2844 9.40933 13.2175 9.59499 13.2115C9.78066 13.2059 9.94312 13.2728 10.0824 13.412L11.3879 14.7175ZM7.21031 6.11874L5.90483 7.42422C5.75397 7.57508 5.58571 7.6447 5.40004 7.6331C5.21437 7.6215 5.05772 7.55187 4.93007 7.42422C4.79082 7.28497 4.72119 7.11949 4.72119 6.92779C4.72119 6.73655 4.78501 6.57711 4.91266 6.44946L7.41919 3.94294C7.48882 3.87331 7.56424 3.82388 7.64547 3.79463C7.7267 3.76585 7.81374 3.75146 7.90657 3.75146C7.9994 3.75146 8.08644 3.76585 8.16767 3.79463C8.2489 3.82388 8.32432 3.87331 8.39395 3.94294L10.9005 6.44946C11.0281 6.57711 11.0919 6.73957 11.0919 6.93684C11.0919 7.13412 11.0281 7.29658 10.9005 7.42422C10.7612 7.56347 10.596 7.6331 10.4047 7.6331C10.213 7.6331 10.0476 7.56347 9.90831 7.42422L8.60283 6.11874V10.4181C8.60283 10.6154 8.53622 10.7806 8.403 10.9139C8.26932 11.0475 8.10384 11.1144 7.90657 11.1144C7.7093 11.1144 7.54405 11.0475 7.41083 10.9139C7.27715 10.7806 7.21031 10.6154 7.21031 10.4181V6.11874Z"
|
|
785
|
-
fill="#155DFC"
|
|
786
|
-
/>
|
|
787
|
-
</svg>
|
|
788
|
-
<div
|
|
789
|
-
className="text-sm font-extrabold leading-[130%]"
|
|
790
|
-
style={{ color: "#155DFC" }}
|
|
791
|
-
>
|
|
792
|
-
{isBalanceVisible ? displayUsdValue : "••••••"}
|
|
793
|
-
</div>
|
|
794
|
-
</button>
|
|
795
|
-
</div>
|
|
796
|
-
</div>
|
|
797
|
-
|
|
798
|
-
{/* Source Token Selector */}
|
|
799
|
-
<div className="relative">
|
|
800
|
-
<button
|
|
801
|
-
type="button"
|
|
802
|
-
onClick={() => setShowSourceTokenSelector(true)}
|
|
803
|
-
className="w-full flex items-center justify-between space-x-3 trails-list-item hover:scale-[1.01] trails-border-radius-list-button px-4 py-2 transition-all duration-200 cursor-pointer"
|
|
804
|
-
>
|
|
805
|
-
<div className="flex items-center space-x-3 flex-1">
|
|
806
|
-
{selectedToken ? (
|
|
807
|
-
<>
|
|
808
|
-
<TokenImage
|
|
809
|
-
symbol={selectedToken.symbol}
|
|
810
|
-
imageUrl={selectedToken.imageUrl}
|
|
811
|
-
chainId={selectedToken.chainId}
|
|
812
|
-
size={24}
|
|
813
|
-
/>
|
|
814
|
-
<div className="text-left flex-1">
|
|
815
|
-
<div className="font-medium trails-text-primary text-sm">
|
|
816
|
-
{selectedToken.symbol}
|
|
817
|
-
</div>
|
|
818
|
-
<div className="text-xs trails-text-muted">
|
|
819
|
-
on{" "}
|
|
820
|
-
{getChainInfo(selectedToken.chainId)?.name || "Unknown"}
|
|
821
|
-
</div>
|
|
822
|
-
</div>
|
|
823
|
-
</>
|
|
824
|
-
) : (
|
|
825
|
-
<>
|
|
826
|
-
<div className="w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
|
|
827
|
-
<Search className="w-4 h-4 text-gray-400" />
|
|
828
|
-
</div>
|
|
829
|
-
<div className="text-left flex-1">
|
|
830
|
-
<div className="font-medium trails-text-muted text-sm">
|
|
831
|
-
From
|
|
832
|
-
</div>
|
|
833
|
-
<div className="text-xs trails-text-muted">
|
|
834
|
-
Select token
|
|
835
|
-
</div>
|
|
836
|
-
</div>
|
|
837
|
-
</>
|
|
838
|
-
)}
|
|
839
|
-
</div>
|
|
840
|
-
|
|
841
|
-
{selectedToken && balanceFormatted && (
|
|
842
|
-
<div className="text-right">
|
|
843
|
-
<div className="font-medium trails-text-primary text-sm">
|
|
844
|
-
{isBalanceVisible
|
|
845
|
-
? (selectedToken as any).balanceUsdFormatted || "$0.00"
|
|
846
|
-
: "••••••"}
|
|
847
|
-
</div>
|
|
848
|
-
<div className="text-xs trails-text-muted">available</div>
|
|
849
|
-
</div>
|
|
850
|
-
)}
|
|
851
|
-
|
|
852
|
-
<ChevronRight className="w-4 h-4 trails-text-muted flex-shrink-0" />
|
|
853
|
-
</button>
|
|
854
|
-
</div>
|
|
855
|
-
|
|
856
|
-
{/* Swap Button */}
|
|
857
|
-
<div className="flex justify-start py-0 pl-4">
|
|
858
|
-
<button
|
|
859
|
-
type="button"
|
|
860
|
-
onClick={handleTokenSwap}
|
|
861
|
-
className="transition-colors cursor-pointer rounded-full border-2 border-white dark:border-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700"
|
|
862
|
-
title="Swap tokens"
|
|
863
|
-
disabled={!selectedToken || !selectedDestToken}
|
|
864
|
-
>
|
|
865
|
-
<ArrowDown
|
|
866
|
-
className={`w-5 h-5 ${!selectedToken ? "text-gray-300 dark:text-gray-600" : "text-gray-900 dark:text-white"}`}
|
|
867
|
-
strokeWidth={2.5}
|
|
868
|
-
/>
|
|
869
|
-
</button>
|
|
870
|
-
</div>
|
|
871
|
-
|
|
872
|
-
{/* Destination Token Selector */}
|
|
873
|
-
<div className="relative">
|
|
874
|
-
<button
|
|
875
|
-
type="button"
|
|
876
|
-
onClick={() => setShowDestinationTokenSelector(true)}
|
|
877
|
-
className="w-full flex items-center justify-between space-x-3 trails-list-item hover:scale-[1.01] trails-border-radius-list-button px-4 py-2 transition-all duration-200 cursor-pointer"
|
|
878
|
-
>
|
|
879
|
-
<div className="flex items-center space-x-3 flex-1">
|
|
880
|
-
{selectedDestToken ? (
|
|
881
|
-
<>
|
|
882
|
-
<TokenImage
|
|
883
|
-
symbol={selectedDestToken.symbol}
|
|
884
|
-
imageUrl={selectedDestToken.imageUrl}
|
|
885
|
-
chainId={(selectedDestToken as any)?.chainId}
|
|
886
|
-
size={24}
|
|
887
|
-
/>
|
|
888
|
-
<div className="text-left flex-1">
|
|
889
|
-
<div className="font-medium trails-text-primary text-sm">
|
|
890
|
-
{selectedDestToken.symbol}
|
|
891
|
-
</div>
|
|
892
|
-
<div className="text-xs trails-text-muted">
|
|
893
|
-
on{" "}
|
|
894
|
-
{getChainInfo((selectedDestToken as any)?.chainId)
|
|
895
|
-
?.name || "Unknown"}
|
|
896
|
-
</div>
|
|
897
|
-
</div>
|
|
898
|
-
</>
|
|
899
|
-
) : (
|
|
900
|
-
<>
|
|
901
|
-
<div className="w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
|
|
902
|
-
<Search className="w-4 h-4 text-gray-400" />
|
|
903
|
-
</div>
|
|
904
|
-
<div className="text-left flex-1">
|
|
905
|
-
<div className="font-medium text-gray-900 dark:text-white text-sm">
|
|
906
|
-
To
|
|
907
|
-
</div>
|
|
908
|
-
<div className="text-xs trails-text-muted">
|
|
909
|
-
Select token
|
|
910
|
-
</div>
|
|
911
|
-
</div>
|
|
912
|
-
</>
|
|
913
|
-
)}
|
|
914
|
-
</div>
|
|
915
|
-
|
|
916
|
-
<ChevronRight className="w-4 h-4 trails-text-muted flex-shrink-0" />
|
|
917
|
-
</button>
|
|
918
|
-
</div>
|
|
919
|
-
|
|
920
|
-
{/* Error Display */}
|
|
921
|
-
{isSameTokenWithoutCustomCalldata ? (
|
|
922
|
-
<ErrorDisplay
|
|
923
|
-
errorPrettified="Cannot swap to the same token on the same chain without custom calldata. Please select a different destination token."
|
|
924
|
-
severity="error"
|
|
925
|
-
/>
|
|
926
|
-
) : (
|
|
927
|
-
<ErrorDisplay
|
|
928
|
-
errorPrettified={quoteErrorPrettified}
|
|
929
|
-
error={quoteError}
|
|
930
|
-
severity="warning"
|
|
931
|
-
/>
|
|
932
|
-
)}
|
|
933
|
-
|
|
934
|
-
<div className="py-2"></div>
|
|
935
|
-
|
|
936
|
-
{/* Swap Button */}
|
|
937
|
-
<form onSubmit={handleSubmit}>
|
|
938
|
-
<button
|
|
939
|
-
type="submit"
|
|
940
|
-
disabled={
|
|
941
|
-
!amount ||
|
|
942
|
-
isSubmitting ||
|
|
943
|
-
!destinationTokenAddress ||
|
|
944
|
-
!isValidCustomToken ||
|
|
945
|
-
isLoadingQuote ||
|
|
946
|
-
!prepareSendQuote ||
|
|
947
|
-
prepareSendQuote?.noSufficientBalance ||
|
|
948
|
-
isSameTokenWithoutCustomCalldata
|
|
949
|
-
}
|
|
950
|
-
className="w-full font-semibold py-4 px-4 trails-border-radius-button transition-colors bg-blue-500 hover:bg-blue-600 disabled:bg-gray-300 text-white disabled:text-gray-500 disabled:cursor-not-allowed cursor-pointer relative"
|
|
951
|
-
>
|
|
952
|
-
{isSubmitting ? (
|
|
953
|
-
<div className="flex items-center justify-center">
|
|
954
|
-
<Loader2 className="w-5 h-5 animate-spin mr-2" />
|
|
955
|
-
<span>{buttonText}</span>
|
|
956
|
-
</div>
|
|
957
|
-
) : isSameTokenWithoutCustomCalldata ? (
|
|
958
|
-
"Select Different Tokens"
|
|
959
|
-
) : prepareSendQuote?.noSufficientBalance ? (
|
|
960
|
-
"Insufficient Balance"
|
|
961
|
-
) : !selectedToken || !selectedDestToken ? (
|
|
962
|
-
"Select tokens to swap"
|
|
963
|
-
) : !amount || parseFloat(amount) <= 0 ? (
|
|
964
|
-
"Enter an amount"
|
|
965
|
-
) : (
|
|
966
|
-
buttonText ||
|
|
967
|
-
`Swap ${selectedToken.symbol} for ${selectedDestToken.symbol}`
|
|
968
|
-
)}
|
|
969
|
-
</button>
|
|
970
|
-
</form>
|
|
971
|
-
|
|
972
|
-
{/* Quote Details */}
|
|
973
|
-
{prepareSendQuote && (
|
|
974
|
-
<div className="space-y-2">
|
|
975
|
-
<QuoteDetails quote={prepareSendQuote} showContent={true} />
|
|
976
|
-
</div>
|
|
977
|
-
)}
|
|
978
|
-
</div>
|
|
979
|
-
</div>
|
|
980
|
-
)
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
export default SimpleSwap
|