14.05.2026 rip

This commit is contained in:
2026-05-14 23:57:47 +03:00
parent 7c8e812d4b
commit 6fc9f38182
13 changed files with 360 additions and 12 deletions

View File

@@ -0,0 +1,177 @@
import { useState, useEffect } from 'react'
import { PrimaryButton } from '@shared/ui'
import {
useWalletBalance, useWalletAddresses, useTokensList,
useRelayQuote, useExecuteRelaySwap, useSignSwap,
useTrxSwapQuote, useFetchTrxQuote, useExecuteTrxSwap,
type Chain, type RelaySwapResponse, type TrxSwapQuoteData,
} from '@features/wallet'
import { useDebounce } from '@shared/lib/hooks/useDebounce'
import { TOKENS_LIST, buildTokensFromBalance, useSwapForm } from '../../swap-form/model/useSwapForm'
import { SwapCard } from '../../swap-form/ui/SwapCard'
import { SwapDirectionButton } from '../../swap-form/ui/SwapDirectionButton'
import { SwapInfoPanel } from '../../swap-form/ui/SwapInfoPanel'
import { SwapConfirmModal } from '../../swap-form/ui/SwapConfirmModal'
import { TrxConfirmModal } from '../../swap-form/ui/TrxConfirmModal'
import { NetworkSelect } from './NetworkSelect'
import styles from './BridgeForm.module.css'
const CHAIN_ID: Record<string, number> = { ETH: 1, BSC: 56, SOL: 792703809 }
const NATIVE_ADDR: Record<string, string> = {
SOL: '11111111111111111111111111111111',
DEFAULT: '0x0000000000000000000000000000000000000000',
}
function nativeAddr(chain: string) {
return NATIVE_ADDR[chain] ?? NATIVE_ADDR.DEFAULT
}
export function BridgeForm() {
const {
fromAmount, fromUsd,
fromToken, toToken,
setFromAmount, setPercent, swapTokens,
setFromToken, setToToken,
} = useSwapForm()
const [fromNetwork, setFromNetwork] = useState('ETH')
const [toNetwork, setToNetwork] = useState('BSC')
const [modalData, setModalData] = useState<RelaySwapResponse | null>(null)
const [trxModalQuote, setTrxModalQuote] = useState<TrxSwapQuoteData | null>(null)
const isTrxNetwork = fromNetwork === 'TRX'
const { data: walletData } = useWalletBalance(fromNetwork as Chain)
const tokenOptions = walletData ? buildTokensFromBalance(walletData) : TOKENS_LIST
useEffect(() => {
if (tokenOptions.length === 0) return
setFromToken(t => tokenOptions.find(o => o.symbol === t.symbol) ?? tokenOptions[0])
setToToken(t => tokenOptions.find(o => o.symbol === t.symbol) ?? (tokenOptions[1] ?? tokenOptions[0]))
}, [walletData, fromNetwork])
const debouncedAmount = useDebounce(fromAmount, 500)
const { data: addresses } = useWalletAddresses()
const { data: tokensList } = useTokensList()
const parsedAmount = parseFloat(debouncedAmount)
const chainId = CHAIN_ID[fromNetwork]
const walletAddress = addresses?.find(a => a.chain === fromNetwork)?.address
const fromContract = tokensList?.find(t => t.chain === fromNetwork && t.symbol === fromToken.symbol)?.contract ?? nativeAddr(fromNetwork)
const toContract = tokensList?.find(t => t.chain === fromNetwork && t.symbol === toToken.symbol)?.contract ?? nativeAddr(fromNetwork)
const quotePayload = !isTrxNetwork && chainId && walletAddress && parsedAmount > 0
? {
user: walletAddress,
recipient: walletAddress,
originChainId: chainId,
destinationChainId: chainId,
originCurrency: fromContract,
destinationCurrency: toContract,
amount: Math.round(parsedAmount * Math.pow(10, fromToken.decimals)).toString(),
tradeType: 'EXACT_INPUT' as const,
}
: null
const { data: quoteData } = useRelayQuote(quotePayload)
const { mutate: executeSwap, isPending: isSwapping } = useExecuteRelaySwap()
const { mutate: signSwap } = useSignSwap()
const trxQuotePayload = isTrxNetwork && parsedAmount > 0
? { from: fromToken.symbol, to: toToken.symbol, amountHuman: debouncedAmount }
: null
const { data: trxQuoteData } = useTrxSwapQuote(trxQuotePayload)
const { mutate: fetchTrxQuote, isPending: isFetchingTrxQuote } = useFetchTrxQuote()
const { mutate: executeTrxSwap } = useExecuteTrxSwap()
const displayToAmount = isTrxNetwork
? (trxQuoteData?.expectedOutFormatted ?? '0')
: (quoteData?.details.currencyOut.amountFormatted ?? '0')
const displayToUsd = isTrxNetwork
? undefined
: quoteData?.details.currencyOut.amountUsd
const gasFee = isTrxNetwork
? trxQuoteData?.fees.network.amountUsd?.toString()
: quoteData?.fees.gas.amountUsd
const isButtonDisabled = isTrxNetwork
? parsedAmount <= 0 || isFetchingTrxQuote
: !quotePayload || isSwapping
function handleSwap() {
if (isTrxNetwork) {
if (!trxQuotePayload) return
fetchTrxQuote(trxQuotePayload, {
onSuccess: (quote) => setTrxModalQuote(quote),
})
} else {
if (!quotePayload) return
executeSwap(quotePayload, {
onSuccess: (data) => setModalData(data),
})
}
}
return (
<div className={styles.form}>
<NetworkSelect label="ИЗ" value={fromNetwork} onChange={setFromNetwork} />
<SwapCard
mode="from"
token={fromToken}
tokenOptions={tokenOptions}
amount={fromAmount}
usd={fromUsd}
onAmountChange={setFromAmount}
onSetPercent={setPercent}
onTokenChange={setFromToken}
hideNetworkSelect
/>
<SwapDirectionButton onClick={swapTokens} />
<NetworkSelect label="В" value={toNetwork} onChange={setToNetwork} />
<SwapCard
mode="to"
token={toToken}
tokenOptions={tokenOptions}
amount={displayToAmount}
usd={displayToUsd}
onTokenChange={setToToken}
hideNetworkSelect
/>
<SwapInfoPanel gasFee={gasFee} />
<PrimaryButton onClick={handleSwap} disabled={isButtonDisabled} />
{modalData && (
<SwapConfirmModal
data={modalData}
onClose={() => setModalData(null)}
onConfirm={() => {
const txData = modalData.steps[0]?.items[0]?.data
if (txData) signSwap({ chain: fromNetwork as Chain, txData })
setModalData(null)
}}
/>
)}
{trxModalQuote && (
<TrxConfirmModal
quote={trxModalQuote}
fromSymbol={fromToken.symbol}
toSymbol={toToken.symbol}
amountHuman={fromAmount}
onClose={() => setTrxModalQuote(null)}
onConfirm={() => {
executeTrxSwap(trxModalQuote.quoteId)
setTrxModalQuote(null)
}}
/>
)}
</div>
)
}