Skip to content

Commit 2e526f0

Browse files
committed
added basic, fuzz, invariant, and gas reporting testing
1 parent 0fb48fa commit 2e526f0

File tree

2 files changed

+112
-0
lines changed

2 files changed

+112
-0
lines changed

src/TokenVault.sol

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,13 @@ contract TokenVault {
2626
totalValue -= amount;
2727
_transferEth(msg.sender, amount); // Uses the optimized internal transfer
2828
}
29+
// In src/TokenVault.sol
30+
function withdrawUnoptimized(uint256 amount) public {
31+
require(balances[msg.sender] >= amount, "Insufficient balance");
32+
balances[msg.sender] -= amount;
33+
totalValue -= amount;
34+
// Less gas-efficient way (e.g., using transfer, which forwards a fixed amount of gas)
35+
// The previous 'withdrawOptimized' uses call, which is typically cheaper for simple transfers
36+
payable(msg.sender).transfer(amount);
37+
}
2938
}

test/TokenVault.t.sol

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
pragma solidity ^0.8.0;
2+
import {Test, console2} from "forge-std/Test.sol";
3+
import {TokenVault} from "../src/TokenVault.sol";
4+
5+
contract TokenVaultTest is Test {
6+
TokenVault public vault;
7+
address public user1 = address(0xBEEF);
8+
uint256 public constant INITIAL_BALANCE = 10 ether;
9+
10+
function setUp() public {
11+
// 1. Deploy the contract
12+
vault = new TokenVault();
13+
14+
// 2. Fund the test account (user1) for transactions
15+
vm.deal(user1, INITIAL_BALANCE);
16+
}
17+
18+
function testDeposit() public {
19+
uint256 depositAmount = 1 ether;
20+
21+
// Use a cheat code to act as user1 and send value
22+
vm.startPrank(user1);
23+
vault.deposit{value: depositAmount}(depositAmount);
24+
vm.stopPrank();
25+
26+
// Assertions
27+
assertEq(
28+
vault.balances(user1),
29+
depositAmount,
30+
"User balance is incorrect"
31+
);
32+
assertEq(vault.totalValue(), depositAmount, "Total value is incorrect");
33+
}
34+
35+
// Example test for a failing scenario (revert)
36+
function testRevertDeposit_WrongAmount() public {
37+
uint256 sentAmount = 1 ether;
38+
uint256 expectedAmount = 2 ether;
39+
40+
// Expect a specific revert string
41+
vm.expectRevert("Must send exact ETH amount");
42+
43+
// The transaction that should revert
44+
vault.deposit{value: sentAmount}(expectedAmount);
45+
}
46+
47+
// Fuzz test for the deposit function
48+
function testFuzz_Deposit(uint96 amount) public {
49+
// 1. Set a constraint using vm.assume()
50+
// We only want to test amounts > 0 and <= our initial balance
51+
vm.assume(amount > 0 && amount <= INITIAL_BALANCE);
52+
53+
uint256 preVaultBalance = address(vault).balance;
54+
uint256 preUserBalance = vault.balances(user1);
55+
56+
// 2. Execute with random 'amount'
57+
vm.startPrank(user1);
58+
vault.deposit{value: amount}(amount);
59+
vm.stopPrank();
60+
61+
// 3. Assert a "property" that should always hold true
62+
assertEq(
63+
vault.balances(user1),
64+
preUserBalance + amount,
65+
"User balance property violated"
66+
);
67+
assertEq(
68+
address(vault).balance,
69+
preVaultBalance + amount,
70+
"Vault ETH balance property violated"
71+
);
72+
}
73+
74+
// Invariant: The totalValue must always equal the sum of all user balances
75+
function invariant_TotalValueEqualsSumOfBalances() public view {
76+
// This invariant function is run after every random call in the sequence.
77+
uint256 sumOfBalances =
78+
vault.balances(user1) +
79+
vault.balances(address(0xDEAD)); // Imagine tracking a few key addresses
80+
81+
assertEq(vault.totalValue(), sumOfBalances, "Invariant violated: totalValue != sum of balances");
82+
}
83+
84+
function testCompareWithdrawGas() public {
85+
uint256 depositAmount = 1 ether;
86+
uint256 withdrawAmount = 0.1 ether;
87+
88+
vm.deal(user1, depositAmount);
89+
vm.prank(user1);
90+
vault.deposit{value: depositAmount}(depositAmount);
91+
92+
// 1. Call the optimized version
93+
vm.prank(user1);
94+
vault.withdrawOptimized(withdrawAmount);
95+
96+
// 2. Call the unoptimized version (requires resetting the state, usually in a separate test)
97+
// To properly compare, you should call them in separate test functions:
98+
// function testWithdrawOptimized() public { /* ... call vault.withdrawOptimized ... */ }
99+
// function testWithdrawUnoptimized() public { /* ... call vault.withdrawUnoptimized ... */ }
100+
101+
// In this example, we'll just run the report on all functions.
102+
}
103+
}

0 commit comments

Comments
 (0)