We identified that the _credit() override in OverlayerWrapCore.sol (line 545) unconditionally
decrements the totalBridgedOut state variable whenever tokens arrive via a
LayerZero OFT cross-chain message:
File: contracts/overlayer/OverlayerWrapCore.sol Lines: 540-548
function _credit(
address to_,
uint256 amountLD_,
uint32 srcEid_
) internal virtual override returns (uint256 amountReceivedLD) {
amountReceivedLD = super._credit(to_, amountLD_, srcEid_);
totalBridgedOut -= amountReceivedLD; // ← UNDERFLOW HERE
}
On satellite (non-hub) chains, totalBridgedOut is initialized to 0 and can
never be incremented because:
_debit() (the only function that increments totalBridgedOut, line 528)
requires tokens to already exist on the chain to send them outbound. But
no tokens can ever exist on a satellite chain because...
_managerMint() (line 403) is gated by onlyHubChain(block.chainid),
preventing any minting on satellite chains.
There is no setter, initializer, or admin function to set totalBridgedOut
to any starting value.
This creates a permanent deadlock:
HOW TO REPRODUCE:
Step 1: Deploy OverlayerWrap on Ethereum mainnet (hub) with hubChainId = 1. Step 2: Deploy OverlayerWrap on Arbitrum (satellite) with hubChainId = 1. Step 3: Configure LayerZero OFT peers between both deployments. Step 4: On Ethereum, a user mints 1,000 USDT+ by depositing 1,000 USDT. Step 5: User calls send() (LayerZero OFT) to bridge 100 USDT+ to Arbitrum. Step 6: On Ethereum: _debit() executes successfully. - Burns 100 USDT+ from user. - totalBridgedOut += 100 → totalBridgedOut = 100. ✓ Step 7: LayerZero relays the cross-chain message to Arbitrum. Step 8: On Arbitrum: _credit() is invoked by LayerZero endpoint. - super._credit() mints 100 USDT+ to user. ✓ - totalBridgedOut -= 100 → uint256(0) - 100 → UNDERFLOW REVERT ✗ Step 9: Entire receive transaction reverts on Arbitrum. Step 10: 100 USDT+ were burned on Ethereum but never minted on Arbitrum. Tokens are permanently stuck. Cross-chain is broken.
Your team can validate this finding by following these steps:
• Open: contracts/overlayer/OverlayerWrapCore.sol
• Go to line 540 (the _credit function)
• URL: https://github.com/Overlayerfi/contracts/blob/519c9e92fd9d80d11e35e9868130f6334b88d676/contracts/overlayer/OverlayerWrapCore.sol#L540-L548
• Observe: totalBridgedOut -= amountReceivedLD executes unconditionally
with no if (block.chainid == hubChainId) guard.
STEP 2 — Confirm totalBridgedOut is initialized to 0 with no setter
• In the same file, line 66: uint256 public totalBridgedOut;
• Search the entire file for any function that sets totalBridgedOut.
• Result: Only _debit() (line 528) increments it. Only _credit() (line 547)
decrements it. There is no initializer, setter, or admin override.
STEP 3 — Confirm minting is hub-chain-only
• In the same file, line 403: _managerMint() has modifier
onlyHubChain(block.chainid).
• URL: https://github.com/Overlayerfi/contracts/blob/519c9e92fd9d80d11e35e9868130f6334b88d676/contracts/overlayer/OverlayerWrapCore.sol#L398-L413
• The modifier (line 78-82) reverts if block.chainid != hubChainId.
• Therefore: on any satellite chain, no tokens can be minted.
STEP 4 — Trace the deadlock • Satellite chain state: totalBridgedOut = 0, totalSupply = 0. • No minting possible → no tokens to _debit() → totalBridgedOut stays 0. • Any _credit() call: 0 - amount → underflow → revert. • Deadlock confirmed.
STEP 5 — Confirm with Solidity behavior
• Solidity 0.8.20 uses checked arithmetic by default.
• uint256(0) - uint256(100) reverts with panic code 0x11
(arithmetic overflow/underflow).
• The subtraction is NOT inside an unchecked {} block.
UPLOADED FILE: C01_CreditUnderflow_PoC.sol
This Foundry PoC test demonstrates the integer underflow on satellite chains. Your team can place it in your test/ directory and run:
forge test --match-contract CreditUnderflowPoC -vvv
The test contains:
Key source code references:
_credit() with unconditional decrement: https://github.com/Overlayerfi/contracts/blob/519c9e92fd9d80d11e35e9868130f6334b88d676/contracts/overlayer/OverlayerWrapCore.sol#L540-L548
_debit() with unconditional increment: https://github.com/Overlayerfi/contracts/blob/519c9e92fd9d80d11e35e9868130f6334b88d676/contracts/overlayer/OverlayerWrapCore.sol#L510-L529
totalBridgedOut declaration (no initializer): https://github.com/Overlayerfi/contracts/blob/519c9e92fd9d80d11e35e9868130f6334b88d676/contracts/overlayer/OverlayerWrapCore.sol#L66
onlyHubChain modifier blocking satellite minting: https://github.com/Overlayerfi/contracts/blob/519c9e92fd9d80d11e35e9868130f6334b88d676/contracts/overlayer/OverlayerWrapCore.sol#L78-L82