OverlayerWrapCore._credit() always executes totalBridgedOut -= amountReceivedLD even on a fresh non-hub deployment where totalBridgedOut starts at 0.totalBridgedOut.Weakness/VRT: Integer underflow / CWE-191 Severity: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H (9.1)
https://github.com/Overlayerfi/contracts/tree/519c9e92fd9d80d11e35e9868130f6334b88d676Affected:
Overlayerfi/contracts@519c9e92fd9d80d11e35e9868130f6334b88d676 — OverlayerWrapCore tracks OFT burns/credits with totalBridgedOut, but the destination-side decrement assumes prior outbound burns on the same deployment.OverlayerWrap from the hub chain to a fresh satellite deployment.totalBridgedOut is still 0.contracts/overlayer/OverlayerWrapCore.sol, which is one of the explicitly scoped files for the scoped repo asset above.totalBridgedOut reflects prior burns on the same deployment.OverlayerWrap balance that should appear on the destination chain after the source burn.PoC walkthrough: the PoC deploys a hub-style OverlayerWrapMock and a fresh satellite-style OverlayerWrapMock, seeds the expected LayerZero endpoint address with inert code so the repo’s constructor path can run locally, mints tokens on the hub instance, and simulates a bridge burn via _debit(). It then triggers the first destination _credit() on the fresh satellite instance and shows that the credit reverts with arithmetic underflow while the hub-side burn already happened. Finally, it runs a control case on the same deployment where _debit() and _credit() succeed together.
0) Prereqs: from repos/contracts, dependencies installed (npm install --force already done in this workspace).
1) Quick verification (single command): bash pocs/ov-bridge-lock_first_inbound_bridge_credit_permanent_lock/run.sh
2) Expected output: BUGGY_SATELLITE_CREDIT_REVERT=1, BUGGY_SATELLITE_TOTAL_SUPPLY=0, and CONTROL_CREDIT_SUCCESS=1 appear in artifacts/run_poc.txt.
3) Production-like reproduction notes: the PoC uses the repo’s own Hardhat build and contracts/test/OverlayerWrapMock.sol, and only stubs the LayerZero endpoint bytecode locally so the bridge accounting path can be exercised without external infrastructure.
contracts/overlayer/OverlayerWrapCore.sol:60-63 stores totalBridgedOut as a plain counter initialized to zero on every deployment.contracts/overlayer/OverlayerWrapCore.sol:510-527 increases totalBridgedOut only during _debit(), i.e. after a local source-chain burn.contracts/overlayer/OverlayerWrapCore.sol:537-543 always subtracts amountReceivedLD during _credit(), which underflows on a fresh destination deployment before any local debit has occurred.totalBridgedOut is zero and there is no guard such as min(totalBridgedOut, amountReceivedLD) or a per-chain source check before subtracting._debit() is the only code path that can increase totalBridgedOut, the first inbound credit to a fresh satellite always hits a Solidity 0.8 underflow and reverts._managerMint(... ) onlyHubChain(block.chainid)), so the satellite cannot self-bootstrap circulating supply and then create a local debit to repair the counter.totalBridgedOut is documented in-code as bridge accounting for OFT burns/credits, so a fresh destination rejecting its first inbound credit contradicts the intended bridge flow.totalBridgedOut == 0).artifacts/run_poc.txt, artifacts/assertions.txt, and artifacts/control_comparison.txt.pocs/ov-bridge-lock_first_inbound_bridge_credit_permanent_lock/bash pocs/ov-bridge-lock_first_inbound_bridge_credit_permanent_lock/run.shpocs/
`-- ov-bridge-lock_first_inbound_bridge_credit_permanent_lock/
|-- README.md # Setup, execution, expected output, and cleanup notes
|-- run.sh # One-command PoC entrypoint that generates the replay artifacts
|-- e2e/
| `-- bridge_lock_poc.js # Hardhat script that reproduces the buggy bridge credit and control case
`-- artifacts/
|-- assertions.txt # Pass/fail assertions mapped to the permanent-lock claim
|-- control_comparison.txt # Buggy first-credit behavior versus working debit-then-credit control
|-- manifest.txt # File inventory with one-line purpose for every PoC file
|-- replay.sh # Deterministic analyst replay helper
`-- run_poc.txt # Full PoC console output
totalBridgedOut on every destination _credit()._credit() against amountReceivedLD > totalBridgedOut and define a separate initialization path for fresh satellite deployments so the first inbound credit can succeed safely.In the prepared workspace, run:
bash pocs/ov-bridge-lock_first_inbound_bridge_credit_permanent_lock/run.sh
The PoC deploys:
OverlayerWrapMockOverlayerWrapMockThe script then:
_debit()_credit() on the fresh satellite instance_debit() and _credit() happen on the same deploymentConfirm the buggy path output contains:
BUGGY_SOURCE_BALANCE_AFTER_DEBIT=13000000000000000000BUGGY_SOURCE_TOTAL_BRIDGED_OUT=7000000000000000000BUGGY_SATELLITE_CREDIT_REVERT=1BUGGY_SATELLITE_REVERT_LINE=... panic code 0x11 ...BUGGY_SATELLITE_TOTAL_SUPPLY=0Confirm the control path output contains:
CONTROL_CREDIT_SUCCESS=1CONTROL_TOTAL_BRIDGED_OUT=0The replay artifacts are written under:
pocs/ov-bridge-lock_first_inbound_bridge_credit_permanent_lock/artifacts/run_poc.txtpocs/ov-bridge-lock_first_inbound_bridge_credit_permanent_lock/artifacts/assertions.txtpocs/ov-bridge-lock_first_inbound_bridge_credit_permanent_lock/artifacts/control_comparison.txtValidation conclusion: