How to use a struct in an upgradable contract

Is it possible to upgrade a struct within an upgradable contract without altering the storage slots of variables stored after the struct? :thinking:

For example:

contract MyContract{
    uint256 a;
    struct Random {
        uint256 b;
        address c;
        bytes d;
    }
    uint256 e;
}

Would it be possible to upgrade the contract to add an address f inside the struct without altering e?

I believe this would work if the struct was listed last in MyContract, but any subsequent upgrade after adding a non-struct variable would then make the struct un-upgradable. Is this statement correct?

Is it not advised to use a struct in an upgradable contract?

Edit:

The example listed in the following Zeppelin blog post does in fact use a struct, but it appears as though this particular struct is not meant to be upgraded at any point.

5 Likes

The answer is it depends where you are actually using your struct.

It’s always a good practice to write tests to check the upgrade process itself.

For laying out the variables in storage, Solidity will first linearize the contract hierarchy, and then start laying out the variables starting from the most base contract, onto the most derived one.

Have a look at the following for more details:

2 Likes

Housekeeping: moved to #support:zeppelinos category and tagging my reply as the solution.

to summarize would it be correct to say that the following patterns are probably upgrade safe?

  1. using in mappings
    struct X
    {
    …
    }
    mapping(uint => X) safemap

  2. saving extra space to extend struct
    struct X{
    …
    uint __reserved[10]
    }
    X public safevar;

1 Like

Hi @sirpy,

Yes, using a struct in a mapping should be upgrade safe.

Reserving space to extend a struct being used in a state variable may be safe, though you should use the same type as you reserved, as there could be issues with packing and alignment.

The latest release of the Upgrades Plugins for Hardhat and Truffle supports structs.

Adding a struct member would produce an error in the case described by @shanefontaine, but it will be allowed in a mapping like the one @sirpy asked about.

2 Likes