DeFi. Безопасность. Взлом Balancer: анализ и отчёты
Недавно СМИ сообщили: “Неизвестные взломали децентрализованный протокол Balancer. По последним данным, хакеры вывели по меньшей мере $128 млн”.
TL;DR
Собраны все основные отчёты, ссылки на твиттер-разборы, а также на транзакции и кошельки взлома. Плюс - дан обширный анализ самой механики взлома Balancer.
Важно!
Если следовать за GitHub, то смарт-контракты Balancer V2 прошли 11 (!!!) аудитов от четырех компаний: OpenZeppelin, Trail of Bits, Certora и ABDK. Последняя проверка состоялась в сентябре 2022 года.
До этого взломы уже были: пример 2023 года на $900k и $228k и 2020 года на $500k.
Оценка взлома: от $88M до >$120M. Официальное заявление: x.com/Balancer/status/1704281611326357567.
Общий анализ от Mikko Ohtamaa и Defimon Signals
См. сам отчёт: x.com/moo9000/status/1985262739493687351
Взлом произошёл из-за ошибки в функции доступа. Суть:
- Уязвимость находилась в функции manageUserBalance().
- Она использовала _validateUserBalanceOp() для проверки операций, но там был ошибочный access check - контракт сравнивал msg.sender (вызвавшего) с op.sender, переданным в параметрах, вместо реальной авторизации.
- Это позволяло любому вызвать WITHDRAW_INTERNAL (kind=1) и вывести активы от имени другого пользователя.
if (kind == UserBalanceOpKind.WITHDRAW_INTERNAL) {
_withdrawFromInternalBalance(asset, sender, recipient, amount);
}Проблема в том, что перед вызовом не проверялось, имеет ли msg.sender право на sender.
Проверка в _validateUserBalanceOp сравнивала:
require(msg.sender == op.sender)
Но (и это важно!) op.sender приходил из данных транзакции, которые атакующий мог задать вручную.
- Можно было выполнить внутренний вывод (WITHDRAW_INTERNAL) с баланса жертвы.
- Злоумышленники скриптом обошли множество пулов и адресов.
- По сообщению Mikko Ohtamaa и Defimon Signals, это затронуло не все версии Balancer, но ключевые контракты были уязвимы.
Детали от ADI
1. Цель атаки
Атака была направлена на хранилища (Vaults) и пулы ликвидности Balancer V2, эксплуатируя уязвимость во взаимодействии смарт-контрактов.
По предварительному анализу - злоумышленник развернул вредоносный контракт, который манипулировал вызовами Vault при инициализации пулов.
2. Механизм взлома
Из-за неправильной авторизации и ошибок обработки callback-вызовов атакующему удалось обойти защитные проверки.
Это позволило выполнять несанкционированные обмены и манипуляции балансами между взаимосвязанными пулами, что привело к молниеносному сливу активов - за считанные минуты.
3. Ключевая транзакция
Эксплойт начался с серии транзакций, включая основную в сети Ethereum:
0xd155207261712c35fa3d472ed1e51bfcd816e616dd4f517fa5959836f5b48569.
Средства с неё были переведены на новый кошелёк под контролем хакера, затем консолидированы и, вероятно, отмыты через миксеры или кроссчейн-мосты:
4. Усиление уязвимости
Композиционная структура Balancer, где пулы активно взаимодействуют друг с другом, усилила эффект уязвимости.
Похожие проблемы уже случались у других AMM-протоколов, особенно в случаях с дефляционными токенами ребалансировкой пулов.
5. Расследование
Финальные данные расследования ещё собираются; над делом работают аудиторы PeckShield и Nansen.
Компрометации приватных ключей не выявлено - это был чисто смарт-контрактный эксплойт.
6. Украденные активы
- Ethereum: ок. $70M+ (основной удар)
- Base и Sonic: ок. $7M
- Другие сети: ок. $2M+
- Основные активы: WETH, wstETH, osETH, frxETH, rsETH, rETH
- Общий (предварительный) ущерб: около $110–116 млн
7. Что делать пользователям?
- Срочно вывести средства из пулов Balancer V2, особенно из потенциально уязвимых.
- Отозвать разрешения (approvals) для контрактов Balancer через Revoke.cash, DeBank или Etherscan.
- Мониторить кошельки - использовать Etherscan или Dune Analytics для отслеживания подозрительных операций.
Отчёт от Dikla Barda, Roaman Zaikin & Oded Vanunu
Данные: research.checkpoint.com/2025/how-an-attacker-drained-128m-from-balancer-through-rounding-error-exploitation,
Как злоумышленник вывел $128 млн из Balancer, используя ошибку округления?
3 ноября 2025 года система мониторинга блокчейна Check Point Research обнаружила сложный эксплойт, нацеленный на контракты Balancer V2 ComposableStablePool.
Атакующий воспользовался потерей точности арифметических вычислений в инвариантных формулах пулов, чтобы вывести $128,64 млн из шести блокчейн-сетей менее чем за 30 минут.
Атака использовала уязвимость округления в функции _upscaleArray.
В сочетании с тщательно подобранными вызовами batchSwap это позволило искусственно занизить цену токена пула (BPT - Balancer Pool Token) и извлекать прибыль через многократные циклы арбитража.
Основная фаза атаки пришлась на момент деплоя контракта злоумышленника - в его конструкторе выполнялось более 65 микро-свопов, каждый из которых усиливал накопленную ошибку округления, что в итоге имело разрушительный эффект.
Введение
Ранним утром 3 ноября 2025 года система Blockchain Threat Analysis от Check Point зафиксировала аномальную активность в сети Ethereum, связанную с контрактом Balancer V2 Vault.
Через несколько минут автоматическая система обнаружила критическую атаку в реальном времени: происходил массовый вывод средств из множества пулов ликвидности.
Атака эксплуатировала математическую уязвимость в том, как ComposableStablePools Balancer обрабатывают свопы с малыми значениями.
Когда балансы токенов доводились до определённых границ округления (диапазон 8-9 wei), целочисленное деление в Solidity приводило к значительной потере точности.
Злоумышленник использовал это, выполняя пакетные последовательности свопов, которые аккумулировали эти крошечные ошибки в катастрофическую манипуляцию инвариантом пула.
Архитектура Balancer V2. Система Vault
Balancer V2 использует централизованный контракт «Vault» (0xBA12222222228d8Ba445958a75a0704d566BF2C8), который хранит все токены всех пулов, отделяя хранение токенов от логики пулов - это снижает расходы на газ и повышает капитальную эффективность.
Такая дизайн-идея с общим пулом ликвидности означала, что одна уязвимость в математике пула могла затронуть все ComposableStablePools одновременно - что и произошло при этой атаке.
Механизм Internal Balance
Система Internal Balance Balancer V2 позволяет пользователям один раз положить токены и затем использовать их в нескольких операциях без повторных ERC20-переводов:
mapping(address => mapping(IERC20 => uint256)) private _internalTokenBalance;
Эта система стала ключевой для атаки. Эксплойт-контракт аккумулировал украденные средства в своём internal balance во время деплоя, а затем выводил их на конечный адрес получателя в последующих транзакциях.
Уязвимость: потеря арифметической точности в математике стабильных пулов. Корень проблемы
ComposableStablePools используют инвариантную формулу StableSwap (по аналогии с Curve) для поддержания ценовой стабильности между похожими активами.
Инвариант D отражает общую стоимость пула, а цена BPT вычисляется как D, делённое на totalSupply.
Однако операции масштабирования (scaling), которые готовят балансы для вычисления инварианта, вносят погрешности округления.
Уязвимый путь исполнения кода:
function _upscaleArray(uint256[] memory amounts, uint256[] memory scalingFactors)
private pure returns (uint256[] memory) {
for (uint256 i = 0; i < amounts.length; i++) {
amounts[i] = FixedPoint.mulDown(amounts[i], scalingFactors[i]);
}
return amounts;
}
// Simplified representation - actual implementation is more complex
function _calculateInvariant(uint256[] memory balances) private pure returns (uint256) {
uint256[] memory scaledBalances = _upscaleArray(balances, scalingFactors);
uint256 invariant = computeStableInvariant(scaledBalances, amplificationParameter);
return invariant;
}mulDown выполняет целочисленное деление с округлением в меньшую сторону.
Когда балансы малы (диапазон 8-9 wei), такое округление создаёт значительные относительные ошибки - до 10% потери точности на операцию.
Эта ошибка точности распространяется на вычисление инварианта D, вызывая аномальное снижение его рассчитанного значения. Поскольку цена BPT равна D / totalSupply, уменьшение D напрямую снижает цену BPT, создавая арбитражные возможности для атакующего.
Отдельные свопы дают пренебрежимо малые потери точности, но в одном batchSwap-транзакции, содержащем 65 операций, эти потери многократно нарастают.
Отсутствие проверки изменений инварианта позволило злоумышленнику систематически подавлять цену BPT через накопленные ошибки округления и извлекать миллионы долларов из каждого пула.
Анализ атаки. Шаблон в три фазы
Атакующий выполнял сложную трёхэтапную последовательность свопов внутри одной batchSwap-транзакции:
- Фаза 1 - Подведение к границе округления: Обменивать большие объёмы BPT на базовые токены, чтобы подтолкнуть баланс одного токена к критическому порогу 8-9 wei, где ошибки округления максимальны.
- Фаза 2 - Триггер потери точности: Выполнять мелкие свопы с участием токена, оказавшегося на границе. Функция _upscaleArray при масштабировании округляет вниз, в результате чего инвариант D недооценивается и цена BPT искусственно падает.
- Фаза 3 - Извлечение прибыли: Минтить или покупать BPT по подавленной цене, а затем сразу же выкупать их обратно за базовые активы по полной стоимости. Разница в цене - чистая прибыль.
Этот трёхфазный цикл повторялся 65 раз в рамках одной batchSwap-транзакции.
Все этапы выполнялись атомарно, что препятствовало внешнему вмешательству и обеспечивало накопление потерь точности в общем состоянии балансов, в итоге извлекая миллионы из каждого целевого пула.
Поняв механизм уязвимости, рассмотрим, как атакующий автоматизировал эксплойт.
Архитектура эксплойт-контракта
Атакующий задеплоил контракт 0x54B53503c0e2173Df29f8da735fBd45Ee8aBa30d с трёхадресной оперативной структурой:
- Exploiter 1: 0x506D1f9EFe24f0d47853aDca907EB8d89AE03207 (деплойер)
- Exploit Contract: 0x54B53503c0e2173Df29f8da735fBd45Ee8aBa30d
- Exploiter 2: 0xAa760D53541d8390074c61DEFeaba314675b8e3f (получатель)
Атака, выполненная в конструкторе
Анализ транзакции 0x6ed07db… показал, что хищение произошло во время деплоя контракта. В конструкторе автоматически выполнялась эксплуатация ошибки округления, одновременно нацеливаясь на два пула Balancer.
В конструкторе было сгенерировано 65 переводов токенов в адрес Balancer Protocol Fees Collector - это сборы своп-комиссий, собранные в ходе манипуляций, а не сами украденные средства.
Объёмы переводов демонстрируют характерную картину итеративной эксплуатации потерь точности: они уменьшаются от 0.414 osETH до 0.000000000000000003 osETH, по мере того как ошибки округления накапливаются и становятся почти пренебрежимыми.
Похищенная сумма отражена в событиях InternalBalanceChanged, которые фиксируют обновления балансов внутри внутренней учётной системы Vault.
Внутренний баланс эксплойт-контракта увеличился на:
- Пул 1 (osETH/wETH-BPT): +4 623 WETH, +6 851 osETH
- Пул 2 (wstETH/wETH-BPT): +1 963 WETH, +4 259 wstETH
- Итого: 6 586 WETH (4 623 + 1 963) + 6 851 osETH + 4 259 wstETH
Эти увеличения internal balance представляют собой фактически похищенные средства. События InternalBalanceChanged показывают, что внутренний счёт эксплойт-контракта в Vault был зачислен на соответствующие суммы.
Хотя сами токены физически оставались внутри контракта Vault, учётная система Vault зарегистрировала эксплойт-контракт как владельца этих балансов, что позволило впоследствии вывести средства.
Функция вывода (Withdrawal)
После того как в конструкторе были аккумулированы похищенные средства, функция 0x8a4f75d6 перевела их эксплойтеру 2 (Exploiter 2).
unction 0x8a4f75d6(address[] calldata targetPools) public {
require(msg.sender == _callTx);
poolIndex = 0;
while (poolIndex < targetPools.length) {
poolId = targetPools[poolIndex].getPoolId();
(tokens[],) = vault.getPoolTokens(poolId);
internalBals[] = vault.getInternalBalance(address(this), tokens);
tokenIndex = 0;
while (tokenIndex < tokens.length) {
operations[tokenIndex] = UserBalanceOp({
kind: 1,
asset: tokens[tokenIndex],
amount: internalBals[tokenIndex],
sender: address(this),
recipient: 0xAa760D53541d8390074c61DEFeaba314675b8e3f
});
tokenIndex++;
}
vault.manageUserBalance(operations);
poolIndex++;
}
}Эта функция выводит внутренний баланс самого контракта.
Объект UserBalanceOp содержит sender, равный адресу эксплойт-контракта, потому что контракт формально владел средствами, накопленными во время выполнения конструктора.
Транзакция 0xd155207… подтверждает, что именно через этот вызов было выведено 6 586 WETH с внутреннего баланса эксплойт-контракта на адрес Exploiter 2.
Двухэтапная атака
Этап 1 - Кража (выполнение конструктора):
- Транзакция:
0x6ed07db1a9fe5c0794d44cd36081d6a6df103fab868cdd75d581e3bd23bc9742 - Действие: деплой эксплойт-контракта
- Метод: конструктор выполняет batchSwap-операции против двух пулов
- Результат: украдено ок. $63M через ошибку округления, средства хранятся во внутреннем балансе контракта
- Доказательства: 65 переводов комиссий + события InternalBalanceChanged, фиксирующие +6 586 WETH, +6 851 osETH, +4 259 wstETH
Этап 2 - Вывод (вызов функции):
- Транзакция:
0xd155207261712c35fa3d472ed1e51bfcd816e616dd4f517fa5959836f5b48569 - Действие: вызов функции 0x8a4f75d6
- Метод: вывод внутреннего баланса на адрес Exploiter 2
- Результат: средства переведены конечному получателю
- Доказательства: вызов manageUserBalance с sender = адрес эксплойт-контракта
Заключение
Взлом Balancer демонстрирует, как математические уязвимости в DeFi-протоколах могут быть превращены в оружие при помощи автоматизации и точной настройки параметров.
Успех атакующего объясняется тем, что незначительные ошибки округления становятся эксплуатируемыми, когда их эффект многократно усиливается десятками операций в рамках одной атомарной транзакции.
Несмотря на многочисленные аудиты, уязвимость осталась незамеченной, потому что традиционное тестирование сосредоточено на корректности отдельных операций, а не на совокупном эффекте последовательных batch-операций.
Индустрии DeFi необходимо двигаться к практике непрерывной валидации безопасности, моделированию экономических атак и адверсариальному тестированию, которое учитывает, как микроскопические погрешности могут накапливаться и превращаться в катастрофические эксплойты.
(НЕ) Официальный отчёт
См. здесь: x.com/Balancer/status/1986104426667401241.
В понедельник в 07:46 UTC система мониторинга Hypernative зафиксировала первые признаки взлома, нацеленного на Balancer V2 Composable Stable Pools.
Дальнейший анализ подтвердил, что уязвимость затронула такие пулы на множестве сетей: Ethereum, Base, Avalanche, Gnosis, Berachain, Polygon, Sonic, Arbitrum и Optimism.
Инцидент ограничился Composable Stable Pools в Balancer V2 и их форками в других сетях (BEX, Beets). Balancer V3 и все остальные типы пулов не пострадали.
Сразу же были начаты меры реагирования в координации с контрибьюторами Balancer, партнёрами по безопасности и whitehat-исследователями.
Удалось сдержать распространение взлома и заморозить или вернуть часть активов. Для этого была организована “военная комната” (war room), которая управляла процессом реагирования и коммуникациями между сетями.
Пулы CSPv6 были переведены в режим восстановления (Recovery Mode), активы заморожены через партнёров, а взаимодействие с whitehat-хакерами происходило в рамках SEAL Safe Harbor.
Хотя окончательные данные ещё проверяются, эксплойт был масштабным. Полный постмортем будет опубликован после завершения всех технических и юридических проверок.
Первопричина (предварительно)
Хранилище Vault V2 поддерживает два типа свопов:
Пакетные свопы позволяют объединять несколько операций в одной транзакции, избегая промежуточных переводов токенов и экономя газ.
Ключевая особенность - “отложенное завершение” (deferred settlement): вызывающий может временно заимствовать токены (эффект флеш-займа), если к концу транзакции они полностью возвращаются.
В Composable Stable Pools токены LP-долей (BPT) обрабатывались как обычные токены.
Это позволило обойти ограничение на минимальный размер пула, что давало возможность довести ликвидность почти до нуля.
Эксплойт возник из-за направления округления в функции upscale(), применяемой при EXACT_OUT-свопах.
Эта функция округляла вниз, когда коэффициенты масштабирования (scaling factors) были нецелыми - ситуация, возникающая при включении курсов токенов в эти коэффициенты.
Атакующие использовали неправильное округление вместе с функцией batchSwap(), чтобы манипулировать балансами пулов и извлекать прибыль.
Во многих случаях похищенные средства временно оставались внутри Vault как “internal balances”, а затем выводились в последующих транзакциях.
- Composable Stable v5 - пострадали, если у пулов истёк период “pause window”.
- Composable Stable v6 - были автоматически приостановлены системой Hypernative и защищены.
- Balancer v3 и не-stable пулы V2 - не затронуты (подтверждено аудитом и внешней проверкой).
Воздействие
Команда сосредоточена на смягчении последствий и возврате средств, пока расследование продолжается.
Оценка убытков согласовывается по всем сетям, типам пулов и адресам совместно с внешними аудиторами и whitehat-командами.
Ведётся единый внутренний реестр, отслеживающий:
- движение средств злоумышленников,
- спасённые активы (whitehat rescues),
- замороженные и возвращённые активы,
- выводы пользователей и протокола.
Окончательные цифры будут опубликованы только после мультисторонней верификации (ончейн-трейсинг, подтверждение партнёров, сверка блок-за-блоком).
Все текущие публичные оценки - не официальны (не окончательны).
Верифицированный отчёт со списком затронутых пулов, методологией и ссылками на транзакции будет опубликован в финальном постмортеме.
Принятые меры
Несмотря на масштаб атаки, оперативные действия существенно снизили потери.
Ниже перечислены подтверждённые меры (указанные суммы — предварительные, для ориентирования):
- SEAL Whitehat Safe Harbor (BIP-726, октябрь 2024) - заранее принятая юридическая рамка позволила быстро и скоординированно подключить whitehat-специалистов.
- Hypernative: автоматическая экстренная приостановка - в 08:06 UTC активировались экстренные контроли CSPv6; к 08:07 UTC все пулы CSPv6 на затронутых сетях были приостановлены, что ограничило дальнейшие потери.
- Все CSPv6-пулы переведены в recovery mode, включая пулы с низким TVL.
- Фабрика CSPv6 отключена, чтобы предотвратить создание новых уязвимых пулов.
- Гейджи (gauges) для пострадавших пулов деактивированы - остановлены эмиссии BAL и партнёрских токенов.
- Вывод ликвидности LP разрешён через режим паузы - крупные LP (в т.ч. Crypto.com - $800k в cdcETH/wstETH и Ether.fi - $1.061M в eBTC/wBTC) смогли безопасно выйти.
- Stakewise Recovery: возвращено 5,041 osETH (ок. $19 млн) и 13,495 osGNO (ок. $1.7–2 млн) - около 73,5% украденных osETH для пропорционального возврата пострадавшим пользователям.
Последствия
Валидаторы Berachain - остановка сети: Работа цепи была приостановлена, чтобы ограничить воздействие уязвимости, связанной с Balancer V2, на платформе BEX. Были инициированы экстренные меры - хардфорк сети: x.com/berachain/status/1985288599152042101.
Sonic Labs - экстренная заморозка: Компания Sonic Labs заморозила адреса предполагаемых злоумышленников, чтобы заблокировать перемещение или конвертацию средств, связанных с Beets - форком Balancer V2, работающим в сети Sonic: x.com/SonicLabs/status/1985401737096671549
MEV-бот в сети Base - возврат средств: Около $150 000 было возвращено в ходе нескольких транзакций, проведённых MEV-ботом в сети Base.
BitFinding Whitehat Recovery - команда BitFinding: Команда BitFinding (whitehat-исследователи) перехватила и вернула активы на сумму примерно $600 000 в сети Ethereum Mainnet. x.com/BitFinding/status/1985377339224871185
Monerium - заморозка EURe: Около 1,3 млн EURe, находившихся в хранилище (Vault), были заморожены, чтобы предотвратить дальнейшее движение средств на затронутых площадках.
Gnosis - ограничения на мост: Совместно с командой Monerium были введены временные ограничения на исходящую активность мостов из сети Gnosis Chain,
что позволило снизить риск распространения атаки между цепочками: x.com/gnosisdotio/status/1985321081255891396
SEAL и партнёры - обращение к злоумышленникам: Продолжаются переговоры в рамках SEAL-фреймворка с целью добиться возврата средств.
Дополнительные whitehat-участники: К процессу подключились несколько команд (включая SNP и других), оказывающих помощь в анализе, спасении и возврате активов.
Работа продолжается
Balancer продолжает сотрудничать с партнёрами, исследователями, биржами и whitehat-командами, чтобы вернуть украденные средства. Полный постмортем с подтверждёнными итогами, ссылками на транзакции и схемой возврата будет опубликован после завершения проверки и сверки данных всеми участниками.
Рекомендации пользователям
- Операции обмена и ликвидности в непострадавших пулах остаются полностью безопасными. Уязвимость касалась только конкретных типов Composable Stable Pool на Balancer V2. Balancer V3 и все прочие пулы V2 не затронуты и работают штатно.
- Для пользователей приостановленных пулов Composable Stable v6 активирован Recovery Mode, позволяющий пропорционально вывести базовые активы.
- Composable Stable v5 пострадали и находятся на стадии расследования - не взаимодействуйте с этими контрактами до официального уведомления.
Вся проверенная информация и инструкции будут публиковаться исключительно через официальные каналы Balancer.
Обновления о ходе возврата, подтверждённых суммах и итоговом постмортеме появятся после завершения межсетевой сверки. До этого момента пользователям рекомендуется ориентироваться только на официальные источники Balancer.
Текущие действия и дальнейшие шаги
Процессы восстановления и сверки продолжаются во всех пострадавших сетях при участии партнёров по безопасности, аудиторов и whitehat-респондентов.
Отслеживание активов и юридически корректный возврат координируются командами SEAL и zeroShadow в рамках Safe Harbor-инициативы Balancer, что обеспечивает прозрачный и законный процесс возвращения средств.
Подтверждённые оценки убытков будут опубликованы после независимой верификации всех пулов, транзакций и операций по возврату.
- Техническое объяснение первопричины (root cause)
- Последовательность действий и методологию восстановления
- Результаты верификации и сверки с партнёрами
- Рекомендации по миграции на Balancer V3
Благодарности
Команда Balancer признаёт серьёзность инцидента и остаётся полностью сосредоточенной на сдерживании, восстановлении и защите пользователей.
Мы продолжаем работу совместно с партнёрами по безопасности и whitehat-командами и будем публиковать только проверенные обновления, за которыми последует подробный постмортем с подтверждёнными цифрами и дальнейшими шагами.
- SEAL, Hypernative, BitFinding, StakeWise, Berachain, whitehat-респондентам, Monerium, Gnosis, Sonic Labs,
- а также всем участникам сообщества, которые первыми предоставили информацию и помогли противостоять дезинформации.
Также благодарим всю DeFi-индустрию - протоколы, фонды, исследователей, валидаторов, сервис-провайдеров и сообщества, чья быстрая координация и солидарность реально помогли в смягчении последствий и возврате средств.
Balancer остаётся верен своим принципам: прозрачность, устойчивость и сотрудничество.
Дополнительная аналитика
Также первичный анализ показал, что проблема связана с функцией batchSwap, использующей отложенное исполнение и ошибку округления в функции upscale. Из-за этого злоумышленники могли проводить сложные цепочки обменов, обходя минимальный уровень ликвидности. Ошибка позволяла манипулировать балансами пулов и переводить активы, при этом часть средств оставалась внутри хранилища Balancer до последующих транзакций.
По другим данным: “Злоумышленники обошли уровни контроля доступа, чтобы напрямую манипулировать балансами активов. Это критический сбой в операционном управлении, а не в основной логике протокола”.
Последствия глобальные
- Bera провела хардфорк;
- Polygon блокировал транзакции;
- Sonic изменила логику S: Sonic Foundation занесла адреса хакеров в чёрный список;
- Сама атака готовилась несколько месяцев.
И как раз решения чейнов в этом всём выглядят самыми спорными и заслуживают отдельного разбора.