This page is a placeholder. All examples on this page are currently AI-generated and are not correct. This documentation will be completed in the future with accurate, tested examples.
Overview
Opcode: 0x43
Introduced: Frontier (EVM genesis)
NUMBER retrieves the current block number - the sequential index of the block in the blockchain. Block numbers start at 0 (genesis) and increment by 1 for each new block.
Specification
Stack Input:
Stack Output:
Gas Cost: 2 (GasQuickStep)
Operation:
Behavior
NUMBER pushes the current block number onto the stack as a 256-bit unsigned integer:
Genesis Block: 0
First Block: 1
Millionth Block: 1,000,000
Current (2024): ~19,500,000
The block number is deterministic and strictly increasing, making it reliable for sequencing and versioning.
Examples
Basic Usage
import { number } from '@tevm/voltaire/evm/block';
import { createFrame } from '@tevm/voltaire/evm/Frame';
const frame = createFrame({
stack: [],
blockContext: {
block_number: 19_500_000n
}
});
const err = number(frame);
console.log(frame.stack); // [19500000n]
console.log(frame.gasRemaining); // Original - 2
Block Range Checks
// Check if within specific block range
const START_BLOCK = 19_000_000n;
const END_BLOCK = 20_000_000n;
number(frame);
const currentBlock = frame.stack[0];
const inRange = currentBlock >= START_BLOCK && currentBlock < END_BLOCK;
console.log(`In range: ${inRange}`);
Block Calculations
// Calculate blocks elapsed
const DEPLOYMENT_BLOCK = 18_000_000n;
number(frame);
const currentBlock = frame.stack[0];
const blocksElapsed = currentBlock - DEPLOYMENT_BLOCK;
// Estimate time elapsed (assuming 12 sec/block post-merge)
const secondsElapsed = blocksElapsed * 12n;
const daysElapsed = secondsElapsed / 86400n;
console.log(`Blocks: ${blocksElapsed}, Days: ~${daysElapsed}`);
Gas Cost
Cost: 2 gas (GasQuickStep)
NUMBER is one of the cheapest EVM operations.
Comparison:
NUMBER: 2 gas
TIMESTAMP, COINBASE, GASLIMIT: 2 gas
BLOCKHASH: 20 gas
SLOAD (cold): 2100 gas
Common Usage
Block-Based Scheduling
contract BlockScheduler {
uint256 public startBlock;
uint256 public endBlock;
constructor(uint256 duration) {
startBlock = block.number;
endBlock = block.number + duration;
}
function isActive() public view returns (bool) {
return block.number >= startBlock && block.number < endBlock;
}
}
Phased Rollout
contract PhasedDeployment {
uint256 public constant PHASE_1 = 19_000_000;
uint256 public constant PHASE_2 = 19_500_000;
uint256 public constant PHASE_3 = 20_000_000;
function currentPhase() public view returns (uint256) {
if (block.number < PHASE_1) return 0;
if (block.number < PHASE_2) return 1;
if (block.number < PHASE_3) return 2;
return 3;
}
function featureEnabled(uint256 phase) public view returns (bool) {
return currentPhase() >= phase;
}
}
Block-Based Rewards
contract BlockRewards {
uint256 public lastRewardBlock;
uint256 public rewardPerBlock = 1 ether;
function claimRewards() external {
uint256 pending = (block.number - lastRewardBlock) * rewardPerBlock;
lastRewardBlock = block.number;
payable(msg.sender).transfer(pending);
}
}
Version Control
contract Versioned {
struct Version {
uint256 blockNumber;
bytes32 codeHash;
}
Version[] public versions;
function upgrade(bytes32 newCodeHash) external {
versions.push(Version({
blockNumber: block.number,
codeHash: newCodeHash
}));
}
function versionAt(uint256 blockNum) public view returns (bytes32) {
for (uint i = versions.length; i > 0; i--) {
if (versions[i-1].blockNumber <= blockNum) {
return versions[i-1].codeHash;
}
}
return bytes32(0);
}
}
Block Number Checkpoint
contract Checkpoint {
mapping(address => uint256) public lastActionBlock;
modifier minBlockGap(uint256 gap) {
require(
block.number >= lastActionBlock[msg.sender] + gap,
"Too soon"
);
lastActionBlock[msg.sender] = block.number;
_;
}
function rateLimit() external minBlockGap(100) {
// Can only be called every 100 blocks (~20 minutes post-merge)
}
}
Security Considerations
Not Suitable for Randomness
Block numbers are predictable and should never be used for randomness:
// DANGEROUS: Completely predictable
function badRandom() public view returns (uint256) {
return uint256(keccak256(abi.encodePacked(block.number)));
}
Block Reorganizations
Block numbers can temporarily decrease during chain reorgs:
contract ReorgAware {
uint256 public highestBlockSeen;
function update() external {
// Possible: block.number < highestBlockSeen during reorg
if (block.number > highestBlockSeen) {
highestBlockSeen = block.number;
}
}
}
Future Block Conditions
Never check for exact future blocks:
// PROBLEMATIC: What if skipped?
require(block.number == 20_000_000); // Fragile
// BETTER: Use ranges
require(block.number >= 20_000_000); // Robust
Block Time Variability
Block production time varies by network and consensus:
contract TimeEstimation {
// Mainnet: ~12 sec/block (post-merge)
// Pre-merge: ~13.2 sec/block average
// L2s: Much faster (2-5 seconds)
function estimateTime(uint256 blocks) public pure returns (uint256) {
return blocks * 12; // seconds (Ethereum mainnet post-merge)
}
}
Block Number Overflow
Theoretical but not practical concern (would take millions of years):
// No overflow risk in practice
uint256 futureBlock = block.number + 1_000_000_000;
Implementation
/**
* NUMBER opcode (0x43) - Get block number
*/
export function number(frame: FrameType): EvmError | null {
// Consume gas (GasQuickStep = 2)
frame.gasRemaining -= 2n;
if (frame.gasRemaining < 0n) {
frame.gasRemaining = 0n;
return { type: "OutOfGas" };
}
// Push block number to stack
if (frame.stack.length >= 1024) return { type: "StackOverflow" };
frame.stack.push(frame.evm.blockContext.block_number);
frame.pc += 1;
return null;
}
Edge Cases
Genesis Block
// Block 0 (genesis)
const frame = createFrame({
blockContext: { block_number: 0n }
});
number(frame);
console.log(frame.stack); // [0n]
Large Block Number
// Far future block
const frame = createFrame({
blockContext: { block_number: 1_000_000_000n }
});
number(frame);
console.log(frame.stack); // [1000000000n]
Maximum u256 Block
// Theoretical maximum (impossible in practice)
const frame = createFrame({
blockContext: { block_number: (1n << 256n) - 1n }
});
number(frame);
console.log(frame.stack); // [max u256]
Stack Overflow
// Stack full (1024 items)
const frame = createFrame({
stack: new Array(1024).fill(0n),
blockContext: { block_number: 19_500_000n }
});
const err = number(frame);
console.log(err); // { type: "StackOverflow" }
Out of Gas
// Insufficient gas
const frame = createFrame({
gasRemaining: 1n,
blockContext: { block_number: 19_500_000n }
});
const err = number(frame);
console.log(err); // { type: "OutOfGas" }
Practical Patterns
Safe Block Range Checks
contract SafeBlockRange {
function isWithinRange(
uint256 start,
uint256 end
) public view returns (bool) {
require(start <= end, "Invalid range");
return block.number >= start && block.number < end;
}
}
Block-Based Epochs
contract Epochs {
uint256 public constant EPOCH_LENGTH = 7200; // ~24 hours
uint256 public genesisBlock;
constructor() {
genesisBlock = block.number;
}
function currentEpoch() public view returns (uint256) {
return (block.number - genesisBlock) / EPOCH_LENGTH;
}
function blocksUntilNextEpoch() public view returns (uint256) {
uint256 currentEpochStart = genesisBlock + (currentEpoch() * EPOCH_LENGTH);
uint256 nextEpochStart = currentEpochStart + EPOCH_LENGTH;
return nextEpochStart - block.number;
}
}
Hardfork Detection
contract HardforkAware {
// Example: Shanghai hardfork at block 17,034,870
uint256 public constant SHANGHAI_BLOCK = 17_034_870;
function isShanghaiActive() public view returns (bool) {
return block.number >= SHANGHAI_BLOCK;
}
function features() public view returns (string[] memory) {
if (block.number >= SHANGHAI_BLOCK) {
// PUSH0, warm coinbase, etc.
}
}
}
Historical Milestones
// Notable Ethereum block numbers
uint256 constant GENESIS = 0;
uint256 constant HOMESTEAD = 1_150_000;
uint256 constant DAO_FORK = 1_920_000;
uint256 constant BYZANTIUM = 4_370_000;
uint256 constant CONSTANTINOPLE = 7_280_000;
uint256 constant ISTANBUL = 9_069_000;
uint256 constant BERLIN = 12_244_000;
uint256 constant LONDON = 12_965_000; // EIP-1559
uint256 constant PARIS = 15_537_394; // The Merge
uint256 constant SHANGHAI = 17_034_870;
uint256 constant CANCUN = 19_426_587; // EIP-4844
Benchmarks
Performance:
- Stack push: O(1)
- No computation required
Gas efficiency:
- 2 gas per query
- ~500,000 queries per million gas
References