F-2026-0004·msg-value-reuse

Transaction-level msg.value reused across multiple native wrap permits enables minting unbacked wrapped tokens

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

The msg.value >= recipient.amount check is evaluated per-permit instead of per-transaction, so the same native value satisfies multiple wrap permits in one call and the bridge mints unbacked wrapped tokens.

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

Description

When wrapping native token, the check require(msg.value >= recipient.amount) at line 370 is evaluated per recipient and per permit. Since msg.value is a transaction-level constant that never decreases, the same msg.value can pass the check for multiple wrap operations, minting more wrapped tokens than native tokens received.

Root Cause

solidity
// Bridge.sol _handleWrap line 370
require(msg.value >= recipient.amount, "Insufficient native token sent");

This check passes independently for each wrap recipient/permit. Additionally, totalTransferred is reset to 0 at the start of each permit (line 303), so cross-permit accounting is absent.

Vulnerable Scenario

  1. Attacker sends msg.value = 100 with two native permits, each containing one wrap recipient for 100 native tokens.
  2. Permit 1: require(msg.value(100) >= 100) passes. Bridge mints ~100 wrapped tokens. totalTransferred = 100 == permit.amount. Check passes.
  3. Permit 2: require(msg.value(100) >= 100) passes again. Bridge mints another ~100 wrapped tokens. totalTransferred = 100 == permit.amount. Check passes.
  4. Result: Bridge received 100 native tokens, minted ~200 wrapped tokens. Solvency broken by 100 native tokens.
03Section · Impact

Impact

  • Native token solvency invariant violation: address(bridge).balance >= wrappedNativeToken.totalSupply() no longer holds.
  • Attacker can unwrap their excess wrapped tokens to steal other users' escrowed native tokens.
  • Repeatable for arbitrary (N permits × msg.value).
04Section · Recommendation

Recommendation

Track cumulative native tokens usage across ALL permits in a single transaction. Replace per-recipient msg.value check with a running counter:

solidity
uint256 nativeTokenUsed = 0;
nativeTokenUsed += recipient.amount;
// In _handleWrap for native tokens:
require(msg.value >= nativeTokenUsed, "Insufficient native tokens");
05Section · Resolution

Resolution

YadaCoin, Confirmed.

Zealynx, Fixed.

Status
Fixed
F-2026-0004

oog
zealynx

Smart Contract Security Digest

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

© 2026 Zealynx