# Flash loan guard bypass

Guards on live balances, unset delays, or tx.origin that flash loans and fresh EOAs defeat

_Area: DeFi oracles, liquidations and flash loans. Seen in 3 disclosed reports; latest 2022-01-21. Updated 2026-10-24._

## How it works
A guard is meant to prove something durable: that the caller held an asset before this block, or cannot act twice within one block. It fails when it measures the wrong thing — a live balance, an unset delay, or a caller identity that can be rotated. Flash loans make any transient condition satisfiable, so a check that only holds "right now" proves nothing about prior state or ownership.

## Where it shows up
- Access gates using `balanceOf(msg.sender)` for ERC20/721/1155 allowlists, tiered sales, or gated mints [E1](https://bugbountylibrary.com/r/c4%3A2021-11-bootfinance%3AM-09.md)
- Cooldown/flash modifiers reading a configurable delay variable that may default to 0 [E2](https://bugbountylibrary.com/r/c4%3A2021-04-vader%3AH-02.md)
- Same-block guards keyed on `tx.origin` rather than `msg.sender` [E3](https://bugbountylibrary.com/r/c4%3A2021-04-vader%3AM-04.md)
- Setters for guard parameters that are unreachable (e.g. DAO-only) so defaults persist [E2](https://bugbountylibrary.com/r/c4%3A2021-04-vader%3AH-02.md)

## How to review
Identify the invariant the guard claims: "this caller held X before this block" or "this caller cannot act twice in one block." Then verify the code enforces it against an adversary who controls transaction content and can borrow anything.
- For balance gates: require ownership recorded in a prior block (snapshot, staked/locked state), not a live `balanceOf` [E1](https://bugbountylibrary.com/r/c4%3A2021-11-bootfinance%3AM-09.md).
- For delay guards: confirm the delay variable is initialized to a nonzero value and its setter is reachable [E2](https://bugbountylibrary.com/r/c4%3A2021-04-vader%3AH-02.md).
- For identity guards: key on `msg.sender` and per-address state, not `tx.origin`; assume callers rotate EOAs and relay via proxies [E3](https://bugbountylibrary.com/r/c4%3A2021-04-vader%3AM-04.md).
- Confirm the guard cannot be satisfied and undone within the same transaction.

## Variants seen
- Transient NFT balance satisfies holder-only sale check, then returned same tx [E1](https://bugbountylibrary.com/r/c4%3A2021-11-bootfinance%3AM-09.md)
- Uninitialized `blockDelay = 0` makes `flashProof` a no-op [E2](https://bugbountylibrary.com/r/c4%3A2021-04-vader%3AH-02.md)
- `tx.origin`-keyed guard bypassed by fresh EOAs bundled in one miner-controlled block [E3](https://bugbountylibrary.com/r/c4%3A2021-04-vader%3AM-04.md)

## Why fixes fail
E2 shows a guard silently disabled by a default value and a DAO-unreachable setter; E3 shows a separate contract's modifier keyed on the wrong identity. In both, a guard that looks present is inert.

## Impact and severity
Bypassed access control on a public sale [E1](https://bugbountylibrary.com/r/c4%3A2021-11-bootfinance%3AM-09.md); disabled flash-attack mitigation on core token functions [E2](https://bugbountylibrary.com/r/c4%3A2021-04-vader%3AH-02.md)[E3](https://bugbountylibrary.com/r/c4%3A2021-04-vader%3AM-04.md). Severity is high where the guard protects minting, pricing, or privileged state; worse when defaults or unreachable setters hide the gap from reviewers.

## Evidence
- E1: https://bugbountylibrary.com/r/c4%3A2021-11-bootfinance%3AM-09.md
- E2: https://bugbountylibrary.com/r/c4%3A2021-04-vader%3AH-02.md
- E3: https://bugbountylibrary.com/r/c4%3A2021-04-vader%3AM-04.md
