核心原则
- 安全第一:安全性是智能合约的首要考量
- 防御纵深:多层安全机制优于单一防护
- 最小权限:合约应只拥有必要的最小权限
- 可升级性设计:预留安全的升级路径
- 透明审计:所有安全决策应可审计追溯
技术栈
- 形式化验证:Certora、K Framework、Act、Scribble
- 静态分析:Slither、Mythril、Securify、Halmos
- 模糊测试:Foundry Fuzz、Echidna、Medusa、Foundry Invariant
- 漏洞模式库:SWC Registry、Decoded Registry、Known Attack Vectors
- 审计工具:Slither、4naly3er、Solodit、Code4rena
最佳实践
代码审计流程
- 首先进行自动化静态分析
- 然后进行人工代码审查
- 最后进行第三方安全审计
- 建立漏洞修复和复审计流程
常见漏洞防护
- 重入攻击:使用 ReentrancyGuard、检查-效果-交互模式
- 整数溢出:使用 Solidity ^0.8.x 或 SafeMath
- 访问控制:正确实现权限修饰符
- 前端运行:使用 Commit-Reveal 方案或 Flashbots
形式化验证应用
- 定义关键不变量规范
- 编写形式化规范文件
- 运行验证器证明属性
- 处理验证失败的反例
Gas 优化安全
- 避免不安全的 unchecked 算术
- 注意存储槽碰撞风险
- 谨慎使用 delegatecall
升级安全策略
- 使用透明代理或 UUPS 模式
- 实现存储布局兼容性检查
- 预留紧急暂停机制
关键约定
命名规范
- 使用 PascalCase 命名错误类型
- 使用 UPPER_SNAKE_CASE 命名常量
- 事件名称应清晰表达含义
错误处理
// 推荐使用自定义错误 error InsufficientBalance(uint256 available, uint256 required); // 而非 require 字符串 // require(balance >= amount, "Insufficient balance");访问控制模式
// 使用 OpenZeppelin AccessControl bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); modifier onlyRole(bytes32 role) { require(hasRole(role, msg.sender), "Access denied"); _; }检查-效果-交互模式
function withdraw(uint256 amount) external { require(balances[msg.sender] >= amount, "Insufficient"); balances[msg.sender] -= amount; // 效果 (bool ok,) = msg.sender.call{value: amount}(""); require(ok, "Transfer failed"); // 交互 }安全事件记录
- 记录所有关键状态变更
- 包含操作者、时间、金额等信息
- 便于事后审计和监控