Skip to content
Last updated: March 11, 2025

SCWE-031: Insecure use of Block Variables

Stable Version v0.0.1

This content is in the version-(v0.0.1) and still under active development, so it is subject to change any time (e.g. structure, IDs, content, URLs, etc.).

Send Feedback

Relationships

Description

In blockchain networks like Ethereum, block variables (block.timestamp, block.number, block.difficulty, etc.) provide information about the current state of the blockchain. However, these values are not fully deterministic and can be manipulated by miners, leading to vulnerabilities in smart contracts.

Block timestamps are not guaranteed to be accurate or consistent, and miners can influence them within a certain range. This can cause issues when contracts depend on precise timing for critical functionality, such as token distribution, access control, or other time-sensitive events.

Potential issues that arise from insecure timestamp usage include:

  • Timestamp Manipulation: Miners can slightly alter block.timestamp to influence time-sensitive logic (e.g., auctions, token distributions, staking rewards).
  • Predictable Randomness: Using block.number or block.difficulty as a source of randomness allows attackers to predict and manipulate outcomes.
  • Exploitable Access Control: Contracts that rely on block timestamps for permissions or actions may be bypassed if timestamps are adjusted.

Remediation

  • Avoid timestamp-based conditions: Where possible, use block numbers instead of timestamps. Block numbers are more reliable and less subject to manipulation.
  • Use Oracles: For time-sensitive contracts, consider using trusted oracles to provide external time data.

Examples

Insecure Block Timestamp Usage- Timestamp-Based Deadlines

pragma solidity ^0.4.0;

contract TimestampExample {
    uint public deadline;

    function setDeadline(uint _deadline) public {
        deadline = _deadline;
    }

    function checkDeadline() public view returns (string) {
        if (now > deadline) {
            return "Deadline passed";
        } else {
            return "Deadline not passed";
        }
    }
}

In the above example, the now keyword retrieves the block's timestamp to compare with the deadline. This creates a potential vulnerability as miners can manipulate the block timestamp within a predefined window.

Fixed Block Timestamp Usage

pragma solidity ^0.4.0;

contract SafeTimestampExample {
    uint public deadline;
    uint public blockNumber;

    function setDeadline(uint _deadline) public {
        deadline = _deadline;
        blockNumber = block.number;
    }

    function checkDeadline() public view returns (string) {
        if (block.number > blockNumber + 1000) { // Assuming a reasonable number of blocks for a deadline
            return "Deadline passed";
        } else {
            return "Deadline not passed";
        }
    }
}
In this fixed version, the contract uses block.number instead of now. This makes the contract less susceptible to timestamp manipulation, as block numbers are more reliable and consistent.

Insecure Lottery Using block.timestamp

pragma solidity ^0.8.0;

contract InsecureLottery {
    address[] public players;

    function enter() public payable {
        require(msg.value > 0.01 ether, "Minimum ETH required");

        players.push(msg.sender);
    }

    function pickWinner() public {
        uint index = uint(block.timestamp) % players.length; // Insecure: Predictable outcome
        payable(players[index]).transfer(address(this).balance);
    }
}
Issue: - Predictability: Since block.timestamp is manipulable within a small range, miners can influence the winner selection. - Attack Vector: A miner could reorder transactions to ensure a specific outcome

Secure Alternative

pragma solidity ^0.8.0;

contract SecureLottery {
    address[] public players;

    function enter() public payable {
        require(msg.value > 0.01 ether, "Minimum ETH required");
        players.push(msg.sender);
    }

    function pickWinner() public {
        require(players.length > 0, "No players joined");
        uint index = uint(keccak256(abi.encodePacked(block.prevrandao, msg.sender, players.length))) % players.length;
        payable(players[index]).transfer(address(this).balance);
    }
}
Fixes: - Uses block.prevrandao (EIP-4399) as an unpredictable source of randomness. - Adds entropy from msg.sender and players.length to prevent miner manipulation