☰
slither-check-erc:基于 Slither 的 ERC 标准符合性自动检测工具实战指南
2026/10/8 8:17:15 网站建设 项目流程
  • 应用安全
  • 区块链

【免费下载链接】slither

Static Analyzer for Solidity and Vyper

项目地址:https://gitcode.com/gh_mirrors/sl/slither
点击查看免费下载

slither-check-erc是 Slither 项目中内置的 ERC 符合性(ERC conformance)检测工具,它通过静态分析自动核对智能合约是否遵循 ERC20、ERC721、ERC1155、ERC4626 等主流代币标准。本文以 ERC-Conformance.md 为骨架,结合仓库源码深入讲解工具的使用方法、检测维度、输出格式与实现原理,帮助开发者在合约发布前自动发现缺失函数、错误返回类型、缺失事件与索引错误等标准背离问题。

工具定位与核心能力

slither-check-erc基于 Slither 的静态分析能力,读取目标合约的 AST 与中间表示(SlithIR),对照标准定义逐项检查,不需要部署或运行合约即可完成验证。根据官方文档,它会检查以下维度:

  • 标准要求的所有函数是否全部存在;
  • 标准要求的所有事件是否全部存在;
  • 函数的返回类型是否正确;
  • 必须声明为view的函数是否为view;
  • 事件的参数是否正确 indexed;
  • 函数是否发出标准要求的事件;
  • 派生合约(derived contracts)不破坏标准的符合性。

从源码结构看,工具主体位于 slither/tools/erc_conformance/main.py,标准定义(每个 ERC 的函数签名、返回类型、事件与索引要求)统一维护在 slither/utils/erc.py 中,而通用检查逻辑实现在 slither/tools/erc_conformance/erc/ercs.py。

检测维度的实现对应

文档声称的检测能力源码实现位置说明
函数是否存在_check_signature先按签名查找函数,若不存在则尝试把同名 public/external 状态变量视为 getter(ercs.py#L15-L61)
返回类型正确_check_signature对比实际返回类型与标准返回类型(ercs.py#L66-L100)
必须为 view_check_signature标准声明view=True时校验函数view属性(ercs.py#L102-L112)
函数是否发出事件_check_signature遍历函数全部 SlithIR 操作,检查是否存在EventCall(ercs.py#L114-L147)
事件参数索引_check_events按标准要求逐参数核对indexed(ercs.py#L150-L181)
派生合约检查generic_erc_checks对contract.derived_contracts递归执行同一套检查(ercs.py#L208-L209)

支持的 ERC 标准

文档列出的支持范围与 slither/utils/erc.py 中ERCS字典的注册条目完全一致,共 11 个标准:

标准状态/来源(源码注释)要点
ERC20FinalToken 标准,含 6 个必需函数 + name/symbol/decimals 可选
ERC223DraftToken 标准,含带bytes、bytes,string的重载transfer
ERC165Final标准接口检测,supportsInterface(bytes4)
ERC721Final非同质化代币,隐含 ERC165 要求
ERC1820Final伪内省注册表,canImplementInterfaceForAddress
ERC777Last CallToken 标准,含Sent/Minted/Burned等事件
ERC1155Final多代币标准,隐含 ERC165 要求
ERC2612ReviewERC20 的 Permit 扩展,叠加 ERC20 全部要求
ERC1363ReviewPayable Token,叠加 ERC20 + ERC165
ERC4524ReviewSafer ERC-20,叠加 ERC20 + ERC165
ERC4626FinalTokenized Vaults,叠加 ERC20

注意复合标准的定义方式:例如ERC2612、ERC1363、ERC4524、ERC4626都是通过 Python 列表拼接直接叠加底层标准(如ERC1363 = [...] + ERC20 + ERC165,见 erc.py),因此选中这些标准检查时,底层标准的全部函数与事件要求也会一并核对。

快速上手:命令行用法

基本用法(对应 ERC-Conformance.md 中的示例):

slither-check-erc contract.sol ContractName

其中:

  • contract.sol:待分析的合约源码或编译项目路径(由 crytic-compile 支持,可指向 Hardhat/Truffle 等项目目录);
  • ContractName:第一个符合该标准的合约名,工具会从它开始,沿继承关系向下检查所有派生合约。

工具已在 pyproject.toml 中注册为命令行入口(slither-check-erc = "slither.tools.erc_conformance.__main__:main"),安装 Slither 后即可直接调用。

常用选项

根据main.py 的参数解析实现,除位置参数外支持以下选项:

选项作用默认值
--erc ERC指定要检查的标准,可用值即上表 11 个标准(如--erc ERC721)erc20
--json FILE将结果导出为 JSON 文件;传--json -输出到标准输出关闭
crytic-compile 默认参数例如--solc-version、--compile-force-framework等编译控制参数由 crytic-compile 决定

指定其他标准示例:

slither-check-erc contract.sol MyToken --erc ERC721 slither-check-erc contract.sol MyVault --erc ERC4626

--erc参数在入口处对ERCS字典做大小写不敏感匹配(args.erc.upper()),若传入未注册的标准会报错Incorrect ERC selected ...并退出(见main.py)。

输出解读:一次完整的 ERC20 检查

文档给出了一个最小化合约示例,这里完整复现其输出格式与语义:

contract ERC20{ event Transfer(address indexed,address,uint256); function transfer(address, uint256) public{ emit Transfer(msg.sender,msg.sender,0); } }

运行slither-check-erc erc20.sol ERC20后,工具报告:

# Check ERC20 ## Check functions [ ] totalSupply() is missing [ ] balanceOf(address) is missing [✓] transfer(address,uint256) is present [ ] transfer(address,uint256) -> () should return bool [✓] Transfer(address,address,uint256) is emitted [ ] transferFrom(address,address,uint256) is missing [ ] approve(address,uint256) is missing [ ] allowance(address,address) is missing [ ] name() is missing (optional) [ ] symbol() is missing (optional) [ ] decimals() is missing (optional) ## Check events [✓] Transfer(address,address,uint256) is present [✓] parameter 0 is indexed [ ] parameter 1 should be indexed [ ] Approval(address,address,uint256) is missing

输出格式约定

  • [✓]/[ ]:符合 / 不符合;
  • 函数检查区块以## Check functions为标题,事件检查区块以## Check events为标题;
  • (optional)后缀表示该函数在标准中是可选的(如 ERC20 的name/symbol/decimals),缺失不会导致违规;无后缀的为必需项;
  • 缩进的子行给出具体违规细节:返回类型错误、非 view、未发出事件、事件参数未索引等;
  • 每个被检查合约都会输出独立的# Check <ContractName>区块,派生合约依此继续追加。

输出语义详解

  • transfer(address,uint256) -> () should return bool:函数签名存在,但声明了空返回,而标准要求返回bool。这来自_check_signature中返回值比对逻辑(ercs.py#L85-L100);
  • Transfer(address,address,uint256) is emitted:工具遍历transfer函数的 SlithIR 操作,发现参数类型为(address,address,uint256)的EventCall(ercs.py#L128-L147);
  • parameter 1 should be indexed:ERC20 标准要求Transfer事件的前两个参数 indexed(定义见 erc.py 的indexes=[True, True, False]),合约中第二个参数未加indexed,因此被标记(ercs.py#L170-L181)。

检查引擎如何工作:源码级剖析

入口流程

main.py 的main()执行以下步骤:

  1. 调用Slither(args.project, **vars(args))对目标项目做完整静态分析,生成合约抽象语法信息;
  2. 用slither.get_contract_from_name(args.contract_name)定位合约,若找不到或匹配不唯一则报错退出;
  3. 从ERCS字典取出该标准的(函数列表, 事件列表),调用generic_erc_checks执行通用检查;
  4. 对 ERC20、ERC1155 额外执行标准专属检查(ADDITIONAL_CHECKS映射);
  5. 若指定--json,将ret结果通过output_to_json序列化导出。

通用检查:函数与事件的核对

generic_erc_checks对传入合约执行两轮核对(ercs.py):

  • 函数核对:对标准函数列表逐一调用_check_signature。查找函数时,若按签名找不到,还会检查是否存在同名public/external状态变量——这是为了兼容 Solidity 编译器为 public 状态变量自动生成 getter 的情况,此时直接把 getter 视为函数参与后续检查(ercs.py#L27-L57);
  • 事件核对:对标准事件列表逐一调用_check_events,核对事件是否存在及参数索引是否符合要求。

随后递归遍历contract.derived_contracts,对每个派生合约重复执行,从而验证"派生合约不破坏符合性"这一条能力。

事件发射检测的原理

函数是否发射事件,是通过 SlithIR 中间表示判断的:遍历函数的all_slithir_operations(),筛出EventCall类型的操作,比对事件名与实参类型列表是否与标准定义一致(ercs.py#L128-L147)。这意味着检测不依赖执行路径穷举,只要求函数体内存在符合签名的emit语句即可判定通过。

标准专属检查

除通用检查外,两个标准还注册了专属检查(main.py):

  • ERC20:check_erc20检查合约是否提供increaseAllowance(address,uint256)或safeIncreaseAllowance(address,uint256),若无则报告not protected for the ERC20 approval race condition(erc20.py),即经典的 approve/transferFrom 竞争条件防护缺失;
  • ERC1155:check_erc1155检查safeBatchTransferFrom是否发射了TransferSingle或TransferBatch事件(erc1155.py)。

这也解释了为何测试输出末尾会出现[ ] ERC20 is not protected for the ERC20 approval race condition这样不属于函数/事件区块的额外条目。

实战:用真实测试用例验证输出

仓库内置了完整的 CI 测试用例,可直接复现工具行为。测试脚本 scripts/ci_test_erc.sh 使用solc-select use 0.5.1固定编译器版本后运行:

slither-check-erc "tests/tools/check_erc/erc20.sol" ERC20

被测合约 tests/tools/check_erc/erc20.sol 只声明了一个public状态变量:

contract ERC20{ uint public totalSupply; }

期望输出 tests/tools/check_erc/test_1.txt 展示了几个值得注意的行为:

  • totalSupply()被判定[✓] present且返回类型(uint256)正确——即使合约没有显式函数,public 状态变量生成的 getter 也被认可;
  • 其余函数全部missing,事件全部missing,末尾追加ERC20 is not protected for the ERC20 approval race condition专属检查结果。

该用例同时验证了两个实现细节:状态变量 getter 兼容逻辑与标准专属检查的输出格式。运行该脚本会将实际输出与test_1.txt做diff对比,任何输出变化都会导致 CI 失败。

JSON 导出与结果结构化

当需要把检测结果接入自动化流程(如 CI 门禁、报告生成)时,使用--json选项:

slither-check-erc contract.sol ERC20 --erc ERC20 --json result.json slither-check-erc contract.sol ERC20 --json - # 输出到 stdout

从main.py 看,JSON 顶层结构为{"erc-conformance": { ... }},其内部键由各检查逻辑写入ret字典的类别组成,主要包括:

  • missing_function:缺失的函数;
  • incorrect_return_type:返回类型错误;
  • should_be_view:应为 view 而非 view 的函数;
  • missing_event/missing_event_index:缺失的事件 / 事件参数索引缺失;
  • missing_event_emmited:函数未发射标准要求的事件;
  • lack_of_erc20_race_condition_protection:ERC20 竞争条件防护缺失(仅 ERC20);
  • erroneous_erc1155_safeBatchTransferFrom_event:ERC1155 批量转账事件缺失(仅 ERC1155)。

每条结果由 slither/utils/output.py 中的Output对象承载,附带标准名、预期/实际类型、函数定位等附加字段,便于程序化解析与定位到具体合约函数。

使用建议与限制

  • 固定编译环境:ERC 标准校验对编译器版本敏感(如事件 indexed 语义、getter 生成规则),建议参照 scripts/ci_test_erc.sh 的做法,用 solc-select 固定 solc 版本后运行,保证结果可复现;
  • 以首个符合标准的合约为入口:contract_name应传继承链中最早实现标准的合约,派生合约会被自动递归检查;
  • 复合标准会叠加底层要求:检查 ERC4626 时会同时要求 ERC20 的全部函数与事件,合约若只实现 ERC4626 新增接口,会在底层 ERC20 项上大量报缺,属预期行为;
  • 事件发射检测是静态的:只要函数体内存在签名匹配的emit即判定通过,工具不做路径敏感或执行条件分析,这是静态工具的固有边界;
  • 它是检查器而非修复器:slither-check-erc只报告不符合项,不会自动改写合约代码。

总结

slither-check-erc把 ERC 标准条文落成可重复执行的静态检查:函数与事件的存在性、签名与返回类型、view 约束、事件索引、事件发射以及派生合约传播,配合 ERC20/ERC1155 的专属安全检查,能在不部署合约的情况下快速定位标准背离。其标准定义集中维护在 slither/utils/erc.py,通用检查引擎在 slither/tools/erc_conformance/erc/ercs.py,入口与参数解析在 slither/tools/erc_conformance/main.py,无论是审计自有代币合约,还是在 CI 流水线中为多标准代币项目加一道符合性门禁,它都是一个开箱即用的基础工具。

  • 应用安全
  • 区块链

【免费下载链接】slither

Static Analyzer for Solidity and Vyper

项目地址:https://gitcode.com/gh_mirrors/sl/slither
点击查看免费下载
上一篇:Pinpoint Web Frontend v3 架构与开发指南:React 19 单体仓库、状态管理与 AI 辅助开发规范
下一篇:gh-dash 通知中心(Notifications)功能深入解析:设计与源码实现

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询