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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions