Перейти к основному содержанию
Разверните Solidity-контракты в Virtual Environment с помощью Foundry или Hardhat. Контракты, задеплоенные в Virtual Environment, называются virtual contracts и появляются в разделе Contracts на вкладке Virtual Contracts. Верифицируйте контракты по мере деплоя. Верифицированные контракты открывают доступ к Debugger, Gas Profiler, Simulator и более информативным представлениям транзакций в обозревателе.

Прежде чем начать

Для верификации в Virtual Environment не нужен отдельный access key: сам RPC URL является аутентификацией, а эндпоинт верификатора — это тот же URL с добавленным /verify.
По умолчанию контракты, верифицированные в Virtual Environment, приватны. В зависимости от настроек видимости публичного обозревателя, код может быть виден снаружи. Проверьте переключатель публичного обозревателя и видимость верификации перед верификацией того, что вы хотите оставить приватным.

Деплой и верификация

Настройте foundry.toml

Верификатор Tenderly сопоставляет хеш метаданных, который компилятор Solidity добавляет к задеплоенному байткоду, с источником, который вы предоставляете. Сохраняйте метаданные в скомпилированном выводе, явно задавая cbor_metadata и bytecode_hash:
foundry.toml
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
solc_version = "0.8.24"
optimizer = true
optimizer_runs = 200

# Required for Tenderly contract verification:
# keep the CBOR metadata so the verifier can match the on-chain bytecode hash.
cbor_metadata = true
bytecode_hash = "ipfs"
cbor_metadata = true и bytecode_hash = "ipfs" — значения Foundry по умолчанию, но некоторые шаблоны их убирают; bytecode_hash = "none" ломает верификацию. Зафиксируйте solc_version и настройки оптимизатора, чтобы повторная верификация компилировала тот же байткод.Если ваш проект использует библиотеки вроде OpenZeppelin, объявите remappings явно в remappings.txt:
remappings.txt
@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/
forge-std/=lib/forge-std/src/
При автообнаружении remappings payload верификации может содержать иные строки remapping, чем использовала сборка, что приводит к ошибкам несоответствия байткода.

Установите переменные окружения

showLineNumbers
export TENDERLY_VIRTUAL_TESTNET_RPC=...   # paste your RPC URL
export TENDERLY_VERIFIER_URL=$TENDERLY_VIRTUAL_TESTNET_RPC/verify
export PRIVATE_KEY=...                    # deployer private key
Проверьте соединение перед деплоем. cast chain-id должен вывести chain ID Virtual Environment, а баланс деплойера должен быть ненулевым:
cast chain-id --rpc-url $TENDERLY_VIRTUAL_TESTNET_RPC
cast balance <DEPLOYER_ADDRESS> --rpc-url $TENDERLY_VIRTUAL_TESTNET_RPC

Деплой с forge create

Запустите forge create с флагами верификации. Аргументы конструктора передаются как сырые значения; Foundry сам ABI-кодирует их и отправляет закодированную форму верификатору:
showLineNumbers
forge create src/Counter.sol:Counter \
  --rpc-url $TENDERLY_VIRTUAL_TESTNET_RPC \
  --private-key $PRIVATE_KEY \
  --broadcast \
  --verify \
  --verifier custom \
  --verifier-url $TENDERLY_VERIFIER_URL \
  --constructor-args 42
Ставьте --constructor-args в конец. Он жадно поглощает остаток командной строки, поэтому любой флаг после него будет воспринят как ещё один аргумент конструктора.

Деплой с помощью Foundry-скрипта

Используйте forge script, чтобы задеплоить несколько контрактов за один запуск. Каждое new Foo(...) в скрипте автоматически отправляется на верификацию, с правильными аргументами конструктора, взятыми из broadcast log. Импорты обрабатываются прозрачно: верификатор получает standard-JSON payload со всеми исходными файлами, которых коснулся компилятор, так что локальные и библиотечные импорты (например, OpenZeppelin) верифицируются без дополнительных шагов.Используйте --slow, чтобы транзакции отправлялись по одной. Без этого пакетная отправка может отправить транзакцию до подтверждения предыдущей, что нестабильно с хостинговым RPC и может создавать гонку с шагом верификации.
showLineNumbers
forge script script/Counter.s.sol:CounterScript \
  --rpc-url $TENDERLY_VIRTUAL_TESTNET_RPC \
  --private-key $PRIVATE_KEY \
  --broadcast \
  --slow \
  --verify \
  --verifier custom \
  --verifier-url $TENDERLY_VERIFIER_URL

Верификация уже задеплоенного контракта

Уже развёрнутые контракты можно верифицировать через forge verify-contract. В отличие от forge create, эта команда ожидает аргументы конструктора уже в ABI-кодированной форме; используйте cast abi-encode для их получения:
showLineNumbers
forge verify-contract $COUNTER_ADDRESS src/Counter.sol:Counter \
  --verifier custom \
  --verifier-url $TENDERLY_VERIFIER_URL \
  --constructor-args $(cast abi-encode "constructor(uint256)" 42) \
  --watch
--watch опрашивает верификатор, пока верификация не завершится, и печатает результат. Для развёртываний с несколькими контрактами запускайте forge verify-contract по одному разу на каждый задеплоенный адрес.

Проверьте панель

В Tenderly Dashboard откройте Contracts и выберите вкладку Virtual Contracts. Ваши только что задеплоенные контракты появятся там, со статусом верификации по каждому контракту.

Устранение неполадок

СимптомВероятная причинаРешение
Ответ верификатора unauthorized / -32004Неверный URL верификатора. Частая ошибка — добавление /verify/etherscan вместо /verify.Убедитесь, что URL верификатора заканчивается ровно на /verify.
Bytecode does not match deployed contractПрисланный исходный код скомпилировался иначе, чем то, что задеплоено: несовпадение optimizer_runs, другая solc_version или удалённый хеш метаданных.Зафиксируйте solc_version, optimizer, optimizer_runs и bytecode_hash в foundry.toml. Выполните forge clean && forge build перед повторной верификацией.
Failed to deserialize contentВерификатор вернул строку ошибки, которую Foundry не может распарсить — обычно оборачивает upstream-ошибку авторизации или пути.Проверьте URL и повторите запуск с -vvvv, чтобы увидеть сырой ответ.
Dry run enabled, not broadcasting transaction при forge createНе передан --broadcast или его поглотил --constructor-args.Переместите --constructor-args в конец, чтобы он был последним флагом.
Только первый контракт верифицируется в forge scriptГонка RPC: вторая транзакция отправлена до получения чека первой.Добавьте --slow.
Верификатор сообщает OK, но обозреватель по-прежнему показывает контракт как неверифицированныйКеширование UI.Сделайте hard-refresh страницы обозревателя. Статус в ответе API является достоверным.

Дальнейшие шаги