Skip to content

Commit d7f4713

Browse files
mds1clouds56Evalir
authored
feat: mapping slot cheats (#449)
* add parent and root for stdstorage * chore: clarify cheatcode names * style: forge fmt * Apply suggestions from code review Co-authored-by: evalir <[email protected]> --------- Co-authored-by: Clouds Flowing <[email protected]> Co-authored-by: evalir <[email protected]>
1 parent 9838df8 commit d7f4713

File tree

3 files changed

+58
-5
lines changed

3 files changed

+58
-5
lines changed

src/StdStorage.sol

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,41 @@ library stdStorageSafe {
168168
return abi.decode(read(self), (int256));
169169
}
170170

171+
function parent(StdStorage storage self) internal returns (uint256, bytes32) {
172+
address who = self._target;
173+
uint256 field_depth = self._depth;
174+
vm.startMappingRecording();
175+
uint256 child = find(self) - field_depth;
176+
(bool found, bytes32 key, bytes32 parent_slot) = vm.getMappingKeyAndParentOf(who, bytes32(child));
177+
if (!found) {
178+
revert(
179+
"stdStorage read_bool(StdStorage): Cannot find parent. Make sure you give a slot and startMappingRecording() has been called."
180+
);
181+
}
182+
return (uint256(parent_slot), key);
183+
}
184+
185+
function root(StdStorage storage self) internal returns (uint256) {
186+
address who = self._target;
187+
uint256 field_depth = self._depth;
188+
vm.startMappingRecording();
189+
uint256 child = find(self) - field_depth;
190+
bool found;
191+
bytes32 root_slot;
192+
bytes32 parent_slot;
193+
(found,, parent_slot) = vm.getMappingKeyAndParentOf(who, bytes32(child));
194+
if (!found) {
195+
revert(
196+
"stdStorage read_bool(StdStorage): Cannot find parent. Make sure you give a slot and startMappingRecording() has been called."
197+
);
198+
}
199+
while (found) {
200+
root_slot = parent_slot;
201+
(found,, parent_slot) = vm.getMappingKeyAndParentOf(who, bytes32(root_slot));
202+
}
203+
return uint256(root_slot);
204+
}
205+
171206
function bytesToBytes32(bytes memory b, uint256 offset) private pure returns (bytes32) {
172207
bytes32 out;
173208

@@ -304,6 +339,14 @@ library stdStorage {
304339
return stdStorageSafe.read_int(self);
305340
}
306341

342+
function parent(StdStorage storage self) internal returns (uint256, bytes32) {
343+
return stdStorageSafe.parent(self);
344+
}
345+
346+
function root(StdStorage storage self) internal returns (uint256) {
347+
return stdStorageSafe.root(self);
348+
}
349+
307350
// Private function so needs to be copied over
308351
function bytesToBytes32(bytes memory b, uint256 offset) private pure returns (bytes32) {
309352
bytes32 out;

src/Vm.sol

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -397,12 +397,13 @@ interface VmSafe {
397397
function startMappingRecording() external;
398398
// Stops recording all map SSTOREs for later retrieval and clears the recorded data.
399399
function stopMappingRecording() external;
400-
// Gets the length of a mapping at a given slot, for a given address.
401-
function getMappingLength(address target, bytes32 slot) external returns (uint256 length);
402-
// Gets the element at index idx of a mapping at a given slot, for a given address.
403-
function getMappingSlotAt(address target, bytes32 slot, uint256 idx) external returns (bytes32 value);
400+
// Gets the number of elements in the mapping at the given slot, for a given address.
401+
function getMappingLength(address target, bytes32 mappingSlot) external returns (uint256 length);
402+
// Gets the elements at index idx of the mapping at the given slot, for a given address. The
403+
// index must be less than the length of the mapping (i.e. the number of keys in the mapping).
404+
function getMappingSlotAt(address target, bytes32 mappingSlot, uint256 idx) external returns (bytes32 value);
404405
// Gets the map key and parent of a mapping at a given slot, for a given address.
405-
function getMappingKeyAndParentOf(address target, bytes32 slot)
406+
function getMappingKeyAndParentOf(address target, bytes32 elementSlot)
406407
external
407408
returns (bool found, bytes32 key, bytes32 parent);
408409
// Writes a breakpoint to jump to in the debugger

test/StdStorage.t.sol

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,15 @@ contract StdStorageTest is Test {
145145
assertEq(uint256(keccak256(abi.encode(address(this), uint256(1)))), slot);
146146
}
147147

148+
function test_StorageMapAddrRoot() public {
149+
(uint256 slot, bytes32 key) =
150+
stdstore.target(address(test)).sig(test.map_addr.selector).with_key(address(this)).parent();
151+
assertEq(address(uint160(uint256(key))), address(this));
152+
assertEq(uint256(1), slot);
153+
slot = stdstore.target(address(test)).sig(test.map_addr.selector).with_key(address(this)).root();
154+
assertEq(uint256(1), slot);
155+
}
156+
148157
function test_StorageMapUintFound() public {
149158
uint256 slot = stdstore.target(address(test)).sig(test.map_uint.selector).with_key(100).find();
150159
assertEq(uint256(keccak256(abi.encode(100, uint256(2)))), slot);

0 commit comments

Comments
 (0)