Status DataClose notification

Overlayer Disclosed Report

First inbound credit on a fresh non-hub OverlayerWrap permanently locks bridged user funds because _credit() underflows totalBridgedOut

Company
Created date
Mar 31 2026

Target

hidden

Vulnerability Details

OverlayerWrapCore._credit() always decrements totalBridgedOut, even on non-hub destination chains. On a fresh non-hub deployment, totalBridgedOut starts at 0, so the first inbound LayerZero credit underflows and reverts with panic 0x11. The source-side _debit() burn has already happened, so the user's bridged funds are left locked.

A non-hub destination cannot bootstrap totalBridgedOut locally because mint() is restricted to the hub chain, and retrying the same inbound credit keeps hitting the same underflow. That means the first inbound transfer into a fresh non-hub destination remains stuck.

The root cause is the mismatch between the source and destination accounting paths in OverlayerWrapCore.sol:

  • _debit() increments totalBridgedOut on the source chain after burning
  • _credit() always decrements totalBridgedOut on the destination after minting
  • the comments above _credit() describe tokens returning to the hub chain, but the implementation does not gate this subtraction to the hub chain

On a fresh non-hub destination:

  • totalBridgedOut starts at 0
  • the first inbound _credit() subtracts a positive amount
  • Solidity 0.8 underflow protection reverts with panic 0x11

The repo docs and deployment scripts describe separate OverlayerWrap deployments across Sepolia networks, so a fresh non-hub destination is a normal path, not an artificial setup.

Validation steps

You can validate this locally with the attached PoC.

What to check in code:

  • In contracts/overlayer/OverlayerWrapCore.sol, confirm _debit() increments totalBridgedOut after burning.
  • In contracts/overlayer/OverlayerWrapCore.sol, confirm _credit() always decrements totalBridgedOut after calling super._credit(...), with no hub-chain gate.
  • In contracts/overlayer/OverlayerWrapCore.sol, confirm local mint() is restricted to the hub chain.
  • In docs/deployments.md and scripts/utils/deployAllSepolia.ts, confirm the intended deployment model includes multiple OverlayerWrap deployments across Sepolia chains.

Then run from the repo root: npx hardhat test --config hardhat.local.config.ts test/SecurityBridge.ts

Expected result:

  • 2 passing

Passing tests:

  • burns on source but reverts on first destination credit because totalBridgedOut underflows
  • cannot bootstrap a fresh non-hub destination after the first failed credit

Those tests show:

  • source-side burn succeeds
  • first inbound credit on a fresh non-hub destination reverts with panic 0x11
  • the destination cannot bootstrap itself locally because mint() is hub-only
  • retrying the same inbound credit still reverts
  • the funds remain locked for that destination path

Attachments

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