Beatland Festival

First Flight #44
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Impact: low
Likelihood: medium
Invalid

timestamp


Description

  • block.timestamp (or block.timestamp / now) is a global variable in Solidity that represents the Unix timestamp of the current block. While commonly used for time-based logic, it is important to understand its limitations.

  • The issue is that block.timestamp is not perfectly precise and is susceptible to manipulation by miners. Miners have a small degree of control over the timestamp of the blocks they mine; they can slightly adjust it (typically within a range of a few seconds to a few minutes relative to real-world time) to influence time-sensitive operations. If a miner has a direct economic incentive, they might attempt to use this to gain an advantage (e.g., by being the first to enter a time-gated event or extending a deadline).

SLITHER OUTPUT:

## timestamp Impact: Low Confidence: Medium - [ ] ID-17
[FestivalPass.createPerformance(uint256,uint256,uint256)](src/FestivalPass.sol#L88-L103) uses timestamp for comparisons Dangerous comparisons: - [require(bool,string)(startTime > block.timestamp,Start time must be in the future)](src/FestivalPass.sol#L93) src/FestivalPass.sol#L88-L103 - [ ] ID-18 [FestivalPass.attendPerformance(uint256)](src/FestivalPass.sol#L106-L117) uses timestamp for comparisons
Dangerous comparisons:
- [require(bool,string)(block.timestamp >= lastCheckIn[msg.sender] + COOLDOWN,Cooldown period not met)](src/FestivalPass.sol#L110)
src/FestivalPass.sol#L106-L117
- [ ] ID-19
[FestivalPass.isPerformanceActive(uint256)](src/FestivalPass.sol#L139-L144) uses timestamp for comparisons
Dangerous comparisons: - [perf.startTime != 0 && block.timestamp >= perf.startTime && block.timestamp <= perf.endTime](src/FestivalPass.sol#L141-L143)
src/FestivalPass.sol#L139-L144
// Root cause in the codebase with @> marks to highlight the relevant section
// ID-17: FestivalPass.createPerformance(uint256,uint256,uint256)
src/FestivalPass.sol#L93
require(startTime > block.timestamp, "Start time must be in the future"); // @> block.timestamp used for future validation
// ID-18: FestivalPass.attendPerformance(uint256)
src/FestivalPass.sol#L110
require(block.timestamp >= lastCheckIn[msg.sender] + COOLDOWN, "Cooldown period not met"); // @> block.timestamp used for cooldown check
// ID-19: FestivalPass.isPerformanceActive(uint256)
src/FestivalPass.sol#L141-L143
return block.timestamp >= perf.startTime && block.timestamp <= perf.endTime; // @> block.timestamp used to check active status

Risk

Likelihood:

  • This will occur if a miner finds a profitable opportunity to slightly manipulate the block timestamp to their advantage for time-sensitive features.

  • This will occur consistently in highly competitive, time-critical scenarios where a small time difference could determine success or failure.

Impact:

  • For very short-duration events or highly precise timing mechanisms, miner manipulation could lead to a minor shift in when an action becomes available or unavailable.

  • While generally low impact for events spanning hours or days, it poses a risk for very granular time-based access control or competitive actions.

  • The contract's time-dependent logic might not execute at the exact intended real-world time, leading to slight deviations.

Proof of Concept

// Scenario: A miner wants to attend a performance exactly at its startTime.
// FestivalPass.createPerformance(startTime, ...) sets `startTime`.
// Later, `attendPerformance` checks `block.timestamp >= perf.startTime`.
// If a miner is close to the `startTime` threshold, they could
// mine a block with a timestamp slightly ahead (or behind, for cooldowns)
// to make their transaction fall within the desired window, potentially
// giving them an unfair advantage over other users.
// Example: If an event starts at `T`, and a miner can set `block.timestamp` to `T - 15s` or `T + 15s`,
// they could effectively open or close a time window for themselves earlier/later.

Recommended Mitigation

(Review all block.timestamp usages. For the current use cases (performance start/end times, cooldowns), the impact is likely low, but it's a known consideration for high-value or highly competitive time-based logic.)

- remove this code
+ add this code
--- a/src/FestivalPass.sol
+++ b/src/FestivalPass.sol
@@ -92,7 +92,10 @@
function createPerformance(uint256 _performanceId, uint256 startTime, uint256 endTime) public onlyOwner {
require(_performanceId != 0, "Performance ID cannot be zero");
require(startTime < endTime, "Start time must be before end time");
- require(startTime > block.timestamp, "Start time must be in the future"); // @> Consider if precision is critical
+ // For non-critical timing, block.timestamp is generally acceptable.
+ // For highly time-sensitive or competitive scenarios, consider:
+ // 1. Using a decentralized oracle (e.g., Chainlink Keepers/Oracles) for time.
+ // 2. Designing game theory such that miner manipulation is not profitable or impactful.
require(performances[_performanceId].startTime == 0, "Performance already exists");
performances[_performanceId] = Performance({
Updates

Lead Judging Commences

inallhonesty Lead Judge 26 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Appeal created

deadmanxxxii Submitter
25 days ago
inallhonesty Lead Judge
24 days ago
inallhonesty Lead Judge 22 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.