Block timestamps have historically been used for a variety of
applications, such as entropy for random numbers (see the Entropy
Illusion for further details), locking funds for periods of time, and
various state-changing conditional statements that are time-dependent.
Miners have the ability to adjust timestamps slightly, which can prove
to be dangerous if block timestamps are used incorrectly in smart
contracts.
Block timestamps should not be used for entropy or generating random
numbers—i.e., they should not be the deciding factor (either directly or
through some derivation) for winning a game or changing an important
state.
Time-sensitive logic is sometimes required; e.g., for unlocking
contracts (time-locking), completing an ICO after a few weeks, or
enforcing expiry dates. It is sometimes recommended to use block.number
and an average block time to estimate times; with a 10 second block
time, 1 week equates to approximately, 60480 blocks. Thus, specifying a
block number at which to change a contract state can be more secure, as
miners are unable to easily manipulate the block number.
Use of Block.timestamp
Block timestamps have historically been used for a variety of applications, such as entropy for random numbers (see the Entropy Illusion for further details), locking funds for periods of time, and various state-changing conditional statements that are time-dependent. Miners have the ability to adjust timestamps slightly, which can prove to be dangerous if block timestamps are used incorrectly in smart contracts.
Instances:
reference/lib/ReferenceVerifiers.sol:48: if (startTime > block.timestamp || endTime <= block.timestamp) { reference/lib/ReferenceOrderFulfiller.sol:166: (block.timestamp - orderParameters.startTime), reference/lib/ReferenceOrderFulfiller.sol:168: (block.timestamp - orderParameters.startTime)) reference/lib/ReferenceOrderCombiner.sol:244: uint256 elapsed = block.timestamp - startTime; contracts/lib/OrderFulfiller.sol:164: uint256 elapsed = block.timestamp - orderParameters.startTime; contracts/lib/Verifiers.sol:43: if (startTime > block.timestamp || endTime <= block.timestamp) { contracts/lib/OrderCombiner.sol:238: uint256 elapsed = block.timestamp - startTime; test/foundry/FulfillBasicOrderTest.sol:216: basicOrderParameters.startTime = block.timestamp; test/foundry/FulfillBasicOrderTest.sol:217: basicOrderParameters.endTime = block.timestamp + 100; test/foundry/FulfillBasicOrderTest.sol:241: basicOrderParameters.startTime = block.timestamp; test/foundry/FulfillBasicOrderTest.sol:242: basicOrderParameters.endTime = block.timestamp + 100; test/foundry/FulfillBasicOrderTest.sol:282: orderComponents.startTime = block.timestamp; test/foundry/FulfillBasicOrderTest.sol:283: orderComponents.endTime = block.timestamp + 100; test/foundry/NonReentrant.t.sol:249: block.timestamp, test/foundry/NonReentrant.t.sol:250: block.timestamp + 1, test/foundry/NonReentrant.t.sol:292: orderComponents.startTime = block.timestamp; test/foundry/NonReentrant.t.sol:293: orderComponents.endTime = block.timestamp + 1; test/foundry/NonReentrant.t.sol:553: orderComponents.startTime = block.timestamp; test/foundry/NonReentrant.t.sol:554: orderComponents.endTime = block.timestamp + 1; test/foundry/NonReentrant.t.sol:571: orderParameters.startTime = block.timestamp; test/foundry/NonReentrant.t.sol:572: orderParameters.endTime = block.timestamp + 1; test/foundry/NonReentrant.t.sol:593: orderComponents.startTime = block.timestamp; test/foundry/NonReentrant.t.sol:594: orderComponents.endTime = block.timestamp + 1; test/foundry/NonReentrant.t.sol:613: orderParameters.startTime = block.timestamp; test/foundry/NonReentrant.t.sol:614: orderParameters.endTime = block.timestamp + 1;
Recommended Mitigation Steps
Block timestamps should not be used for entropy or generating random numbers—i.e., they should not be the deciding factor (either directly or through some derivation) for winning a game or changing an important state.
Time-sensitive logic is sometimes required; e.g., for unlocking contracts (time-locking), completing an ICO after a few weeks, or enforcing expiry dates. It is sometimes recommended to use block.number and an average block time to estimate times; with a 10 second block time, 1 week equates to approximately, 60480 blocks. Thus, specifying a block number at which to change a contract state can be more secure, as miners are unable to easily manipulate the block number.