代币合约没开源是什么意思:区块浏览器上的源码验证状态怎么看
浏览器显示未验证,不代表项目从未公开源码;已经验证,也不代表代码安全。先把这两件事分清,再看 Etherscan、Sourcify 的标记各覆盖了什么。

「合约没开源」和「合约未验证」不能画等号。前者说源码没有公开,后者说你查的验证服务尚未确认有一份源码与该地址的链上字节码匹配。项目可能已经在代码仓库公开源码,却还没在这家浏览器完成验证。以太坊这类公开链上的合约字节码仍然可查,只是机器指令比源码难读。
反过来,「已验证」说明源码与链上字节码按该服务的规则匹配成功。代码写得安不安全、有没有后门、能不能增发,验证标记一个字都没说。状态在区块浏览器的合约地址页上看,Etherscan 分四种叫法,Sourcify 分两档。
合约未验证,到底缺的是什么
合约是用 Solidity 这类语言写的,部署之前要编译成字节码,链上存的、执行的都是字节码。ethereum.org 的源码验证文档写得很直接:每个合约编译后的字节码在链上都是公开的,但这种低级语言对开发者和用户来说都很难读懂。
源码验证补的就是这一块。同一篇文档把过程列了出来:把源码文件和编译设置交给编译器;编译器输出字节码;取出指定地址上已部署合约的字节码;把两份字节码拿来比。对上了,浏览器才把这份源码挂到这个地址下面,并显示验证标记。
这里有个细节决定了后面几种状态名的差别。Solidity 官方文档的 Contract Metadata 一章写道,编译器默认会生成一份元数据文件,里面记着编译器版本、编译设置和用到的源文件,并把这份文件的 IPFS 哈希附在运行时字节码的末尾。元数据里又含有全部源文件的哈希,所以文档说,源码里哪怕只改一个空格,元数据就不同,字节码也跟着不同。
也就是说,比对可以做到两种粗细。一种连末尾这段元数据哈希一起比,一种只比前面真正执行的那部分。ethereum.org 管前一种叫完整验证;注释、变量名这类内容不影响前面真正执行的那部分字节码,变的只是末尾的元数据哈希;元数据哈希没对上或没被纳入比对的,算部分匹配,文档称这是目前更常见的验证方式。
在 Etherscan 上看验证状态:四种叫法
位置在合约地址页的合约代码标签,验证过的合约在这个标签上带一个徽标。Etherscan 官方的验证类型说明把状态分成四档,徽标颜色和能读到的内容都不一样。
| 状态名 | Etherscan 的说明 | 你能读到什么 |
|---|---|---|
| Unverified | 合约部署后的默认状态 | 只有合约创建时的字节码,没有源码 |
| Similar Match | 创建字节码与另一个已在 Etherscan 部署并验证过的合约相同,自动匹配上的;不考虑构造参数。黄色徽标 | 看到的源码是别人为另一个地址提交的那份 |
| Exact Match | 对应实际部署的合约代码,连同部署时带的构造参数。绿色徽标 | 完整源码和部署细节,可以用页面自带的读、写合约功能 |
| Runtime Match | 运行时字节码与源码编译结果一致;不考虑部署字节码和构造参数。徽标旁带一条说明,注明只按运行时字节码匹配 | 有源码,但部署那一步传了什么参数不在比对之内 |
黄色的 Similar Match 别当成普通的「已验证」略过去。它表示系统发现这个地址的创建字节码和某个已验证合约一样,自动匹配了那份源码;这个标记并不能证明有人专门为当前地址完成了 Exact Match 验证。两边的创建字节码相同,这是机器比出来的;构造参数没有比。构造参数是部署时传进合约的初始值,两个字节码相同的合约,传进去的值可以完全不同。碰到黄色徽标,部署时的构造参数要从创建交易或部署记录另行核对。参数涉及的权限如果后来能修改,还要读当前状态;当前值不能反推部署时的初始值,查当前权限的方法见在区块浏览器上查代币管理权限。
还有一处名字上的坑。Etherscan 的 Exact Match 说的是「连构造参数都对上」,没有提元数据哈希。ethereum.org 那篇文档写的是:Etherscan 不比对元数据哈希,因此 Etherscan 上的匹配属于部分匹配。同一个词,到了 Sourcify 那边是另一个意思。
Sourcify 的 Exact Match 和 Match 差在哪
Sourcify 是一个源码验证服务。对于 Solidity 合约,它还会用元数据哈希判断源码是否与部署时的文件完全一致。它的结果只有两档,定义写在 Exact Match vs Match 这一页:
- Exact Match(旧称 perfect match):链上字节码与重新编译的结果逐字节相同,库地址和 immutable 变量除外,元数据哈希也对得上。文档说,源文件里哪怕只加一条注释、改一个变量名,这一档就拿不到。
- Match(旧称 partial match):除元数据哈希外,字节码按其匹配规则一致;和 Exact Match 一样,库地址与 immutable 值也有单独处理的例外。源码的注释、变量名或源文件路径等内容可能与部署时的文件不同。
改名的原因文档也交代了:partial 这个词常让人误以为合约没验证。两档都算验证通过。
拿一个实际页面看。在 Sourcify 仓库里打开以太坊主网上 TetherToken 的合约记录页,地址是 0xdAC17F958D2ee523a2206206994597C13D831ec7。USDT 在这里只当界面样例。

页面上从上往下能读到这些:
- 地址下面一行是 on Ethereum Mainnet (1),括号里是链 ID。同一个地址在别的链上可以是另一份合约,网络要看准,区分方法见同名代币怎么区分:网络与合约地址。
- 徽标是 Match。按上面的定义,这一档没有提供元数据哈希完全一致的保证;它按前述规则核对字节码,库地址与 immutable 值的例外同样适用。仅看这个徽标,读不出具体是哪项源文件信息不同。
- 徽标旁边两个绿点,分别标着 Runtime Bytecode 和 Creation Bytecode。
- 表格里 Contract Name 是 TetherToken,Compiler 是 solc 0.4.18+commit.9cf6e910,Deployer 是
0x36928500Bc1dCd7af6a2B4008875CC336b927D57,Block Number 是 4634748。 - Verified At 写的是 2024-08-08 13:56:23 UTC。这是验证记录生成的时间,别把它读成合约上线的时间。
拿到 Match 而不是 Exact Match,不构成对这个合约的负面判断。它只告诉你,Sourcify 上这份源码在执行逻辑上与链上字节码一致,而文本上未必和部署者手里那份一字不差。两档都提供了可读源码;需要对照审计报告里的特定版本时,还得核对仓库和文件,不能把 Match 当成源文件逐字相同的证明。
合约已验证是不是就安全?
不是。验证回答的问题很窄:这份源码是不是这个地址上跑的那份代码。ethereum.org 的原话是,源码验证让用户和开发者确信,公布出来的合约代码就是合约地址上运行的那份。保证的内容到此为止。
它不涉及下面这些:
- 代码里有没有后门或者漏洞。
- 有没有增发、冻结、暂停、改税率的权限,这些权限在谁手里。源码里明写着管理员可以冻结地址的合约,照样能拿到绿色徽标,因为源码和字节码确实对得上。
- 有没有人审过。源码验证本身是编译与比对,不要求先完成人工安全审计;验证徽标也没有交代是否另做过审计。
- 这个地址是不是你以为的那个币。仿冒合约同样可以把源码验证了。
Solana 官方文档讲 verified builds 时有一句话,放到哪条链上都适用:经过验证的构建,不应被认为比未验证的更安全,它的作用是让人能自己确认源码和链上部署的东西一致。
那验证有什么用?它是读源码那几项检查的入场券。ethereum.org 写道,公开源码文件让审计者这类关心合约的人更容易评估潜在的攻击面;同一篇文档也提到,没有验证的话,后门、有争议的访问控制机制、可被利用的漏洞都可能藏在合约里不被发现。拿到经过验证的源码后,你可以直接读权限函数,再把它与审计报告的范围对照。审计报告怎么和部署地址对上,在代币审计报告怎么看里讲过,这里不重复。
本文引用的各项状态定义核对于 2026 年 10 月。状态的叫法会改,Sourcify 就把 perfect / partial 换成了现在这两个名字。照着查的时候如果名字对不上,去两家的官方文档里找现行的定义。
代理合约:验证过的可能只是外面那层
你查的代币地址有可能是一个代理合约。Etherscan 的代理合约说明给的定义是:代理合约是一个中间合约,把调用转交给另一个被称为「实现」的合约。ethereum.org 的合约升级文档说得更细:代理合约存数据,不放业务逻辑,逻辑在另一份合约里。
这就成了两个地址、两份字节码。源码验证是按地址做的,代理那个地址验证过,只说明代理这层薄薄的转发代码有源码。真正决定代币怎么转、能不能增发的逻辑在实现合约里,那个地址要单独看验证状态。
在 Etherscan 上,代理验证成功后,合约页会多出 Read as Proxy 和 Write as Proxy 两栏,用的是实现合约的 ABI。从这里找到实现地址,点过去,再看一遍它的验证徽标。
可升级的代理,实现是能换的。ethereum.org 写道,让代理指向一份新的逻辑合约,用户调用代理时执行的代码就变了;它列的缺点里有一条:用户必须信任开发者不会随意改动合约。Etherscan 那边的说法是,实现地址升级过的话,重新跑一次代理验证,新的实现地址才会被记下来。同一页还提醒,代理未必真的把调用转给了检测到的那个外部合约。
所以核过的结论有时效。假设你上个月核过实现合约是 Exact Match,这个月又要用到这份笔记,实现地址这一项得重看,不能沿用上个月的笔记。
Solana 上对应的东西叫 verified builds
不是 EVM 的链,做法不同,道理一样。Solana 官方文档的 Verified Builds 一页写道,verified builds 用来确认部署到网络上的可执行程序与仓库里的源码一致,方法是把链上程序的哈希和从源码本地构建出来的程序的哈希做比较。验证结果可以在 Solana Explorer 和 SolanaFM 上查,OtterSec 维护的 API 也提供查询。
两点和以太坊这边能对上。一是前面引过的那句:验证过的构建不应被当成更安全。二是程序升级之后,文档说 API 会检测到升级并把程序改回未验证,直到重新验证。和代理合约换实现是同一个提醒:验证状态跟着代码走,代码变了要重看。
碰到未验证的合约,笔记里该怎么记
未验证本身不能推出「这是骗局」,也不能推出「项目从未公开源码」。先记下在哪家服务、哪条链上查到了什么状态;若还没拿到能与链上代码对应的资料,下面几项就暂记「未确认」:
- 管理权限。浏览器没有经过验证的源码时,不能只凭这一页确认完整的管理权限。ABI(调用接口说明)也可能由项目文档或编译产物单独提供,但有 ABI 仍不等于已确认实现代码,不能据此断言没有其他权限。
- 审计对照。先找报告对应的源码版本与编译设置,再核对能否复现链上字节码。浏览器未验证,不代表无法自行比对;如果你没有拿到这组对应证据,项目方一句「审计过」仍不足以确认报告覆盖当前合约。
- 代理的实现合约。代理源码已验证、实现源码尚未验证,要分开记录。不能用代理的验证标记替实现合约背书。
查的时候别只查一处。Etherscan 上显示未验证的地址,可以再到 Sourcify 按链和地址查一次,两边是各自独立的验证记录。Blockscout 是开源的区块浏览器,也提供合约验证服务,这条链如果有 Blockscout 的浏览器,同样可以查。查过的服务都没有对应记录,就写「在这些服务上未查到验证结果」,不要扩成「源码没有公开」。
记的时候把状态名原样抄下来,别统一写成「已开源」。Similar Match、Runtime Match 各自没覆盖哪一段,前面都列了,抄原词才知道回头该补查什么。再加上核对的年月、查的是哪条链、是代理的话实现地址是哪个。
「未验证」是该停一下的地方:权限和审计两项都空着的币,等于最要紧的两项没有依据。要不要在这种情况下继续,由你自己定,但别拿「合约地址能在浏览器上搜到」当成它们已经查过。其余不依赖源码的项,比如持仓分布和流动性,照常查,整套顺序在如何研究一个代币里。
关于合约验证状态的几个追问
Etherscan 上的黄色验证徽标和绿色的有什么不同?
按 Etherscan 官方的验证类型说明,黄色徽标对应 Similar Match:这个合约的创建字节码与另一个已在 Etherscan 验证过的合约相同,系统自动匹配上的,不考虑构造参数。绿色徽标对应 Exact Match:对应实际部署的合约代码,连同部署时带的构造参数。碰到黄色徽标,部署时传入的初始值要另外核对。
合约在 Etherscan 上未验证,在 Sourcify 上会不会是已验证?
有可能。两边是各自独立的验证记录,ethereum.org 的源码验证文档把 Etherscan、Sourcify、Blockscout 列为不同的验证工具。在一处查到未验证时,可以按链和合约地址到另一处再查一次,查过的几处都没有对应结果时,记明查询服务、链与地址,不据此断言源码没有公开。
Sourcify 显示 Match 而不是 Exact Match,是不是源码有问题?
这个状态本身不说明源码有问题。按 Sourcify 文档,Match 表示字节码按其规则匹配,但不保证元数据哈希一致;库地址和 immutable 值另有处理例外。注释、变量名和源文件路径等内容可能与部署时的源码不同。Match 旧称 partial match,文档说明改名是因为 partial 一词常让人误以为合约没有验证,两档都算验证通过。