F-2026-0008·coupled-state-overwrite

Forced feeCollector update during ownership transfer leads to potential fee misdirection

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

transferOwnership and rotateToPublicKey silently overwrite feeCollector with the new owner address, redirecting protocol fees away from the configured treasury until the new owner manually restores it.

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

Description

Both transferOwnership() and rotateToPublicKey() automatically overwrite feeCollector with the new owner address as a side effect of transferring ownership. There is no way to transfer ownership without also redirecting all protocol fees.

Vulnerable Scenario

  1. The owner has feeCollector set to a dedicated fee-receiving address (e.g., a multisig or treasury).
  2. The owner calls rotateToPublicKey() to rotate to the next key in the pre-rotation chain.
  3. feeCollector is silently overwritten to latest.prerotatedKeyHash at line 754, before transferOwnership() overwrites it again at line 203.
  4. All protocol fees now flow to the new owner address instead of the dedicated treasury.
  5. The new owner must immediately call setFeeCollector() to restore the intended fee destination, creating a race window where fees can be misdirected.
03Section · Impact

Impact

Protocol fees are silently redirected to the new owner address on every ownership transfer or key rotation. If the new owner is not monitoring for this, fees accumulate in an unintended address. During the window between ownership transfer and a corrective setFeeCollector() call, any wrap/unwrap operations will send fees to the wrong destination.

04Section · Recommendation

Recommendation

Decouple the feeCollector update from ownership transfer. Remove feeCollector = newOwner from transferOwnership() and feeCollector = latest.prerotatedKeyHash from rotateToPublicKey(). If automatic fee redirection is desired, make it opt-in via a separate parameter or a dedicated function call after the transfer.

05Section · Resolution

Resolution

YadaCoin, Confirmed. Removed the feeCollector state variable entirely. Fees now flow directly to owner(), eliminating the forced overwrite issue.

Zealynx, Fixed. Confirmed the removal of feeCollector resolves the fee misdirection vector. Fresh deployment strategy addresses any storage layout concerns from the refactor.

Status
Fixed
F-2026-0008

oog
zealynx

Smart Contract Security Digest

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

© 2026 Zealynx