Безопасность
Что имеет ценность
- ETH и токены пулов — в позициях, которые никто не может вывести. ETH стороны покидает её пул, только когда эту сторону в него продают: трейдер или выравнивание hook после сделки, разведшей три цены.
- Банки, комиссии создателей и комиссии протокола — их хранит hook в виде прав требования на ETH в PoolManager Uniswap V4, с отдельным учётом для каждой дуэли.
Что это защищает
- Заблокированная ликвидность. Только фабрика может открывать зарегистрированные пулы или добавлять в них ликвидность при запуске; вывод и пожертвование откатываются.
- Ограниченные получатели. Банк покупает только победителя своей дуэли; комиссии создателя идут только создателю дуэли: кошельку, который её запустил, или адресу, которому он передал роль и который её принял. Комиссии протокола идут в текущее казначейство, которое владелец фабрики может сменить через 48 часов, в том числе для уже накопленных комиссий протокола.
- Ограниченные полномочия фабрики. У токенов нет владельца и функции выпуска, а hook нельзя ни обновить, ни приостановить. Владелец фабрики может приостановить новые запуски, предложить ограниченные условия для будущих дуэлей или сменить казначейство после задержки. Он также может сразу снизить долю протокола в комиссии за своп всех дуэлей, вплоть до нуля, или вернуть её обратно, но никогда не выше условий дуэли: то, что протокол не берёт, идёт в банки. Владелец может навсегда отказаться от роли (
renounceOwnership): тогда условия, казначейство, ставка протокола и приостановка остаются как есть, кроме уже предложенного изменения, которое любой может применить после задержки. - Расчёт перед свопом. Первый своп после закрытия рассчитывает завершённое среднее до начала торговли. Потолок выкупа находится примерно на 10 % выше ориентира, хранимого с точностью до долей тика: это 30-минутное экспоненциальное среднее тиков пула ETH, ограниченное снизу текущим тиком, чтобы оно сразу следовало за ценой вниз.
- Раунды только в ETH. Соотношение и среднее раунда используют два пула ETH. Переход A/B, который не сдвигает ни одной цены в ETH, не меняет ни сигнал, ни активность раунда, который иначе остался бы тихим.
- Комиссии только при полном исполнении. Свопы с предварительным списанием комиссии откатываются, если не могут полностью исполнить запрошенную сумму. Сетевой газ — отдельно.
- Изолированное выравнивание. Сохраняются только прибыльные циклы, ограниченные ETH продающего пула. Неудачная попытка откатывается и пропускается; при нехватке газа откатывается своп (
ArbitrageStarved), чтобы вызывающий не мог пропустить выравнивание, придержав газ. Это не гарантирует совпадения цен и не защищает от всех торговых потерь. - Одинаковые правила на любом маршруте. Hook взимает комиссии, налоги и сжигания, будь то роутер Duel или любой другой роутер V4.
Фабрика начинает работу с приостановленными запусками. Пока paused() равно true, запустить дуэль может только owner(); вызовы остальных откатываются с CreationPaused. Владелец открывает запуски через setPaused(false). Существующие дуэли продолжают торговаться.
Что показывает сайт
- Изображения только с сайта. Страницы показывают изображения дуэли только с собственного адреса Duel и только обработанные сайтом: JPEG, PNG или WebP, сначала прочитанный по заголовку (не более 1536 × 1536 пикселей и 5 МБ), декодированный в WebAssembly, вписанный в 1024 пикселя и заново закодированный в WebP без метаданных. Политика безопасности страниц не позволяет им загружать изображения откуда-либо ещё.
- Изолированные кодеки. Изображения декодируются в отдельном Worker, к которому обращается только сайт и который ничего не хранит: ни ключей, ни хранилища. Уязвимость в кодеке затронула бы только изображение, которое он читает. Сайт проверяет ответ этого Worker (статичный WebP ожидаемого размера) перед сохранением, а декодеры на C запускаются заново для каждого изображения.
- Дуэли, запущенные напрямую через контракт. В их данных может быть указан любой адрес. Сайт не показывает изображения по https; изображение из IPFS он загружает один раз через публичный шлюз (не более 5 МБ и 10 секунд), обрабатывает его как загрузку и показывает свою копию или инициалы стороны.
- Ограниченные загрузки. Сайт принимает изображения только со своих страниц: десять в минуту с одного адреса и ограниченное число в сутки. Изображение, которое не указано ни в одной дуэли, удаляется через сутки.
Как это проверялось
- Автоматические тесты покрывают поведение контрактов, веб-приложение и бота оповещений. Покрытие — это метрика тестов, а не доказательство безопасности любого возможного поведения.
- Форк-тесты проверяют интеграцию с развёрнутыми в Robinhood Chain контрактами Uniswap V4, включая Universal Router, Permit2 и V4 Quoter.
- Регрессионные тесты сохраняют случаи, найденные при внутренней проверке, включая учёт комиссий, граничные случаи запуска, расчёт и опустошённые пулы.
- Инвариантные тесты проверяют учёт на случайных последовательностях сделок, расчётов и выплат: права требования на ETH должны совпадать с причитающимися суммами, а промежуточные токены не должны оставаться в hook, фабрике или роутере. Прохождение заданного числа прогонов не доказывает безопасность для любой возможной последовательности.
Внутренняя проверка нашла и исправила маршрут с точным выходом в обход антиснайп-налога, соотношение, которое не следовало за стороной, купленной в одиночку, опустошённые пулы, которые неверно читала ориентирная цена выкупа, и небезопасные ссылки в метаданных дуэли.
Как сообщить об уязвимости
Если вы считаете, что нашли уязвимость, спасибо, что нашли время сообщить о ней. Напишите нам в личные сообщения в Telegram, @chatwithdivine, что вы нашли и, если можете, как это воспроизвести, и дайте нам время разобраться, прежде чем что-либо публиковать. Мы быстро ответим и будем держать вас в курсе.
Мы никогда не пишем первыми: доверяйте только @chatwithdivine.