I have deployed an upgradable contract shown below.
I recently realized that I had missed adding the __ReentrancyGuard_init();
statement to the initialize function.
I had also added _disableInitializers()
to the constructor.
So to initialize __ReentrancyGuard_init();
, I added a new function initializeV2()
with a reinitialize modifier and tried to deploy a new implementation, but it wasn't enough. Thus added __ReentrancyGuard_init();
to the old initialize
function and tried to upgrade.
Surprisingly the upgrade worked and I called the initializeV2()
function as well.
As per my understanding, initializeV2()
should not have been executed as I have added _disableInitializers()
to the constructor in the initial code.
Can someone, help me understand how this was possible?
Check the logs here, the "Initialized" event has emitted twice.
Initial Code
// SPDX-License-Identifier: MIT
pragma solidity 0.8.27;
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { ERC4626Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol";
import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract LpToken is
ERC4626Upgradeable,
UUPSUpgradeable,
ReentrancyGuardUpgradeable
{
// ========================== Functions ========================== //
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() payable {
_disableInitializers();
}
function initialize(address usdc, string memory _name, string memory _symbol) public initializer {
__UUPSUpgradeable_init();
__ERC4626_init(IERC20(usdc));
__ERC20_init(_name, _symbol);
}
function _authorizeUpgrade(address newImplementation) internal override onlyOwner { }
}
Upgraded Code
// SPDX-License-Identifier: MIT
pragma solidity 0.8.27;
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { ERC4626Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol";
import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract LpToken is
ERC4626Upgradeable,
UUPSUpgradeable,
ReentrancyGuardUpgradeable
{
// ========================== Functions ========================== //
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() payable {
_disableInitializers();
}
function initialize(address usdc, string memory _name, string memory _symbol) public initializer {
__UUPSUpgradeable_init();
__ReentrancyGuard_init(); // $$$$$$$$ THIS LINE $$$$$$$$ //
__ERC4626_init(IERC20(usdc));
__ERC20_init(_name, _symbol);
}
function _authorizeUpgrade(address newImplementation) internal override onlyOwner { }
// $$$$$$$$ THIS NEW FUNCTION $$$$$$$$ //
/// @notice Second initializer to add ReentrancyGuard
/// @custom:oz-upgrades-unsafe-allow constructor
function initializeV2() public reinitializer(2) {
__ReentrancyGuard_init();
}
}