Opcode Summary
| Property | Value |
|---|---|
| Opcode | 0x40 |
| Mnemonic | BLOCKHASH |
| Gas | 20 |
| Stack Input | blockNum |
| Stack Output | blockHash(blockNum) |
| Behavior | Returns the Keccak-256 hash of the specified block’s header, but only if the block is among the 256 most recent complete blocks (block.number - 256 <= blockNum < block.number). Returns 0 for the current block (blockNum == block.number), any future block, or any block older than 256 blocks from the current head. The 256-block window is enforced at the protocol level and cannot be circumvented from within the EVM. |
Threat Surface
BLOCKHASH is the EVM’s only native access to historical block data, and its overwhelmingly common (mis)use is as a source of on-chain randomness. This makes it one of the most deceptively dangerous opcodes: it costs only 20 gas, has no side effects, and appears to produce a uniformly distributed 256-bit value — yet every property of BLOCKHASH conspires against secure randomness generation.
The threat surface centers on four properties:
-
Block hashes are deterministic and publicly visible. Every node in the network knows every block hash. A contract that derives a “random” number from
blockhash(block.number - 1)produces a value that any other contract or off-chain observer can compute in advance. An attacker deploys a contract that reads the same blockhash, computes the outcome, and only participates when guaranteed to win. This is not a theoretical concern — it is the most exploited pattern in Ethereum gambling history. -
Validators (formerly miners) control block production. The block proposer decides which block hash gets committed to the chain. If the value at stake exceeds the block reward, a rational validator can withhold a block whose hash produces an unfavorable outcome, re-proposing with a different set of transactions to get a different hash. Post-merge, the proposer knows
PREVRANDAOand the set of transactions before committing, giving them a free option to influence any blockhash-derived randomness. -
BLOCKHASH returns 0 for the current block — a pervasive developer footgun.
blockhash(block.number)returns 0, not the current block’s hash. Developers who writeuint256 random = uint256(blockhash(block.number))get a constant zero, which either makes the “random” value deterministic or — if used as an entropy source combined with other values — dramatically reduces the entropy space. This is a documented bug class that appears in audit after audit. -
The 256-block window creates timing vulnerabilities. If a contract stores a block number and later reads its hash, but more than 256 blocks have elapsed (roughly 51 minutes on Ethereum mainnet), the hash returns 0. An attacker can deliberately delay revelation past the 256-block window to force a known-zero hash value, subverting any randomness or verification scheme that depends on the stored block hash.
Smart Contract Threats
T1: Predictable Randomness — Contract-Level Hash Precomputation (Critical)
BLOCKHASH is deterministic: for any given block number, every contract and off-chain observer computes the identical hash. When a contract uses blockhash(block.number - 1) (or any past block number known at call time) to derive a random outcome, an attacker contract can replicate the computation and selectively participate only when the outcome is favorable.
-
Same-transaction prediction. An attacker deploys a contract that calls the target lottery, computes
blockhash(block.number - 1)within the same transaction, and reverts if the outcome is unfavorable. The attacker pays only gas for losing attempts (which revert and refund most gas) and collects the prize on winning attempts. This is a zero-risk, guaranteed-profit attack. -
Front-running with precomputed hashes. If the target contract uses a blockhash that is already finalized (e.g., the hash of 10 blocks ago), the attacker observes the hash off-chain, computes the outcome, and submits a transaction only when they will win.
-
Combining with other “entropy” sources. Contracts that hash
blockhashtogether withblock.timestamp,block.difficulty/PREVRANDAO,msg.sender, or contract storage believe they’re adding entropy. All of these are either publicly known or miner/validator-influenceable, so the combination offers no meaningful security improvement.
Why it matters: This is the single most exploited vulnerability class in on-chain gambling. SmartBillions, Fomo3D’s airdrop mechanism, and dozens of lottery contracts have been drained using this exact technique.
T2: Current Block Hash Returns Zero — The Zero-Value Bug (High)
blockhash(block.number) returns 0, not the current block’s hash. The current block’s hash cannot be known during its execution because the hash depends on the block’s contents, which are still being determined. This creates two vulnerability patterns:
-
Accidental zero entropy. A developer writes
uint256 seed = uint256(blockhash(block.number))expecting a random value but gets 0 on every invocation. If this seed is used directly as a random number, the outcome is constant and trivially predictable. If it’s hashed with other values, one input is always zero, reducing entropy. -
Zero-hash as winning condition. If a lottery selects a winner based on
blockhash(blockNum) % numPlayers == 0, andblockNumresolves to the current block (or an expired block),blockhashreturns 0, and0 % numPlayers == 0always selects player 0. An attacker who is player 0 can force this condition.
Why it matters: This bug appears in production contracts because the behavior is counterintuitive. Developers expect blockhash(block.number) to return the hash of the block being executed, not zero. The Solidity documentation explicitly warns against this, but the mistake persists.
T3: 256-Block Window Expiration (High)
BLOCKHASH only serves hashes for the most recent 256 complete blocks. Any block number outside [block.number - 256, block.number - 1] returns 0. This creates a timing attack surface:
-
Delayed revelation attack. A contract stores
block.numberat commit time and readsblockhash(storedBlockNumber)at reveal time. If the attacker waits more than 256 blocks (~51 minutes on mainnet) before triggering the reveal, the hash returns 0 — a known value. The attacker can then manipulate any scheme that relies on this hash being unpredictable. -
Commit-reveal scheme breakage. Two-phase commit-reveal randomness protocols that store a future block number as the randomness source must enforce a reveal deadline within 256 blocks. Without this enforcement, an attacker can always choose to reveal late and get a zero hash, or reveal on time, whichever outcome is more favorable.
-
Cross-chain and L2 timing differences. On L2s with faster block times, 256 blocks may expire in minutes rather than ~51 minutes, shrinking the safe window. Contracts deployed across chains with different block intervals may hit the expiration unexpectedly.
Why it matters: The 256-block limit is a hard protocol constraint. Contracts that fail to enforce reveal deadlines within this window give attackers a free option: play if the hash is favorable, wait it out for a zero hash if not.
T4: Validator/Proposer Manipulation (High)
Post-merge Ethereum uses a proof-of-stake consensus where the block proposer is known in advance and controls the block’s contents. A proposer who is also participating in a blockhash-dependent contract can manipulate outcomes:
-
Block withholding. The proposer computes the block hash that would result from their proposed block. If the hash produces an unfavorable outcome in a blockhash-dependent contract, the proposer can skip their slot (forfeiting the block reward and MEV) and wait for the next proposer to produce a block with a different hash. This is economically rational whenever the contract’s payout exceeds the forgone block reward (~0.05 ETH for consensus rewards + variable MEV).
-
Transaction ordering and inclusion. The proposer selects which transactions to include and in what order. Since the block hash depends on the full block contents (including all transactions and their ordering), the proposer can reorder transactions or include/exclude specific transactions to influence the resulting hash.
-
MEV searcher collusion. Even non-proposer validators can collude with MEV searchers through Flashbots or similar systems. Searchers can bundle transactions that exploit blockhash-dependent contracts, with the proposer receiving a share of the profits for including the bundle.
Why it matters: Post-merge Ethereum makes proposer identity predictable (via the beacon chain lookahead), which means any blockhash-dependent contract with sufficient value will attract rational manipulation from the upcoming proposer.
T5: Commit-Reveal Schemes Using Blockhash Can Be Gamed (Medium)
Commit-reveal protocols that use a future blockhash as the randomness source after participants commit their choices are vulnerable to a last-revealer advantage:
-
Selective revelation. In a commit-reveal game, the last player to reveal can compute the outcome using the now-known blockhash and their committed value. If the outcome is unfavorable, they simply don’t reveal (forfeiting their commit deposit but avoiding a larger loss). This gives the last revealer a free option.
-
Proposer-participant collusion. If the proposer for the block whose hash will serve as randomness is also a game participant (or is colluding with one), they can influence both the block hash and the reveal timing, gaining near-complete control over the outcome.
-
Commit deposit economics. The security of commit-reveal depends on the commit deposit being high enough to make non-revelation unprofitable. If the game’s payout exceeds the deposit, rational players will always defect when the blockhash-derived outcome is unfavorable.
Why it matters: Commit-reveal is often recommended as an improvement over raw blockhash randomness, but it only shifts the attack surface from prediction to selective revelation. Without external, non-manipulable randomness (e.g., Chainlink VRF), the fundamental problem persists.
Protocol-Level Threats
P1: EIP-2935 — Extended Block Hash History (Low)
EIP-2935 (“Serve historical block hashes from state”), included in the Pectra upgrade, stores the last 8,191 block hashes in a system contract at 0x0000F90827F1C53a10cb7A02335B175320002935. Crucially, the BLOCKHASH opcode itself is unchanged — it still returns 0 for blocks older than 256. The extended history is available only by querying the system contract via SLOAD.
Security implications:
- Does not fix the randomness problem. Accessing older block hashes via the system contract does not make them less predictable — they were finalized long ago and are publicly known.
- Reduces the 256-block window attack. Contracts that need to verify a historical block hash (for proofs, not randomness) can now query beyond 256 blocks via the system contract, closing the delayed-revelation-to-zero attack for verification use cases.
- L2 and stateless client benefits. The primary motivation is enabling stateless clients and cross-chain verification, not improving randomness.
P2: Validator MEV and Blockhash-Dependent Protocols (Medium)
At the protocol level, any mechanism that lets validators profit from influencing block hashes creates an MEV externality. Blockhash-dependent contracts that secure significant value effectively become MEV opportunities for proposers:
- The proposer can compute outcomes for different block constructions and select the most profitable one.
- As proposer-builder separation (PBS) matures, block builders who construct blocks for proposers gain the same influence over block hashes.
- This creates a protocol-level incentive misalignment: the security of blockhash-dependent applications depends on proposers acting against their economic interest.
P3: Consensus Safety (Low)
BLOCKHASH is deterministic across all client implementations — it reads from the block header chain, which is identical on every node. No consensus bugs have been attributed to BLOCKHASH. The introduction of EIP-2935’s system contract adds a new code path for historical hash retrieval, but the BLOCKHASH opcode itself remains unchanged.
Edge Cases
| Edge Case | Behavior | Security Implication |
|---|---|---|
blockhash(block.number) — current block | Returns 0 | Most common BLOCKHASH bug. Developers expect the current block’s hash but get zero. Any randomness derived from this value is constant. |
blockhash(block.number - 1) — previous block | Returns the valid hash of the parent block | This is the most recent valid blockhash, but it’s publicly known to every contract and observer in the same block. Not secret or unpredictable. |
blockhash(block.number - 256) — oldest valid block | Returns the valid hash | Boundary case: still within the 256-block window. |
blockhash(block.number - 257) — just expired | Returns 0 | Off by one from the valid window. Contracts that store a block number and reveal later hit this when exactly 257 blocks pass. |
blockhash(futureBlockNumber) — future block | Returns 0 | Future block hashes don’t exist yet. Returns 0, not an error. |
blockhash(0) — genesis block | Returns 0 (unless within 256 blocks of genesis) | On mainnet, always 0 since block 0 is millions of blocks ago. On fresh testnets/devnets, may return the actual genesis hash if block.number < 256. |
| Block number > 2^256 - 1 (overflow) | Input is taken modulo 2^256 due to EVM 256-bit arithmetic | Practically unreachable; no security concern on any real chain. |
blockhash after a chain reorganization | Returns hash of the canonical chain’s block | During a reorg, the hash changes retroactively. Contracts that cached a blockhash off-chain may see a different value on-chain post-reorg. |
Real-World Exploits
Exploit 1: SmartBillions Lottery — $120K Drained via Blockhash Prediction (October 2017)
Root cause: The SmartBillions lottery contract used blockhash as its sole source of randomness, allowing attackers to predict winning numbers by computing the same hash in an attacking contract.
Details: SmartBillions was an Ethereum-based lottery that publicly challenged hackers to break their smart contract, offering 1,500 ETH (~$450,000) as a bounty. The lottery generated winning numbers using blockhash(block.number - 1) combined with other on-chain values. Two attackers exploited the fundamental flaw: since blockhash returns a value visible to all contracts in the same block, the attackers deployed contracts that computed the lottery outcome in the same transaction and only participated when guaranteed to win.
The attackers drained 400 ETH (~$120,000) before SmartBillions emergency-withdrew the remaining funds. The attack was trivial: the attacker’s contract called blockhash(block.number - 1), computed the lottery result, and called the lottery’s play function only when the result was a win — all atomically within a single transaction.
BLOCKHASH’s role: The entire attack hinged on BLOCKHASH being deterministic and publicly readable. Any contract executing in the same block computes the identical hash, making prediction a matter of replicating the lottery’s arithmetic.
Impact: ~$120K stolen. SmartBillions had to pause the contract and redesign their randomness scheme. The incident became a canonical example of why BLOCKHASH must not be used for randomness.
References:
- SmartBillions: Hackathon smart contract hacked with $120,000
- CoinGeek: SmartBillions bet $450K they can’t be hacked — then got hacked
Exploit 2: Fomo3D — Block Stuffing + Blockhash-Based Airdrop Manipulation (August 2018)
Root cause: Fomo3D used blockhash-based randomness for its airdrop lottery and a “last buyer wins” mechanic that was exploitable via block stuffing. The attacker combined both to guarantee profits.
Details: Fomo3D was one of Ethereum’s most popular gambling games, with a prize pool exceeding 10,000 ETH. The game had two attack surfaces related to block data:
-
Airdrop randomness: The airdrop lottery used
keccak256(abi.encodePacked(block.timestamp, block.difficulty, ...))to determine winners. Attackers deployed contracts that pre-computed the airdrop outcome and only purchased keys when the airdrop was guaranteed. -
Block stuffing for the grand prize: The grand prize went to the last key purchaser when a countdown timer expired. The attacker (address
0xa169) deployed a contract that monitored the game state viagetCurrentRoundInfo(). When the timer was nearly expired and the attacker was the last buyer, the contract submitted high-gas transactions (190-501 gwei) designed to fill entire blocks, preventing competing purchases for3 minutes. The attacker won 10,469 ETH ($3M at the time).
The attacker’s contract used assert() to intentionally consume all gas in failed transactions, ensuring blocks were fully stuffed even when the transactions reverted.
BLOCKHASH’s role: The airdrop exploit directly used blockhash-based randomness prediction. The grand prize exploit used block-level manipulation (stuffing) rather than blockhash directly, but both attacks relied on the predictability and manipulability of block-level data that BLOCKHASH exposes.
Impact: 10,469 ETH (~$3M) won through block stuffing in round 1 alone. Subsequent rounds (Last Winner, Super Card) were similarly exploited with declining returns as the techniques became widely known.
References:
- Apriorit: Blockchain Vulnerabilities — Fomo3D Exploit Explained
- SECBIT: How the Winner Got Fomo3D Prize — A Detailed Explanation
Exploit 3: 1000 Guess — PRNG Prediction via Block Variables (2018, CVE-2018-12454)
Root cause: The 1000 Guess lottery used sha256(block.number, block.timestamp, block.coinbase, block.difficulty, ...) combined with “private” contract variables as its PRNG. All inputs were publicly readable, making the output fully predictable.
Details: 1000 Guess was an Ethereum lottery game that combined multiple block variables — including values derivable from or related to BLOCKHASH — with contract storage variables marked as private in Solidity. The developers assumed that private variables were secret, but all contract storage is publicly readable via eth_getStorageAt. An attacker read the “private” seed values from storage, combined them with the publicly known block variables, and pre-computed winning outcomes.
This exploit was assigned CVE-2018-12454 and was representative of a broader class: researchers analyzing 3,649 Ethereum contracts found 43 with vulnerable PRNG implementations using blockhash and related block variables.
BLOCKHASH’s role: While the contract used multiple block variables rather than blockhash directly, the vulnerability class is identical: all block-level data (blockhash, timestamp, difficulty/PREVRANDAO, coinbase) is deterministic and publicly known. BLOCKHASH is the most commonly used of these for randomness, and its predictability underpins the entire class of attacks.
Impact: Direct financial losses from the 1000 Guess exploit were modest, but CVE-2018-12454 documented the systematic vulnerability across dozens of contracts. The broader research found 43 exploitable contracts among those analyzed.
References:
- CVE-2018-12454: Attack on PRNG in 1000 Guess
- Predicting Random Numbers in Ethereum Smart Contracts (Positive Technologies)
Exploit 4: Roast Football Protocol — Blockhash/Block Variable Lottery Drain (December 2022)
Root cause: Roast Football’s lottery function used keccak256(abi.encodePacked(block.number, block.timestamp, buyer, _balances[pair])) for randomness. All inputs were publicly observable on-chain.
Details: The attacker observed that every input to the hash function was deterministic and publicly readable at the time of transaction execution. They deployed a contract that pre-computed the lottery outcome using the same inputs and only submitted transactions when the result was a win. The attacker won twice in fifty attempts, extracting 12 BNB from the protocol on BSC (Binance Smart Chain).
BLOCKHASH’s role: The contract used block.number and block.timestamp rather than blockhash directly, but the vulnerability is the same class — reliance on publicly known, deterministic block data for randomness. blockhash(block.number - 1) would have been equally exploitable.
Impact: 12 BNB stolen. While modest in dollar terms, the exploit demonstrated that blockhash-class randomness vulnerabilities persist well into 2022, years after the SmartBillions and Fomo3D incidents.
References:
Attack Scenarios
Scenario A: Same-Transaction Blockhash Prediction
// Vulnerable lottery: uses previous block's hash as randomness
contract VulnerableLottery {
uint256 public prize;
function play(uint256 guess) external payable {
require(msg.value == 1 ether);
prize += msg.value;
uint256 answer = uint256(blockhash(block.number - 1)) % 10;
if (guess == answer) {
payable(msg.sender).transfer(prize);
prize = 0;
}
}
}
// Attacker: computes the same blockhash and only plays when winning
contract LotteryExploit {
function attack(VulnerableLottery lottery) external payable {
// Compute the exact same value the lottery will compute
uint256 answer = uint256(blockhash(block.number - 1)) % 10;
// Always win -- same block, same hash, same answer
lottery.play{value: 1 ether}(answer);
}
receive() external payable {}
}Scenario B: 256-Block Window Expiration Attack
// Vulnerable commit-reveal without reveal deadline enforcement
contract VulnerableCommitReveal {
struct Commit {
bytes32 hash;
uint256 revealBlock;
}
mapping(address => Commit) public commits;
function commit(bytes32 commitHash) external payable {
require(msg.value == 1 ether);
// Store block number + 10 as the randomness source
commits[msg.sender] = Commit(commitHash, block.number + 10);
}
function reveal(uint256 choice, bytes32 salt) external {
Commit memory c = commits[msg.sender];
require(keccak256(abi.encodePacked(choice, salt)) == c.hash);
// BUG: No deadline check. If >256 blocks pass, blockhash returns 0.
uint256 randomness = uint256(blockhash(c.revealBlock));
// Attacker waits 257+ blocks, randomness is always 0
uint256 outcome = (uint256(keccak256(
abi.encodePacked(randomness, choice)
))) % 2;
if (outcome == 1) {
payable(msg.sender).transfer(2 ether);
}
delete commits[msg.sender];
}
}
// Attacker: waits for the 256-block window to expire
// then computes: keccak256(abi.encodePacked(0, choice)) and reveals
// only if the result is favorable. If not, forfeits 1 ETH commit.Scenario C: Validator Block Withholding
// High-value lottery using blockhash of a future block
contract HighStakesLottery {
uint256 public drawBlock;
uint256 public pot;
mapping(uint256 => address[]) public tickets;
function buyTicket(uint256 number) external payable {
require(msg.value == 10 ether);
tickets[number].push(msg.sender);
pot += msg.value;
}
function setDrawBlock() external {
// Draw will use the hash of a block 5 blocks in the future
drawBlock = block.number + 5;
}
function draw() external {
require(block.number > drawBlock);
require(block.number <= drawBlock + 256);
uint256 winningNumber = uint256(blockhash(drawBlock)) % 1000;
address[] memory winners = tickets[winningNumber];
// ... distribute pot to winners ...
}
}
// Attack vector (requires being the block proposer at drawBlock):
// 1. Proposer for slot at drawBlock buys tickets for multiple numbers
// 2. Proposer computes hash of their proposed block
// 3. If hash % 1000 matches one of their tickets: propose the block
// 4. If not: skip the slot (forfeit ~0.05 ETH block reward)
// 5. When pot >> block reward, this is profitable in expectationScenario D: Current Block Hash Zero-Value Exploit
// Vulnerable: uses blockhash(block.number) which ALWAYS returns 0
contract BrokenRaffle {
address[] public players;
function enter() external payable {
require(msg.value == 0.1 ether);
players.push(msg.sender);
}
function pickWinner() external {
require(players.length >= 10);
// BUG: blockhash(block.number) is always 0!
uint256 index = uint256(blockhash(block.number)) % players.length;
// index is always 0 % players.length == 0
// The first player entered ALWAYS wins
payable(players[0]).transfer(address(this).balance);
delete players;
}
}
// Attacker: be the first player to enter. Always wins.Mitigations
| Threat | Mitigation | Implementation |
|---|---|---|
| T1: Predictable randomness | Use Chainlink VRF or equivalent verifiable randomness oracle | Chainlink VRF v2/v2.5 provides cryptographically proven random values that neither the oracle nor the contract deployer can predict or manipulate |
| T1: Same-transaction prediction | Separate the randomness-determining block from the action block | Use a commit-reveal scheme where the random seed comes from a block mined after the user’s commitment, combined with Chainlink VRF |
| T2: Current block returns 0 | Never use blockhash(block.number) | Static analysis tools (Slither, Mythril) flag this pattern. Always use block.number - 1 at minimum, though this is still predictable |
| T3: 256-block expiration | Enforce reveal deadlines within the 256-block window | require(block.number <= commitBlock + 250) — use 250, not 256, to provide a safety margin |
| T3: Delayed revelation to zero | Add a penalty for non-revelation | Slash the commit deposit if reveal is not submitted within the deadline |
| T4: Validator manipulation | Use external randomness that validators cannot influence | Chainlink VRF, RANDAO with commit-reveal, or threshold BLS signatures from a decentralized committee |
| T4: Block withholding economics | Ensure no single outcome exceeds the block reward | Impractical for high-value applications; use VRF instead |
| T5: Commit-reveal last-revealer advantage | Require simultaneous reveals or use threshold cryptography | Threshold secret sharing ensures no single participant can compute the outcome without cooperation |
| General: All randomness threats | Never use blockhash (or any block variable) as a sole randomness source | Treat blockhash, block.timestamp, block.prevrandao, and block.number as publicly known, validator-influenceable values |
Compiler/EIP-Based Protections
- Chainlink VRF v2/v2.5: The industry-standard solution for on-chain randomness. Provides a verifiable random number with a cryptographic proof that the VRF coordinator cannot manipulate. Supported on Ethereum, Polygon, Arbitrum, Optimism, Avalanche, BSC, and more.
- EIP-4399 / PREVRANDAO (The Merge, 2022): Replaced
block.difficultywithPREVRANDAO, which provides RANDAO randomness from the beacon chain. While better thanblockhash, it is still biasable by the block proposer (who can choose to skip a slot) and should not be used as a sole randomness source for high-value applications. - EIP-2935 (Pectra): Extends historical block hash availability to 8,191 blocks via a system contract. Does not change BLOCKHASH opcode semantics but allows contracts to verify historical block hashes beyond the 256-block window through storage reads.
- Slither / Mythril / Semgrep: Static analysis tools that detect
blockhash(block.number)and insecure randomness patterns. Slither’sweak-prngdetector specifically flags blockhash-based randomness.
Severity Summary
| Threat ID | Category | Severity | Likelihood | Real-World Precedent |
|---|---|---|---|---|
| T1 | Smart Contract | Critical | High | SmartBillions ($120K), Fomo3D airdrop, dozens of lottery exploits |
| T2 | Smart Contract | High | High | Recurring audit finding; contracts with blockhash(block.number) deploy regularly |
| T3 | Smart Contract | High | Medium | Delayed-reveal attacks on commit-reveal lotteries |
| T4 | Smart Contract | High | Medium | Economically rational for high-value lotteries post-merge; Fomo3D block manipulation |
| T5 | Smart Contract | Medium | Medium | Theoretical but well-documented; commit deposit economics usually deter |
| P1 | Protocol | Low | Low | EIP-2935 in Pectra — expands access, doesn’t change BLOCKHASH semantics |
| P2 | Protocol | Medium | Medium | MEV extraction from blockhash-dependent contracts is a known proposer incentive |
| P3 | Protocol | Low | Low | No consensus bugs attributed to BLOCKHASH |
Related Opcodes
| Opcode | Relationship |
|---|---|
| PREVRANDAO (0x44) | Returns the beacon chain RANDAO value (replaced DIFFICULTY post-merge). Better entropy source than BLOCKHASH but still biasable by the block proposer. Neither should be used alone for high-value randomness. |
| TIMESTAMP (0x42) | Returns the current block’s timestamp. Frequently combined with BLOCKHASH for randomness — both are publicly known and validator-influenceable, so combining them adds no meaningful security. |
| NUMBER (0x43) | Returns the current block number. Used to reference which block’s hash to retrieve via BLOCKHASH. Also used (insecurely) as a randomness input alongside blockhash. |
| COINBASE (0x41) | Returns the block proposer’s (formerly miner’s) address. Sometimes used as a randomness input, but the proposer controls this value, making it trivially manipulable. |