Platform: Code4rena
Start Date: 05/05/2022
Pot Size: $125,000 DAI
Total HM: 17
Participants: 62
Period: 14 days
Judge: leastwood
Total Solo HM: 15
Id: 120
League: ETH
Rank: 9/62
Findings: 1
Award: $6,389.44
🌟 Selected for report: 1
🚀 Solo Findings: 1
🌟 Selected for report: 0x1337
6389.4401 DAI - $6,389.44
https://github.com/code-423n4/2022-05-alchemix/blob/de65c34c7b6e4e94662bf508e214dcbf327984f4/contracts-full/AlchemicTokenV2Base.sol#L20 https://github.com/code-423n4/2022-05-alchemix/blob/de65c34c7b6e4e94662bf508e214dcbf327984f4/contracts-full/CrossChainCanonicalBase.sol#L12 https://github.com/code-423n4/2022-05-alchemix/blob/de65c34c7b6e4e94662bf508e214dcbf327984f4/contracts-full/TransmuterV2.sol#L26 https://github.com/code-423n4/2022-05-alchemix/blob/de65c34c7b6e4e94662bf508e214dcbf327984f4/contracts-full/CrossChainCanonicalAlchemicTokenV2.sol#L7
For upgradeable contracts, there must be storage gap to "allow developers to freely add new state variables in the future without compromising the storage compatibility with existing deployments" (quote OpenZeppelin). Otherwise it may be very difficult to write new implementation code. Without storage gap, the variable in child contract might be overwritten by the upgraded base contract if new variables are added to the base contract. This could have unintended and very serious consequences to the child contracts, potentially causing loss of user fund or cause the contract to malfunction completely.
Refer to the bottom part of this article: https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable
Several contracts are intended to be upgradeable contracts in the code base, including
However, none of these contracts contain storage gap. The storage gap is essential for upgradeable contract because "It allows us to freely add new state variables in the future without compromising the storage compatibility with existing deployments". Refer to the bottom part of this article:
https://docs.openzeppelin.com/contracts/3.x/upgradeable
As an example, both the AlchemicTokenV2Base
and the CrossChainCanonicalBase
are intended to act as the base contracts in the project. If the contract inheriting the base contract contains additional variable, then the base contract cannot be upgraded to include any additional variable, because it would overwrite the variable declared in its child contract. This greatly limits contract upgradeability.
Manual review
Recommend adding appropriate storage gap at the end of upgradeable contracts such as the below. Please reference OpenZeppelin upgradeable contract templates.
uint256[50] private __gap;
#0 - 0xfoobar
2022-05-30T06:49:12Z
Sponsor confirmed
#1 - 0xleastwood
2022-06-12T21:05:22Z
Agree with warden and severity. Storage gaps are essential wherever inheritance is used by an upgradeable contract.
#2 - 0xleastwood
2022-07-25T20:01:45Z
I believe this to be a valid finding with the correct severity because:
For these reasons, I think medium severity to be justified.
#3 - 0xleastwood
2022-07-25T20:26:46Z
In this issue, context is important. This would have been downgraded to QA in two scenarios:
In the case of Alchemix, CrossChainCanonicalAlchemicTokenV2.sol
inherits CrossChainCanonicalBase.sol
and AlchemicTokenV2Base.sol
which both contain storage variables. Any upgrade to CrossChainCanonicalBase.sol
would overwrite storage in AlchemicTokenV2Base.sol
.