Skip to content

Instantly share code, notes, and snippets.

@tinchoabbate
Created September 21, 2023 15:30
Show Gist options
  • Save tinchoabbate/67b195b95fe77a5b9e3c6cc4bf80b3f7 to your computer and use it in GitHub Desktop.
Save tinchoabbate/67b195b95fe77a5b9e3c6cc4bf80b3f7 to your computer and use it in GitHub Desktop.
Short fuzzing exercise

Fuzzing Exercise

This is a short and simple exercise to start sharpening your smart contract fuzzing skills with Foundry.

The scenario is simple. There's a registry contract that allows callers to register by paying a fixed fee in ETH. If the caller sends too little ETH, execution should revert. If the caller sends too much ETH, the contract should give back the change.

Things look good according to the unit test we coded in the Registry.t.sol contract.

Your goal is to code at least one fuzz test for the Registry contract. By following the brief specification above, the test must be able to detect a bug in the register function.

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract Registry {
error PaymentNotEnough(uint256 expected, uint256 actual);
uint256 public constant PRICE = 1 ether;
mapping(address account => bool registered) private registry;
function register() external payable {
if(msg.value < PRICE) {
revert PaymentNotEnough(PRICE, msg.value);
}
registry[msg.sender] = true;
}
function isRegistered(address account) external view returns (bool) {
return registry[account];
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Test, console2} from "forge-std/Test.sol";
import {Registry} from "../src/Registry.sol";
contract RegistryTest is Test {
Registry registry;
address alice;
function setUp() public {
alice = makeAddr("alice");
registry = new Registry();
}
function test_register() public {
uint256 amountToPay = registry.PRICE();
vm.deal(alice, amountToPay);
vm.startPrank(alice);
uint256 aliceBalanceBefore = address(alice).balance;
registry.register{value: amountToPay}();
uint256 aliceBalanceAfter = address(alice).balance;
assertTrue(registry.isRegistered(alice), "Did not register user");
assertEq(address(registry).balance, registry.PRICE(), "Unexpected registry balance");
assertEq(aliceBalanceAfter, aliceBalanceBefore - registry.PRICE(), "Unexpected user balance");
}
/** Code your fuzz test here */
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Test, console2} from "forge-std/Test.sol";
import {Registry} from "../src/Registry.sol";
contract RegistryTest is Test {
Registry registry;
address alice;
function setUp() public {
alice = makeAddr("alice");
registry = new Registry();
}
function test_register() public {
uint256 amountToPay = registry.PRICE();
vm.deal(alice, amountToPay);
vm.startPrank(alice);
uint256 aliceBalanceBefore = address(alice).balance;
registry.register{value: amountToPay}();
uint256 aliceBalanceAfter = address(alice).balance;
assertTrue(registry.isRegistered(alice), "Did not register user");
assertEq(address(registry).balance, registry.PRICE(), "Unexpected registry balance");
assertEq(aliceBalanceAfter, aliceBalanceBefore - registry.PRICE(), "Unexpected user balance");
}
/** Almost the same test, but this time fuzzing amountToPay detects the bug (the Registry contract is not giving back the change) */
function test_fuzz_register(uint256 amountToPay) public {
vm.assume(amountToPay >= 1 ether);
vm.deal(alice, amountToPay);
vm.startPrank(alice);
uint256 aliceBalanceBefore = address(alice).balance;
registry.register{value: amountToPay}();
uint256 aliceBalanceAfter = address(alice).balance;
assertTrue(registry.isRegistered(alice), "Did not register user");
assertEq(address(registry).balance, registry.PRICE(), "Unexpected registry balance");
assertEq(aliceBalanceAfter, aliceBalanceBefore - registry.PRICE(), "Unexpected user balance");
}
}
@Parth-Sharma-tonchi
Copy link

Yes, it gives following error output instead:
`Ran 1 test for test/registry.t.sol:RegistryTest
[FAIL. Reason: Unexpected registry balance: 1000000000000000001 != 1000000000000000000; counterexample: calldata=0xeddb66010000000000000000000000000000000000000000000000000de0b6b3a7640001 args=[1000000000000000001 [1e18]]] testRegisterFuzz(uint256) (runs: 1, μ: 45509, ~: 45509)
Suite result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 23.53ms (22.14ms CPU time)

Ran 1 test suite in 24.79ms (23.53ms CPU time): 0 tests passed, 1 failed, 0 skipped (1 total tests)

Failing tests:
Encountered 1 failing test in test/registry.t.sol:RegistryTest
[FAIL. Reason: Unexpected registry balance: 1000000000000000001 != 1000000000000000000; counterexample: calldata=0xeddb66010000000000000000000000000000000000000000000000000de0b6b3a7640001 args=[1000000000000000001 [1e18]]] testRegisterFuzz(uint256) (runs: 1, μ: 45509, ~: 45509)

Encountered a total of 1 failing tests, 0 tests succeeded`
Thanks @tinchoabbate

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment