Skip to content

Instantly share code, notes, and snippets.

@0xGorilla
Created September 13, 2022 09:51
Show Gist options
  • Save 0xGorilla/70b3d4bda1eece336119b66f708f255e to your computer and use it in GitHub Desktop.
Save 0xGorilla/70b3d4bda1eece336119b66f708f255e to your computer and use it in GitHub Desktop.
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4 <0.9.0;
library StorageAddress {
/// @notice compute the storage address of any element of a dynamic array, by its index
/// @dev Primitive type agnostic. Elements are supposedly taking one word in storage
/// (for other length, you'd have to split/concat them accordingly).
/// Main use is in conjunction with hevm.store, as the second parameter
/// hevm.store(myContract, offset, valueToWrite)
/// @param slot the storage slot where the array is in the contract
/// @param index the index in the array
/// @return elementAddress the address in storage of myArray[index]
function getArrayOffset(uint256 slot, uint256 index) internal pure returns (bytes32 elementAddress) {
assembly {
mstore(0x0, slot)
elementAddress := add(keccak256(0x0, 32), index)
}
}
/// @notice compute the storage address of a 2 levels nested mapping myMapping[levelOne][levelTwo]
/// @dev primitive type agnostic, limited only by the current overloads
/// (ie currently supports uint=>uint=>x, address=>uint=>x, uint=>address, address=>address)
/// Main use is to use it in conjunction with hevm.store, as the second parameter
/// hevm.store(myContract, offset, valueToWrite).
/// This can be easily extended by adding "layers" of mstore/keccak as needed.
/// @param levelOne the first key of the mapping
/// @param levelTwo the second key of the mapping
/// @param slot the storage slot where the mapping is in the contract
/// @return offset the address in storage of myMapping[levelOne][levelTwo]
function offsetNested(
address levelOne,
address levelTwo,
uint256 slot
) internal pure returns (bytes32 offset) {
return offsetNested(addressToBytes32(levelOne), addressToBytes32(levelTwo), slot);
}
function offsetNested(
uint256 levelOne,
address levelTwo,
uint256 slot
) internal pure returns (bytes32 offset) {
return offsetNested(bytes32(levelOne), addressToBytes32(levelTwo), slot);
}
function offsetNested(
address levelOne,
uint256 levelTwo,
uint256 slot
) internal pure returns (bytes32 offset) {
return offsetNested(addressToBytes32(levelOne), bytes32(levelTwo), slot);
}
function offsetNested(
uint256 levelOne,
uint256 levelTwo,
uint256 slot
) internal pure returns (bytes32 offset) {
return offsetNested(bytes32(levelOne), bytes32(levelTwo), slot);
}
// ----- private -----
function addressToBytes32(address addressIn) private pure returns (bytes32 converted) {
assembly {
mstore(0x0, addressIn)
converted := mload(0x0)
}
}
function offsetNested(
bytes32 levelOne,
bytes32 levelTwo,
uint256 slot
) private pure returns (bytes32 offset) {
assembly {
mstore(0x0, levelOne)
mstore(0x20, slot)
let hash_first_level := keccak256(0x0, 64)
mstore(0x0, levelTwo)
mstore(0x20, hash_first_level)
offset := keccak256(0x0, 64)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment