F-2025-0009·upgradeable-init

Missing parent contract initialization in upgradeable contract

Fixednfterc721erc20
TL;DR

The contract uses non-upgradeable parent contracts (ReentrancyGuard) instead of their upgradeable versions and skips parent initializer calls, leaving _status in the wrong storage context.

Severity
LOW
Impact
MEDIUM
Likelihood
LOW
Method
MManual review
CAT.
Complexity
MEDIUM
Exploitability
LOW
02Section · Description

Description

The contract is currently implemented as a non-upgradeable contract: it uses non-upgradeable parent contracts (ReentrancyGuard) instead of their upgradeable versions (ReentrancyGuardUpgradeable) and fails to initialize parent contracts properly. This creates two issues:

  1. Wrong contract type: Uses ReentrancyGuard which has a constructor that sets _status = _NOT_ENTERED in implementation contract storage, not proxy storage.
  2. Missing parent initialization: The initialize() function does not call parent initializers.

Current problematic code:

solidity
// Wrong import: should be ReentrancyGuardUpgradeable
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
// Using non-upgradeable contract
contract KnowledgeMarket is Initializable, ERC4908, ReentrancyGuard {
// Missing parent initializations
function initialize(address payable _treasury, uint32 _fee) public initializer {
if (_treasury == address(0)) revert ZeroAddress();
if (_fee > 10000) revert InvalidFee();
platformTreasury = _treasury;
platformFeePercent = _fee;
// Missing: __ERC721_init(), __ReentrancyGuard_init()
}
  • ReentrancyGuard constructor sets _status = 1 in implementation contract storage.
  • In a proxy pattern, this initialization happens in the wrong storage context.
  • ReentrancyGuardUpgradeable requires explicit __ReentrancyGuard_init() call.
  • Without proper initialization, _status defaults to 0, but nonReentrant modifier checks _status != _ENTERED (2), so it still works by accident.
03Section · Impact

Impact

Inconsistent storage layout in upgradeable proxy patterns; latent reentrancy guard semantics depend on default values rather than explicit initialization, complicating future upgrades.

04Section · Recommendation

Recommendation

  1. Change imports to upgradeable versions (ReentrancyGuardUpgradeable).
  2. Update contract inheritance.
  3. Initialize all parent contracts properly in initialize:
solidity
function initialize(address payable _treasury, uint32 _fee) public initializer {
__ERC721_init("Knowledge Market Access", "KMA");
__ERC721Enumerable_init();
__ReentrancyGuard_init();
if (_treasury == address(0)) revert ZeroAddress();
if (_fee > 10000) revert InvalidFee();
platformTreasury = _treasury;
platformFeePercent = _fee;
}
05Section · Resolution

Resolution

Ipal Network: Confirmed. We agreed with the recommendation.

Zealynx: Not Fixed: The KnowledgeMarket.sol contract still lacks proper parent contract initialization. It should use ReentrancyGuardUpgradeable and call __ReentrancyGuard_init() in the initialize function.

UPDATE: Fixed.

Status
Fixed
F-2025-0009

oog
zealynx

Smart Contract Security Digest

Monthly exploit breakdowns, audit checklists, and DeFi security research — straight to your inbox

© 2026 Zealynx