Prerequisites:
- 02-amm-concept
Математика Uniswap V2
Зачем это блокчейну?
В предыдущем уроке мы изучили формулу xy=k теоретически. Теперь разберем, как Uniswap V2 реализует эту формулу в Solidity — целочисленная арифметика, комиссии через множитель 997/1000, и LP токены как доля пула.
Uniswap V2 — самый развернутый и изученный AMM в истории DeFi. Его контракты аудированы, форкнуты сотни раз (SushiSwap, PancakeSwap), и остаются эталоном для понимания AMM. Разбирая V2, вы получаете фундамент для понимания V3, V4 и всех производных протоколов.
Целочисленная формула V2
Почему нет float?
Solidity не поддерживает числа с плавающей точкой. Все вычисления — uint256. Uniswap V2 решает проблему комиссии 0.3% элегантно:
// Вместо: amountOut = reserveOut * amountIn * 0.997 / (reserveIn + amountIn * 0.997)
// V2 использует:
amountOut = reserveOut * amountIn * 997 / (reserveIn * 1000 + amountIn * 997)
Умножение на 997 и 1000 — это целочисленный эквивалент (1 - 0.003). Никаких дробей, никаких погрешностей.
Пошаговый расчет
Формула в деталях
Полная формула Uniswap V2 для свопа:
Почему 997/1000?
- Комиссия = 0.3% = 3/1000
(1 - 3/1000) = 997/1000- Целочисленное умножение:
amountIn * 997вместоamountIn * 0.997 - Компенсация в знаменателе:
reserveIn * 1000балансирует масштаб
Архитектура контрактов V2
Uniswap V2 состоит из трех уровней контрактов:
Factory
UniswapV2Factory — реестр всех пулов:
createPair(tokenA, tokenB)— создает новый пул черезcreate2getPair(tokenA, tokenB)— возвращает адрес пула- Один пул на каждую уникальную пару токенов
- Контролирует fee-to (протокольная комиссия, изначально отключена)
Pair (Pool)
UniswapV2Pair — ядро протокола:
- Хранит резервы двух токенов
- Выполняет свопы (
swap()) - Минтит и сжигает LP токены (
mint(),burn()) - Сам является ERC-20 токеном (LP token)
- Реализует TWAP oracle (кумулятивные цены)
Router
UniswapV2Router02 — пользовательский интерфейс:
swapExactTokensForTokens()— своп с точным входомswapTokensForExactTokens()— своп с точным выходомaddLiquidity()— добавление ликвидностиremoveLiquidity()— удаление ликвидности- Маршрутизация multi-hop свопов (ETH -> USDC -> DAI)
- Wrapping ETH в WETH
Пользователь всегда взаимодействует с Router. Router вызывает Pair. Factory нужен только для создания пулов.
LP токены: расчет долей
Первый провайдер
При первом депозите LP получает токены по формуле геометрического среднего:
Где MINIMUM_LIQUIDITY = 1000 навсегда блокируется на address(0).
Зачем MINIMUM_LIQUIDITY? Без него первый LP мог бы:
- Создать пул с 1 wei TokenA + 1 wei TokenB
- Получить 1 LP токен
- Отправить большую сумму токенов напрямую в контракт пула
- Каждый LP токен теперь стоит огромную сумму
- Последующие LP получат 0 токенов из-за округления
Блокировка 1000 LP-токенов предотвращает эту атаку.
Последующие провайдеры
Минимум из двух защищает существующих LP: если провайдер вносит несбалансированную сумму, он получает меньше LP-токенов. Избыток остается в пуле — это “подарок” существующим LP.
Вывод ликвидности
LP сжигает токены и получает пропорциональную долю обоих активов:
amountA = liquidityBurned * reserveA / totalSupply
amountB = liquidityBurned * reserveB / totalSupply
SimpleDEX: учебный контракт
Для практики мы создали SimpleDEX (contracts/defi/SimpleDEX.sol) — минимальный constant-product AMM, реализующий все описанные концепции.
Ключевые функции
addLiquidity:
// Первый провайдер: геометрическое среднее
liquidityMinted = sqrt(amountA * amountB) - MINIMUM_LIQUIDITY;
// Последующие: минимум из соотношений
uint256 liquidityA = (amountA * totalLiquidity) / reserveA;
uint256 liquidityB = (amountB * totalLiquidity) / reserveB;
liquidityMinted = min(liquidityA, liquidityB);
swap (Uniswap V2 integer math):
uint256 amountInWithFee = amountIn * (FEE_DENOMINATOR - FEE_NUMERATOR); // * 997
uint256 numerator = reserveOut * amountInWithFee;
uint256 denominator = (reserveIn * FEE_DENOMINATOR) + amountInWithFee;
amountOut = numerator / denominator;
SafeERC20: SimpleDEX использует OpenZeppelin SafeERC20 для безопасных переводов токенов. Некоторые токены (например, USDT) не возвращают bool при transfer() — SafeERC20 обрабатывает это корректно.
ВАЖНО: SimpleDEX — учебный контракт. Он НЕ содержит защиту от reentrancy, flash loan атак, MEV. Не используйте в production.
Эффективность капитала
| Метрика | V2 | V3 | Преимущество |
|---|---|---|---|
Диапазон ликвидности | [0, infinity) | [price_a, price_b] | V3: LP выбирает диапазон |
ETH/USDC $100K | Глубина: ~$100K | Глубина: ~$424K (x4.24) | V3: 4.24x эффективнее |
Заработок комиссий | Весь диапазон, мало | Только в диапазоне, много | V3: до 4000x больше |
LP токен | ERC-20 (взаимозаменяемый) | NFT (уникальная позиция) | V2: проще, V3: гибче |
Управление | Set & forget | Активное управление | V2: проще |
Проблема V2
V2 распределяет ликвидность по всему ценовому диапазону от 0 до бесконечности. Для пары ETH/USDC:
- Реальные цены ETH двигаются в диапазоне ~5000
- Ликвидность в диапазоне 500 и $5000+ никогда не используется
- Более 99% капитала LP работает вхолостую
Решение: V3
Uniswap V3 (следующий урок) позволяет LP выбирать конкретный ценовой диапазон. Это увеличивает эффективность капитала до 4000x. Та же сумма капитала обеспечивает в разы большую глубину ликвидности в нужном диапазоне.
Практика: Fork тесты
Forge (Foundry)
Запустите fork-тест против реального Uniswap V2 на mainnet:
# Требуется MAINNET_RPC_URL (Alchemy/Infura)
forge test --match-path test/defi/UniswapV2Fork.t.sol --profile fork -vv
Тест test_swapETHForDAI:
- Получает ожидаемый output через
getAmountsOut - Выполняет
swapExactETHForTokensс 1% slippage - Проверяет баланс DAI у трейдера
Тест test_priceImpact:
- Сравнивает цену за ETH при 0.01 ETH vs 100 ETH
- Подтверждает, что большая сделка получает худшую цену
Hardhat (viem)
npx hardhat test test/defi/UniswapV2Fork.test.ts --network mainnetFork
Аналогичные тесты с использованием hre.viem.getPublicClient() и hre.viem.getWalletClients().
Результат: Вы своими руками вызовете настоящий Uniswap V2 Router на форке Ethereum mainnet и увидите, как формула 997/1000 работает на реальных резервах.
Что дальше
В следующем уроке (DEFI-04) разберем Uniswap V3: Concentrated Liquidity — тики, ценовые диапазоны, NFT-позиции, и как V3 решает проблему неэффективного капитала V2. Также заглянем в Uniswap V4 (singleton, hooks, flash accounting).
Finished the lesson?
Mark it as complete to track your progress