I want to establish a pattern that ensures no storage slot collisions between current and future (compliant) versions of a contract behind a transparent proxy. In particular, contracts that are meant to be inherited.
I'm aware of the reserved slots pattern:
contract InheritMeImSafe {
address public foo;
bool public bar;
bytes32[50] reservation; // reserve 50 slots for future variables
}
I'm also aware of the storage layout of structs and mappings but I'm struggling to find confirmation of how I think about mapped structs.
Consider:
contract InheritMe {
struct ThingStruct {
bool foo;
uint bar;
}
mapping(address => ThingStruct) public things;
...
Given that it would be inherited by an upgradeable contract, I want to be sure that the struct is extendable without causing storage collisions (provided one declares new variables at the end, of course). I also don't want superfluous code for no reason. I think it is not necessary to reserve slots in the struct. Is it?
We could, for example:
struct ThingStruct {
bool foo;
uint bar;
bytes32[50]; // reserve 50 slots for future struct members. Needed?
}
I have not found a lot of documentation about the layout of structs, mappings but nothing to confirm my interpretation of how they work together in mapped structs. I don't want to be wrong about this.
Declare a reservation or not?
Thanks! :-)