Skip to content

Potential false positive Missing initializer calls for one or more parent contracts for ERC20PermitUpgradeable #1175

@heueristik

Description

@heueristik

Context

Previously, I've filed an issue that got resolved by following the suggestion by @ericglau (see the test passing).

Adding ERC20BurnableUpgradeable worked similarly (see the test passing).

Problem

However, after adding ERC20PermitUpgradeable

contract Token is ERC20Upgradeable, ERC20PermitUpgradeable, ERC20BurnableUpgradeable, UUPSUpgradeable {
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    function initialize(address initialOwner) external initializer {
        __Token_init(initialOwner);
    }

    function __Token_init(address initialOwner) internal onlyInitializing {
        __Context_init_unchained();
        __ERC20_init_unchained("Token", "TOK");
        __ERC20Permit_init_unchained("Token");
        __EIP712_init_unchained({name: "Token", version: "1"});
        __Nonces_init_unchained();
        __ERC20Burnable_init_unchained();
        __UUPSUpgradeable_init_unchained();

        __Token_init_unchained(initialOwner);
    }

    function __Token_init_unchained(address initialOwner) internal onlyInitializing {
        _mint(initialOwner, 100);
    }

    function _authorizeUpgrade(address newImplementation) internal pure override {
        {
            newImplementation;
        }
        // NOTE: In this MWE, anyone can upgrade. DO NOT USE IN PRODUCTION.
    }
}

the test

contract TokenTest is Test {
    address internal _defaultSender;
    Token internal _tokenProxy;

    function setUp() public {
        (, _defaultSender,) = vm.readCallers();

        // Deploy proxy and mint tokens for the `_defaultSender`.
        vm.prank(_defaultSender);
        _tokenProxy = Token(
            Upgrades.deployUUPSProxy({
                contractName: "Token.sol:Token",
                initializerData: abi.encodeCall(Token.initialize, _defaultSender)
            })
        );
    }

    function test_balance() public view {
        assertEq(_tokenProxy.balanceOf(_defaultSender), 100);
    }
}

results again in an error (see the test failing)

[FAIL: Upgrade safety validation failed:
✘  src/Token.sol:Token

      src/Token.sol:23: Missing initializer calls for one or more parent contracts: `ERC20PermitUpgradeable`
          Call the parent initializers in your initializer function
          https://zpl.in/upgrades/error-001
      
      src/Token.sol:27: Missing initializer calls for one or more parent contracts: `ERC20PermitUpgradeable`
          Call the parent initializers in your initializer function
          https://zpl.in/upgrades/error-001

FAILED] setUp() (gas: 0)

I've provided a minimal example to reproduce the error in this repo https://github.com/heueristik/token.
To reproduce the problem, run

git clone --recursive https://github.com/heueristik/token
cd token
npm install @openzeppelin/upgrades-core@latest
forge clean && forge build && forge test

What parent initializer am I missing?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions