- function testNum() external { - uint256 num = 2; - ... some logic about `num` ... - } + function testNum(uint256 num) external { + ... some logic about `num` ... + }
举例:
target:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// SPDX-License-Identifier: MIT pragma solidity 0.8.20;
// INVARIANT: doMath should never return 0 contract StatelessFuzzCatches { /* * @dev Should never return 0 */ function doMath(uint128 myNumber) public pure returns (uint256) { if (myNumber == 2) { return 0; } return 1; } }
test:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// SPDX-License-Identifier: SEE LICENSE IN LICENSE pragma solidity ^0.8.18;
import {StatelessFuzzCatches} from "../../src/invariant-break/StatelessFuzzCatches.sol"; import {Test, console2} from "forge-std/Test.sol";
contract StatelessFuzzCatchesTest is Test { StatelessFuzzCatches sfc;
function setUp() public { sfc = new StatelessFuzzCatches(); }
function testFuzzCatchesBugStateless(uint128 randomNumber) public view { assert(sfc.doMath(randomNumber) != 0); } }
// example // SPDX-License-Identifier: MIT pragma solidity 0.8.20;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
/* * This contract represents a vault for ERC20 tokens. * * INVARIANT: Users must always be able to withdraw the exact balance amout out. */ contract HandlerStatefulFuzzCatches { error HandlerStatefulFuzzCatches__UnsupportedToken();
using SafeERC20 for IERC20;
mapping(IERC20 => bool) public tokenIsSupported; mapping(address user => mapping(IERC20 token => uint256 balance)) public tokenBalances;
modifier requireSupportedToken(IERC20 token) { if (!tokenIsSupported[token]) revert HandlerStatefulFuzzCatches__UnsupportedToken(); _; }
constructor(IERC20[] memory _supportedTokens) { for (uint256 i; i < _supportedTokens.length; i++) { tokenIsSupported[_supportedTokens[i]] = true; } }
// SPDX-License-Identifier: SEE LICENSE IN LICENSE pragma solidity ^0.8.20;
import {Test} from "forge-std/Test.sol"; import {StdInvariant} from "forge-std/StdInvariant.sol"; import {HandlerStatefulFuzzCatches} from "../../../src/invariant-break/HandlerStatefulFuzzCatches.sol"; import {MockUSDC} from "../../mocks/MockUSDC.sol"; import {YeildERC20} from "../../mocks/YeildERC20.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract AttemptedBreakTest is StdInvariant, Test { HandlerStatefulFuzzCatches handlerStatefulFuzzCatches; YeildERC20 yeildERC20; MockUSDC mockUSDC; IERC20[] supposedTokens; uint256 startingAmount;
// 这次的 fuzzing test 需要一个用户来进行存款,取款,所以我们可以通过 Foundry cheatcode 来创建一个用户 address user = makeAddr("user");
function setUp() public { // 使用 cheatcode,这样能把下面这些合约的 msg.sender 设置为 `user` vm.startPrank(user); yeildERC20 = new YeildERC20(); mockUSDC = new MockUSDC(); startingAmount = yeildERC20.INITIAL_SUPPLY(); mockUSDC.mint(user, startingAmount); vm.stopPrank();
supposedTokens.push(mockUSDC); supposedTokens.push(yeildERC20); handlerStatefulFuzzCatches = new HandlerStatefulFuzzCatches(supposedTokens);
function testStartingAmountTheSame() public view { assert(startingAmount == yeildERC20.balanceOf(user)); assert(startingAmount == mockUSDC.balanceOf(user)); }
function invariant_testInvaraintBreaks() public { vm.startPrank(user); handlerStatefulFuzzCatches.withdrawToken(mockUSDC); handlerStatefulFuzzCatches.withdrawToken(yeildERC20); vm.stopPrank();
// SPDX-License-Identifier: SEE LICENSE IN LICENSE pragma solidity ^0.8.20;
import {Test} from "forge-std/Test.sol"; import {HandlerStatefulFuzzCatches} from "../../../src/invariant-break/HandlerStatefulFuzzCatches.sol"; import {YeildERC20} from "../../mocks/YeildERC20.sol"; import {MockUSDC} from "../../mocks/MockUSDC.sol";
contract Handler is Test { HandlerStatefulFuzzCatches handlerStatefulFuzzCatches; MockUSDC mockUSDC; YeildERC20 yeildERC20; address user;
function invariant_testInvaraintHandler() public { vm.startPrank(user); handlerStatefulFuzzCatches.withdrawToken(mockUSDC); handlerStatefulFuzzCatches.withdrawToken(yeildERC20); vm.stopPrank();
// SPDX-License-Identifier: SEE LICENSE IN LICENSE pragma solidity ^0.8.20;
import {Test} from "forge-std/Test.sol"; import {StdInvariant} from "forge-std/StdInvariant.sol"; import {HandlerStatefulFuzzCatches} from "../../../src/invariant-break/HandlerStatefulFuzzCatches.sol"; import {MockUSDC} from "../../mocks/MockUSDC.sol"; import {YeildERC20} from "../../mocks/YeildERC20.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {Handler} from "./Handler.t.sol";
contract AttemptedBreakTest is StdInvariant, Test { HandlerStatefulFuzzCatches handlerStatefulFuzzCatches; YeildERC20 yeildERC20; MockUSDC mockUSDC; IERC20[] supposedTokens; uint256 startingAmount;
// 这次的 fuzzing test 需要一个用户来进行存款,取款,所以我们可以通过 Foundry cheatcode 来创建一个用户 address user = makeAddr("user");
Handler handler;
function setUp() public { // 使用 cheatcode,这样能把下面这些合约的 msg.sender 设置为 `user` vm.startPrank(user); yeildERC20 = new YeildERC20(); mockUSDC = new MockUSDC(); startingAmount = yeildERC20.INITIAL_SUPPLY(); mockUSDC.mint(user, startingAmount); vm.stopPrank();
supposedTokens.push(mockUSDC); supposedTokens.push(yeildERC20); handlerStatefulFuzzCatches = new HandlerStatefulFuzzCatches(supposedTokens); // handler fuzzing test 不再像 open stateful fuzzing 直接对要进行模糊测试的合约进行 fuzzing,而是通过 handler 再 待测试合约的方式 // targetContract(address(handlerStatefulFuzzCatches)); handler = new Handler(handlerStatefulFuzzCatches, mockUSDC, yeildERC20, user);
function invariant_testInvaraintHandler() public { vm.startPrank(user); handlerStatefulFuzzCatches.withdrawToken(mockUSDC); handlerStatefulFuzzCatches.withdrawToken(yeildERC20); vm.stopPrank();