关于 solidity-checklist
solidity-checklist 是面向 Solidity 开发者的链上操作预检技能,要求执行前按六层流程核查权限、依赖、参数、安全与本地测试,并在执行后验证和记录结果。
主要功能
- 01六层链上操作预检流程
- 02核查权限与合约依赖
- 03验证参数和安全风险
- 04本地测试、模拟与估算
- 05执行后检查并记录经验
适用场景
- 01发送交易前检查权限和参数
- 02部署或升级合约前进行安全预检
- 03排查交易模拟失败原因
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ 0xlayerghost/ solidity-agent-kit --skill solidity-checklist安装后在 Claude Code 中输入/solidity-checklist确认已加载。
详细介绍
Solidity 检查清单
语言规则
- 始终使用用户正在使用的语言进行回复。 如果用户用中文提问,就用中文回复。如果用户用英文提问,就用英文回复。
此技能的用途
大多数链上操作失败都是因为跳过了系统性验证。此技能不再采用被动的循环:
部署 -> 失败 -> 粘贴错误 -> 询问 AI -> 重试 -> 再次失败 -> 再次询问
而是强制执行主动验证流程:
检查权限 -> 检查依赖项 -> 检查参数 -> 检查安全性 -> 本地测试 -> 执行 -> 记录知识
6 层预检流程
每项链上操作在执行前都必须按顺序通过以下 6 层检查。
Layer 1: PERMISSIONS — Do I have the right to do this?
Layer 2: DEPENDENCIES — Do I understand what this affects?
Layer 3: PARAMETERS — Are my inputs correct?
Layer 4: SECURITY — Is this safe to execute?
Layer 5: TESTING — Have I verified locally first?
Layer 6: EXECUTE & CAPTURE — Do it, then record what I learned.
第 1 层:权限检查
进行任何链上调用前,请确认调用者具有所需的访问权限。
| 检查项 | 命令 | 通过条件 |
|---|---|---|
| 合约所有者 | cast call <CONTRACT> "owner()" --rpc-url <RPC> | 返回预期的部署者/管理员地址 |
| 基于角色的访问控制 | cast call <CONTRACT> "hasRole(bytes32,address)" <ROLE_HASH> <CALLER> --rpc-url <RPC> | 返回 true |
| 白名单状态 | cast call <CONTRACT> "isWhitelisted(address)" <CALLER> --rpc-url <RPC> | 返回 true(如适用) |
| 代币授权额度 | cast call <TOKEN> "allowance(address,address)" <OWNER> <SPENDER> --rpc-url <RPC> | 返回值 >= 所需金额 |
| 暂停状态 | cast call <CONTRACT> "paused()" --rpc-url <RPC> | 返回 false |
决策规则
| 结果 | 操作 |
|---|---|
| 所有检查均通过 | 继续第 2 层 |
| 缺少权限 | 先修复权限(授予角色/批准/取消暂停),然后重新检查 |
| 不确定需要哪些权限 | 阅读合约源码,在目标函数中查找所有 onlyOwner、onlyRole、require |
第 2 层:依赖项检查
了解影响范围——此操作会涉及哪些合约和状态?
| 检查项 | 方法 |
|---|---|
| 直接依赖项 | 阅读目标函数源码,列出所有外部调用(如 IERC20(token).transfer(...) 等) |
| 上游合约 | 哪些合约持有对该合约地址的引用? |
| 下游影响 | 此状态变更是否会触发回调、事件或链下索引器? |
| 初始化状态 | cast call <CONTRACT> "initialized()" --rpc-url <RPC> — 合约是否已完成全部设置? |
| 关联地址 | 验证合约状态中存储的所有地址均正确且非零 |
依赖关系图模板
对多合约系统进行操作前,请在脑海中(或书面)绘制关系图:
ContractA (owner: deployer)
├── references: TokenB (ERC20), RouterC
├── authorized callers: deployer, ContractD
└── state deps: must be initialized, TokenB must be approved
ContractD (owner: deployer)
├── references: ContractA, OracleE
└── state deps: OracleE must have price feed set
决策规则
| 结果 | 操作 |
|---|---|
| 依赖项明确 | 继续第 3 层 |
| 依赖项未知 | 阅读合约源码或询问他人——切勿盲目继续 |
| 需要重新部署 | 追踪完整的依赖关系图,找出所有需要更新地址的合约 |
第 3 层:参数验证
发送前验证每一项输入。
| 检查项 | 常见问题 |
|---|---|
| 地址格式 | 网络地址错误、零地址、校验和不匹配 |
| 代币精度 | 对 6 位小数的代币(USDC/USDT)使用 18 位小数 |
| 金额精度 | 1e18 与 1e6 相差 12 个数量级 |
| 函数选择器 | 调用了错误的函数或错误的重载 |
| 枚举值 | 传入了无效的枚举索引 |
| 数组长度 | 批量操作中的数组长度不匹配 |
| 截止时间/时间戳 | 截止时间已过、时区错误,或混淆了 block.timestamp 与 Unix 时间戳 |
验证命令
# Check token decimals before calculating amounts
cast call <TOKEN> "decimals()" --rpc-url <RPC>
# Verify address is a contract (not EOA)
cast code <ADDRESS> --rpc-url <RPC>
# Empty (0x) = EOA, non-empty = contract
# Decode your own calldata to double-check
cast calldata-decode "functionName(type1,type2)" <CALLDATA>
# Estimate gas before sending (catches obvious reverts)
cast estimate <CONTRACT> "functionName(args)" --from <CALLER> --rpc-url <RPC>
决策规则
| 结果 | 操作 |
|---|---|
| 所有参数均已验证 | 继续第 4 层 |
| 发现精度不匹配 | 使用正确的精度重新计算 |
| 地址是 EOA,但预期为合约 | 地址错误——请核实部署信息 |
第 4 层:安全快速检查
执行前检查常见安全风险。
| 风险 | 检查项 | 风险信号 |
|---|---|---|
| 重入 | 函数是否在更新状态前进行外部调用? | 状态更新前进行外部调用 |
| 抢跑交易 | 这是否是对价格敏感的操作(交换、清算)? | 缺少滑点保护或截止时间 |
| 访问控制 | 函数是否恰当地限制调用者? | 敏感函数缺少 onlyOwner / onlyRole |
| 数值处理 | 函数是否正确处理 msg.value? | 接受 ETH 却未使用,或反之 |
| 授权额度 | 我授权的额度是否超过必要范围? | 对未知合约调用 approve(spender, type(uint256).max) |
| 私钥泄露 | 开发者是否选择了密钥库或被 gitignore 忽略的 .env? | 在命令、脚本、日志或已提交文件中出现明文私钥 |
私钥规则(强制)
# NEVER put a literal private key in the command
cast send ... --private-key 0xdead...
# Option A: Foundry Keystore (recommended)
cast send ... --account <KEYSTORE_NAME>
# Set up keystore if not done
cast wallet import <NAME> --interactive
# Option B: load PRIVATE_KEY from a gitignored .env
source .env
cast send ... --private-key "$PRIVATE_KEY"
执行前让开发者选择选项 A 或选项 B。切勿读取、打印或提交 .env。
决策规则
| 结果 | 操作 |
|---|---|
| 没有风险信号 | 继续第 5 层 |
| 发现重入风险 | 在继续前添加 ReentrancyGuard,或修复 CEI 模式 |
| 存在抢跑交易风险 | 添加滑点/截止时间参数 |
| 私钥泄露 | 停止 — 轮换密钥,然后使用密钥库或被 gitignore 忽略的 .env |
第 5 层:本地测试
未经本地或分叉环境验证的交易,绝不发送。
| 方法 | 命令 | 适用场景 |
|---|---|---|
| 单元测试 | forge test --match-test <testName> -vvvv | 新增/修改合约函数 |
| 分叉测试 | forge test --fork-url <RPC> --match-test <testName> -vvvv | 针对实时状态进行测试 |
| 演练脚本 | forge script <Script> --rpc-url <RPC> -vvvv(不加 --broadcast) | 部署或复杂操作 |
| Gas 估算 | cast estimate <CONTRACT> "func(args)" --from <CALLER> --rpc-url <RPC> | 执行任何 cast send 前 |
| 模拟 | cast call <CONTRACT> "func(args)" --from <CALLER> --rpc-url <RPC> | 快速测试函数调用 |
测试决策树
Is this a new or modified contract?
├── YES → Write forge test → Run test → Pass? → Proceed
│ → Fail? → Fix and re-test
└── NO (calling existing deployed contract)
├── Simple read? → cast call (Layer 3 already covers this)
└── State-changing? → cast estimate first
├── Estimate succeeds → Proceed to Layer 6
└── Estimate reverts → Debug with cast call + revert reason → Fix → Re-estimate
决策规则
| 结果 | 操作 |
|---|---|
| 测试通过 / 估算成功 | 继续第 6 层 |
| 测试失败 | 修复问题 — 切勿部署有问题的代码 |
| 估算回滚 | 很可能是第 1 层(权限)或第 3 层(参数)的问题 — 返回检查 |
第 6 层:执行并记录
执行操作、验证是否成功,并记录经验。
执行
# Option A: send transaction using Foundry Keystore (recommended)
cast send <CONTRACT> "functionName(type1,type2)" <arg1> <arg2> \
--account <KEYSTORE_NAME> \
--rpc-url <RPC>
# Option A: deploy using Foundry Keystore
forge script <Script> \
--rpc-url <RPC> \
--account <KEYSTORE_NAME> \
--broadcast \
--gas-limit <LIMIT> \
-vvvv
# Option B: load PRIVATE_KEY from a gitignored .env, then send or deploy
source .env
cast send <CONTRACT> "functionName(type1,type2)" <arg1> <arg2> \
--private-key "$PRIVATE_KEY" \
--rpc-url <RPC>
forge script <Script> \
--rpc-url <RPC> \
--private-key "$PRIVATE_KEY" \
--broadcast \
--gas-limit <LIMIT> \
-vvvv
执行后验证(强制)
| 检查项 | 命令 |
|---|---|
| TX 状态 | 检查回执中的 status 字段:1 = 成功,0 = 失败 |
| 状态变更 | 使用 cast call 验证预期状态是否已变更 |
| 事件发出 | 检查回执中的 logs 是否包含预期事件 |
| 余额变更 | 使用 cast balance 或 cast call balanceOf 验证代币流转 |
经验记录
每次操作成功或失败后,都要记录所得经验:
| 记录内容 | 位置 |
|---|---|
| 含参数的成功命令 | 个人命令手册(.md 文件) |
| 遇到的新错误及解决方法 | 调试笔记 |
| 合约地址及其作用 | 项目架构文档 |
| 已发现的权限/角色要求 | 合约依赖关系图 |
| 常见操作的 Gas 成本 | Gas 参考表 |
反模式:一次性消耗
最大的杠杆:把每次与 AI 的交互都转化为自己的知识,而不是一次性消耗。
如果你向 AI 询问了某条命令,并且它能正常运行:
- 理解每个参数为何如此设置
- 用自己的话记录下来
- 下次先自己写一遍 — 再用 AI 验证
按操作类型快速参考
| 操作 | 关键层 | 最常见的失败原因 |
|---|---|---|
approve | L1(所有者)、L3(金额/小数位数) | 小数位数错误 |
transfer | L1(余额)、L3(金额/小数位数) | 余额不足 |
addLiquidity | L1(批准两个代币)、L3(金额/比例)、L5(估算) | 缺少批准 |
removeLiquidity | L1(LP 批准)、L3(minAmounts)、L4(滑点) | 滑点容忍度过低 |
stake | L1(批准 + 白名单)、L2(质押合约状态)、L3(金额) | 质押未激活 / 不在白名单中 |
unstake | L1(质押者状态)、L2(锁定期)、L3(金额) | 锁定期尚未结束 |
deploy | L3(构造函数参数)、L4(完整安全检查)、L5(必须进行分叉测试) | 构造函数参数不匹配 |
upgrade | L1(代理管理员)、L2(存储布局)、L4(存储冲突)、L5(必须进行分叉测试) | 存储布局不兼容 |
预检摘要卡
每次进行链上操作前,都打印这份思维检查清单:
[ ] L1 PERMISSIONS — Can I call this? (owner/role/approve/whitelist/paused)
[ ] L2 DEPENDENCIES — What does this touch? (contracts/state/events)
[ ] L3 PARAMETERS — Are inputs correct? (address/decimals/amount/selector)
[ ] L4 SECURITY — Is this safe? (reentrancy/frontrun/key exposure)
[ ] L5 TESTING — Did I test locally? (forge test/estimate/dry-run)
[ ] L6 EXECUTE — Send it, verify it, record what I learned.
6 个层级,2 分钟。节省数小时的调试时间和浪费的 Gas。
评论
支持 Markdown