OverlayerWrapCore tracks bridged supply in a local storage variable, totalBridgedOut, and mutates it in both OFT hooks:
_debit() increases totalBridgedOut_credit() decreases totalBridgedOutThis accounting is incorrect for a multi-chain OFT deployment because _debit() executes on the source-chain deployment while _credit() executes on a different destination-chain deployment with its own independent storage.
As a result, a normal user bridge from the hub chain to a fresh or net-inflow satellite chain can revert on the destination side:
OverlayerWrap on the hub chain._debit() burns/sends the user’s tokens and increments the hub’s local totalBridgedOut._credit() is called to mint the bridged tokens.totalBridgedOut == 0, because the source-side debit happened on a different contract instance._credit() executes totalBridgedOut -= amountReceivedLD.The user has already lost custody on the source chain, while the destination chain cannot deliver the bridged tokens until protocol intervention changes code or state.
Likelihood and impact:
Affected code
contracts/overlayer/OverlayerWrapCore.sol
Relevant 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;
}
The issue is validated with a dedicated Hardhat unit test that models the realistic architecture:
OverlayerWrapMock deployed as the hub instanceOverlayerWrapMock deployed as the satellite instancetestDebit(...) on the hubtestCredit(...) on the fresh satelliteRun the attached PoC:
npx hardhat test test/OverlayerWrap.ts --grep "fresh satellite"
The test passes by proving all of the following:
totalBridgedOut increasestotalBridgedOut == 0testCredit(...) on the satellite reverts0