Upgrading base and child contracts

Sir.
If A contract inherits from B contract and both contacts needs to be upgraded. Is there any rule needed to abide by to avoid A contract’s variable-layout is broken by B contract’s new variable-layout?
Thanks.

1 Like

Hi @zhengger,

Welcome to the community :wave:

Yes there is. See the following from the documentation.

You also cannot add new variables to base contracts, if the child has any variables of its own.

A workaround for this is to declare unused variables on base contracts that you may want to extend in the future, as a means of “reserving” those slots. Note that this trick does not involve increased gas usage.
From: https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#modifying-your-contracts

OpenZeppelin Contracts Upgrades uses storage gaps for this purpose, see: https://docs.openzeppelin.com/contracts/3.x/upgradeable#storage_gaps

Feel free to ask all the questions that you need.

1 Like

Thank you, Sir!

I have two other questions about multiple inheritances.
I have a contract O which n inherits Ownable contract:

    contract O is Ownable {
        constructor(){
            _;
        };
}

Now I want to upgrade O to be upgradable and I made some changes as described below:

    contract O is Initializable, OwnableUpgradeable {
        function initializeO ()  initializer  {
            __Ownable_init(); 
            _;
        }
    }

// Question 1: Should I use __Ownable_init__unchained() instead of __Ownable_init() and why;
In the article you recommended it(unchained) is “used to avoid the double initialization problem” so whenever possible it’s better to use __{ContractName}_init_unchained. Did I catch it?

// Question 2: Can I omit Initializable because OwnableUpgradeable has inherited Initializable already.

Thanks!

1 Like

Hi @zhengger,

It is recommended to use __{ContractName}_init. You only need to use __{ContractName}_init_unchained where you have multiple inheritance.
See: https://docs.openzeppelin.com/contracts/3.x/upgradeable#multiple-inheritance

For clarity you should inherit Initializable as your initialize function should use the initializer modifier to ensure it is only called once.

1 Like

Thank you so much, Sir!
I have a custom struct in O contract like this:

struct UserInfo {
	bool toBePaid;
	uint256 depositAmount;
    uint256 mintedAmount;
}

And according to this link, ## Why can’t I use custom types like structs and enums?, it said that the plugins do not support such contracts. Could you teach me how to use custom types inside upgradable contracts. Here is how to use UserInfo struct in the O contract safely.
Much thanks again.

1 Like

Hi @zhengger,

It depends how you are using your struct. If you are using it with a mapping then this should be upgrade safe. If you are using it in a state variable, then you can only add to the last field in the struct if the state variable is the last state variable.

The Upgrades Plugins don’t currently support checking enums and structs for upgrade safety, see: https://docs.openzeppelin.com/upgrades-plugins/1.x/faq#why-cant-i-use-custom-types

For checking for upgrade safety I suggest writing higher level tests that check all the state before and after an upgrade, see the proxy tests for an example: OpenZeppelin Upgrades: Step by Step Tutorial for Truffle

1 Like

Thank you, Sir!

I will write test script to check the security as you said.
This is the way I want to use it in my contract O:

contract O {

	struct UserInfo {
		bool toBePaid;
		uint256 depositAmount;
        uint256 mintedAmount;
	}
	mapping(address => UserInfo) userInfoMap;
        }

Thanks again!

1 Like