F-2026-0002·wrong-variable-used

confirmingPublicKeyHash derived from wrong public key causes all key pair registrations to revert after inception

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

validateTransaction computes confirmingPublicKeyHash from unconfirmed.publicKey instead of confirming.publicKey, so the subsequent equality check is always false and every key pair registration reverts after the inception key.

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

Description

NOTE: this bug is fixed in KeyLogRegistryUpgrade.sol, and KeyLogRegistry.sol should not be used in production!

In KeyLogRegistry.sol, validateTransaction computes the confirming key's hash from the wrong public key:

solidity
// KeyLogRegistry.sol line 183-184
address unconfirmedPublicKeyHash =
getAddressFromPublicKey(unconfirmed.publicKey);
address confirmingPublicKeyHash =
getAddressFromPublicKey(unconfirmed.publicKey); // BUG: should be confirming.publicKey

Both hashes are derived from unconfirmed.publicKey. The subsequent check at line 217:

solidity
require(getAddressFromPublicKey(confirming.publicKey) == confirmingPublicKeyHash, "Invalid confirmingPublicKey");

requires hash(confirming.publicKey) == hash(unconfirmed.publicKey), which is always false for valid key pairs.

Vulnerable Scenario

  1. Deploy KeyLogRegistry behind a UUPS proxy.
  2. Register key A via registerKeyLog (inception), succeeds because isPair=false returns at line 214 before the buggy check at line 217 is reached.
  3. Attempt key rotation by calling registerKeyLogPair(keyB, keyC) where:
    • Key B is the next rotation key (prevPublicKeyHash = hash(A))
    • Key C is the confirming key (prevPublicKeyHash = hash(B))
  4. validateTransaction computes:
    • unconfirmedPublicKeyHash = hash(B) (line 183, correct)
    • confirmingPublicKeyHash = hash(B) (line 184, BUG, should be hash(C))
  5. Line 217 evaluates: require(hash(C) == hash(B)), always false, reverts with "Invalid confirmingPublicKey".
  6. Key chain is permanently stuck at inception. No rotation, wrap, or unwrap is possible.
03Section · Impact

Impact

  • All registerKeyLogPair calls revert with "Invalid confirmingPublicKey".
  • No key rotation pairs can be registered, key chains are permanently stuck after inception.
  • All wrap/unwrap operations that require pair registration are blocked.
  • Bridge is non-functional for any operation requiring key pair rotation.
04Section · Recommendation

Recommendation

Fixed in KeyLogRegistryUpgrade.sol line 184:

solidity
address confirmingPublicKeyHash =
getAddressFromPublicKey(confirming.publicKey); // FIXED
05Section · Resolution

Resolution

YadaCoin, Confirmed.

Zealynx, Fixed.

Status
Fixed
F-2026-0002

oog
zealynx

Smart Contract Security Digest

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

© 2026 Zealynx