# Foundry Cheatcodes जो आप शायद use नहीं कर रहे
हर कोई vm.prank और vm.expectRevert जानता है। लेकिन Foundry में 100+ cheatcodes हैं। यहाँ सबसे underrated और powerful ones।
1. vm.assume — Fuzzing के लिए
Fuzzing tests में invalid inputs को skip करना common है। vm.assume इसे clean बनाता है।
Without vm.assume
function testTransfer(address to, uint amount) public {
if (to == address(0)) return; // Skip
if (amount == 0) return; // Skip
if (amount > token.balanceOf(user)) return; // Skip
vm.prank(user);
token.transfer(to, amount);
assertEq(token.balanceOf(to), amount);
}
Problem: Early returns waste fuzzing runs। Foundry इन्हें "successful" count करता है।
With vm.assume
function testTransfer(address to, uint amount) public {
vm.assume(to != address(0));
vm.assume(amount > 0);
vm.assume(amount <= token.balanceOf(user));
vm.prank(user);
token.transfer(to, amount);
assertEq(token.balanceOf(to), amount);
}
Benefit: Foundry automatically invalid inputs reject करता है। Real test cases ज्यादा चलते हैं।
Pro Tip: Combine Assumptions
vm.assume(
to != address(0) &&
amount > 0 &&
amount <= 1e30 // Reasonable upper bound
);
---
2. vm.store — Direct Storage Manipulation
किसी भी contract का storage directly modify करो। Incredibly powerful initialization और edge case testing के लिए।
Example: Set Uniswap Pool Reserves
function testFlashLoan() public {
IUniswapV2Pair pair = IUniswapV2Pair(pairAddress);
// Directly set reserves to 1M USDC, 500 ETH
vm.store(
address(pair),
bytes32(uint(8)), // Slot 8 = reserve0
bytes32(uint(1_000_000e6))
);
vm.store(
address(pair),
bytes32(uint(9)), // Slot 9 = reserve1
bytes32(uint(500 ether))
);
// Now test flash loan with known reserves
}
Finding Storage Slots
forge inspect MyContract storage-layout
Mapping Storage Slots
Mappings का slot calculate करना:
// mapping(address => uint) public balances; at slot 5
address user = 0x123...;
bytes32 slot = keccak256(abi.encode(user, uint(5)));
vm.store(tokenAddress, slot, bytes32(uint(1000 ether)));
// User now has 1000 tokens!
---
3. vm.mockCall — External Dependencies Mock करें
Third-party contracts mock करो without deploying them।
Example: Mock Chainlink Oracle
function testPriceDependent() public {
address oracle = 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419;
// Mock latestRoundData() to return $2000 ETH price
vm.mockCall(
oracle,
abi.encodeWithSelector(
AggregatorV3Interface.latestRoundData.selector
),
abi.encode(
uint80(0), // roundId
int256(2000e8), // answer
uint256(0), // startedAt
block.timestamp, // updatedAt
uint80(0) // answeredInRound
)
);
// Now vault.getETHPrice() returns 2000
vault.deposit{value: 1 ether}();
assertEq(vault.collateralValue(address(this)), 2000e18);
}
Mock Multiple Calls
// First call returns 2000, second returns 1800
vm.mockCall(oracle, selector, abi.encode(2000e8));
uint price1 = vault.getPrice(); // 2000
vm.mockCall(oracle, selector, abi.encode(1800e8));
uint price2 = vault.getPrice(); // 1800
// Test liquidation on price drop
---
4. vm.expectEmit — Event Testing Done Right
Events verify करना tricky है। vm.expectEmit इसे simple बनाता है।
Basic Usage
function testDeposit() public {
// Expect Transfer event with these parameters
vm.expectEmit(true, true, false, true);
emit Transfer(address(0), user, 100e18);
vault.deposit{value: 1 ether}();
}
Parameters Explained
vm.expectEmit(
checkTopic1, // Check first indexed param?
checkTopic2, // Check second indexed param?
checkTopic3, // Check third indexed param?
checkData // Check non-indexed params?
);
Example: Partial Matching
event Swap(
address indexed from,
address indexed to,
uint amountIn,
uint amountOut // Don't care about exact amount
);
// Check from/to, ignore amounts
vm.expectEmit(true, true, false, false);
emit Swap(user, address(vault), 0, 0);
router.swap(tokenA, tokenB, 100e18);
---
5. vm.recordLogs — Capture All Events
सभी events capture करो और programmatically analyze करो।
function testBatchTransfer() public {
vm.recordLogs();
vault.batchTransfer([user1, user2, user3], [10, 20, 30]);
Vm.Log[] memory logs = vm.getRecordedLogs();
uint transferCount = 0;
for (uint i = 0; i < logs.length; i++) {
if (logs[i].topics[0] == keccak256("Transfer(address,address,uint256)")) {
transferCount++;
}
}
assertEq(transferCount, 3, "Should emit 3 Transfer events");
}
Decode Event Data
for (uint i = 0; i < logs.length; i++) {
if (logs[i].topics[0] == keccak256("Transfer(address,address,uint256)")) {
address from = address(uint160(uint(logs[i].topics[1])));
address to = address(uint160(uint(logs[i].topics[2])));
uint amount = abi.decode(logs[i].data, (uint));
console.log("Transfer:", from, to, amount);
}
}
---
6. vm.snapshot + vm.revertTo — State Management
Complex test setups को snapshot करो और reuse करो।
contract VaultTest is Test {
uint snapshotId;
function setUp() public {
// Deploy contracts
vault = new Vault();
token.mint(user, 1000e18);
// Save initial state
snapshotId = vm.snapshot();
}
function testScenarioA() public {
vault.deposit(100e18);
// ... test logic
// Revert to clean state
vm.revertTo(snapshotId);
}
function testScenarioB() public {
// Starts from clean snapshot
vault.deposit(200e18);
// ... different test
}
}
Benefit: No need to redeploy contracts। Tests 3x faster! ⚡
---
7. vm.warp + vm.roll — Time Travel
Time और block number manipulate करो।
vm.warp — Set block.timestamp
function testVesting() public {
vault.startVesting(user, 1000e18, 365 days);
// Jump 6 months ahead
vm.warp(block.timestamp + 180 days);
uint claimable = vault.claimable(user);
assertApproxEqRel(claimable, 500e18, 0.01e18); // ~50%
// Jump to end
vm.warp(block.timestamp + 185 days);
assertEq(vault.claimable(user), 1000e18); // 100%
}
vm.roll — Set block.number
function testBlockDelay() public {
vault.propose(proposalId);
// Need 100 blocks to elapse
vm.roll(block.number + 100);
vault.execute(proposalId); // Now allowed
}
---
8. vm.createSelectFork — Fork Testing
Multiple forks के बीच switch करो।
contract CrossChainTest is Test {
uint mainnetFork;
uint arbitrumFork;
function setUp() public {
mainnetFork = vm.createFork("mainnet");
arbitrumFork = vm.createFork("arbitrum");
}
function testBridge() public {
// Start on mainnet
vm.selectFork(mainnetFork);
bridge.deposit{value: 1 ether}(user);
// Switch to Arbitrum
vm.selectFork(arbitrumFork);
bridge.claim(user);
assertEq(user.balance, 1 ether);
}
}
Pro Tip: Fork at Specific Block
// Fork at block where hack happened
uint forkId = vm.createFork("mainnet", 15_000_000);
vm.selectFork(forkId);
// Replay attack and test fix
---
9. vm.chainId — Multi-Chain Testing
Chain ID change करो — EIP-712 signatures test करने के लिए useful।
function testSignatureReplay() public {
// Sign on Ethereum mainnet (chainId 1)
vm.chainId(1);
bytes32 digest = vault.getDigest(user, 100e18);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, digest);
// Try to replay on Polygon (chainId 137)
vm.chainId(137);
vm.expectRevert("Invalid signature");
vault.withdrawWithSignature(user, 100e18, v, r, s);
}
---
10. vm.signCompact — Compact Signatures
EIP-2098 compact signatures (64 bytes instead of 65)।
function testCompactSig() public {
bytes32 digest = keccak256("message");
// Standard signature: 65 bytes
(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest);
// Compact signature: 64 bytes
(bytes32 r2, bytes32 vs) = vm.signCompact(privateKey, digest);
// vs encodes both s and v
// Saves 32 bytes in calldata (cheaper!)
}
Use Case: Batch Signatures
// Standard: 10 signatures = 650 bytes
// Compact: 10 signatures = 640 bytes (10 bytes saved)
function batchVerify(bytes32[] memory r, bytes32[] memory vs) external {
for (uint i = 0; i < r.length; i++) {
address signer = recoverCompact(digest, r[i], vs[i]);
// ...
}
}
---
Bonus: Combine Cheatcodes
Real power तब आती है जब आप इन्हें combine करते हैं:
function testComplexScenario() public {
// 1. Fork mainnet at specific block
uint forkId = vm.createFork("mainnet", 18_000_000);
vm.selectFork(forkId);
// 2. Mock Chainlink price
vm.mockCall(oracle, selector, abi.encode(2000e8));
// 3. Manipulate storage to give user tokens
bytes32 slot = keccak256(abi.encode(user, 0));
vm.store(tokenAddress, slot, bytes32(uint(1000e18)));
// 4. Record events
vm.recordLogs();
// 5. Warp time forward
vm.warp(block.timestamp + 30 days);
// 6. Execute as user
vm.prank(user);
vault.compound();
// 7. Verify events
Vm.Log[] memory logs = vm.getRecordedLogs();
assertGt(logs.length, 0);
}
---
Best Practices
Conclusion
Foundry's cheatcodes incredibly powerful हैं। vm.prank से आगे देखो — testing superpowers unlock करो।
Practice करो:
- Fuzzing में
vm.assumeuse करो
- Complex setups
vm.snapshotसे optimize करो
- External dependencies
vm.mockCallसे mock करो
- Events
vm.expectEmitसे verify करो
Happy testing! 🧪