DEX swap-интерфейс — это больше, чем кнопка "Обменять". Под капотом: получение котировок on-chain, расчёт price impact и slippage, двухшаговый approve+swap, обработка native ETH и WETH. Мы сталкивались с кейсами, когда игнорирование deadline приводило к зависшим транзакциям, а отсутствие debounce на котировках — к спаму RPC. Один из наших клиентов хотел полный контроль над комиссиями, поэтому мы спроектировали гибридную архитектуру: собственный роутер для пары с native токеном и агрегатор 0x для остальных. Результат — на 35% ниже газовые издержки по сравнению с чистым Uniswap. В этой статье разберём, как собрать надёжный интерфейс для обмена токенов: от выбора роутера до компонента с настройками. Закажите разработку DEX-интерфейса для вашего проекта.
Как работает DEX swap-интерфейс?
Основные технические сложности, которые мы решаем:
- N+1 запросов котировок: без debounce каждый символ в поле amount отправляет RPC-запрос, что блокирует UI и расходует лимиты провайдера.
- Некорректный расчёт price impact: при больших суммах прямая котировка через getAmountsOut не учитывает глубину пула — нужна симуляция или расчёт по резервам.
- Отсутствие обработки deadline: если транзакция не прошла за заданный период (обычно 20 минут), она может быть выполнена по невыгодному курсу.
- Путаница с native ETH и WETH: при отправке ETH нужно вызывать swapExactETHForTokens, а не swapExactTokensForTokens, иначе контракт не примет native актив.
Два подхода: собственный роутер vs агрегатор
| Критерий | Собственный роутер (Uniswap v2/v3 fork) | Агрегатор (0x, 1inch, Paraswap) |
|---|---|---|
| Контроль | Полный — вы управляете контрактом | Зависит от API — могут изменить роутер |
| Лучшая цена | Только ваш пул | Маршрутизация по всем DEX |
| Сложность интеграции | Высокая — нужен deploy контракта и интерфейс | Низкая — запрос к API и подпись data |
| Комиссии | Настраиваемые (можно получать fee) | Протокольный сбор (0.5% по умолчанию) + газ |
| Обновления | Самостоятельный deploy | Автоматически (провайдер обновляет роутеры) |
Когда стоит выбирать агрегатор?
Если важна лучшая цена для пользователя и быстрая интеграция — агрегатор. При тестировании агрегатор обеспечивал лучшую цену в 95% случаев по сравнению с собственным роутером. Мы рекомендуем гибридный подход: базовый роутер для своего токена + агрегатор для всех остальных пар.Как выбрать между собственным роутером и агрегатором? — разработка dex интерфейса
Если вам нужен эксклюзивный пул ликвидности и полный контроль над комиссиями — выбирайте собственный роутер. Если важна удобство интеграции — агрегатор. Гибридный подход даёт лучшее из двух миров: контроль на своей паре и лучшие котировки на внешних.
Как мы это делаем: кейс с Uniswap v2 роутером
В нашей практике был случай, когда клиенту понадобился эксклюзивный пул для своего токена. Используем стек: viem для взаимодействия с контрактами, Wagmi для управления кошельком, TanStack Query для кеширования котировок. Ключевой момент — debounce запросов через хук useDebouncedValue. Как указано в Uniswap v2 documentation, функция getAmountsOut возвращает массив сумм.
// lib/swap.ts import { createPublicClient, http, parseAbi, formatUnits, parseUnits } from 'viem'; const ROUTER_ABI = parseAbi([ 'function getAmountsOut(uint256 amountIn, address[] path) view returns (uint256[])', 'function swapExactTokensForTokens(uint256,uint256,address[],address,uint256) returns (uint256[])', 'function swapExactETHForTokens(uint256,address[],address,uint256) payable returns (uint256[])', 'function swapExactTokensForETH(uint256,uint256,address[],address,uint256) returns (uint256[])', ]); const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' as const; export async function getQuote( tokenIn: `0x${string}` | 'ETH', tokenOut: `0x${string}` | 'ETH', amountIn: bigint, decimalsIn: number, decimalsOut: number, ): Promise<{ amountOut: bigint; path: `0x${string}`[]; priceImpact: number }> { const client = createPublicClient({ chain: mainnet, transport: http() }); const addressIn = tokenIn === 'ETH' ? WETH : tokenIn; const addressOut = tokenOut === 'ETH' ? WETH : tokenOut; // Прямой маршрут const directPath: `0x${string}`[] = [addressIn, addressOut]; // Маршрут через WETH (если токены не имеют прямой пары) const wethPath: `0x${string}`[] = [addressIn, WETH, addressOut]; const [directResult, wethResult] = await client.multicall({ contracts: [ { address: ROUTER, abi: ROUTER_ABI, functionName: 'getAmountsOut', args: [amountIn, directPath] }, { address: ROUTER, abi: ROUTER_ABI, functionName: 'getAmountsOut', args: [amountIn, wethPath] }, ], allowFailure: true, }); const directOut = directResult.status === 'success' ? (directResult.result as bigint[])[directResult.result.length - 1] : 0n; const wethOut = wethResult.status === 'success' ? (wethResult.result as bigint[])[(wethResult.result as bigint[]).length - 1] : 0n; const bestOut = directOut >= wethOut ? directOut : wethOut; const bestPath = directOut >= wethOut ? directPath : wethPath; // Price impact — разница между spot price и реальной ценой // (упрощённо через резервы пула) const priceImpact = 0; // в реальном проекте считается по резервам return { amountOut: bestOut, path: bestPath, priceImpact }; } Хук useQuote с debounce и автоматическим обновлением каждые 15 секунд:
// hooks/useQuote.ts import { useQuery } from '@tanstack/react-query'; import { useDebouncedValue } from '@/hooks/useDebouncedValue'; export function useQuote( tokenIn: string, tokenOut: string, amountIn: string, decimalsIn: number, decimalsOut: number, ) { const debouncedAmount = useDebouncedValue(amountIn, 400); const amountWei = debouncedAmount ? parseUnits(debouncedAmount, decimalsIn) : 0n; return useQuery({ queryKey: ['quote', tokenIn, tokenOut, amountWei.toString()], queryFn: () => getQuote( tokenIn as `0x${string}`, tokenOut as `0x${string}`, amountWei, decimalsIn, decimalsOut, ), enabled: amountWei > 0n, staleTime: 15_000, refetchInterval: 15_000, }); } Почему важен deadline и обработка ошибок?
Мы выставляем deadline = 20 минут от текущего времени. Если транзакция не прошла за это время из-за низкого газа или перегрузки сети, контракт отклонит её. Это защищает от исполнения торговли по плохому курсу. Дополнительно ловим ошибки через viem decodeErrorResult и показываем пользователю понятную причину. При превышении slippage предлагаем повторить с новыми параметрами.
Процесс работы
- Аналитика: обсуждаем требования — какие токены, сети, нужен ли агрегатор, кастомные комиссии.
- Проектирование: выбираем роутер (собственный/агрегатор/гибрид), проектируем UX поток approve+swap, определяем параметры slippage и deadline.
- Реализация: пишем смарт-контракты (если нужны), интерфейс на React + viem/Wagmi, интеграцию с кошельками (MetaMask, WalletConnect).
- Тестирование: симулируем торговли в тестовых сетях, проверяем price impact, проскальзывание, краевые случаи (отмена approve, нехватка баланса).
- Деплой: деплоим контракты, настраиваем окружение (mainnet/testnet), публикуем интерфейс на Vercel/IPFS.
Сроки ориентировочно
| Тип | Срок |
|---|---|
| Swap виджет (Uniswap v2 роутер) | 5–7 дней |
| DEX с агрегатором, 2+ сети | 2–3 недели |
| Полный кастомизированный DEX с собственным пулом и аналитикой | от 4 недель |
Точная стоимость рассчитывается индивидуально — пишите, оценим ваш проект.
Что входит в работу
- Исходный код компонентов (React/Next.js) с комментариями.
- Документация по интеграции и настройке.
- Тесты (unit + e2e на Cypress).
- Доступы к репозиторию и CI/CD.
- Обучение команды (1-2 часа воркшопа).
- Поддержка в течение 2 недель после запуска.
Мы занимаемся разработкой DeFi-интерфейсов более 5 лет и реализовали 10+ проектов для клиентов из США и Европы. Гарантируем корректную обработку транзакций и соответствие современным стандартам безопасности. Получите консультацию по архитектуре вашего DEX.







