# Self-transfer balance inflation

Transfer or reward paths where from equals to still credits a balance without real movement

_Area: DeFi accounting and math bugs. Seen in 3 disclosed reports; latest 2025-01-14. Updated 2026-10-23._

## How it works
A transfer is assumed to move value from one party to another. When `from == to`, no value should move. The bug is that the code still applies both sides of the accounting: it credits the recipient and debits the sender, or credits both parties, without recognizing they are the same account. The net effect is a mint. The broken assumption is that `from` and `to` are distinct; nothing enforces it, so a zero-cost self-move creates balance or rewards out of nothing.

## Where it shows up
- Transfer, reward, or notional functions with no `from != to` (or `id`/`account` aliasing) check.
- Reward logic that credits both sender and receiver on every transfer [E1](https://bugbountylibrary.com/r/dhl%3A20250114-idolsnft.md).
- Functions that cache two storage structs into memory locals and write both back; when the keys alias, the later write clobbers the earlier one [E2](https://bugbountylibrary.com/r/c4%3A2021-09-swivel%3AH-03.md).
- Token `transfer` that credits the recipient before debiting the sender, so a self-transfer nets positive [E3](https://bugbountylibrary.com/r/dhl%3A20230908-apig.md).
- `isContract` or similar guards that a constructor-deployed caller can bypass [E1](https://bugbountylibrary.com/r/dhl%3A20250114-idolsnft.md).

## How to review
For every value-moving path, check the invariant: a transfer where source and destination are the same account must be a strict no-op (or be rejected). Concretely:
- Confirm an explicit `require(from != to)` or equivalent early return.
- For memory write-back patterns, verify that aliased keys cannot cause one write to overwrite another; trace the final stored state for `f == t` [E2](https://bugbountylibrary.com/r/c4%3A2021-09-swivel%3AH-03.md).
- For reward/fee accounting, confirm credits are tied to actual net balance change, not to the event of a transfer [E1](https://bugbountylibrary.com/r/dhl%3A20250114-idolsnft.md).
- For tokens, confirm debit happens before (or atomically with) credit, and that self-transfer leaves balance unchanged [E3](https://bugbountylibrary.com/r/dhl%3A20230908-apig.md).
- Check whether contract-detection guards can be evaded at deployment time [E1](https://bugbountylibrary.com/r/dhl%3A20250114-idolsnft.md).

## Variants seen
- Reward-bearing NFT credits both parties on transfer; self-transfer mints stETH rewards [E1](https://bugbountylibrary.com/r/dhl%3A20250114-idolsnft.md).
- Vault notional transfer with memory write-back; aliasing overwrites the decremented balance with the incremented one, minting unbacked notional [E2](https://bugbountylibrary.com/r/c4%3A2021-09-swivel%3AH-03.md).
- BEP20 token credits recipient before debiting sender; self-transfer inflates balance, dumped into AMM pairs [E3](https://bugbountylibrary.com/r/dhl%3A20230908-apig.md).

## Impact and severity
High to critical. Evidence shows direct fund loss: ~97 stETH drained [E1](https://bugbountylibrary.com/r/dhl%3A20250114-idolsnft.md), ~$169K (~59.5 ETH and ~72.1k USDT) pulled from PancakeSwap pairs [E3](https://bugbountylibrary.com/r/dhl%3A20230908-apig.md), and unbacked notional minted [E2](https://bugbountylibrary.com/r/c4%3A2021-09-swivel%3AH-03.md). Severity rises when the inflated balance is immediately liquid (AMM pairs, reward tokens) and when the loop is cheap to repeat, as with self-transfers that cost only gas.

## Evidence
- E1: https://bugbountylibrary.com/r/dhl%3A20250114-idolsnft.md
- E2: https://bugbountylibrary.com/r/c4%3A2021-09-swivel%3AH-03.md
- E3: https://bugbountylibrary.com/r/dhl%3A20230908-apig.md
