欢迎订阅专栏:3分钟Solidity--智能合约--Web3区块链技术必学
Foundry vm.store
使用 vm.store在测试期间直接写入合约的存储槽。
这适用于以下场景:
- 无需调用合约函数即可设置测试场景
- 绕过访问控制以测试特定状态
- 测试正常情况下难以触发的边缘情况
对于映射类型,可通过 keccak256(abi.encode(key, mappingSlot))计算存储槽位置。
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import {Test, console2} from "forge-std/Test.sol";
contract Vault {
// Slot 0
address public owner;
// Slot 1
uint256 public password;
// Slot 2
mapping(address => uint256) public balances;
constructor(uint256 _password) {
owner = msg.sender;
password = _password;
}
function withdraw() external {
require(msg.sender == owner, "not owner");
uint256 bal = balances[msg.sender];
balances[msg.sender] = 0;
payable(msg.sender).transfer(bal);
}
}
contract StoreTest is Test {
Vault vault;
function setUp() public {
vault = new Vault(12345);
}
// vm.store(address account, bytes32 slot, bytes32 value)
// - account: 合约地址
// - slot: 要写入的存储槽
// - value: 要写入的值
function test_store_simple_slot() public {
// 插槽0存储所有者地址
// 将所有者更改为address(1)
vm.store(address(vault), bytes32(uint256(0)), bytes32(uint256(uint160(address(1)))));
assertEq(vault.owner(), address(1));
// 插槽1存储密码
// 将密码更改为999
vm.store(address(vault), bytes32(uint256(1)), bytes32(uint256(999)));
assertEq(vault.password(), 999);
}
function test_store_mapping() public {
// 对于映射关系,槽位的计算方式为:
// keccak256(abi.encode(key, mapping_slot))
address user = address(0xBEEF);
uint256 mappingSlot = 2; // balances is at slot 2
// 计算balances[user]的存储槽
bytes32 slot = keccak256(abi.encode(user, mappingSlot));
// 将用户余额设置为100以太币
vm.store(address(vault), slot, bytes32(uint256(100 ether)));
assertEq(vault.balances(user), 100 ether);
}
}