Code to reproduce
I think I've handled the initializer calls for my upgradeable ERC20 token contract correctly.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import {Upgrades} from "@openzeppelin/foundry-upgrades/Upgrades.sol";
import {Test} from "forge-std/Test.sol";
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
Environment
- OS:
- macOS 15.3.1 (sequoia)
- Apple M1
- Forge
forge Version: 1.0.0-stable Commit SHA: e144b82070619b6e10485c38734b4d4d45aebe04 Build Timestamp: 2025-02-13T20:02:34.979686000Z (1739476954) Build Profile: maxperf
- OZ
- openzeppelin-contracts-upgradeable:
v5.3.0
- openzeppelin-foundry-upgrades:
v0.4.0
- openzeppelin-contracts-upgradeable:
Am I doing something wrong? Thank you for your help