Should __Ownable_init() be called in the constructor of an OwnableUpgradeable implementation contract

i read this article UUPSUpgradeable Vulnerability Post-mortem describing how an attacker can initialize the implementation contract and become the owner, which naively seems harmless, but nevertheless creates a security vulnerability.

however, i don't understand what the conclusion is for the best-practice fix. what if, for all my OwnableUpgradeable implementation contracts, i just add

constructor() initializer {

does this solve the issue?

on a related (or unrelated?) note i made an edit from constructor() to constructor() initializer because I see there was recently a change from

function __Ownable_init() internal initializer


function __Ownable_init() internal onlyInitializing

In the previous case, simply calling __Ownable_init() would be enough to toggle _initialized but now it appears to be necessary to also label the constructor with initializer

Ok, sorry for the repeated notes here. But I now think the best solution is to just write

constructor() initializer {}

at the beginning of the contract, with no logic inside {}. This way, _initialized == true in the storage of the implementation contract, and no one is the owner. This appears to be what is done in the wizard, although the reason wasn't clear to me at first.

You can have a look at this documentation: Writing Upgradeable Contracts - OpenZeppelin Docs

And you can also share your code at here.