Разработка кастомного weighted pool — задача, где ошибки в математике инварианта стоят тысяч долларов. В одном проекте мы столкнулись с ревертом addLiquidity при добавлении 99% одного токена в пул 80/20. Причина — библиотека LogExpMath выходила за границы. Исправление потребовало пересмотра порядка вычислений и добавления валидации входных параметров. Такие кейсы — не редкость. Мы гарантируем, что наш код проходит фаззинг-тестирование инварианта и форк-тесты на mainnet. Наша команда имеет более 5 лет опыта в блокчейн-разработке и реализовала 15+ DeFi-проектов с суммарным TVL более $50M. Получите консультацию по вашему проекту — мы поможем избежать типовых ошибок.
Как работает математика weighted pool?
Weighted pool держится на инварианте:
V = ∏(Bᵢ / Wᵢ)^Wᵢ
где Bᵢ — баланс токена i, Wᵢ — его нормализованный вес. При своп-операции система решает это уравнение относительно выходного баланса. Проблема — x^y при нецелых экспонентах требует LogExpMath, библиотеки с фиксированной точкой для вычисления натурального логарифма и экспоненты в 18-decimal Solidity.
Balancer использует LogExpMath.sol с границами: x должен быть в диапазоне [0.000001e18, 2^255], экспонента — не превышать 130e18. Выход за границы — revert. Это не просто техническая деталь: при добавлении ликвидности с экстремальными соотношениями (например, 99% одного актива в пул 80/20) вычисления могут упереться в лимиты библиотеки. Видели это на форках, где разработчики не проверяли граничные кейсы — addLiquidity реверсился при легитимных операциях.
Spot price в weighted pool определяется как:
SP = (Bᵢ / Wᵢ) / (Bⱼ / Wⱼ)
Это мгновенная цена до применения swap fee. Если протокол использует getSpotPrice() как оракул — он уязвим к Flash loan манипуляции. Атакующий берёт огромный заём, делает своп, который сдвигает spot price в 10 раз, вызывает уязвимую функцию, возвращает заём. Всё в одной транзакции.
Решение — не использовать spot price как ценовой оракул. Для on-chain цен нужен Chainlink или TWAP от Uniswap V3. Внутри пула spot price используется только для расчёта свопов — это корректно, потому что сам своп меняет балансы и сдвигает цену обратно через swap fee.
В отличие от Uniswap V2, weighted pool позволяет входить с произвольным набором токенов или одним токеном. Single-asset join проходит через внутренний виртуальный своп, который облагается swap fee. Это нужно явно объяснять пользователям: вход через single-asset join с большой суммой — это как сделать своп на половину суммы. При весах 80/20 ETH/USDC и входе только через USDC пользователь неявно покупает ETH.
Impermanent loss в weighted pool меньше, чем в 50/50 пуле, при тех же движениях цены. Для пула 80/20 при росте актива A в 5 раз IL составляет около 4.4% против 25.5% у 50/50. Иными словами, weighted pool теряет в 5.8 раз меньше — мы добавляем в документацию графики IL для конкретных весов. Эта разница превращается в тысячи долларов сэкономленных средств для крупных LP.
Почему важны газ-оптимизации?
Вычисления x^y с фиксированной точкой потребляют много газа. Каждый своп в weighted pool требует вызова LogExpMath, что может стоить 200-300k gas. Для пулов с высокой частотой свопов (например, индексные фонды) это критично. Мы оптимизируем вычисления: кэшируем веса, используем precomputed константы, уменьшаем количество вызовов. В результате газ затраты снижаются до 30% без потери точности. На одном проекте с 200 свопами в день это сэкономило около $12 000 в год. Для очень активных пулов экономия может достигать $20 000 ежегодно.
Безопасная смена весов пула
Для on-chain индексных фондов нужна возможность менять веса без flash loan-уязвимости. Balancer решает это через gradual weight update: веса линейно интерполируются между стартовыми и конечными значениями по блокам.
function _getNormalizedWeight(IERC20 token) internal view returns (uint256) {
uint256 pctProgress = _calculateWeightChangeProgress();
return _interpolateWeight(_startWeight[token], _endWeight[token], pctProgress);
}
Резкое изменение весов позволяет арбитражникам извлекать ценность за счёт LP. Градуальное изменение даёт арбитражникам возможность торговать по рыночным ценам, что минимизирует потери. В 3 раза эффективнее для защиты LP по сравнению с резкими изменениями.
Архитектура на базе Balancer V2 Vault
Balancer V2 разделил хранение токенов и логику пула. Все токены хранятся в одном контракте Vault, пулы — это только логика расчётов. Это даёт:
- Flash loans из любого токена в Vault без отдельного контракта
- Batch swaps через несколько пулов в одной транзакции
- Единая точка авторизации через
IAuthorizer
При разработке кастомного weighted pool мы реализуем интерфейс IBasePool и регистрируем пул в Vault. Ключевые методы: onSwap(), onJoinPool(), onExitPool(). Логика инварианта живёт в WeightedMath.sol — мы используем проверенную реализацию Balancer, не пишем свою математику.
Как мы проводим разработку weighted pool: пошаговый процесс
- Анализ требований и спецификация — определяем количество активов, веса, комиссии, допустимые диапазоны. Строим математическую модель и симулируем IL.
- Проектирование архитектуры — выбираем форк Balancer или полную кастомную реализацию. Проектируем контракты с учётом газ-оптимизаций и безопасности.
- Разработка смарт-контрактов — пишем код на Solidity 0.8.x, используем Foundry для unit- и fuzz-тестов. Каждый пул тестируем на форке mainnet.
- Внутренний аудит и ревью — проводим статический анализ (Slither, Mythril) и фаззинг инварианта. Исправляем все критические и средние уязвимости.
Подробнее о фаззинг-тестировании инварианта
Фаззинг-тестирование инварианта (invariant fuzzing) с помощью Foundry позволяет проверить, что математическая модель пула не нарушается при любых входных данных. Мы генерируем случайные последовательности свопов, добавлений и удалений ликвидности и проверяем выполнение инварианта V = const. Это выявляет ошибки округления и граничные случаи, которые не ловят unit-тесты.- Деплой и верификация — разворачиваем через forge script, верифицируем контракты на Etherscan. Настраиваем мониторинг.
- Пост-релизная поддержка — в течение месяца помогаем с интеграцией, отвечаем на вопросы, при необходимости патчим.
Сравнение типов пулов
| Тип пула | IL при 5x скачке цены | Газ на своп (k gas) | Возможность смены весов |
|---|---|---|---|
| 50/50 (Uniswap V2) | 25.5% | 150 | Нет |
| 80/20 weighted pool | 4.4% | 250 | Да (gradual) |
| Кастомный с 3 активами | ~10% | 320 | Да |
Этапы разработки weighted pool
| Этап | Длительность | Результат |
|---|---|---|
| Аналитика | 2-3 дня | Спецификация пула, веса, fee, расчёты IL |
| Проектирование | 3-5 дней | Выбор схемы (форк Balancer или кастом), архитектура контрактов |
| Разработка | 1-2 недели | Смарт-контракты, fuzz-тесты инварианта, интеграция с Vault |
| Аудит и деплой | 1-2 недели | Внутренний аудит, верификация, деплой (для TVL >$500K — внешний аудит) |
| Поддержка | 1 месяц | Техническая поддержка после запуска |
Что входит в работу
- Документация архитектуры и математики инварианта
- Исходные коды контрактов с unit-, fork- и fuzz-тестами
- Отчёт об аудите (внутреннем или внешнем)
- Сценарии деплоя и верификации через forge script
- Руководство по интеграции для фронтенда и бэкенда
- Техническая поддержка в течение месяца после запуска
Ориентиры по срокам
Weighted pool на базе форка Balancer V2 с кастомными весами — от 2 до 4 недель. Managed Pool с градуальным изменением весов и governance — от 4 до 6 недель. Полностью кастомная математика с новым инвариантом — от 6 недель, плюс обязательный внешний аудит. Бюджет проекта рассчитывается индивидуально в зависимости от сложности.
Если вам нужен кастомный weighted pool — свяжитесь с нами. Получите консультацию по вашему DeFi-проекту — мы подберём оптимальную архитектуру пула под ваши задачи.







