# Balance check during MagicSpend validation cannot ensure that MagicSpend has enough balance to cover the requested fund

MagicSpend's validation-time balance check ignores batched UserOps, so two withdrawals each passing validation can exceed the contract balance and revert the second postOp.

- Payout: Contest reward
- Severity: medium
- Program: Coinbase
- Credited: doublespending
- Disclosed: 2024-05-01
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2024-03-coinbase-findings/issues/110
- Library page: https://bugbountylibrary.com/r/c4%3A2024-03-coinbase%3AM-01
- Tags: domain:web3 impact:dos kind:contest platform:code4rena sev:medium tech:evm technique:race-window technique:source-review vuln:business-logic

**Target:** ERC-4337 paymaster (MagicSpend) validation/postOp balance accounting
**Root cause:** validatePaymasterUserOp checks address(this).balance against each withdrawAmount independently, without accounting for other UserOps in the same bundle that will also withdraw during the execution loop.
**Steps:**
1. Deploy/observe MagicSpend with balance 200 ETH wei.
2. Submit a bundle of two UserOps to EntryPoint.handleOps: userOp1 withdrawAmount=100, userOp2 withdrawAmount=150.
3. Validation loop calls validatePaymasterUserOp for each; both pass since 200>=100 and 200>=150.
4. Execution loop runs userOp1 postOp: forceSafeTransferETH sends 100, balance becomes 100.
5. userOp2 postOp attempts to send 150 but balance is 100, so transfer reverts and the bundle fails.
**How found:** Read EntryPoint's two-phase validation/execution loops and noticed MagicSpend's balance check runs per-op during validation while actual transfers happen later in postOp, so concurrent withdrawals are not tallied.
**Exploitation:** Attacker (or any user) submits a bundle of multiple MagicSpend withdrawals whose sum exceeds the contract balance; each passes validation individually, but later postOp transfers revert, griefing the paymaster/bundler and causing failed bundles.
**Insight:** For ERC-4337 paymasters, any state/balance check in validatePaymasterUserOp must account for all other UserOps in the same bundle, since validation runs for every op before any execution; per-op checks against live balance are insufficient.
