Sourcify 验证合约的核心动作只有一步:把编译产物提交上去,让它用相同的源码和编译设置重新编译,然后比对链上字节码。但提交什么文件取决于你用的是什么编译工具,提交错了,结果要么是 No Match,要么是 already_verified 的报错。验证完成后,结果分两档:Exact Match(完全匹配)和 Match(旧称 Partial Match),前者证明源码一个字节都没改,后者说明功能相同但注释、变量名或文件路径有差异。
提交什么:取决于你的编译工具
Sourcify 验证入口在 verify.sourcify.dev,选择链和合约地址后,需要提供源码和编译元数据。元数据文件是编译时自动生成的 JSON,里面记录了编译器版本、优化设置等关键信息。没有它,Sourcify 无法用正确的设置重新编译。
不同工具的提交方式差别很大。Hardhat 用户最省事的方式是直接上传 artifacts/build-info/ 下的 build-info JSON 文件,这个文件包含了完整的编译输入和输出,Sourcify 能从中解析出所有需要的东西。Foundry 用户对应的是 out/build-info/ 下的文件。Remix 用户需要上传合约的 .sol 文件以及 artifacts/ 目录下的元数据 JSON。如果你用 solc 命令行编译,需要同时提供 solc --metadata 生成的元数据和 .sol 文件。
一个容易踩的坑是依赖文件。如果你的合约 import 了 OpenZeppelin 或其他库,手动逐个上传依赖文件非常容易出错,任何一个文件路径对不上都会导致验证失败。更稳妥的做法是生成一个包含所有依赖源码的单一 metadata 文件,Hedera 文档提供了一个生成脚本,思路可以借鉴:把依赖内联到 metadata 里,一次上传,避免遗漏。
结果核验:Exact Match 和 Match 差在哪
验证完成后,Sourcify 返回的 match 字段是你要核验的对象。exact_match 意味着链上字节码的 CBOR 元数据哈希与你提交的编译产物完全一致,源码的每一个字节、每一行注释、每一个变量名都与部署时相同。match 意味着字节码逻辑部分一致,但元数据哈希不同——常见原因是注释、变量名、文件路径有改动,或者你在提交时用了 flattened 文件(把所有源码合并成一个文件),这会破坏原始的元数据哈希。
如果你需要的是证明这份源码就是部署时用的源码,exact_match 是唯一可接受的结果。如果你只是想让区块浏览器显示源码和 ABI、方便别人阅读和交互,match 在实际使用中已经足够,因为合约的功能逻辑是相同的。Sourcify 官方也提到,他们之所以把 partial match 改名为 match,就是因为 partial 这个词让人误以为合约没有通过验证。
有一个特殊情况需要知道:带 immutable 变量的合约。Solidity 的 immutable 变量在部署时会被填入运行时字节码,所以重新编译产生的字节码和链上实际存储的字节码在这个位置是不同的。Sourcify 的处理方式是在比对前把两边的 immutable 位置都置零,然后再比较。这不影响验证结果的有效性,但你需要知道这个机制存在,否则看到字节码 diff 时会困惑。
验证失败了,先查这四处
第一,编译器版本和优化设置。这是最常见的失败原因。你提交的 metadata 里的设置必须与部署时完全一致。如果元数据文件本身是对的,Sourcify 会自动使用其中的设置;如果你手动填了编译器版本或优化次数,填错了就会导致字节码不匹配。BlockVision 的 Monad 验证文档也把编译器版本、优化设置、元数据一致性列为验证失败的首要排查项。
第二,already_verified 报错。这个错误的意思不是验证失败,而是这个合约已经有一个匹配记录,你这次提交没有产生更好的匹配。Sourcify 的 GitHub issue 里有人遇到过这种情况:API 显示合约已验证,但 repo 页面打不开,原因是缓存或查询问题。如果你确认自己提交的源码是正确的,但收到这个错误,先去 repo.sourcify.dev 查一下该地址在当前链上是否已经有验证记录。如果已有 match 记录而你想升级为 exact_match,需要确保提交的源码和元数据与部署时完全相同,否则不会覆盖原有记录。
第三,合约地址和链选错了。Sourcify 支持多链,同一个合约地址在不同链上可能是不同的合约。在 verify.sourcify.dev 提交时,先确认你选的链 ID 与部署链一致。Hedera 的主网是 295,测试网是 296,这类具体数字在提交前核对一遍,比提交后排查省时间。
第四,构造函数参数。如果你手动提交源码而不是用 build-info 文件,构造函数参数需要包含在提交内容中,否则 Sourcify 重新编译出的 creation bytecode 会缺少这些参数,导致比对失败。用 Hardhat 或 Foundry 的 build-info 文件通常能避免这个问题,因为参数已经包含在编译输出里了。
验证之后:在哪里看结果
Sourcify 自己的仓库页面 repo.sourcify.dev 是查看验证详情的地方。选择链和地址后,可以看到编译设置、ABI、元数据、源码文件,以及带注释的字节码(annotated bytecode),它会高亮显示字节码中的变换和函数签名。存储布局(Storage Layout)视图还能直接看到变量的槽位分布,对于审计和调试很有用。
如果你部署在 Hedera 这类有独立区块浏览器的链上,验证状态会自动同步。HashScan 从 Sourcify 读取验证结果,合约页面会显示源码和验证徽章。你不需要在 HashScan 上重复提交,在 Sourcify 验证通过后,浏览器那边会自己更新。
我的判断:优先用 build-info,不要手动拼文件
如果你用 Hardhat 或 Foundry,直接上传 build-info 文件是出错概率最低的方式。手动选择源码文件、逐个添加依赖、填写编译设置,每一步都有出错的可能,而 build-info 文件是编译工具自己生成的,包含了 Sourcify 需要的全部信息。只有当你用的工具没有生成 build-info(比如 Remix 或纯 solc 命令行),才需要考虑手动提交,并且要格外注意依赖文件的完整性。
另外,如果你对源码是否被篡改有严格需求,验证后确认结果是 exact_match 而不是 match。match 在功能层面是可信的,但它不能证明注释和变量名没有被动过。对于需要向用户证明代码就是这份代码的场景,exact_match 才是你要的结果。
参考资料
- Hedera·Verify a Smart Contract on HashScan,页面发布或更新日期:页面未标明更新日期;核查日期:2026-10-20。
- Hedera·Verifying Smart Contracts,页面发布或更新日期:页面未标明更新日期;核查日期:2026-10-20。
- Sourcify Docs·Verify via UI,页面发布或更新日期:页面未标明更新日期;核查日期:2026-10-20。
- Sourcify Docs·Exact Match vs Match,页面发布或更新日期:页面未标明更新日期;核查日期:2026-10-20。
- Sourcify Docs·Verification with Immutable Variables,页面发布或更新日期:页面未标明更新日期;核查日期:2026-10-20。
- BlockVision·Verify Smart Contract on Monad Explorer,页面发布或更新日期:页面未标明更新日期;核查日期:2026-10-20。
- GitHub·Verification of unichain contracts fails constantly (Issue #2429),页面发布或更新日期:页面未标明更新日期;核查日期:2026-10-20。
- GitHub·Verification Failed: already_verified (Issue #2483),页面发布或更新日期:页面未标明更新日期;核查日期:2026-10-20。
- Hedera·Smart Contract Verification API,页面发布或更新日期:页面未标明更新日期;核查日期:2026-10-20。
- Sourcify Docs·Repo UI,页面发布或更新日期:页面未标明更新日期;核查日期:2026-10-20。
- Hedera Blog·Smart Contract Verification: Sourcify.dev Now Supported,页面发布或更新日期:页面未标明更新日期;核查日期:2026-10-20。



