SCWE-065: Block Values as a Proxy for Time
Stable Version v1.0
This content is in the version-(v1.0) and still under active development, so it is subject to change any time (e.g. structure, IDs, content, URLs, etc.).
Relationships¶
- CWE-20: Improper Input Validation
https://cwe.mitre.org/data/definitions/20.html
Description¶
Using block values (such as block.timestamp, block.number, or block.difficulty / block.prevrandao post-merge) as a proxy for time in Ethereum smart contracts can be problematic. Block values are determined by validators (or miners on PoW chains) and can be manipulated within certain limits, making them unreliable for time-sensitive logic. Relying on these values for critical decisions like deadlines or expiration dates can result in unexpected behaviors, such as manipulations by validators (or miners on PoW chains) or unintended contract states.
Remediation¶
To mitigate this vulnerability, account for the manipulation bounds of block values. block.timestamp can vary by ~15 seconds per block; design deadlines and time checks with this tolerance in mind. Time oracles are uncommon; the standard approach is to use block.timestamp with appropriate buffers. Where precision is critical, consider commit-reveal or multi-block confirmation.
Vulnerable Contract Example¶
contract TimeSensitive {
uint public deadline;
constructor(uint _deadline) {
deadline = _deadline;
}
function hasExpired() public view returns (bool) {
return block.timestamp > deadline; // Relies on block.timestamp
}
}
Fixed Contract Example¶
pragma solidity ^0.8.0;
contract TimeSensitive {
uint public constant DEADLINE_BUFFER = 15; // seconds — accounts for block.timestamp manipulation
uint public deadline;
constructor(uint _deadline) {
// Set deadline with buffer so expiry is robust to ~15s timestamp variance
deadline = _deadline + DEADLINE_BUFFER;
}
function hasExpired() public view returns (bool) {
return block.timestamp > deadline;
}
}
block.timestamp. Avoid relying on sub-minute precision.