Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: add halmos-cheatcodes example test #136

Merged
merged 1 commit into from
Jul 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions tests/expected/all.json
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,16 @@
"num_bounded_loops": null
}
],
"test/Token.t.sol:TokenTest": [
{
"name": "checkBalanceInvariant()",
"exitcode": 1,
"num_models": 2,
"num_paths": null,
"time": null,
"num_bounded_loops": null
}
],
"test/UnsupportedOpcode.t.sol:X": [
{
"name": "checkFoo()",
Expand Down
59 changes: 59 additions & 0 deletions tests/test/Token.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.0 <0.9.0;

import {SymTest} from "halmos-cheatcodes/SymTest.sol";
import {Test} from "forge-std/Test.sol";

/// @custom:halmos --solver-timeout-assertion 60000
contract TokenTest is SymTest, Test {
Token token;

function setUp() public {
token = new Token();

// set the balances of three arbitrary accounts to arbitrary symbolic values
for (uint i = 0; i < 3; i++) {
address receiver = svm.createAddress('receiver');
uint256 amount = svm.createUint256('amount');
token.transfer(receiver, amount);
}
}

function checkBalanceInvariant() public {
// consider two arbitrary distinct accounts
address caller = svm.createAddress('caller');
address others = svm.createAddress('others');
vm.assume(others != caller);

// record their current balances
uint256 oldBalanceCaller = token.balanceOf(caller);
uint256 oldBalanceOthers = token.balanceOf(others);

// consider an arbitrary function call to the token from the caller
vm.prank(caller);
bytes memory data = svm.createBytes(100, 'data');
(bool success,) = address(token).call(data);
vm.assume(success);

// ensure that the caller cannot spend others' tokens
assert(token.balanceOf(caller) <= oldBalanceCaller);
assert(token.balanceOf(others) >= oldBalanceOthers);
}
}

contract Token {
mapping(address => uint) public balanceOf;

constructor() {
balanceOf[msg.sender] = 1e27;
}

function transfer(address to, uint amount) public {
_transfer(msg.sender, to, amount);
}

function _transfer(address from, address to, uint amount) public {
balanceOf[from] -= amount;
balanceOf[to] += amount;
}
}