Adding an inherited base contract to a live UUPS proxy shifts storage slots: use the Upgradeable variant
Scenario: a deployed UUPS upgradeable ERC-20 declared as
contract MyToken is ERC20BurnableUpgradeable, OwnableUpgradeable, UUPSUpgradeable, PausableUpgradeable
with its own mappings and a uint256[50] __gap. The upgrade adds ERC20Votes to the inheritance list.
This corrupts state. Solidity lays out storage in C3-linearized order: all inherited contracts' variables come first, in linearization order, then the contract's own. Inserting a new base that declares state variables pushes MyToken's variables to higher slots, so after the upgrade totalSpent, allocations, isWhitelisted, deploymentTime and veTokenMigrator read whatever ERC20Votes now occupies. The __gap at the end does not help — it reserves room after your variables, not before them. There is no revert; reads silently return wrong values.
Two fixes:
1. Preferred: inherit the upgradeable variant, @openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20VotesUpgradeable.sol. The upgradeable library places each module's state in its own namespaced/offset location precisely so adding one does not disturb sibling layouts.
2. If a non-upgradeable base is unavoidable, pin your own variables' offsets by moving them into an abstract contract and placing it *before* the new base in the inheritance list:
abstract contract MyTokenStorage { /* existing vars, then uint256[50] private __gap; */ } contract MyToken is ERC20BurnableUpgradeable, OwnableUpgradeable, UUPSUpgradeable, PausableUpgradeable, MyTokenStorage, ERC20Votes { ... }
Since MyTokenStorage linearizes ahead of ERC20Votes, your slots keep their original indices and the new base consumes the reserved gap region instead.
Always verify with a layout diff — forge inspect <C> storage-layout or the OpenZeppelin Upgrades plugin's validation — rather than reasoning about linearization by hand.
Source: adapted from a Stack Exchange answer by tenbits (question by Barney Chambers), https://ethereum.stackexchange.com/questions/159437/will-upgrading-a-smart-contract-to-inherit-from-a-new-base-class-cause-potential — CC BY-SA 4.0