# Revert-and-retry randomness grinding

Random draw observable and revertible by caller in the same transaction as value transfer

_Area: DeFi accounting and math bugs. Seen in 2 disclosed reports; latest 2021-06-21. Updated 2026-10-29._

## How it works
A contract picks a value it treats as unpredictable (token ID, winner, fee tier) from inputs the caller can see before the transaction settles: blockhash, nonce, or a weak seed. The caller inspects the outcome and reverts unless it is favorable, retrying until they win. The randomness assumption breaks because the draw is neither hidden from the caller nor binding on them. A related seed-source failure: a library's RNG falls back to `Math.random()` plus wall-clock time when its CSPRNG branch never runs on the target runtime, so outputs are predictable rather than grindable [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-vh45-f885-3848.md).

## Where it shows up
- Random selection (`getRandomTokenIdFromFund`, nonce+blockhash) adjacent to payment, redeem, or swap logic in the same transaction [E2](https://bugbountylibrary.com/r/c4%3A2021-05-nftx%3AM-01.md)[E3](https://bugbountylibrary.com/r/c4%3A2021-05-nftx%3AM-09.md)
- Caller is a contract able to conditionally revert on the result [E2](https://bugbountylibrary.com/r/c4%3A2021-05-nftx%3AM-01.md)
- RNG seeded from `Math.random()` plus wall-clock time [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-vh45-f885-3848.md)
- CSPRNG branch gated on `window.crypto` rather than `globalThis.crypto`, so it never runs in Node [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-vh45-f885-3848.md)
- One module-wide RNG reused for private keys and ephemeral signing scalars [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-vh45-f885-3848.md)

## How to review
Identify every value the protocol treats as unpredictable and ask: can the caller observe it before committing state, and can they revert and retry? The invariant: a draw must be hidden from the caller until after settlement, or unavailable to contract callers (EOA-only), and must come from a CSPRNG seeded correctly on the target runtime. For RNG libraries, confirm the secure branch actually executes in the deployment environment, not just in browsers [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-vh45-f885-3848.md).

## Variants seen
- On-chain nonce+blockhash selection grindable by revert-and-retry from a contract [E2](https://bugbountylibrary.com/r/c4%3A2021-05-nftx%3AM-01.md)
- Same-transaction random token pick letting a user dodge the direct redeem fee by reverting [E3](https://bugbountylibrary.com/r/c4%3A2021-05-nftx%3AM-09.md)
- Weak RNG seeding (Math.random + time) making keys and scalars predictable [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-vh45-f885-3848.md)

## Why fixes fail
Restricting to EOA addresses the grind; merely changing the seed source does not, because the caller can still observe and revert [E2](https://bugbountylibrary.com/r/c4%3A2021-05-nftx%3AM-01.md). Fee logic that relies on the same in-transaction draw remains bypassable until the draw is separated from settlement [E3](https://bugbountylibrary.com/r/c4%3A2021-05-nftx%3AM-09.md).

## Impact and severity
Bypassed fees and unfair NFT selection [E2](https://bugbountylibrary.com/r/c4%3A2021-05-nftx%3AM-01.md)[E3](https://bugbountylibrary.com/r/c4%3A2021-05-nftx%3AM-09.md); predictable private keys and signing scalars [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-vh45-f885-3848.md). Severity rises when value transfer and the draw share a transaction, and when the RNG is reused across keys.

## Evidence
- E1: https://bugbountylibrary.com/r/ghsa%3AGHSA-vh45-f885-3848.md
- E2: https://bugbountylibrary.com/r/c4%3A2021-05-nftx%3AM-01.md
- E3: https://bugbountylibrary.com/r/c4%3A2021-05-nftx%3AM-09.md
