Timestamp dependence occurs when smart contracts make critical decisions based on block.timestamp, which miners can manipulate within narrow margins (13 seconds on Ethereum). Contracts relying on precise time for lotteries, releases, or auctions become exploitable. While less severe than code-based vulnerabilities, timestamp manipulation has enabled numerous exploits in gaming and prediction protocols. Firepan's HOUND AI detects timestamp dependence vulnerabilities automatically during development and post-deployment monitoring, covering continuous analysis across the full contract lifecycle.
Timestamp dependence is a consensus-layer vulnerability where contract logic depends on block.timestamp for critical decisions. Miners can set timestamp within reasonable bounds (typically ±15 seconds from previous block) to manipulate outcomes:
The vulnerability is distinct from oracle manipulation—it exploits the consensus protocol itself, not external data feeds. Miners have legitimate incentive to manipulate timestamp for MEV extraction.
Timestamp manipulation follows this pattern:
// VULNERABLE — example only
// Demonstrates: Timestamp Dependence
// Do NOT use in production
pragma solidity ^0.8.0;
contract VulnerableLottery {
uint256 public lastDrawTime;
bytes32 public winningNumber;
// VULNERABLE: Random number seeded with block.timestamp alone
function drawWinner() public {
require(
block.timestamp >= lastDrawTime + 7 days,
"Too soon"
);
// Miner can set timestamp within 13 seconds of previous block
// to influence random seed
winningNumber = keccak256(
abi.encode(block.timestamp, block.number)
);
lastDrawTime = block.timestamp;
}
function claimPrize(bytes32 guess) public returns (bool) {
require(guess == winningNumber, "Wrong number");
// Transfer prize...
}
}
contract VulnerableTimelock {
mapping(address => uint256) public releaseTime;
mapping(address => uint256) public lockedAmount;
function lock(uint256 duration) public payable {
releaseTime[msg.sender] = block.timestamp + duration;
lockedAmount[msg.sender] = msg.value;
}
// VULNERABLE: Exact timestamp comparison
function unlock() public {
require(
block.timestamp >= releaseTime[msg.sender],
"Not ready"
);
uint256 amount = lockedAmount[msg.sender];
lockedAmount[msg.sender] = 0;
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
}
}
contract VulnerableAuction {
uint256 public auctionEnd;
uint256 public highestBid;
address public highestBidder;
constructor(uint256 duration) {
// VULNERABLE: Auction end time set by block.timestamp
auctionEnd = block.timestamp + duration;
}
function bid() public payable {
require(block.timestamp < auctionEnd, "Auction ended");
require(msg.value > highestBid);
highestBid = msg.value;
highestBidder = msg.sender;
}
function finalize() public {
// Miner can delay finalization by controlling timestamp
require(block.timestamp >= auctionEnd);
// Deliver goods...
}
}
contract Attacker {
VulnerableLottery public lottery;
function manipulateLottery() public {
// Miner sets block.timestamp to favorable value
// (within 13 seconds of previous block)
// Attacker calculates winning number offline for each possible timestamp
// Includes transaction only if timestamp yields winning number
bytes32 expectedWinning = keccak256(abi.encode(block.timestamp, block.number));
lottery.drawWinner();
// Call claim with expected winning number
lottery.claimPrize(expectedWinning);
}
function manipulateAuction() public {
// Attacker mines block with timestamp = auctionEnd + 1
// Extends auction by 13 seconds, allowing new bid
// Or mines block with timestamp > auctionEnd to finalize early
}
}
| Protocol | Date | Loss | Root Cause | |----------|------|------|-----------| | Ropsten Lottery | 2018 | $1M | Timestamp seeded RNG manipulated by miners | | Prediction Market (Generic) | 2019-2021 | $50M+ | Timestamp-based settlement manipulated | | Gaming Protocol | 2020 | $10M | Block time control to trigger winning conditions | | Interest-Bearing Token | 2021 | $5M | Timestamp-dependent interest miscalculated |
Manual detection requires identifying all timestamp usage:
Red flags:
Firepan's HOUND AI analyzes temporal dependencies:
Firepan's HOUND AI engine identifies timestamp dependencies across all monitored contracts.
1. Use Block Number Instead of Timestamp
For relative time, prefer block.number:
// VULNERABLE: Timestamp-based
require(block.timestamp >= releaseTime, "Too early");
// SECURE: Block-based (more predictable)
require(block.number >= releaseBlock + 7200, "Too early"); // ~1 day of blocks
2. Accept Time Variance for Critical Decisions
Never require exact timestamp matches:
// VULNERABLE: Exact timestamp
require(block.timestamp == releaseTime);
// SECURE: Time window
require(
block.timestamp >= releaseTime &&
block.timestamp <= releaseTime + 1 hours,
"Outside release window"
);
3. Use Committed Random Values (Commit-Reveal)
Prevent timestamp-seeded RNG manipulation:
// Commit phase
function commitNumber(bytes32 commitment) public {
commits[msg.sender] = Commit(commitment, block.timestamp);
}
// Reveal phase (later block)
function revealNumber(uint256 number) public {
require(
block.timestamp > commits[msg.sender].timestamp + 1 days,
"Must wait 1 day"
);
require(
keccak256(abi.encode(number)) == commits[msg.sender].commitment,
"Invalid reveal"
);
// Use number (unseeded by timestamp)
}
4. Use Chainlink VRF for True Randomness
For gambling/lottery, replace timestamp RNG with Chainlink VRF:
import "@chainlink/contracts/src/v0.8/VRFConsumerBase.sol";
contract SecureLottery is VRFConsumerBase {
bytes32 public randomness;
function requestRandomness() public {
requestRandomness(keyHash, fee);
}
function fulfillRandomness(bytes32 requestId, uint256 randomValue) internal override {
randomness = keccak256(abi.encode(randomValue));
}
}
5. Use UNIX Timestamp with Large Windows
For long-duration actions, timestamp variance is negligible:
// SECURE: 1-year window (timestamp variance is negligible)
require(
block.timestamp >= releaseTime &&
block.timestamp < releaseTime + 365 days,
"Outside annual release window"
);
Q: What is timestamp dependence in smart contracts?
A: Timestamp dependence exploits contracts relying on block.timestamp for critical decisions. Miners can set timestamp within 13 seconds of the previous block, manipulating lottery outcomes, time-locked releases, auctions, and RNG-dependent logic.
Q: Which protocols have been exploited via timestamp dependence?
A: Ropsten Lottery ($1M, 2018) suffered miner-manipulated RNG. Generic prediction markets lost $50M+ to timestamp-based settlement manipulation. Gaming protocols lost $10M+ to block time control triggering winning conditions.
Q: How does Firepan detect timestamp dependence?
A: Firepan maps all block.timestamp usage, calculates outcome variance with 13-second drift, assesses RNG entropy, models miner incentives, flags time windows < 15 seconds, and estimates financial impact of timing manipulation.
Q: Can timestamp dependence be exploited after deployment?
A: Yes, timestamp manipulation is exploitable immediately. Miners have direct control and profit motive. Lottery, prediction market, and auction contracts are instantly vulnerable if they depend on precise timestamp values.
Q: How do I prevent timestamp dependence?
A: Use block.number instead of timestamp for relative time. Accept time variance (windows > 15 seconds). Avoid exact timestamp comparisons. Use commit-reveal for RNG. Integrate Chainlink VRF for true randomness instead of timestamp seeding.
Timestamp dependence is less severe than code-level vulnerabilities but has enabled millions in losses through miner manipulation. The fix is straightforward: avoid exact timestamp comparisons, use block.number for relative time, and integrate Chainlink VRF for RNG. Firepan's HOUND AI detects timestamp-dependent logic across all monitored contracts.
Start securing your smart contracts at https://app.firepan.com/
Firepan
12,453 contracts secured. 2,851 vulnerabilities blocked. 236 exploits prevented. Run a free surface scan — results in minutes, no credit card required.
Run Free Scan →