Работает на: публичных сетях (mainnet и testnet). Чтобы верифицировать контракты в Virtual Environment, см. Деплой и верификация контрактов и Верификация прокси-контрактов с помощью Foundry.
forge create, forge script и forge verify-contract, посредством своего API верификации, совместимого с Etherscan. Верификация бывает либо приватной (исходный код виден только внутри вашего проекта в Tenderly), либо публичной (виден всем, у кого есть ссылка).
Прежде чем начать
Вам понадобятся slug вашего аккаунта и проекта Tenderly, ключ доступа Tenderly (Dashboard → Account Settings → Authorization) и профинансированный аккаунт в целевой сети. Верификатор Tenderly сопоставляет хеш метаданных, который компилятор Solidity добавляет к развёрнутому байткоду, с исходным кодом, который вы отправляете. Сохраняйте метаданные в результатах компиляции и фиксируйте настройки компилятора, чтобы они не изменились между моментом деплоя и моментом верификации:foundry.toml
cbor_metadata = true и bytecode_hash = "ipfs" — это значения по умолчанию в Foundry, но некоторые шаблоны их убирают; bytecode_hash = "none" ломает верификацию. Расхождение в настройках компилятора между деплоем и верификацией приводит к ошибке Bytecode does not match deployed contract.
В примерах ниже используется Base Sepolia (chain ID 84532). Настройка окружения:
.env
URL верификатора
Каждая команда верификации Foundry принимает--verifier-url, указывающий на API верификации Tenderly, и --etherscan-api-key $TENDERLY_ACCESS_KEY для аутентификации:
| Сегмент | Что это |
|---|---|
account/$TENDERLY_ACCOUNT | Slug вашего аккаунта Tenderly (пользователя или организации). Регистрозависимый. |
project/$TENDERLY_PROJECT | Проект, к которому будет привязан верифицированный контракт. |
network/$NETWORK_ID | Сеть, в которой развёрнут контракт, в виде десятичного chain ID (1 для Ethereum Mainnet, 8453 для Base, 84532 для Base Sepolia). |
| без суффикса | Приватная верификация: контракт виден только внутри вашего проекта. |
суффикс /public | Публичная верификация: исходный код контракта виден всем, у кого есть ссылка, без входа в Tenderly. |
Приватная верификация
Приватно верифицированные контракты видны только участникам вашего проекта в разделе Contracts в Dashboard.showLineNumbers
forge verify-contract ожидает, что аргументы конструктора уже закодированы в ABI; используйте cast abi-encode, чтобы получить их. --watch опрашивает верификатор, пока верификация не завершится, и печатает результат.
При успехе Foundry печатает строку
URL: https://etherscan.io/address/.... Это особенность отображения потока, совместимого с Etherscan; контракт был верифицирован в Tenderly, а не в Etherscan. Подтвердите это в Dashboard в разделе Contracts.Публичная верификация
Замените URL верификатора на вариант с суффиксом/public; больше ничего менять не нужно. Страница с верифицированным исходным кодом станет доступна всем, у кого есть ссылка, без входа в Tenderly.
showLineNumbers
Деплой и верификация одной командой
forge create и forge script принимают те же флаги встроенно, поэтому деплой и верификацию можно запустить одной командой. В отличие от forge verify-contract, обе они принимают аргументы конструктора как сырые значения и сами кодируют их в ABI:
showLineNumbers
forge script с флагом --slow; каждый контракт, который развёртывает скрипт, верифицируется автоматически, а аргументы конструктора берутся из broadcast-лога. Без --slow пакетирование broadcast может отправить транзакцию до подтверждения предыдущей, из-за чего может произойти гонка с шагом верификации:
showLineNumbers
Верификация контракта, который вы не разворачивали
Когда контракт верифицирован в публичном обозревателе (Etherscan, Basescan), но не в вашем проекте Tenderly, клонируйте верифицированный исходный код локально с помощьюforge clone и повторно верифицируйте его через API Tenderly:
showLineNumbers
forge clone скачивает верифицированный исходный код из Etherscan-совместимого API исходной сети (используйте ключ соответствующего обозревателя для каждой сети), восстанавливает структуру проекта и фиксирует настройки компилятора, которые использовал исходный деплойер. Запустите forge build в клонированной директории, чтобы убедиться, что она собирается; ошибки разрешения импортов обычно связаны с remappings.txt.
Затем верифицируйте в Tenderly:
showLineNumbers
Bytecode does not match deployed contract, передайте исходные настройки компилятора явно. Все они перечислены на странице обозревателя, где контракт уже верифицирован, в разделе исходного кода контракта:
showLineNumbers
Контракты, живущие в директории lib
forge build компилирует только то, что достижимо из src/. Контракт, который существует только внутри зависимости в lib/ (прокси, стандартная реализация ERC), никогда не попадает в кеш сборки, и forge verify-contract падает с ошибкой:
src/Imports.sol, который импортирует контракт, а затем пересоберите:
src/Imports.sol
Imports.sol никогда не отправляется в верификатор; он лишь заставляет компилятор кешировать контракт и его зависимости. После этого верифицируйте, используя полный путь контракта в lib/, поскольку исходный код физически находится именно там.
Для TransparentUpgradeableProxy, который вы не разворачивали, конструктор имеет вид (address _logic, address initialOwner, bytes _data), и большую его часть можно восстановить из состояния сети:
showLineNumbers
foundry.toml или скриптам деплоя разворачивающего проекта.
Устранение неполадок
| Симптом | Вероятная причина | Решение |
|---|---|---|
unauthorized | Отсутствует или неверный --etherscan-api-key, либо ключ доступа был отозван. | Сгенерируйте новый ключ доступа и заново задайте TENDERLY_ACCESS_KEY. |
not found | Неверный slug аккаунта, slug проекта или chain ID в URL верификатора. | Перепроверьте каждый сегмент URL. Slug аккаунта регистрозависимый. |
Bytecode does not match deployed contract | Отправленный исходный код компилируется иначе, чем то, что развёрнуто: несоответствие optimizer_runs, другая solc_version или версия EVM, либо срезанный хеш метаданных. | Зафиксируйте solc_version, optimizer, optimizer_runs и bytecode_hash в foundry.toml или передайте --compiler-version --optimizer-runs --evm-version явно. Перед повторной верификацией выполните forge clean && forge build. |
Failed to deserialize content | Верификатор вернул строку ошибки, которую Foundry не может распарсить, обычно оборачивающую ошибку авторизации или пути от вышестоящего сервиса. | Перепроверьте URL и запустите повторно с -vvvv, чтобы увидеть сырой ответ. |
Failed to get standard json input - cannot resolve file at lib/... | Контракт нигде не импортируется из src/, поэтому отсутствует в кеше сборки. | Создайте src/Imports.sol, который импортирует его, затем forge build --force. См. Контракты, живущие в директории lib. |
Dry run enabled, not broadcasting transaction при forge create | Не передан --broadcast, либо он проглочен флагом --constructor-args. | Переместите --constructor-args в конец списка флагов. |
Верифицируется только первый контракт при forge script | Гонка на RPC: вторая транзакция была отправлена до получения квитанции первой. | Добавьте --slow. |
| Верификация успешна, но контракт не появляется в проекте | Неверный slug проекта в URL верификатора. | Повторно верифицируйте с правильным slug. Контракт попадёт в тот проект, на который указывал URL. |