在浏览器上验证合约
合约验证把 Solidity 源码与已部署的字节码一起发布,让用户和开发者能读代码、从浏览器 UI 调用函数、对照源码审查合约行为。
在 OXN 上,部署字节码本就公开——与以太坊一致——所以验证不会额外泄漏任何东西。它会做的是公开源码,这一点是你自己决定要不要公开。
验证可选,但推荐
以下情况应该验证:
- 你希望用户信任它。验证过的合约让任何人在交互前能读代码。
- 你希望其他开发者组合它。验证过的接口更易集 成。
- 你希望启用浏览器的 "Read Contract" / "Write Contract" UI。这些功能需要验证过的 ABI。
以下情况可以跳过:
- 逻辑是商业机密的合约。
- 还在活跃开发中的合约(验证最终版本,不必每次迭代都验证)。
即便不验证,编译后的字节码在链上依然公开。验证只是加上人类可读形式。
验证方法
OXN 区块浏览器支持两条路径:
- 直接上传——把源码粘到浏览器的验证表单里。
- Hardhat / Foundry 插件——通过 CLI 自动化。
具体插件 / API 在浏览器验证端点稳定后确定。工作流敲定后本页会更新具体命令。过渡期内,直接上传对所有合约都能用。
浏览器需要什么
任何验证方法都需要提供:
- Solidity 源码——每个文件,包括 import。对你仓库外的 import(OpenZeppelin、库),浏览器通常支持"扁平化"上传。
- 编译器版本——精确匹配(例如
0.8.24)。 - 优化器设置——必须与部署时一致(
enabled、runs)。 evmVersion——OXN 上必须是paris。- 构造参数——ABI 编码后的形式(如果 构造有参数)。
扁平化源码
有些验证流程要单个扁平文件:
# Hardhat
npx hardhat flatten contracts/MyToken.sol > flat.sol
# Foundry
forge flatten src/MyToken.sol > flat.sol
两个工具都把所有 import 内联到一个文件里。注意扁平化后 SPDX-License-Identifier 可能重复——工具通常会合并,但偶尔留下冲突需要手动修。
ABI 编码构造参数
如果合约有构造参数,浏览器需要它们的 ABI 编码 hex 字符串:
# Foundry
cast abi-encode "constructor(uint256,string)" 1000000000000000000 "Hello"
// ethers.js
const iface = new ethers.Interface(["constructor(uint256,string)"]);
const encoded = iface.encodeDeploy([1_000_000_000_000_000_000n, "Hello"]);
console.log(encoded.slice(2)); // 如果浏览器要求,去掉 0x 前缀
验证代理合约
对 OpenZeppelin 代理,代理与实现合约都要验证。之后浏览器可能检测到代理模式,让用户像与实现合约交互一样与代理交互。
代理验证的具体步骤取决于浏览器 UI——查看浏览器上的帮助。
排查
"Bytecode mismatch。" 最常见的失败。原因:
- 编译器版本差一个 patch(
0.8.24vs0.8.25)。 - 优化器 runs 不一致(
200vs1000)。 evmVersion不一致(parisvsshanghai)。- Metadata 哈希(
bytecodeHash)不一致——想要可复现构建,在编译器里设为"none"。
排查验证表单前先确认你 artifact 里的字节码与链上一致:
const artifact = require("./artifacts/contracts/MyToken.sol/MyToken.json");
const onchain = await provider.getCode("0xYourContractAddress");
console.log("Match:", artifact.deployedBytecode === onchain);
"Metadata not found。" 有些验证器要 Solidity metadata JSON 与源码一起。Hardhat 在 artifacts/build-info/ 下产出;Foundry 每个 artifact 的 metadata key 里存。