From OpenZeppelin's docs, there is the following section:
Initializer functions are not linearized by the compiler like constructors. Because of this, each __{ContractName}_init function embeds the linearized calls to all parent initializers. As a consequence, calling two of these init functions can potentially initialize the same contract twice.
The function __{ContractName}_init_unchained found in every contract is the initializer function minus the calls to parent initializers, and can be used to avoid the double initialization problem, but doing this manually is not recommended. We hope to be able to implement safety checks for this in future versions of the Upgrades Plugins.
This basically means that multiple inheritance doesn't play nice with initializers.
Generally, a _<CONTRACT_NAME>_init_unchained
method defines the initialization of the derived contract while excluding initializing the parents that it derives from (you'd have to explicitly initialize those parents in your contract), while a _<CONTRACT_NAME>_init
method defines the initialization of the derived contract along with all the parents that it derives from. In your case, the unchained method happens to require no parent initialization .
In some cases with multiple inheritance, the derived contract that you define could have an collision where two its parent contracts share a parent contract that they both derive from. In such a case, the _<CONTRACT_NAME>_init
call on the shared parent that exists in both initializers of the collided contracts would result in attempting to initialize the shared parent contract twice, which causes an error. OpenZeppelin's upgradable contracts have been improving a lot in the past few years in order to minimize the possibility of such collisions. Consider using the Contracts Wizards and ticking the "upgradeability" check box to showcase writing smart contracts with multiple inheritance without being faced with such issues.