stdStorage is a wrapper around the record and accesses cheatcodes. It allows you to find and write to storage slots associated with specific variables without knowing the storage layout. It works by recording SLOAD and SSTORE operations during a function call.
Key Capabilities:
- Find Slots: Use
.target(address).sig(selector).find() to locate a slot. - Write to Slots: Use
.checked_write(value) to write to a found slot. - Mappings: For mappings, use
.with_key(key) before calling .find(). - Structs: For structs, use
.depth(n) to specify the field depth (0 for the first field, 1 for the second, etc.). - Hidden Storage: It can find slots even if they are accessed via assembly (e.g.,
sload with a random hash).
Caveat: While it can find slots for packed storage variables, you cannot safely write to them; execution will throw an error unless the slot is uninitialized (bytes32(0)).
import "forge-std/Test.sol";
contract TestContract is Test {
using stdStorage for StdStorage;
Storage test;
function setUp() public {
test = new Storage();
}
function testFindExists() public {
uint256 slot = stdstore.target(address(test)).sig("exists()").find();
assertEq(slot, 0);
}
function testWriteExists() public {
stdstore.target(address(test)).sig("exists()").checked_write(100);
assertEq(test.exists(), 100);
}
function testFindMapping() public {
uint256 slot = stdstore
.target(address(test))
.sig(test.map_addr.selector)
.with_key(address(this))
.find();
assertEq(uint(vm.load(address(test), bytes32(slot))), 1);
}
function testFindStruct() public {
uint256 slot_for_a_field = stdstore
.target(address(test))
.sig(test.basicStruct.selector)
.depth(0)
.find();
assertEq(uint(vm.load(address(test), bytes32(slot_for_a_field))), 1);
}
}