For upgradeable contracts, inheriting contracts may introduce new variables. In order to be able to add new variables to the upgradeable contract without causing storage collisions, a storage gap should be added to the upgradeable contract.
If no storage gap is added, when the upgradable contract introduces new variables,
it may override the variables in the inheriting contract.
Storage gaps are a convention for reserving storage slots in a base contract, allowing future versions of that contract to use up those slots without affecting the storage layout of child contracts.
To create a storage gap, declare a fixed-size array in the base contract with an initial number of slots.
This can be an array of uint256 so that each element reserves a 32 byte slot. Use the naming convention __gap so that OpenZeppelin Upgrades will recognize the gap:
contract Base {
uint256 base1;
uint256[49] __gap;
}
contract Child is Base {
uint256 child;
}
Openzeppelin Storage Gaps notification:
Storage Gaps
This makes the storage layouts incompatible, as explained in Writing Upgradeable Contracts.
The size of the __gap array is calculated so that the amount of storage used by a contract
always adds up to the same number (in this case 50 storage slots).
Recommended Mitigation Steps
Consider adding a storage gap at the end of the upgradeable abstract contract
Lines of code
https://github.com/code-423n4/2022-10-thegraph/blob/main/contracts/l2/gateway/L2GraphTokenGateway.sol#L23
Vulnerability details
Impact
L2GraphTokenGateway.sol
GraphTokenUpgradeable.sol
L2GraphToken.sol
BridgeEscrow.sol
For upgradeable contracts, inheriting contracts may introduce new variables. In order to be able to add new variables to the upgradeable contract without causing storage collisions, a storage gap should be added to the upgradeable contract.
If no storage gap is added, when the upgradable contract introduces new variables, it may override the variables in the inheriting contract.
Storage gaps are a convention for reserving storage slots in a base contract, allowing future versions of that contract to use up those slots without affecting the storage layout of child contracts. To create a storage gap, declare a fixed-size array in the base contract with an initial number of slots. This can be an array of uint256 so that each element reserves a 32 byte slot. Use the naming convention __gap so that OpenZeppelin Upgrades will recognize the gap:
Classification for a similar problem: https://code4rena.com/reports/2022-05-alchemix/#m-05-no-storage-gap-for-upgradeable-contract-might-lead-to-storage-slot-collision
Openzeppelin Storage Gaps notification:
Recommended Mitigation Steps
Consider adding a storage gap at the end of the upgradeable abstract contract