开始之前
- 一个 Virtual Environment。从仪表板复制它的 RPC URL。
- 该 Virtual Environment 上一个已注资的部署者地址。
/verify。
部署与验证
- Foundry
- Hardhat
配置 foundry.toml
Tenderly 的验证器会将 Solidity 编译器附加到已部署字节码上的元数据哈希与您提交的源代码进行匹配。通过显式设置 cbor_metadata 和 bytecode_hash,将元数据保留在编译输出中:foundry.toml
cbor_metadata = true 和 bytecode_hash = "ipfs" 是 Foundry 的默认值,但某些模板会剥离它们;bytecode_hash = "none" 会破坏验证。固定 solc_version 和 optimizer 设置,以便重新验证时编译出相同的字节码。如果您的项目使用像 OpenZeppelin 这样的库,请在 remappings.txt 中显式声明 remappings:remappings.txt
设置环境变量
showLineNumbers
cast chain-id 应打印 Virtual Environment 的链 ID,部署者余额应为非零:使用 forge create 部署
带上 verify 标志运行 forge create。构造函数参数作为原始值传入;Foundry 会对它们进行 ABI 编码,并将编码后的形式提交给验证器:showLineNumbers
使用 Foundry 脚本部署
使用forge script 在一次运行中部署多个合约。脚本中的每个 new Foo(...) 都会自动提交进行验证,构造函数参数会从广播日志中正确获取。导入会被透明处理:验证器会收到一个标准 JSON 载荷,其中包含编译器接触过的每个源文件,因此本地导入和库导入(例如 OpenZeppelin)无需额外步骤即可完成验证。使用 --slow 让交易一次发送一笔。不加它,广播批处理可能在上一笔交易被确认之前提交下一笔,这在托管 RPC 上不稳定,并且可能与验证步骤发生竞争。showLineNumbers
验证已有的合约
已经部署的合约可以使用forge verify-contract 验证。与 forge create 不同,此命令期望构造函数参数已经过 ABI 编码;请使用 cast abi-encode 生成它们:showLineNumbers
--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 不同或元数据哈希被剥离。 | 在 foundry.toml 中固定 solc_version、optimizer、optimizer_runs 和 bytecode_hash。重新验证前运行 forge clean && forge build。 |
Failed to deserialize content | 验证器返回了一个 Foundry 无法解析的错误字符串,通常包裹着上游身份验证或路径错误。 | 重新检查 URL 并使用 -vvvv 重新运行以查看原始响应。 |
在 forge create 上 Dry run enabled, not broadcasting transaction | 未传入 --broadcast,或被 --constructor-args 吞掉。 | 将 --constructor-args 移到最后一个标志。 |
forge script 只有第一个合约被验证 | RPC 竞态:第二笔交易在第一笔收据之前就已提交。 | 添加 --slow。 |
验证器报告 OK,但浏览器仍显示未验证 | UI 缓存。 | 硬刷新浏览器页面。API 响应中的状态是权威的。 |
后续步骤
- 使用 Foundry 验证代理合约:验证 UUPS 实现及其 ERC1967 代理,包括升级。
- 暂存合约:与您的团队共享 RPC 链接和合约地址。
- 暂存您的 dApp:将您的前端指向 Virtual Environment。
- Fork Virtual Environment:从 Virtual Environment 状态分支出来。