Skip to content

Potential false positive "Missing initializer calls for one or more parent contracts" #1150

@heueristik

Description

@heueristik

I am cross-linking an issue I've already raised in OpenZeppelin/openzeppelin-foundry-upgrades#104:

I think I've handled the initializer calls for my upgradeable ERC20 token contract correctly.

contract Token is ERC20Upgradeable, 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");
        __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.
    }
}

However, deploying it with Upgrades.deployUUPSProxy in the test below

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);
    }
}

fails with the following error

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

    src/Token.sol:29: Missing initializer calls for one or more parent contracts: `ERC20Upgradeable`
        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
forge clean && forge build && forge test

Here is the workflow in which the test from above fails: https://github.com/heueristik/token/actions/runs/14624117496/job/41031361022#step:7:10

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