Bad storage gap resize when adding inherited contract

Hey, I'm having a similar issue. This time it's slightly more complex, but appears to be directly related. When I add a new variable in my V2 contract it works without issue.

contract V1 is ERC721Upgradeable {
    uint256[50] private __gap;
}

contract V2 is ERC721Upgradeable {
    uint256 public someNumber;
    uint256[49] private __gap;
}

When I add ERC721URIStorageUpgradeable into the same upgrade

contract V1 is ERC721Upgradeable {
    uint256[50] private __gap;
}

contract V2 is ERC721Upgradeable, ERC721URIStorageUpgradeable {
    uint256 public someNumber;
    uint256[49] private __gap;
}

I receive the error:

Error: New storage layout is incompatible

@openzeppelin\contracts-upgradeable\token\ERC721\extensions\ERC721URIStorageUpgradeable.sol:74: Upgraded `__gap` to an incompatible type
  - Bad storage gap resize from 48 to 49
    Size cannot increase
      at assertStorageUpgradeSafe (node_modules\@openzeppelin\upgrades-core\src\storage\index.ts:35:11)
      at validateImpl (node_modules\@openzeppelin\hardhat-upgrades\src\utils\validate-impl.ts:42:31)
      at async deployBeaconImpl (node_modules\@openzeppelin\hardhat-upgrades\src\utils\deploy-impl.ts:84:3)
      at async Proxy.upgradeBeacon (node_modules\@openzeppelin\hardhat-upgrades\src\upgrade-beacon.ts:21:32)
      at async Context.<anonymous> (test\MintClipUpgradeable\suites\V2\clone.test.ts:20:5)

I tried a version where I don't change the storage in my contracts

contract V1 ERC721Upgradeable{
    uint256[50] private __gap;
}

contract V2 is ERC721Upgradeable, ERC721URIStorageUpgradeable {
    uint256[50] private __gap;
}

It worked fine. Is this simply a case where I should do two upgrades?

Like this:

contract V1 is ERC721Upgradeable {
    uint256[50] private __gap;
}

contract V2 is ERC721Upgradeable {
    uint256 public someNumber;
    uint256[49] private __gap;
}

contract V3 is ERC721Upgradeable, ERC721URIStorageUpgradeable {
    uint256 public someNumber;
    uint256[49] private __gap;
}

I'm not sure why it's throwing an error when I try to include the ERC721URIStorageUpgradeable in the same upgrade. Any help would be appreciated. Thanks!

1 Like

Adding the ERC721URIStorageUpgradeable inheritance actually uses up 50 more slots, because ERC721URIStorageUpgradeable itself has an additional variable and a 49 slot gap.

Your proposed V1 to V2 to V3 would not work, because ERC721URIStorageUpgradeable's variables would conflict with V2's someNumber.

You could define V1 to V2 to V3 as follows and upgrade them in sequence, as long as there is no child contract currently inheriting this because V3's new variables exist past the original storage slots.

contract V1 is ERC721Upgradeable {
    uint256[50] private __gap;

    // functions
}
contract V2 is ERC721Upgradeable, ERC721URIStorageUpgradeable {
    // ERC721URIStorageUpgradeable used up the 50 slot gap

    // functions
}
contract V3 is ERC721Upgradeable, ERC721URIStorageUpgradeable {
    // these are new variables added at the end of storage layout
    uint256 abc;
    uint256[49] private __gap;

    // functions
}

I've opened issue https://github.com/OpenZeppelin/openzeppelin-upgrades/issues/703 address the scenario of upgrading directly from V1 to V3.

That's really good to know, is it a potential solution to add the contract storage as a separate contract or will there be the same collision?

contract V1Storage {
    uint256[50] private __gap;
}

contract V1 is V1Storage, ERC721Upgradeable {

    // functions
}
contract V2Storage {
    uint256 abc;
    uint256[49] private __gap;
}

contract V2 is V2Storage, ERC721Upgradeable, ERC721URIStorageUpgradeable {

    // functions
}

What you described should also be allowed, but the plugin currently gives an error. I've added this example in the issue.

1 Like