plugins/contract-security/skills/forge-foundry-test/SKILL.md
当用户要求编写 Foundry 单元测试, Fork 主网测试, vm.createSelectFork, vm.etch, 格式化测试输出, 数值格式化, 或需要在真实链状态上测试合约时应使用此技能
npx skillsauth add phpmac/skills forge-foundry-testInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
| 工具 | 用途 | 检测命令 |
|------|------|----------|
| forge | 编译/测试 | which forge |
标准 Foundry 测试, 不依赖链上状态.
| 命名模式 | 用途 |
|----------|------|
| *Test.t.sol | 普通单元测试 |
| *Fork.t.sol | Fork 主网测试 |
# 运行所有测试
forge test -vvv
# 运行指定文件
forge test --match-path test/MyTest.t.sol -vvv
# 运行指定函数
forge test --match-test test_FunctionName -vvv
# 离线模式 (不连接网络)
forge test --match-test testFunc -vvv --offline
# 过滤输出
forge test --match-test testFunc -vvv --offline 2>&1 | grep -E "(阶段|结果|Error)" | head -40
使用主网真实状态测试已部署合约, 分两种模式:
替换链上合约的运行时代码, 保留链上真实存储. 适合:
function setUp() public {
vm.createSelectFork("bsc_mainnet");
address tokenAddr = vm.envAddress("JM_TOKEN");
vm.etch(tokenAddr, type(JMTokenV2).runtimeCode);
jmToken = JMTokenV2(payable(tokenAddr));
// 从合约读取关联地址, 禁止硬编码
router = IPancakeRouter(jmToken.pancakeRouter());
pair = IPancakePair(jmToken.lpPair());
}
只读取主网数据, 不替换代码. 适合:
function setUp() public {
vm.createSelectFork("bsc_mainnet");
jmToken = JMTokenV2(payable(vm.envAddress("JM_TOKEN")));
}
| Cheatcode | 说明 |
|-----------|------|
| vm.createSelectFork(rpc) | 创建并切换到 Fork |
| vm.etch(addr, runtimeCode) | 替换合约代码, 保留存储 |
| vm.deal(addr, amount) | 设置地址余额 |
| vm.prank(sender, origin) | 模拟调用 (双参数解决 onlyEOA) |
| vm.roll(blockNumber) | 设置区块高度 |
| vm.warp(timestamp) | 设置区块时间戳 |
| vm.envAddress("KEY") | 从 .env 读取地址 |
vm.envAddress() 读取地址, 禁止硬编码jmToken.lpPair() 而非硬编码见: fork-test-template.md
解决 console.log 直接输出 uint256 原始 wei 值的问题:
// 原始输出
用户余额: 200000000000000000
// 格式化后
用户余额: 0.2 BNB
| 函数 | 用途 | 示例 |
|------|------|------|
| _formatEther(value) | wei 转可读格式 | 200000000000000000 -> "0.2" |
| _logStage(title) | 阶段分隔 | -> 阶段名称 |
| _logLine(label, before, after) | 数值变化 | [余额]: 1 +0.5 = 1.5 |
| _logValue(label, value) | 键值对 | 价格: 0.2 |
| _formatWithUnit(value, unit) | 带单位 | "0.2 BNB" |
function test_PrivateSale() public {
console.log(unicode"=== 私募测试 ===");
_logStage(unicode"阶段1 -> 准备账户");
uint256 userBalanceBefore = user.balance;
vm.deal(user, 1 ether);
_logLine(unicode"用户BNB", userBalanceBefore, user.balance);
_logStage(unicode"阶段2 -> 执行私募");
console.log(string.concat(unicode" [私募价格]: ", _formatEther(price), unicode" BNB"));
_logStage(unicode"阶段3 -> 验证结果");
console.log(string.concat(unicode" [用户获得JM]: ", _formatEther(jmToken.balanceOf(user)), unicode" JM"));
}
输出效果:
=== 私募测试 ===
-> 阶段1 -> 准备账户
[用户][BNB]: 0 +1 = 1
-> 阶段2 -> 执行私募
[私募价格]: 0.2 BNB
-> 阶段3 -> 验证结果
[用户获得JM]: 6000 JM
string.concat(unicode"中文", vm.toString(value))console.log(string.concat(unicode" [用户地址]: ", vm.toString(user)))见: test-formatting-template.md
_formatEther() 假设精度 18 位, 非 18 位需调整除数data-ai
当用户提到 Linux 提权/本地提权/local privilege escalation/获取root权限/内核漏洞利用/LPE/SUID/sudo滥用/容器逃逸/权限提升检测; 或要求在Linux系统上从普通用户提升到root权限; 或查询CVE提权漏洞(如Dirty Pipe/CopyFail/Dirty Frag/PwnKit/Looney Tunables); 或需要安全加固建议时应使用此技能
tools
当用户要求 "计算仓位", "仓位管理", "止损比例", "凯利公式", "盈亏比", "资金管理", "半凯利", "反马丁格尔", "固定风险", "position sizing", "策略评估", "策略体检", "SQN", "夏普比率", "卡玛比率", "期望值", "获利因子", "MAE", "MFE", "R乘数", "索提诺", "蒙特卡洛", "样本外测试", "策略回测" 或需要计算合约交易的最优仓位/止损/资金分配/策略质量评估时应使用此技能. 覆盖仓位管理/策略评估/交易解剖/压力测试的完整框架. 即使用户只是提到 "这笔交易该下多少", "策略好不好", "复盘怎么算" 等模糊描述也应触发.
development
当用户要求 "提取API", "逆向APK", "分析APP接口", "提取业务端点", "React Native逆向", "Flutter逆向" 或需要从移动应用提取后端API信息时使用此技能. 覆盖APK解包/JS bundle分析/kernel_blob.bin分析/H5页面参数发现/Spring Boot API验证全流程. 支持React Native和Flutter两种框架.
research
当用户要求 "推荐VPS", "选服务器", "对比服务商", "建站VPS", "便宜VPS", "VPS推荐" 或需要研究/筛选/对比VPS服务商时应使用此技能. 从 hostloc/lowendtalk/lowendbox/测评站搜集真实评测数据, 交叉验证后给出可溯源排名.