With UUPS Upgradeability, "Explicit type conversion" error when initializing an inherited contract's constructor

Hello,
I am refactoring a contract to make it UUPS upgradeable, when I try to pass the argument of an inherited contract's constructor, I get this error:

TypeError: Explicit type conversion not allowed from "literal_string "AW 2"" to "contract ERC721Permit".

Refactored code:

function initialize(address _template, address _whitelist) initializer public {
        __ERC721_init_unchained("AW 2", "AW-V2");
        ERC721Permit("AW 2");
        __AccessControl_init_unchained();
        __UUPSUpgradeable_init_unchained();
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
    }

Original properly functioning code:

constructor(address _template, address _whitelist)
        ERC721("AW 2", "AW-V2")
        ERC721Permit("AW 2")

ERC721Permit's constructor:

constructor(string memory name) EIP712(name, "1") {}

Appreciate any insight on how to fix this. Thank you!

Hi, a few suggestions:

  1. Use _init rather than _init_unchained for all of the contracts that you inherit. The _init functions will include linearized calls to their parent initializers.
  2. For ERC721Permit, you should also use an initializer rather than a constructor (otherwise, your proxy storage may not be properly initialized when you deploy it). All of the rules for upgradeable contracts would apply.

using _init instead of _init_unchained and using initialize in ERC721Permit, still generates linearization error:

TypeError: Linearization of inheritance graph impossible

ERC721Permit update:

    function initialize(string memory name) initializer public {
        __EIP712_init(name, "1");
        __UUPSUpgradeable_init();
        __AccessControl_init();
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
    }

Logic contract update:

function initialize(address _template, address _whitelist) initializer public {
        __ERC721_init("AW 2", "AW-V2");
         __ERC721Permit_init("AW 2");
        __AccessControl_init();
        __UUPSUpgradeable_init();
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
    }

Try changing the order of your inherited contracts in the contract MyContract is ... section of your logic contract. You can also remove some dependencies from your ERC721Permit if they are not needed, e.g. you probably don't need UUPSUpgradeable in both of ERC721Permit and your logic contract.

it now generates this error:

DeclarationError: Undeclared identifier.
  --> contracts/MyContract.sol:
   |
41 |         __ERC721PermitUpgradeable_init("AW 2");

this is the inherited contracts section (i tried all possibilities for the inheritance order):

abstract contract MyContract is ERC721EnumerableUpgradeable, ERC721PermitUpgradeable, IMyContract {

and the initialize:

function initialize(address _template, address _whitelist) initializer public {
        __ERC721_init("AW 2", "AW-V2");
        __ERC721PermitUpgradeable_init("AW V2");
        __AccessControl_init();
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
    }

Would you be able to share your code or an example that can be used to reproduce the issue?

it is a private repo.

To summarize, if you get a linearization error, refer to the following Solidity doc: https://docs.soliditylang.org/en/v0.8.13/contracts.html#multiple-inheritance-and-linearization. Inheritance uses C3 linearization and you need to ensure that your inheritance lists (in all of your contracts) follow an order that is linearizable.

1 Like