📋 What Is It?
Insecure Randomness occurs when a smart contract derives a "random" number from on-chain values that are predictable or manipulable. A blockchain is deterministic and fully public: every node must re-execute a transaction and reach the same state, so nothing in the EVM is genuinely random. Values like block.timestamp, a block hash, or block.prevrandao are visible before the transaction commits, computable within the same transaction, or influenceable by the block proposer. An attacker who predicts or biases the outcome wins whatever the randomness was meant to guard-a lottery draw, a game result, a rare NFT mint, or a reward allocation.
⚠️ Common Exploits
- Compute-then-revert: attacker recomputes the draw in the same tx, plays only on a win
- Predictable block hash:
blockhash(n-1)is already fixed and public before your tx runs - Proposer bias: validator nudges
timestampor skips a slot to steerprevrandao - Mempool reordering: front-run/back-run the deterministic result using ordering alone
- Reveal withholding: commit-reveal without slashing lets a loser never reveal
- Reveal front-running: the last revealer steers the combined seed
- Address grinding: seed from
msg.sendergrinds throwaway/CREATE2 addresses to win - Weak PRNG expansion: one public seed plus a counter unrolls every "random" draw
🔴 Attack Flow
↓
2. Reproduce the exact math in an attacker contract
↓
3. Compute the outcome in the same block
↓
4. IF win → call target; ELSE → revert (pay only gas)
↓
5. IMPACT: Retry each block until the prize pool is drained!
❌ Vulnerable Code
✅ Secure Code
✓ Prevention Checklist
- Never seed value-bearing randomness from block or message variables
- Use a verifiable randomness oracle (Chainlink VRF) with an on-chain proof
- Separate the randomness request from its consumption across two transactions
- Ensure the outcome does not exist in the tx that acts on it (kills compute-then-revert)
- Require enough block confirmations to resist reorgs on high-value draws
- If on-chain only, use commit-reveal with deposits, slashing, and timeouts
- Salt commitments as
keccak256(secret, salt)to stop brute-forcing - Bind and mix every revealed secret so no last actor can steer the seed
- Close the entry set before randomness is requested
- Charge a non-refundable entry cost to remove free retries
- Assume an adversary who is also a block proposer and can revert on a loss
- Do not rely on
msg.sender == tx.originas the primary control
🔍 Detection & Tools
Analysis Tools:
Secure Randomness & Libraries:
How to Test:
- Run Slither: it flags
weak-prng/ block-timestamp / block-value usage - Grep for banned sources:
block.timestamp,blockhash,prevrandao,difficulty,coinbase,gasleft - Write an adversarial test that reproduces the contract's own RNG expression
- Add a compute-then-revert attacker contract and assert it cannot force a win
- Review specifically for proposer bias and reveal withholding, not just "looks random"
- Confirm the randomness is delivered in a later tx than the one requesting it
🌍 Real-World Incident Classes
- Predictable lottery / raffle draws: the winner index is a
keccak256of block variables (timestamp, block hash, prevrandao) read at draw time. Because those values are readable at execution, an entrant computes the winning ticket first and enters only in blocks where it wins-draining the pool over many rounds. - On-chain games of chance (dice / coin-flip / slots): the bet is placed and resolved in the same transaction from a block value. A wrapper contract computes the roll first and only bets on a guaranteed win, using free reverts to retry until the house edge inverts.
- NFT rarity / mint-order sniping: trait rarity is derived at mint from block variables or the minter's own inputs. Bots precompute which mint yields a rare token, mint only the valuable tiers (reverting on common results), and capture the collection's tail of value.
- Proposer-biased draws: when the prize exceeds a block reward, the block producer nudges
block.timestampwithin tolerance or skips a slot to influenceprevrandao-bending even "future block" designs toward the adversary who builds that block. - Commit-reveal games gamed by withholding: without a slashable deposit or timeout, a participant who dislikes the pending outcome simply never reveals, forcing a re-draw or stalling settlement at no cost.
Note: these are recurring classes of exploit, not claims about a single named victim. Exact loss totals differ between reports and many exploited contracts are never publicly attributed; the durable lesson is that on-chain randomness derived from block or message data is predictable, cheap to exploit, and repeatedly abused.
📌 Quick Tips
- DO NOT seed randomness from
block.timestamporblockhash - DO NOT trust
prevrandao/difficultyfor high-value payouts - DO NOT resolve a bet or draw in the same transaction that places it
- DO use a verifiable oracle (Chainlink VRF) with an on-chain proof
- DO split the randomness request from its consumption
- DO harden commit-reveal with deposits, slashing, and timeouts
- DO require confirmations to resist reorg manipulation