Риски CREATE2: предсказуемые адреса, повторный деплой и ошибки в фабриках контрактов

CREATE2 даёт мощный инструмент — детерминированные адреса контрактов (см. 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-протоколы: с учётом всех инвариантов, жизненного цикла адресов и поведения сети.

См. также

Task Runner