Status DataClose notification

Overlayer Disclosed Report

Inbound satellite bridge credits revert because _credit() underflows totalBridgedOut

Company
Created date
Mar 27 2026

Target

hidden

Vulnerability Details

OverlayerWrapCore tracks bridged supply with totalBridgedOut. On the source side, _debit() increases this value after tokens are burned for a cross-chain transfer. On the destination side, _credit() always decreases it again.

The problem is that this accounting is applied on every chain, not only on the hub chain where the tracked bridged-out amount actually exists.

In the current logic:

  • _debit() burns/debits OFT tokens on the source chain and then increments totalBridgedOut
  • _credit() mints/credits OFT tokens on the destination chain and then decrements totalBridgedOut

This only works if the same contract instance had previously increased totalBridgedOut.

On a satellite chain, totalBridgedOut starts at 0. When a user bridges tokens from the hub chain to the satellite chain, the destination _credit() tries to subtract the received amount from zero. This causes an arithmetic underflow and the inbound credit reverts.

As a result, an inbound bridge delivery to a non-hub chain can fail deterministically. The source-side debit can already have happened on the hub side, while the destination-side credit fails, creating a lock in the user bridge flow.

Relevant vulnerable logic:

function debit( address from, uint256 amountLD_, uint256 minAmountLD_, uint32 dstEid_ ) internal virtual override returns (uint256 amountSentLD, uint256 amountReceivedLD) { (amountSentLD, amountReceivedLD) = super.debit( from, amountLD_, minAmountLD_, dstEid_ ); totalBridgedOut += amountSentLD; }

function credit( address to, uint256 amountLD_, uint32 srcEid_ ) internal virtual override returns (uint256 amountReceivedLD) { amountReceivedLD = super.credit(to, amountLD_, srcEid_); totalBridgedOut -= amountReceivedLD; }

Reproduction flow:

  1. Deploy one wrap instance representing the hub chain.
  2. Deploy a second wrap instance representing a satellite chain.
  3. Mint wrapped tokens on the hub instance.
  4. Simulate an outbound bridge burn/debit on the hub instance.
  5. Confirm the hub-side totalBridgedOut increased.
  6. Simulate the inbound bridge credit on the satellite instance.
  7. Observe that the satellite-side credit reverts because totalBridgedOut underflows from zero.

This is not theoretical. I reproduced it locally with a runnable Hardhat PoC and observed the satellite-side inbound credit revert with panic code 0x11.

Validation steps

  1. Checkout the audited commit:
    • 519c9e92fd9d80d11e35e9868130f6334b88d676
  2. Use the attached PoC script and helper files.
  3. Run the PoC script with Hardhat on a local test network.
  4. The PoC:
    • deploys a local mock LayerZero endpoint
    • deploys one wrap contract representing the hub side
    • deploys one wrap contract representing the satellite side
    • mints tokens on the hub side
    • simulates an outbound bridge debit on the hub side
    • attempts the inbound bridge credit on the satellite side
  5. Observe the output:

hub totalBridgedOut after debit: 5000000000000000000 satellite totalBridgedOut before credit: 0 expected revert on satellite inbound credit Error: VM Exception while processing transaction: reverted with panic code 0x11 (Arithmetic operation overflowed outside of an unchecked block)

  1. This confirms:
  • the hub-side bridge debit increases totalBridgedOut
  • the satellite-side instance still has totalBridgedOut == 0
  • the inbound satellite credit reverts because _credit() decrements a zero balance
  1. The attached mock and PoC reproduce the failure deterministically without privileged access or unusual preconditions.

Attachments

hidden
CommentsReport History
Comments on this report are hidden
Details
Statedisclosed
Severity
Critical
Bounty$69
Visibilitypartially
VulnerabilityBlockchain
Participants
hidden