building-secure-contracts

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Building Secure Contracts Skill

构建安全合约Skill

<!-- Agent: skill-updater | Task: #6 | Session: 2026-03-01 --> <identity> Smart contract and secure API contract security analysis skill. Implements Trail of Bits and OpenSCV-aligned methodology for detecting reentrancy attacks, access control failures, integer overflows, and invariant violations in Solidity (EVM) and Rust (Solana) contracts. Addresses the $1.8B+ DeFi exploit landscape (Q3 2025) through systematic vulnerability analysis. </identity> <capabilities> - Checks-Effects-Interactions (CEI) pattern enforcement and verification - Reentrancy attack surface mapping (cross-function, cross-contract, read-only) - Access control audit: missing modifiers, privilege escalation, role confusion - Integer arithmetic analysis: overflow, underflow, precision loss, rounding direction - Contract invariant identification and formal verification setup - Storage collision and proxy upgrade security analysis - Oracle manipulation and price feed dependency analysis - Flash loan attack surface enumeration - EVM vs Solana security model comparison and platform-specific risk identification - OpenSCV vulnerability taxonomy classification for all findings </capabilities>
<!-- Agent: skill-updater | Task: #6 | Session: 2026-03-01 --> <identity> 智能合约与安全API合约安全分析技能。采用与Trail of Bits和OpenSCV对齐的方法论,用于检测Solidity(EVM)和Rust(Solana)合约中的重入攻击、访问控制故障、整数溢出和不变量违反问题。通过系统化漏洞分析,应对2025年第三季度累计超18亿美元的DeFi漏洞攻击风险。 </identity> <capabilities> - Checks-Effects-Interactions(CEI)模式强制校验与验证 - 重入攻击面映射(跨函数、跨合约、只读场景) - 访问控制审计:缺失修饰符、权限提升、角色混淆 - 整数运算分析:溢出、下溢、精度损失、舍入方向问题 - 合约不变量识别与形式化验证配置 - 存储冲突与代理升级安全分析 - 预言机操纵与价格喂价依赖分析 - 闪电贷攻击面枚举 - EVM与Solana安全模型对比与平台特有风险识别 - 所有漏洞发现遵循OpenSCV漏洞分类标准 </capabilities>

Overview

概述

This skill applies systematic security analysis to smart contracts and secure API contracts. The core principle: every state mutation must be proven safe through invariant verification before an external call executes. It covers both EVM (Solidity) and Solana (Rust) ecosystems with platform-specific vulnerability patterns.
Vulnerability taxonomy: OpenSCV (94 classified security issues) Critical patterns: CEI, reentrancy guards, access modifiers, SafeMath equivalents Risk landscape: $1.8B+ in DeFi exploits Q3 2025 (access control: $953M, reentrancy: $420M)
本技能对智能合约和安全API合约执行系统化安全分析,核心原则为:每次状态变更必须在外部调用执行前通过不变量验证证明其安全性。覆盖EVM(Solidity)和Solana(Rust)生态,支持平台特有漏洞模式检测。
漏洞分类体系:OpenSCV(94个已分类安全问题) 关键校验模式:CEI、重入防护、访问修饰符、SafeMath等效实现 风险现状:2025年第三季度DeFi攻击累计损失超18亿美元(访问控制:9.53亿美元,重入攻击:4.2亿美元)

When to Use

适用场景

  • Before deploying any smart contract to mainnet
  • When auditing existing contracts for security vulnerabilities
  • When reviewing API contracts for invariant violations
  • When adding new entry points or external calls to existing contracts
  • When upgrading proxy contracts (storage slot collision risk)
  • When integrating oracles, flash loans, or third-party DeFi protocols
  • 任意智能合约部署到主网前
  • 审计已上线合约的安全漏洞
  • 排查API合约的不变量违反问题
  • 为现有合约新增入口点或外部调用时
  • 升级代理合约时(排查存储槽冲突风险)
  • 集成预言机、闪电贷或第三方DeFi协议时

Iron Laws

铁律

  1. NEVER make external calls before updating state — Checks-Effects-Interactions (CEI) is non-negotiable; any external call before state update is a reentrancy vector regardless of perceived safety.
  2. NEVER assume access control is correct without reading every modifier — access control failures account for ~53% of 2024 DeFi losses; verify every
    onlyOwner
    ,
    onlyRole
    , and custom guard.
  3. NEVER trust integer arithmetic without explicit bounds checking — Solidity 0.8+ has native overflow protection but custom assembly, unchecked blocks, and Rust/Solana code require explicit verification.
  4. ALWAYS enumerate all contract invariants before analysis — invariants are the ground truth for correctness; a violation is always a bug; document them in NatSpec before reviewing the implementation.
  5. ALWAYS test reentrancy across full call chains, not just single functions — cross-function reentrancy (withdraw + transfer sharing state) is as dangerous as direct reentrancy.
  1. 永远不要在更新状态前发起外部调用 —— Checks-Effects-Interactions(CEI)是不可妥协的原则;无论看起来多安全,状态更新前的任意外部调用都是重入攻击向量。
  2. 永远不要在未读取所有修饰符实现的前提下假设访问控制是安全的 —— 访问控制故障占2024年DeFi损失的约53%;需逐一校验每个
    onlyOwner
    onlyRole
    和自定义防护逻辑。
  3. 永远不要在没有显式边界检查的情况下信任整数运算 —— Solidity 0.8+自带溢出防护,但自定义汇编、unchecked代码块和Rust/Solana代码仍需显式验证。
  4. 分析前必须枚举所有合约不变量 —— 不变量是正确性的基础事实,违反就必然是bug;审查实现前需将所有不变量记录在NatSpec中。
  5. 必须测试完整调用链的重入风险,而非仅测试单个函数 —— 跨函数重入(提现和转账共享状态)与直接重入风险等级一致。

Phase 1: Contract Reconnaissance

阶段1:合约勘察

Goal: Map the attack surface before deep analysis.
目标:深度分析前完成攻击面梳理。

Steps

步骤

  1. Enumerate entry points: All external/public functions, fallback, receive
  2. Identify state-mutating functions: Functions that modify storage
  3. Map access control boundaries: Roles, modifiers, ownership checks
  4. Catalog external calls:
    call()
    ,
    transfer()
    , ERC20 hooks, interface calls
  5. Identify trust boundaries: User input, oracle feeds, cross-contract calls
  1. 枚举入口点:所有external/public函数、fallback、receive函数
  2. 识别状态变更函数:修改存储的函数
  3. 映射访问控制边界:角色、修饰符、所有权校验
  4. 盘点外部调用
    call()
    transfer()
    、ERC20钩子、接口调用
  5. 识别信任边界:用户输入、预言机喂价、跨合约调用

Output Format

输出格式

markdown
undefined
markdown
undefined

Contract Reconnaissance

合约勘察

Entry Points

入口点

  • withdraw(uint256 amount)
    — external, state-mutating, calls msg.sender
  • deposit()
    — payable, updates balances mapping
  • withdraw(uint256 amount)
    — external, 状态变更, 调用msg.sender
  • deposit()
    — payable, 更新balances映射

Access Control Map

访问控制映射

  • onlyOwner
    : [list of functions]
  • onlyRole(ADMIN_ROLE)
    : [list of functions]
  • No modifier (verify intent): [list of functions]
  • onlyOwner
    : [函数列表]
  • onlyRole(ADMIN_ROLE)
    : [函数列表]
  • 无修饰符(校验设计意图): [函数列表]

External Calls

外部调用

  • msg.sender.call{value: amount}("")
    at withdraw():L45
  • token.transferFrom(...)
    at deposit():L23
  • msg.sender.call{value: amount}("")
    位于 withdraw():L45
  • token.transferFrom(...)
    位于 deposit():L23

Trust Boundaries

信任边界

  • User-supplied amount at withdraw():L40
  • Oracle price feed at getPrice():L67 — manipulation risk
undefined
  • withdraw():L40的用户输入amount
  • getPrice():L67的预言机价格喂价 —— 存在操纵风险
undefined

Phase 2: Reentrancy Analysis

阶段2:重入攻击分析

Goal: Identify all reentrancy vectors (direct, cross-function, read-only).
目标:识别所有重入向量(直接、跨函数、只读)。

Checks-Effects-Interactions Verification

Checks-Effects-Interactions验证

For each function with external calls:
markdown
undefined
针对每个含外部调用的函数:
markdown
undefined

Function: withdraw(uint256 amount)

函数: withdraw(uint256 amount)

CEI Order Analysis

CEI顺序分析

  • L40: CHECK — require(balances[msg.sender] >= amount) ✓
  • L45: EXTERNAL CALL — msg.sender.call{value: amount}("") ← VIOLATION
  • L48: EFFECT — balances[msg.sender] -= amount ← STATE AFTER CALL
FINDING: Classic reentrancy — balance updated after external call. Fix: Move L48 before L45 (CEI pattern) Severity: Critical
  • L40: 校验 — require(balances[msg.sender] >= amount) ✓
  • L45: 外部调用 — msg.sender.call{value: amount}("") ← 违反CEI
  • L48: 生效 — balances[msg.sender] -= amount ← 状态更新在调用后
发现: 典型重入漏洞 —— 外部调用后才更新余额 修复方案: 将L48移至L45之前(遵循CEI模式) 严重等级: 危急

Fixed Pattern

修复后代码

solidity
require(balances[msg.sender] >= amount);
balances[msg.sender] -= amount;  // Effect BEFORE external call
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
undefined
solidity
require(balances[msg.sender] >= amount);
balances[msg.sender] -= amount;  // 状态更新在外部调用之前
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
undefined

Cross-Function Reentrancy Check

跨函数重入检查

Identify shared state between functions that both make external calls:
markdown
undefined
识别存在外部调用的函数之间的共享状态:
markdown
undefined

Shared State: balances mapping

共享状态: balances映射

  • withdraw() reads + writes balances + makes external call
  • emergencyWithdraw() reads + writes balances + makes external call RISK: Reentrancy from withdraw() into emergencyWithdraw() bypasses checks
undefined
  • withdraw() 读写balances + 发起外部调用
  • emergencyWithdraw() 读写balances + 发起外部调用 风险: 从withdraw()重入到emergencyWithdraw()可绕过校验
undefined

Phase 3: Access Control Audit

阶段3:访问控制审计

Goal: Verify every state-mutating function has appropriate guards.
目标:验证每个状态变更函数都有合适的防护逻辑。

Access Control Checklist

访问控制检查清单

For each function:
markdown
undefined
针对每个函数:
markdown
undefined

Function Audit: updateTreasury(address newTreasury)

函数审计: updateTreasury(address newTreasury)

  • Has access modifier? → NO ← FINDING: Missing onlyOwner
  • Modifier verified in contract? → N/A (not present)
  • Owner transferable safely? → N/A
  • Time lock for critical changes? → NO
Severity: Critical — anyone can redirect protocol treasury Fix: Add
onlyOwner
modifier and time-lock for parameter changes
undefined
  • 存在访问修饰符? → 否 ← 漏洞: 缺失onlyOwner
  • 修饰符在合约中已验证? → 不适用(不存在)
  • 所有权转移逻辑安全? → 不适用
  • 关键变更配置时间锁? → 否
严重等级: 危急 —— 任意用户均可转移协议国库资产 修复方案: 新增
onlyOwner
修饰符,并为参数变更配置时间锁
undefined

Role Confusion Patterns

角色混淆模式

markdown
undefined
markdown
undefined

Role Check: PAUSER_ROLE vs ADMIN_ROLE

角色校验: PAUSER_ROLE vs ADMIN_ROLE

  • pause() requires: PAUSER_ROLE
  • unpause() requires: PAUSER_ROLE (RISK: pauser can also unpause)
  • grantRole() requires: ADMIN_ROLE
Issue: Pauser can unilaterally pause and unpause — should require separate roles Severity: Medium
undefined
  • pause() 要求: PAUSER_ROLE
  • unpause() 要求: PAUSER_ROLE(风险: 暂停者可自行解除暂停)
  • grantRole() 要求: ADMIN_ROLE
问题: 暂停者可单方面执行暂停和解除暂停操作,应拆分角色 严重等级: 中危
undefined

Phase 4: Integer Arithmetic Analysis

阶段4:整数运算分析

Goal: Identify overflow, underflow, precision loss, and rounding direction bugs.
目标:识别溢出、下溢、精度损失和舍入方向bug。

Arithmetic Boundary Analysis

运算边界分析

markdown
undefined
markdown
undefined

Function: calculateReward(uint256 principal, uint256 rate)

函数: calculateReward(uint256 principal, uint256 rate)

  • L88:
    uint256 reward = principal * rate / 1e18
    • Multiplication before division: OK (avoids precision loss)
    • Overflow check: principal * rate could overflow if both > sqrt(uint256.max)
    • Rounding: truncates toward zero — check if favors protocol or user
    • unchecked
      block? → NO → Solidity 0.8+ protects this
  • L88:
    uint256 reward = principal * rate / 1e18
    • 先乘后除: 符合规范(避免精度损失)
    • 溢出检查: 若principal和rate均大于sqrt(uint256.max),相乘会溢出
    • 舍入规则: 向零截断 —— 需确认对协议或用户是否有利
    • 是否在
      unchecked
      块中? → 否 → Solidity 0.8+ 自带防护

Unchecked Block Analysis

Unchecked块分析

  • L102-108:
    unchecked { ... }
    • Why unchecked? Check comment and verify mathematician's claim
    • Is the claimed impossibility of overflow actually proven?
    • [UNVERIFIED] claim: "amount < balance guarantees no underflow"
undefined
  • L102-108:
    unchecked { ... }
    • 为什么使用unchecked? 检查注释并验证算法合理性
    • 所谓的不会溢出的声明是否得到了实际证明?
    • [未验证] 声明: "amount < balance保证不会发生下溢"
undefined

Phase 5: Invariant Verification

阶段5:不变量验证

Goal: Identify and verify all contract-level invariants.
markdown
undefined
目标:识别并验证所有合约级不变量。
markdown
undefined

Contract Invariants: LiquidityPool

合约不变量: 流动性池

  1. Solvency: sum(balances) == address(this).balance — [VERIFIED L90]
  2. Total supply: totalSupply == sum(all user shares) — [UNVERIFIED]
  3. Fee bound: fee <= MAX_FEE (1000 bps) — [VERIFIED by require at L45]
  4. Non-zero denominator: totalSupply > 0 before share calculation — [VIOLATED at L67, division-by-zero risk on first deposit]
  1. 偿付能力: sum(balances) == address(this).balance — [已验证 L90]
  2. 总供应量: totalSupply == sum(所有用户份额) — [未验证]
  3. 手续费上限: fee <= MAX_FEE (1000 bps) — [已通过L45的require校验]
  4. 分母非零: 份额计算前totalSupply > 0 — [违反 L67, 首次存款存在除零风险]

Invariant Violation Findings

不变量违反发现

FINDING: Invariant 4 violated — first depositor can cause division by zero
  • Location: L67
    shares = amount * totalSupply / totalAssets
  • When: totalSupply == 0 on first deposit
  • Impact: DoS attack on first deposit; protocol initialization blocked
  • Fix: Handle zero totalSupply case separately with initial share ratio
undefined
漏洞: 违反不变量4 —— 首次存款可能触发除零错误
  • 位置: L67
    shares = amount * totalSupply / totalAssets
  • 触发条件: 首次存款时totalSupply == 0
  • 影响: 首次存款触发DoS攻击,协议初始化被阻断
  • 修复方案: 单独处理totalSupply为零的场景,设置初始份额比例
undefined

Output: Security Report

输出:安全报告

markdown
undefined
markdown
undefined

Security Report: [Contract Name]

安全报告: [合约名称]

Summary

摘要

  • Functions analyzed: N
  • Findings: N (Critical: X, High: Y, Medium: Z, Low: W)
  • Invariants verified: N of M
  • CEI violations: N
  • 分析函数数量: N
  • 漏洞数: N(危急: X, 高危: Y, 中危: Z, 低危: W)
  • 已验证不变量: M个中的N个
  • CEI违反数: N

Critical Findings

危急漏洞

[F-01] Reentrancy in withdraw()

[F-01] withdraw()存在重入漏洞

  • Location:
    src/Pool.sol:L45
  • Pattern: External call before state update (CEI violation)
  • Impact: Complete fund drainage
  • Fix: Apply CEI pattern — update state before external call
  • 5 Whys: [root cause chain]
  • 位置:
    src/Pool.sol:L45
  • 模式: 状态更新前发起外部调用(CEI违反)
  • 影响: 资产被完全盗走
  • 修复方案: 遵循CEI模式 —— 外部调用前更新状态
  • 5Why分析: [根因链]

Invariant Status

不变量状态

InvariantStatusEvidence
sum(balances) == balanceVERIFIEDL90 invariant check
totalSupply == sum(shares)UNVERIFIEDNo test coverage
不变量状态证据
sum(balances) == balance已验证L90 不变量检查
totalSupply == sum(shares)未验证无测试覆盖

Recommendations

建议

  1. [Critical] Fix reentrancy in withdraw() before deployment
  2. [High] Add reentrancy guard as defense-in-depth
  3. [Medium] Add formal invariant tests via Foundry invariant suite
undefined
  1. [危急] 部署前修复withdraw()的重入漏洞
  2. [高危] 新增重入防护作为纵深防御措施
  3. [中危] 通过Foundry不变量测试套件添加形式化不变量测试
undefined

Integration with Agent-Studio

与Agent-Studio集成

Recommended Workflow

推荐工作流

  1. Invoke
    audit-context-building
    for initial code reconnaissance
  2. Invoke
    building-secure-contracts
    for contract-specific analysis
  3. Feed findings into
    security-architect
    for threat modeling
  4. Use
    static-analysis
    (Semgrep/CodeQL) for automated confirmation
  5. Use
    medusa-security
    for fuzzing-based invariant testing
  1. 调用
    audit-context-building
    完成初始代码勘察
  2. 调用
    building-secure-contracts
    执行合约专属分析
  3. 将漏洞发现输入
    security-architect
    完成威胁建模
  4. 使用
    static-analysis
    (Semgrep/CodeQL)自动确认人工发现的漏洞
  5. 使用
    medusa-security
    执行基于模糊测试的不变量测试

Complementary Skills

互补技能

SkillRelationship
audit-context-building
Builds initial mental model before contract analysis
security-architect
Consumes findings for threat modeling and STRIDE
static-analysis
Automated SAST confirmation of manual findings
medusa-security
Fuzzing and property-based testing for invariants
variant-analysis
Finds similar vulnerability patterns across codebase
web3-expert
Solidity/Ethereum ecosystem expertise
技能名称关联关系
audit-context-building
合约分析前构建初始认知模型
security-architect
消费漏洞发现结果用于威胁建模和STRIDE分析
static-analysis
自动化SAST验证人工发现的漏洞
medusa-security
不变量的模糊测试和基于属性的测试
variant-analysis
挖掘代码库中相似的漏洞模式
web3-expert
Solidity/Ethereum生态专业支持

Anti-Patterns

反模式

Anti-PatternWhy It FailsCorrect Approach
Auditing only the happy pathReentrancy and access control bugs are invisible in happy pathExplicitly trace every error path and external call
Trusting function name for access control
onlyAdmin()
might not check the actual admin role
Read the modifier implementation, not just its name
Assuming Solidity 0.8 prevents all integer bugs
unchecked
blocks, assembly, and casting bypass protection
Audit all
unchecked
blocks and type casts explicitly
Skipping cross-function reentrancyCross-function reentrancy bypasses single-function guardsMap shared state across ALL functions making external calls
Leaving invariants implicitUnwritten invariants are unverified risksDocument every invariant in NatSpec before analysis
反模式缺陷原因正确做法
仅审计正常业务路径重入和访问控制bug在正常路径下不可见显式追踪每一条错误路径和外部调用
通过函数名判断访问控制安全性
onlyAdmin()
可能并未校验实际的管理员角色
读取修饰符的实现,而非仅看名称
假设Solidity 0.8能防范所有整数bug
unchecked
块、汇编和类型转换会绕过防护
显式审计所有
unchecked
块和类型转换
忽略跨函数重入检查跨函数重入可绕过单函数防护映射所有发起外部调用的函数的共享状态
不显性声明不变量未记录的不变量是未验证的风险分析前将所有不变量记录在NatSpec中

Memory Protocol

记忆协议

Before starting: Check
.claude/context/memory/learnings.md
for prior contract audits of the same protocol or token standard.
During analysis: Write incremental findings to context report as discovered. Do not wait until the end.
After completion: Record key findings and patterns to
.claude/context/memory/learnings.md
. Record architecture decisions (CEI enforcement patterns, invariant frameworks) to
decisions.md
.
开始前: 检查
.claude/context/memory/learnings.md
中同一协议或代币标准的历史合约审计记录。
分析中: 发现漏洞时即时写入上下文报告,不要等到分析结束。
完成后: 将关键漏洞和模式记录到
.claude/context/memory/learnings.md
,将架构决策(CEI强制模式、不变量框架)记录到
decisions.md