# [WP-H5] L1Migrator.sol#migrateETH() does not send bridgeMinter's ETH to L2 causing ETH get frozen in the contract

L1Migrator.migrateETH forwards only msg.value as L1 call value to Arbitrum, so bridgeMinter's withdrawn ETH can never reach L2 and stays frozen.

- Payout: Audit contest
- Severity: high
- Program: Livepeer
- Found by: WatchPug
- Disclosed: 2022-02-11
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2022-01-livepeer-findings/issues/205
- Library page: https://bugbountylibrary.com/r/c4%3A2022-01-livepeer%3AH-01
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** L1Migrator.sol migrateETH / Arbitrum L1 gateway bridge
**Root cause:** migrateETH withdraws ETH from BridgeMinter into the contract but passes only msg.value as _l1CallValue to sendTxToL2, so the retryable ticket's L2 call value (amount) is unfunded and the L2 call always fails.
**Steps:**
1. Read Arbitrum Inbox.sol: all msg.value is deposited to callValueRefundAddress on L2.
2. In L1Migrator.migrateETH, `amount = IBridgeMinter(bridgeMinterAddr).withdrawETHToL1Migrator()` pulls ETH into L1Migrator.
3. Call `sendTxToL2(l2MigratorAddr, address(this), msg.value, amount, ...)` — _l1CallValue is only msg.value, not msg.value + amount.
4. On L2 the retryable ticket attempts a call with value=amount but lacks funds, so it reverts; the withdrawn ETH remains stuck in L1Migrator with no path to L2.
**How found:** Cross-referenced Arbitrum Inbox semantics (msg.value becomes L2 call value funding) against migrateETH's sendTxToL2 arguments and noticed the withdrawn bridgeMinter amount was never added to _l1CallValue.
**Insight:** When bridging via Arbitrum retryable tickets, the L2 call value must be funded by msg.value on the L1 call; audit any contract that withdraws funds internally then bridges them to confirm the withdrawn amount is added to _l1CallValue, not just passed as _l2CallValue.
