F-2026-0015·uninitialized-implementation

Missing _disableInitializers() in constructor allows initialization of implementation contracts

Fixedbridgecross-chainkey-registrygithub.com/pdxwebdev/yadakeyeventwallet
TL;DR

Bridge, WrappedToken, MockERC20, and WrappedTokenFactory have no constructor calling _disableInitializers(), violating the OpenZeppelin defense-in-depth pattern for UUPS implementations.

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

Description

KeyLogRegistry correctly prevents initialization of the implementation contract by calling _disableInitializers() in its constructor:

solidity
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}

However, four other UUPS upgradeable contracts, Bridge, WrappedToken, MockERC20, and WrappedTokenFactory, have no constructor and therefore leave their implementation contracts initializable.

This means anyone can call initialize() directly on the bare implementation contract (not the proxy), setting arbitrary values for bridge, owner, and other initialization parameters. While this does not affect existing proxies (each proxy has its own storage), it violates the defense-in-depth pattern that OpenZeppelin explicitly recommends for all UUPS contracts.

03Section · Recommendation

Recommendation

Add the standard constructor to all UUPS upgradeable contracts:

solidity
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
04Section · Resolution

Resolution

YadaCoin, Confirmed.

Zealynx, Fixed.

Status
Fixed
F-2026-0015

oog
zealynx

Smart Contract Security Digest

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

© 2026 Zealynx