# [High] Function MigrateWithdrawal() may set gas limit so high for old withdrawals when migrating them by mistake and they can't be relayed in the L1 and users funds would be lost

MigrateWithdrawal overestimates gas limit using 16 gas/byte for zero bytes, so large-data legacy withdrawals exceed L1 block gas and become unrelayable after migration.

- Payout: Contest reward
- Severity: medium
- Program: Optimism
- Credited: unforgiven
- Disclosed: 2023-01-19
- Write-up quality: 6/10
- Original: https://github.com/sherlock-audit/2023-01-optimism-judging/issues/235
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-01-optimism%3AM-4
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:medium tech:evm tech:go technique:source-review vuln:accounting-error vuln:dos

**Target:** op-chain-ops/crossdomain/migrate.go MigrateWithdrawal()
**Root cause:** Gas limit computed as len(data)*16 + 200_000 charges 16 gas per byte even for zero bytes, which cost only 4 intrinsic gas, overestimating up to 4x.
**Steps:**
1. Craft a LegacyWithdrawal whose ABI-encoded relayMessage data contains ~2M zero bytes.
2. Call MigrateWithdrawal(); gasLimit = len(data)*16 + 200_000 exceeds 30M L1 block gas.
3. Attempt to relay on L1: setting tx gas below computed limit is rejected by OptimismPortal; setting above exceeds block gas limit, so relay is impossible.
**How found:** Code review of MigrateWithdrawal gas estimation, comparing 16 gas/byte assumption against Ethereum's 4-gas zero-byte intrinsic cost.
**Insight:** When estimating calldata gas, zero bytes cost 4 not 16; overestimating can push the required gas limit above the block gas cap, permanently bricking messages that were relayable before.
