# Persisted msg.value in a loop of delegate calls can be used to drain ETH from your proxy

MIMOProxy's payable batch() reuses a single msg.value across multiple delegatecalls to execute(), letting an approved caller double-spend proxy ETH.

- Payout: Contest reward
- Severity: medium
- Program: Mimo DeFi
- Credited: peritoflores
- Disclosed: 2022-10-17
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2022-08-mimo-findings/issues/153
- Library page: https://bugbountylibrary.com/r/c4%3A2022-08-mimo%3AM-04
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:chained technique:source-review vuln:access-control vuln:business-logic

**Target:** MIMOProxy / BoringBatchable batch() delegatecall loop
**Root cause:** msg.value persists across delegatecalls, so a payable batch() forwarding the same value to each execute() call lets each inner call spend the full msg.value from proxy funds rather than the attacker's.
**Steps:**
1. Fund MIMOProxy with ETH (e.g. `owner.sendTransaction({to: mimoProxy.address, value: DEPOSIT_AMOUNT})`)
2. Call `mimoProxy.batch([execute(vaultActions, depositETH), execute(vaultActions, depositETH)], true, {value: DEPOSIT_AMOUNT})`
3. Each delegatecall to execute() forwards DEPOSIT_AMOUNT from proxy balance, doubling the deposit
4. Alternatively, owner grants a mistyped selector matching a target's `fallback() external payable`, attacker calls batch() with that selector repeatedly to drain proxy ETH
**How found:** Recalled samczsun's 'Two Rights Might Make a Wrong' writeup on msg.value persistence in delegatecall loops and applied it to MIMOProxy inheriting BoringBatchable; built a PoC modifying the team's own test to show doubled deposit.
**Exploitation:** Attacker needs an existing approval (owner-granted permission on execute) or an owner typo in a granted selector plus a payable fallback on the target; then a single payable batch() with N repeated execute() items spends N×msg.value from the proxy's ETH balance.
**Insight:** When a contract inherits a payable multicall/batch that delegatecalls a payable execute(), check whether msg.value is re-spent per iteration; also check whether a mistyped granted selector can hit a payable fallback to weaponize the same primitive.
