Короткий ответ: CREATE2 вычисляет адрес контракта из адреса вызывающего deployer, 32-байтового salt и хеша init code. Это позволяет заранее знать адрес будущего контракта, но одинаковый salt сам по себе не гарантирует совпадение результата: должны совпасть deployer и байтовый код инициализации, а целевой адрес не должен быть уже занят.
Чем CREATE2 отличается от обычного CREATE
Обычный CREATE выводит адрес нового контракта из адреса создателя и его nonce. Поэтому следующий адрес зависит от истории создания аккаунтов и от того, сколько раз этот deployer уже запускал CREATE. CREATE2 добавляет возможность задать salt и использует хеш init code. Формула EIP-1014 берёт последние 20 байт Keccak-256 от конкатенации байта 0xff, адреса deployer, 32 байт salt и Keccak-256 init code. Длина внешнего preimage фиксирована – 85 байт.
Упрощённо это выглядит так: адрес = последние 20 байт keccak256(0xff || deployer || salt || keccak256(init_code)). Символ || здесь означает соединение последовательностей байтов, а не текстовых строк. Salt всегда кодируется как 32-байтовое значение, а адрес deployer – как 20 байт. Поэтому ошибки в ABI-кодировании, неверная длина salt или использование другого адреса фабрики дадут другой результат, даже если визуально входные значения кажутся похожими.
Ключевое отличие состоит в предсказуемости до отправки транзакции. Клиент или другой контракт может вычислить адрес заранее и использовать его как точку взаимодействия, хотя кода по этому адресу пока нет. Так строятся контрфактические адреса, фабрики контрактов и некоторые схемы аккаунтов. Для общего контекста жизненного цикла контракта полезно также объяснение о материале о transient storage в EVM; CREATE2 отвечает за адрес развертывания, а не за временное хранение состояния во время вызова.
Из чего складывается итоговый адрес
Первый вход – deployer, то есть адрес аккаунта или контракта, который непосредственно выполняет CREATE2. Если пользователь вызывает фабрику, deployer для формулы – фабрика, а не обязательно внешний кошелёк пользователя. Две фабрики с одинаковым salt и init code получат разные адреса, потому что отличаются их адреса. Обновление или повторное развертывание фабрики по другому адресу также меняет семейство выводимых адресов.
Второй вход – salt. Протокол обычно формирует его из идентификатора пользователя, параметров кампании, номера версии или других данных, чтобы получить адрес, уникальный для конкретного назначения. Это не секрет и не ключ доступа; salt лишь меняет хешируемые данные. Если контракт сам принимает произвольные salts, проект должен предотвращать непреднамеренные коллизии в своём пространстве параметров и ясно объяснять, какие поля входят в salt. Одинаковый salt у разных deployer не ведёт к одинаковому адресу.
Третий вход – init code, то есть байты, исполняемые при создании контракта и обычно содержащие constructor и аргументы конструктора. Хешируется именно код инициализации, а не только итоговый runtime bytecode. Изменение версии компилятора, оптимизаций, исходного кода, библиотечных адресов или constructor arguments способно изменить init code hash. При ручной проверке нужно использовать те же байты, что фактически передаются в CREATE2, а не собирать приблизительную копию по исходнику.
Зачем адрес вычисляют до развертывания
Предсказуемые адреса позволяют отправить активы или настроить взаимодействие до появления кода, если протокол гарантирует последующее развертывание нужного контракта. Один пример – смарт-аккаунт, адрес которого можно показать заранее. Другой – фабрика, которая создаёт пары или пулы по детерминированному адресу. Интеграции могут заранее рассчитать адрес контракта из параметров и не хранить отдельный справочник для каждого экземпляра.
Удобство не означает, что пустой адрес уже способен выполнять правила будущего контракта. До развертывания адрес не валидирует транзакции и не защищает лежащие там активы логикой будущего кода. Если пользователь отправил средства на предсказанный адрес, нужно убедиться, что правильный deployer действительно сможет развернуть туда ожидаемый код и что constructor не имеет неожиданных параметров. Неверная оценка может оставить средства недоступными или направить их под контроль другой логики.
В DeFi детерминированные адреса полезны для однотипных контрактов и маршрутизации, но вычисление адреса не подтверждает качество реализации. Например, адрес пула может быть рассчитан из фабрики, двух токенов и параметров fee, однако нужно ещё сверить сеть, контракт фабрики, порядок токенов и конкретную версию байткода. В пользовательском интерфейсе проверяйте исходные данные, а не только красивый адрес. Похожий принцип проверки параметров применяется и при чтении транзакций в обзоре EulerSwap.
Коллизии и ограничения CREATE2
Если код или nonce уже присутствует по вычисленному адресу, создание завершается ошибкой. EIP-684 задаёт поведение при коллизии: контракт нельзя создать в адресе с ненулевым nonce или непустым кодом. Поэтому формула может быть детерминированной, но попытка развертывания не обязательно успешна. До отправки транзакции проверьте состояние адреса в нужной сети и убедитесь, что другая транзакция не успела занять его раньше.
Теоретические совпадения хешей не являются повседневным риском, который пользователь способен устранить выбором «уникального» salt. Практические проблемы чаще возникают из-за повторного использования параметров, путаницы между адресом фабрики и инициатора, отличающегося init code или неверного network ID при построении конфигурации. Проверяйте не только шестнадцатеричную строку результата, но и все поля, которыми её вычислили.
SELFDESTRUCT не даёт простого универсального способа стереть контракт и занять тот же адрес заново в пределах одной транзакции. Правила изменений состояния EVM и nonce делают такие ожидания неверными. Перед проектированием повторного развертывания изучайте действующие правила сети и версию EVM, а не старые примеры из блогов. Ошибку при фактической попытке создания можно исследовать по trace, как в руководстве по чтению revert trace, но симуляция не заменяет проверку параметров.
Практический порядок проверки
Чтобы проверить вычисление, найдите официальный адрес фабрики в нужной сети и выясните, какой адрес фактически вызывает opcode. Затем получите точное значение salt и сырой init code вместе с constructor arguments. Пересчитайте хеш локально над байтами, используя реализацию Keccak-256 и корректное дополнение до размеров, предусмотренных формулой. Не подменяйте Keccak вариантом SHA3-256: совпадающие названия часто приводят к ошибке в сторонних инструментах.
Сравните рассчитанный адрес с тем, что показывает клиент или обозреватель. Если значения расходятся, проверяйте кодирование по шагам: 0xff, 20-байтовый deployer, 32-байтовый salt, затем хеш точных байтов init code. Зафиксируйте версию компилятора и оптимизации, если код собирается локально. Для factory-контракта проверьте её реализацию и исходники, поскольку она может преобразовывать пользовательские параметры перед передачей в CREATE2.
После развертывания проверьте транзакцию создания, адрес результата, runtime code и события фабрики. Сверьте, что контракт имеет ожидаемую реализацию и constructor state, а не просто совпадающий адрес. При дальнейших переводах сначала проверьте адрес и сеть, затем тестируйте значимые операции на небольших суммах, если сценарий этого требует. CREATE2 полезен именно потому, что адрес можно рассчитать заранее; надёжность всё равно зависит от точных входных данных и корректной проверки кода.
Итог
Проверяйте актуальные параметры по действующей документации проекта и оценивайте каждый уровень риска отдельно. Техническая возможность не равна гарантии безопасности или финансового результата. Для практических операций используйте официальные сети, проверенные адреса и небольшие тестовые суммы, когда это уместно.



