CREATE2 даёт мощный инструмент — детерминированные адреса контрактов (см. CREATE2 и детерминированные адреса). Но вместе с этим появляются и специфические риски: предсказуемые адреса, возможное переиспользование адресов, скрытые предположения о коде по адресу и ошибки в фабриках.
Эта страница — чек-лист по основным рискам и типичным ошибкам при использовании CREATE2.
Предсказуемые адреса: благо и угроза одновременно
Формула CREATE2 делает адрес контракта полностью предсказуемым:
- address = last_20_bytes( keccak256(0xff, deployer, salt, keccak256(init_code)) )
Это удобно для counterfactual deployment и фабрик, но:
- злоумышленник тоже может заранее вычислить адрес и подготовить атаки;
- любые ончейн-инварианты вида «если по адресу X когда-нибудь появится контракт с кодом Y» должны учитывать, что адрес X известен и без деплоя.
Типичные сценарии:
- фронтранинг:
- протокол планирует задеплоить контракт по известному адресу;
- атакующий пытается подменить фабрику или условия деплоя, чтобы по этому адресу оказался «его» код;
- логика «простых кошельков»:
- средства заранее отправляют на будущий адрес кошелька;
- если фабрика не уникальна/надёжна, кто-то ещё может развернуть по этому адресу другой код, который заберёт баланс.
Переиспользование адресов и SELFDESTRUCT
Классическая угроза: повторный деплой по тому же адресу, если:
- контракт был уничтожен через SELFDESTRUCT;
- затем тем же deployer с тем же salt и тем же init_code разворачивается новый контракт.
Последствия:
- история адреса становится неоднозначной: раньше там был один код, теперь — другой;
- инварианты «адрес X всегда соответствует коду Y» ломаются;
- off-chain-логика и аналитика могут неверно интерпретировать состояние.
После EIP-6780 на Ethereum L1 поведение SELFDESTRUCT изменилось (см. SELFDESTRUCT после EIP-6780):
- повторное использование адреса на Mainnet в прежнем виде существенно ограничено;
- но в других EVM-сетях (L2, сайдчейны, форки) старая модель может сохраняться.
Безопасный подход:
- не строить критичные инварианты на предположении, что «по адресу никогда не сменится код»;
- явно учитывать поведение SELFDESTRUCT на целевой сети;
- для протоколов, чувствительных к повторному деплою, вводить дополнительные проверки (например, хеш кода или версию).
Ошибки в init-коде и расхождение «ожидаемого» и фактического контракта
Адрес CREATE2 жёстко привязан к keccak256(init_code) (см. Keccak-256). Частая ошибка:
- фронтенд/скрипт вычисляет адрес будущего контракта по одной версии init-кода;
- в процессе сборки или деплоя используется другая версия (иной байткод, иные конструкторные параметры);
- фактический код по детерминированному адресу не совпадает с тем, что ожидали.
Риски:
- подписи пользователей или оффчейн-соглашения оказываются «привязаны» к адресу, где код с другой логикой;
- протоколы, доверяющие адресу как «сидящему на конкретной логике», становятся уязвимыми к некорректным допущениям.
Рекомендации:
- фиксировать точную версию init-кода в конфигурации деплоя;
- использовать проверяемое воспроизведение (reproducible builds) и хеш init-кода в ончейн-структурах;
- при критичной логике проверять не только адрес, но и хеш кода (code hash) после деплоя.
Управление salt и злоупотребления пользовательскими salt
Salt — 32-байтовое значение, которое влияет на адрес. Ошибки:
- пользовательский salt без ограничений:
Если контракт позволяет любому пользователю передавать произвольный salt, он может:
- создавать множество контрактов с неожиданными адресами;
- пытаться «подсадить» протокол на конкретный адрес, который выглядит «правильным», но на самом деле контролируется им.
- коллизии salt в фабрике:
- фабрика не проверяет, что для данного salt контракт ещё не создан;
- многократные попытки вызвать CREATE2 с теми же параметрами могут приводить к ошибкам или неожиданному поведению.
Практики:
- проектировать формат salt (например, включать туда ID пользователя, хеш параметров и т.п.);
- явно хранить в фабрике mapping salt → alreadyDeployed;
- не использовать «сырой» пользовательский ввод как salt без нормализации и проверок.
Риски в фабриках и апгрейдируемых системах
CREATE2 почти всегда используется через фабрики контрактов:
- фабрика выбирает salt и init_code;
- вызывает CREATE2 и получает предсказуемый адрес.
Ключевые уязвимости:
- компрометированный админ фабрики:
- может задеплоить «злую» реализацию по ожидаемым адресам;
- особенно критично для смарт-кошельков и DeFi-протоколов, где создаются пользовательские «счета» через фабрику.
- изменение логики фабрики:
- при обновлении контракта-фабрики (через прокси и DELEGATECALL) меняется и init-код будущих контрактов;
- старые вычисленные адреса перестают соответствовать новым контрактам, хотя формально формула CREATE2 остаётся той же.
- ошибка в конструкторе:
- конструктор может писать в storage фабрики (или связанного контракта) неожиданные данные;
- при массовых деплоях это приводит к расхождению состояния с предположениями протокола.
Защита:
- минимальная и проверенная логика в фабрике;
- строгая модель прав (мультисиг, timelock) на её апгрейды;
- отдельный аудит фабрик, использующих CREATE2.
Взаимодействие с account abstraction и смарт-кошельками
В схемах абстракции аккаунтов:
- адрес кошелька пользователя заранее вычисляется через CREATE2;
- на этот адрес могут приходить средства до деплоя контракта.
Риски:
- если фабрику можно заменить/скомпрометировать, злоумышленник может:
- развернуть другой контракт по тому же адресу;
- забрать заранее отправленные средства или изменить правила управления кошельком.
- неверные предположения фронтенда:
- интерфейс показывает пользователю «будущий кошелёк»;
- но не проверяет, какой код туда реально задеплоен.
Минимально необходимая защита:
- жёстко фиксировать фабрику и версию кода в протоколе;
- проверять code hash кошелька после деплоя;
- не полагаться на один только адрес как на индикатор «правильного» аккаунта.
Рекомендации по безопасному дизайну с CREATE2
- Чётко документировать:
- формулу адреса;
- формат salt;
- ожидаемый init-код.
- Для критичных сценариев:
- хранить в ончейн-структурах хеш кода или версию реализации;
- проверять соответствие кода ожиданиям при первой инициализации.
- Учитывать особенности сети:
- поведение SELFDESTRUCT (см. SELFDESTRUCT после EIP-6780);
- наличие/отсутствие повторного деплоя по тому же адресу.
- Отдельно аудитить:
- фабрики контрактов;
- account abstraction-схемы;
- взаимодействие CREATE2 с прокси и DELEGATECALL.
CREATE2 остаётся мощным и полезным инструментом, но его использование нужно проектировать так же осторожно, как и сложные DeFi-протоколы: с учётом всех инвариантов, жизненного цикла адресов и поведения сети.
