跳到主要内容

在浏览器上验证合约

合约验证把 Solidity 源码与已部署的字节码一起发布,让用户和开发者能读代码、从浏览器 UI 调用函数、对照源码审查合约行为。

在 OXN 上,部署字节码本就公开——与以太坊一致——所以验证不会额外泄漏任何东西。它做的是公开源码,这一点是你自己决定要不要公开。

验证可选,但推荐

以下情况应该验证:

  • 你希望用户信任它。验证过的合约让任何人在交互前能读代码。
  • 你希望其他开发者组合它。验证过的接口更易集成。
  • 你希望启用浏览器的 "Read Contract" / "Write Contract" UI。这些功能需要验证过的 ABI。

以下情况可以跳过:

  • 逻辑是商业机密的合约。
  • 还在活跃开发中的合约(验证最终版本,不必每次迭代都验证)。

即便不验证,编译后的字节码在链上依然公开。验证只是加上人类可读形式。

验证方法

OXN 区块浏览器支持两条路径:

  1. 直接上传——把源码粘到浏览器的验证表单里。
  2. Hardhat / Foundry 插件——通过 CLI 自动化。

具体插件 / API 在浏览器验证端点稳定后确定。工作流敲定后本页会更新具体命令。过渡期内,直接上传对所有合约都能用。

浏览器需要什么

任何验证方法都需要提供:

  • Solidity 源码——每个文件,包括 import。对你仓库外的 import(OpenZeppelin、库),浏览器通常支持"扁平化"上传。
  • 编译器版本——精确匹配(例如 0.8.24)。
  • 优化器设置——必须与部署时一致(enabledruns)。
  • 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.24 vs 0.8.25)。
  • 优化器 runs 不一致(200 vs 1000)。
  • evmVersion 不一致(paris vs shanghai)。
  • 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 里存。

下一步