# msg.value reused in loops

Payable loops forwarding full msg.value per iteration, overpaying from the contract's own native balance

_Area: DeFi accounting and math bugs. Seen in 3 disclosed reports; latest 2024-08-05. Updated 2026-10-23._

## How it works
`msg.value` is transaction-scoped: it is the total native token sent with the call, not a per-item budget. When a payable function loops over items and forwards `msg.value` (or `call{value: msg.value}`) on each pass, the same native balance is spent once per iteration. The contract's own latent native balance covers the difference, so the loop pays out more than the caller supplied. The broken assumption is that `msg.value` is "the amount for this item"; it is actually "the amount for the whole call".

## Where it shows up
- Payable functions with a `for`/`while` loop containing `call{value: msg.value}` or a helper that forwards `msg.value` [E1](https://bugbountylibrary.com/r/c4%3A2024-06-thorchain%3AM-02.md)[E2](https://bugbountylibrary.com/r/c4%3A2022-03-lifinance%3AM-04.md).
- Batch routers, multicall, swap aggregators, batch transfer-out helpers [E1](https://bugbountylibrary.com/r/c4%3A2024-06-thorchain%3AM-02.md)[E2](https://bugbountylibrary.com/r/c4%3A2022-03-lifinance%3AM-04.md).
- Fallback/refund paths that pay a caller-controlled address when a swap or call fails [E1](https://bugbountylibrary.com/r/c4%3A2024-06-thorchain%3AM-02.md).
- Per-order credit logic that reads `msg.value` inside a loop over a calldata array [E3](https://bugbountylibrary.com/r/c4%3A2021-11-nested%3AM-08.md).
- Inherited Multicall that preserves `msg.value` across batched delegatecalls [E3](https://bugbountylibrary.com/r/c4%3A2021-11-nested%3AM-08.md).

## How to review
The invariant: total native value leaving the contract in one transaction must equal `msg.value` (plus any explicitly accounted refund), never a multiple of it. Flag any `call{value: msg.value}` inside a loop body. Check that each iteration forwards a per-item amount derived from calldata, and that the sum of forwarded amounts is bounded by `msg.value`. For per-order credit, confirm ETH credited per order is split from `msg.value`, not the full amount each time [E3](https://bugbountylibrary.com/r/c4%3A2021-11-nested%3AM-08.md). Check fallback/refund recipients are not attacker-chosen [E1](https://bugbountylibrary.com/r/c4%3A2024-06-thorchain%3AM-02.md).

## Variants seen
- Batch transfer-out loop re-spends `msg.value`; failed swap falls back to paying an attacker-set target [E1](https://bugbountylibrary.com/r/c4%3A2024-06-thorchain%3AM-02.md).
- Swap loop forwards `msg.value` per swap, draining latent native balance [E2](https://bugbountylibrary.com/r/c4%3A2022-03-lifinance%3AM-04.md).
- Batched ETH deposit orders each credited the full `msg.value`, over-crediting up to contract holdings [E3](https://bugbountylibrary.com/r/c4%3A2021-11-nested%3AM-08.md).

## Impact and severity
Full drain of the contract's native balance in E1 and E2; over-credited deposits up to contract holdings in E3. Severity is high where the contract holds a latent native balance and the recipient is caller-controlled [E1](https://bugbountylibrary.com/r/c4%3A2024-06-thorchain%3AM-02.md); lower where only the caller's own funds are misrouted. Latent balances and attacker-controlled fallback targets make it worse.

## Evidence
- E1: https://bugbountylibrary.com/r/c4%3A2024-06-thorchain%3AM-02.md
- E2: https://bugbountylibrary.com/r/c4%3A2022-03-lifinance%3AM-04.md
- E3: https://bugbountylibrary.com/r/c4%3A2021-11-nested%3AM-08.md
