部署完成只是开始——未验证的合约是黑盒。终端用户、审计师、甚至你 3 个月后的自己,都需要可读的源码来理解合约行为。验证是 DeFi 协议的信任基础。
13.5.1 验证方式对比
| 方式 | 原理 | 信任度 | 自动化 |
|---|---|---|---|
| Etherscan 验证 | 提交 Solidity 源码 + 编译器设置,Etherscan 编译比对 | 中(依赖 Etherscan) | 是(API) |
| Sourcify 去中心化验证 | IPFS 存储完整源码 + 元数据 JSON | 高(去中心化) | 是 |
| ABI 验证 | 只提交接口 | 低 | — |
| 手动比对 | 人工读取字节码 | 不可行 | 否 |
验证流程
graph LR
Dev[开发者] --> |"deploy + 源码 + settings"| Etherscan
Etherscan --> |"编译 + 比对 bytecode"| OK{匹配?}
OK --> |是| Verified[✅ 验证通过<br/>显示源码]
OK --> |否| Debug[调整编译器版本 / 优化设置 / 字节码哈希]
Debug --> Dev
style Verified fill:#c8e6c9
style Debug fill:#ffebee
13.5.2 自动化验证脚本
typescript
/**
* 自动化验证配置(TypeScript 模拟)
*/
interface VerificationConfig {
apiKey: string; // Etherscan / BscScan / 等
network: string; // "mainnet" / "sepolia" / "arbitrum"
contractAddress: string;
contractName: string;
compilerVersion: string; // e.g. "v0.8.19+commit.7dd6d404"
optimization: boolean;
optimizationRuns: number;
constructorArgs: string; // ABI 编码后的构造函数参数
sourceCode: string; // 完整的 Solidity 源码或 flattened
}
async function verifyContract(config: VerificationConfig): Promise<boolean> {
const apiUrl = {
mainnet: "https://api.etherscan.io/api",
sepolia: "https://api-sepolia.etherscan.io/api",
arbitrum: "https://api.arbiscan.io/api",
polygon: "https://api.polygonscan.com/api",
}[config.network];
// 1. 提交源码 + 编译设置
// 2. 等待编译(30s-120s)
// 3. 轮询状态
// 4. 返回结果
console.log(`Verifying {config.contractAddress}...`);
return true;
}
// 验证成功后,区块浏览器显示:
// - 完整的 Solidity 源码
// - 自动生成的 Read / Write 界面
// - 事件日志解码
// - ABI 导出常见验证失败原因
| 原因 | 解决 |
|---|---|
| 编译器版本不对 | 检查 solc --version,使用精确版本 |
| 优化设置不匹配 | 必须确认 runs 数一致 |
| 构造函数参数 | 需要 ABI 编码,不仅仅是值 |
| 导入路径 | Flatten(内联所有 import)或使用标准 JSON 输入 |
| License 注释 | 需要 SPDX-License-Identifier |
13.5.3 去中心化替代:Sourcify
Sourcify 是一个开源、去中心化的验证系统:
- 源码存储在 IPFS 上
- 任何人可以运行验证节点
- 与 Etherscan 不同,不会因为 API 限制/公司政策/地域限制而无法访问
- MetaMask 使用 Sourcify 自动获取已验证合约的 ABI
Sourcify 完美验证(Full Match)标准
typescript
/**
* 完美验证要求:编译输出的 metadata 完全匹配
* metadata 包含所有编译器设置、源码路径、版本、库地址等
*/
interface CompilationMetadata {
compiler: { version: string };
language: string;
output: { abi: any[]; devdoc: any; userdoc: any };
settings: {
compilationTarget: Record<string, string>;
optimizer: { enabled: boolean; runs: number };
evmVersion?: string;
libraries?: Record<string, string>;
};
sources: Record<string, { content: string; keccak256: string }>;
version: number;
}
// 完美验证 = 链上 bytecode + metadata 指纹 == 重新编译输出
function isFullMatch(
onChainBytecode: string,
compiledBytecode: string,
metadata: CompilationMetadata,
): boolean {
// metadata 末尾以 CBOR 编码嵌入编译输出的最后是 Solidity 标准
// 这里做简化比对
const metadataHash = JSON.stringify(metadata.settings);
const embedded = onChainBytecode.slice(-100); // metadata hash 在末尾
return onChainBytecode.startsWith(compiledBytecode.slice(2, 20)) &&
embedded.includes(metadataHash.slice(0, 20));
}13.5.4 区块浏览器的调试功能
| 功能 | Etherscan | Blockscout(开源) |
|---|---|---|
| 交易追踪(Trace) | 支持 | 支持(内部) |
| 状态差异(State Diff) | 支持 | 部分 |
| 事件日志解码 | 自动(已验证) | 需 ABI |
| 合约读取/写入 | UI 交互 | UI 交互 |
| 代币持有者 | 有 | 有 |
| NFT 元数据展示 | 有 | 有 |
> ← 13.4 部署 | 前往 → 13.6 CI/CD 流水线 |*
评论
0评论加载中…