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.







