Commit ff2cd43b authored by Mark Tyneway's avatar Mark Tyneway

contracts-bedrock: update `StorageSetter`

Adds a new setter that takes a list of slots to set. This allows a
single `upgradeToAndCall` to be used with this contract that sets many
storage slots in a single call. This will simplify future upgrades
using the 2 step upgrade model.

Includes fuzz tests for the new functionality. The complexity with
the fuzz testing of this operation is that we cannot pass in duplicate
keys, otherwise it is impossible to make assertions against because
there is no way to hook in to the call. Perhaps if we had a way to see
every state diff then we would be able to make assertions against that.
parent 3166702f
...@@ -34,5 +34,5 @@ ...@@ -34,5 +34,5 @@
"src/universal/OptimismMintableERC20Factory.sol": "0x8d4cbf4cc30a0bb72925b5d2e0386b8f91559f00933a9c7cf3dcc118e34fe61b", "src/universal/OptimismMintableERC20Factory.sol": "0x8d4cbf4cc30a0bb72925b5d2e0386b8f91559f00933a9c7cf3dcc118e34fe61b",
"src/universal/OptimismMintableERC721.sol": "0x4c73bf8474fa7eb091796a4db7e57bc5f26d50a3d1cfcb78d5efa47ced5ced2b", "src/universal/OptimismMintableERC721.sol": "0x4c73bf8474fa7eb091796a4db7e57bc5f26d50a3d1cfcb78d5efa47ced5ced2b",
"src/universal/OptimismMintableERC721Factory.sol": "0x935fd97018b6ef10fa813d9d43ab7a77c80885f7a8d7feb430097645cb2abd2c", "src/universal/OptimismMintableERC721Factory.sol": "0x935fd97018b6ef10fa813d9d43ab7a77c80885f7a8d7feb430097645cb2abd2c",
"src/universal/StorageSetter.sol": "0x6372647d8a67d243bc2fb40d2c4bf5807022d94d52d9423cfed27a7d57918635" "src/universal/StorageSetter.sol": "0x394ec39ef24b44f54549deec6183cace8eea2e5313cde8d5a6e0411a481c5953"
} }
\ No newline at end of file
...@@ -9,9 +9,15 @@ import { Storage } from "src/libraries/Storage.sol"; ...@@ -9,9 +9,15 @@ import { Storage } from "src/libraries/Storage.sol";
/// WARNING: this contract is not safe to be called by untrusted parties. /// WARNING: this contract is not safe to be called by untrusted parties.
/// It is only meant as an intermediate step during upgrades. /// It is only meant as an intermediate step during upgrades.
contract StorageSetter is ISemver { contract StorageSetter is ISemver {
/// @notice Represents a storage slot key value pair.
struct Slot {
bytes32 key;
bytes32 value;
}
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 1.0.0 /// @custom:semver 1.1.0
string public constant version = "1.0.0"; string public constant version = "1.1.0";
/// @notice Stores a bytes32 `_value` at `_slot`. Any storage slots that /// @notice Stores a bytes32 `_value` at `_slot`. Any storage slots that
/// are packed should be set through this interface. /// are packed should be set through this interface.
...@@ -19,6 +25,14 @@ contract StorageSetter is ISemver { ...@@ -19,6 +25,14 @@ contract StorageSetter is ISemver {
Storage.setBytes32(_slot, _value); Storage.setBytes32(_slot, _value);
} }
/// @notice Stores a bytes32 value at each key in `_slots`.
function setBytes32(Slot[] calldata slots) public {
uint256 length = slots.length;
for (uint256 i; i < length; i++) {
Storage.setBytes32(slots[i].key, slots[i].value);
}
}
/// @notice Retrieves a bytes32 value from `_slot`. /// @notice Retrieves a bytes32 value from `_slot`.
function getBytes32(bytes32 _slot) external view returns (bytes32) { function getBytes32(bytes32 _slot) external view returns (bytes32) {
return Storage.getBytes32(_slot); return Storage.getBytes32(_slot);
......
...@@ -13,6 +13,11 @@ import { Test } from "forge-std/Test.sol"; ...@@ -13,6 +13,11 @@ import { Test } from "forge-std/Test.sol";
contract Storage_Roundtrip_Test is Test { contract Storage_Roundtrip_Test is Test {
StorageSetter setter; StorageSetter setter;
/// @notice A set of storage slots to pass to `setBytes32`.
StorageSetter.Slot[] slots;
/// @notice Used to deduplicate slots passed to `setBytes32`.
mapping(bytes32 => bool) keys;
function setUp() external { function setUp() external {
setter = new StorageSetter(); setter = new StorageSetter();
} }
...@@ -34,4 +39,21 @@ contract Storage_Roundtrip_Test is Test { ...@@ -34,4 +39,21 @@ contract Storage_Roundtrip_Test is Test {
assertEq(setter.getBytes32(slot), hash); assertEq(setter.getBytes32(slot), hash);
assertEq(hash, vm.load(address(setter), slot)); assertEq(hash, vm.load(address(setter), slot));
} }
/// @dev All keys must be unique in the input so deduplication is required.
function test_setGetBytes32Multi_succeeds(StorageSetter.Slot[] calldata _slots) external {
for (uint256 i; i < _slots.length; i++) {
if (keys[_slots[i].key]) {
continue;
}
slots.push(_slots[i]);
keys[_slots[i].key] = true;
}
setter.setBytes32(slots);
for (uint256 i; i < slots.length; i++) {
assertEq(setter.getBytes32(slots[i].key), slots[i].value);
assertEq(slots[i].value, vm.load(address(setter), slots[i].key));
}
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment