import { useMemo, useState } from 'react' import { Notification } from '@shared/ui' import { useDebounce } from '@shared/lib/hooks/useDebounce' import { usePools, useLpPositions, useLpQuote, useAddLp, useRemoveLp, getPoolsErrorMessage, type LpDepositInput, type LpPosition, } from '@features/pools' import { PoolsTable } from './PoolsTable' import { LpDepositCard } from './LpDepositCard' import { LpPositionsPanel } from './LpPositionsPanel' import { LpRemoveModal } from './LpRemoveModal' import styles from './PoolsWidget.module.css' type Toast = { status: 'success' | 'error' | 'info'; message: string } | null // Предзаполнение диапазона при выборе пула: текущая цена ±5%. const RANGE_SPREAD = 0.05 export function PoolsWidget() { const poolsQuery = usePools() const positionsQuery = useLpPositions() const addM = useAddLp() const removeM = useRemoveLp() const pools = poolsQuery.data ?? [] const positions = positionsQuery.data?.positions ?? [] const [selected, setSelected] = useState(null) const [amount0, setAmount0] = useState('') const [amount1, setAmount1] = useState('') const [priceLower, setPriceLower] = useState('') const [priceUpper, setPriceUpper] = useState('') const [slippage, setSlippage] = useState('1') const [useNativeEth, setUseNativeEth] = useState(true) const [removeTarget, setRemoveTarget] = useState(null) const [toast, setToast] = useState(null) const pool = pools.find((p) => p.poolAddress === selected) ?? null function handleSelect(poolAddress: string) { setSelected(poolAddress) const next = pools.find((p) => p.poolAddress === poolAddress) // Предзаполняем диапазон ±5% от текущей цены (юзер может поправить вручную). if (next) { setPriceLower((next.currentPrice * (1 - RANGE_SPREAD)).toFixed(2)) setPriceUpper((next.currentPrice * (1 + RANGE_SPREAD)).toFixed(2)) } setAmount0('') setAmount1('') } // Дебаунсим вход квоты, чтобы не дёргать /lp/quote на каждый символ. const dAmount0 = useDebounce(amount0, 400) const dAmount1 = useDebounce(amount1, 400) const dPriceLower = useDebounce(priceLower, 400) const dPriceUpper = useDebounce(priceUpper, 400) const dSlippage = useDebounce(slippage, 400) const quoteInput = useMemo(() => { if (!pool) return null const slippageNum = parseFloat(dSlippage) return { pool, amount0Human: dAmount0, amount1Human: dAmount1, priceLower: parseFloat(dPriceLower) || 0, priceUpper: parseFloat(dPriceUpper) || 0, slippageBps: Number.isFinite(slippageNum) ? Math.round(slippageNum * 100) : undefined, useNativeEth, } }, [pool, dAmount0, dAmount1, dPriceLower, dPriceUpper, dSlippage, useNativeEth]) const quoteQuery = useLpQuote(quoteInput) function handleSubmit() { if (!pool) return const slippageNum = parseFloat(slippage) const input: LpDepositInput = { pool, amount0Human: amount0, amount1Human: amount1, priceLower: parseFloat(priceLower) || 0, priceUpper: parseFloat(priceUpper) || 0, slippageBps: Number.isFinite(slippageNum) ? Math.round(slippageNum * 100) : undefined, useNativeEth, } addM.mutate(input, { onSuccess: () => { setToast({ status: 'success', message: `Депозит в пул ${pool.symbol0}/${pool.symbol1} выполнен` }) setAmount0('') setAmount1('') }, onError: (e) => setToast({ status: 'error', message: getPoolsErrorMessage(e) }), }) } function handleConfirmRemove(percent: number) { if (!removeTarget) return removeM.mutate( { tokenId: removeTarget.tokenId, percent }, { onSuccess: () => { setRemoveTarget(null) setToast({ status: 'success', message: 'Ликвидность выведена на кошелёк' }) }, onError: (e) => setToast({ status: 'error', message: getPoolsErrorMessage(e) }), } ) } return (
setRemoveTarget(positions.find((p) => p.tokenId === tokenId) ?? null)} removePending={removeM.isPending} isLoading={positionsQuery.isLoading} error={positionsQuery.isError ? getPoolsErrorMessage(positionsQuery.error) : null} />
{removeTarget && ( setRemoveTarget(null)} /> )} {toast && setToast(null)} />}
) }