// SPDX-License-Identifier: MIT pragma solidity >=0.7.0 <0.9.0; import "../src/StdStorage.sol"; import "../src/Test.sol"; contract StdStorageTest is Test { using stdStorage for StdStorage; StorageTest internal test; function setUp() public { test = new StorageTest(); } function test_StorageHidden() public { assertEq(uint256(keccak256("my.random.var")), stdstore.target(address(test)).sig("hidden()").find()); } function test_StorageObvious() public { assertEq(uint256(0), stdstore.target(address(test)).sig("exists()").find()); } function test_StorageExtraSload() public { assertEq(16, stdstore.target(address(test)).sig(test.extra_sload.selector).find()); } function test_StorageCheckedWriteHidden() public { stdstore.target(address(test)).sig(test.hidden.selector).checked_write(100); assertEq(uint256(test.hidden()), 100); } function test_StorageCheckedWriteObvious() public { stdstore.target(address(test)).sig(test.exists.selector).checked_write(100); assertEq(test.exists(), 100); } function test_StorageCheckedWriteSignedIntegerHidden() public { stdstore.target(address(test)).sig(test.hidden.selector).checked_write_int(-100); assertEq(int256(uint256(test.hidden())), -100); } function test_StorageCheckedWriteSignedIntegerObvious() public { stdstore.target(address(test)).sig(test.tG.selector).checked_write_int(-100); assertEq(test.tG(), -100); } function test_StorageMapStructA() public { uint256 slot = stdstore.target(address(test)).sig(test.map_struct.selector).with_key(address(this)).depth(0).find(); assertEq(uint256(keccak256(abi.encode(address(this), 4))), slot); } function test_StorageMapStructB() public { uint256 slot = stdstore.target(address(test)).sig(test.map_struct.selector).with_key(address(this)).depth(1).find(); assertEq(uint256(keccak256(abi.encode(address(this), 4))) + 1, slot); } function test_StorageDeepMap() public { uint256 slot = stdstore.target(address(test)).sig(test.deep_map.selector).with_key(address(this)).with_key( address(this) ).find(); assertEq(uint256(keccak256(abi.encode(address(this), keccak256(abi.encode(address(this), uint256(5)))))), slot); } function test_StorageCheckedWriteDeepMap() public { stdstore.target(address(test)).sig(test.deep_map.selector).with_key(address(this)).with_key(address(this)) .checked_write(100); assertEq(100, test.deep_map(address(this), address(this))); } function test_StorageDeepMapStructA() public { uint256 slot = stdstore.target(address(test)).sig(test.deep_map_struct.selector).with_key(address(this)) .with_key(address(this)).depth(0).find(); assertEq( bytes32(uint256(keccak256(abi.encode(address(this), keccak256(abi.encode(address(this), uint256(6)))))) + 0), bytes32(slot) ); } function test_StorageDeepMapStructB() public { uint256 slot = stdstore.target(address(test)).sig(test.deep_map_struct.selector).with_key(address(this)) .with_key(address(this)).depth(1).find(); assertEq( bytes32(uint256(keccak256(abi.encode(address(this), keccak256(abi.encode(address(this), uint256(6)))))) + 1), bytes32(slot) ); } function test_StorageCheckedWriteDeepMapStructA() public { stdstore.target(address(test)).sig(test.deep_map_struct.selector).with_key(address(this)).with_key( address(this) ).depth(0).checked_write(100); (uint256 a, uint256 b) = test.deep_map_struct(address(this), address(this)); assertEq(100, a); assertEq(0, b); } function test_StorageCheckedWriteDeepMapStructB() public { stdstore.target(address(test)).sig(test.deep_map_struct.selector).with_key(address(this)).with_key( address(this) ).depth(1).checked_write(100); (uint256 a, uint256 b) = test.deep_map_struct(address(this), address(this)); assertEq(0, a); assertEq(100, b); } function test_StorageCheckedWriteMapStructA() public { stdstore.target(address(test)).sig(test.map_struct.selector).with_key(address(this)).depth(0).checked_write(100); (uint256 a, uint256 b) = test.map_struct(address(this)); assertEq(a, 100); assertEq(b, 0); } function test_StorageCheckedWriteMapStructB() public { stdstore.target(address(test)).sig(test.map_struct.selector).with_key(address(this)).depth(1).checked_write(100); (uint256 a, uint256 b) = test.map_struct(address(this)); assertEq(a, 0); assertEq(b, 100); } function test_StorageStructA() public { uint256 slot = stdstore.target(address(test)).sig(test.basic.selector).depth(0).find(); assertEq(uint256(7), slot); } function test_StorageStructB() public { uint256 slot = stdstore.target(address(test)).sig(test.basic.selector).depth(1).find(); assertEq(uint256(7) + 1, slot); } function test_StorageCheckedWriteStructA() public { stdstore.target(address(test)).sig(test.basic.selector).depth(0).checked_write(100); (uint256 a, uint256 b) = test.basic(); assertEq(a, 100); assertEq(b, 1337); } function test_StorageCheckedWriteStructB() public { stdstore.target(address(test)).sig(test.basic.selector).depth(1).checked_write(100); (uint256 a, uint256 b) = test.basic(); assertEq(a, 1337); assertEq(b, 100); } function test_StorageMapAddrFound() public { uint256 slot = stdstore.target(address(test)).sig(test.map_addr.selector).with_key(address(this)).find(); assertEq(uint256(keccak256(abi.encode(address(this), uint256(1)))), slot); } function test_StorageMapAddrRoot() public { (uint256 slot, bytes32 key) = stdstore.target(address(test)).sig(test.map_addr.selector).with_key(address(this)).parent(); assertEq(address(uint160(uint256(key))), address(this)); assertEq(uint256(1), slot); slot = stdstore.target(address(test)).sig(test.map_addr.selector).with_key(address(this)).root(); assertEq(uint256(1), slot); } function test_StorageMapUintFound() public { uint256 slot = stdstore.target(address(test)).sig(test.map_uint.selector).with_key(100).find(); assertEq(uint256(keccak256(abi.encode(100, uint256(2)))), slot); } function test_StorageCheckedWriteMapUint() public { stdstore.target(address(test)).sig(test.map_uint.selector).with_key(100).checked_write(100); assertEq(100, test.map_uint(100)); } function test_StorageCheckedWriteMapAddr() public { stdstore.target(address(test)).sig(test.map_addr.selector).with_key(address(this)).checked_write(100); assertEq(100, test.map_addr(address(this))); } function test_StorageCheckedWriteMapBool() public { stdstore.target(address(test)).sig(test.map_bool.selector).with_key(address(this)).checked_write(true); assertTrue(test.map_bool(address(this))); } function testFail_StorageCheckedWriteMapPacked() public { // expect PackedSlot error but not external call so cant expectRevert stdstore.target(address(test)).sig(test.read_struct_lower.selector).with_key(address(uint160(1337))) .checked_write(100); } function test_StorageCheckedWriteMapPackedSuccess() public { uint256 full = test.map_packed(address(1337)); // keep upper 128, set lower 128 to 1337 full = (full & (uint256((1 << 128) - 1) << 128)) | 1337; stdstore.target(address(test)).sig(test.map_packed.selector).with_key(address(uint160(1337))).checked_write( full ); assertEq(1337, test.read_struct_lower(address(1337))); } function testFail_StorageConst() public { // vm.expectRevert(abi.encodeWithSignature("NotStorage(bytes4)", bytes4(keccak256("const()")))); stdstore.target(address(test)).sig("const()").find(); } function testFail_StorageNativePack() public { stdstore.target(address(test)).sig(test.tA.selector).find(); stdstore.target(address(test)).sig(test.tB.selector).find(); // these both would fail stdstore.target(address(test)).sig(test.tC.selector).find(); stdstore.target(address(test)).sig(test.tD.selector).find(); } function test_StorageReadBytes32() public { bytes32 val = stdstore.target(address(test)).sig(test.tE.selector).read_bytes32(); assertEq(val, hex"1337"); } function test_StorageReadBool_False() public { bool val = stdstore.target(address(test)).sig(test.tB.selector).read_bool(); assertEq(val, false); } function test_StorageReadBool_True() public { bool val = stdstore.target(address(test)).sig(test.tH.selector).read_bool(); assertEq(val, true); } function test_StorageReadBool_Revert() public { vm.expectRevert("stdStorage read_bool(StdStorage): Cannot decode. Make sure you are reading a bool."); this.readNonBoolValue(); } function readNonBoolValue() public { stdstore.target(address(test)).sig(test.tE.selector).read_bool(); } function test_StorageReadAddress() public { address val = stdstore.target(address(test)).sig(test.tF.selector).read_address(); assertEq(val, address(1337)); } function test_StorageReadUint() public { uint256 val = stdstore.target(address(test)).sig(test.exists.selector).read_uint(); assertEq(val, 1); } function test_StorageReadInt() public { int256 val = stdstore.target(address(test)).sig(test.tG.selector).read_int(); assertEq(val, type(int256).min); } } contract StorageTest { uint256 public exists = 1; mapping(address => uint256) public map_addr; mapping(uint256 => uint256) public map_uint; mapping(address => uint256) public map_packed; mapping(address => UnpackedStruct) public map_struct; mapping(address => mapping(address => uint256)) public deep_map; mapping(address => mapping(address => UnpackedStruct)) public deep_map_struct; UnpackedStruct public basic; uint248 public tA; bool public tB; bool public tC = false; uint248 public tD = 1; struct UnpackedStruct { uint256 a; uint256 b; } mapping(address => bool) public map_bool; bytes32 public tE = hex"1337"; address public tF = address(1337); int256 public tG = type(int256).min; bool public tH = true; bytes32 private tI = ~bytes32(hex"1337"); constructor() { basic = UnpackedStruct({a: 1337, b: 1337}); uint256 two = (1 << 128) | 1; map_packed[msg.sender] = two; map_packed[address(uint160(1337))] = 1 << 128; } function read_struct_upper(address who) public view returns (uint256) { return map_packed[who] >> 128; } function read_struct_lower(address who) public view returns (uint256) { return map_packed[who] & ((1 << 128) - 1); } function hidden() public view returns (bytes32 t) { bytes32 slot = keccak256("my.random.var"); /// @solidity memory-safe-assembly assembly { t := sload(slot) } } function const() public pure returns (bytes32 t) { t = bytes32(hex"1337"); } function extra_sload() public view returns (bytes32 t) { // trigger read on slot `tE`, and make a staticcall to make sure compiler doesn't optimize this SLOAD away assembly { pop(staticcall(gas(), sload(tE.slot), 0, 0, 0, 0)) } t = tI; } }