OverlayerWrapCore uses totalBridgedOut to preserve the hub-chain backing invariant. _debit() adds to it and _credit() subtracts from it on every chain.
The first inbound bridge transfer to a non-hub deployment reverts in _credit() because that destination instance starts with totalBridgedOut == 0, so totalBridgedOut -= amountReceivedLD underflows.
Because the source OFT debit already burned the user's source-chain balance before the destination credit runs, the user's funds are trapped in the failed cross-chain message flow.
contracts/overlayer/OverlayerWrapCore.sol:63contracts/overlayer/OverlayerWrapCore.sol:510contracts/overlayer/OverlayerWrapCore.sol:537totalBridgedOut is hub-chain accounting, but both _debit() and _credit() update it unconditionally on all deployments. A fresh satellite chain has no prior outbound amount, so its first inbound credit always executes 0 - amountReceivedLD.
_debit() burns the user's source balance and increments totalBridgedOut._credit() runs on the non-hub deployment with local totalBridgedOut == 0.totalBridgedOut -= amountReceivedLD underflows and reverts.This matches the program's Critical scope:
overlayer-bridge-lock-poc.zip
audit/HACKENPROOF-01-bridge-first-inbound-credit-locks-funds.mdtest/audit/OverlayerWrapBridgeLock.tscontracts/test/MockLayerZeroEndpointV2.solhardhat.audit.config.tsnpx hardhat test test/audit/OverlayerWrapBridgeLock.ts --config hardhat.audit.config.tsThe test passes and proves that the first hub -> satellite bridge transfer locks funds because the destination _credit() underflows while the source-side burn has already happened.
Observed test name:
locks the first hub->satellite transfer because destination _credit underflows
Restrict totalBridgedOut accounting to the hub chain only:
_debit() only on the hub chain_credit() only on the hub chainAlternatively, split the accounting into separate outbound-from-hub and inbound-to-hub variables instead of using one per-chain counter.
totalSupply() + totalBridgedOut on the hub chain.