Blockchain security isn't optional.

Protect your smart contracts and DeFi protocols with Three Sigma, a trusted security partner in blockchain audits, smart contract vulnerability assessments, and Web3 security.

Get a Quote Today

Introduction

In the previous episode of the series (Part 3) we mastered negative testing and event verification. Today we get to play time-lord: vm.roll and vm.warp let your tests hop to any block number or timestamp, perfect for deadlines, vesting schedules, and block-based locks.

Cheatcode vm.roll, Controlling the Block Number

vm.roll(uint256 newHeight) jumps the blockchain’s block number to newHeight (keeping timestamp unchanged). This is useful for testing time-based or block-based logic. For example, many contracts lock functionality until a certain block or allow actions only when n blocks have passed. You can simulate that easily:

image

In the test, after deploying at N, you’d do:

image

Because Forge uses a deterministic EVM, the above call will indeed see block.numberunlockBlock, allowing the action to succeed. Conversely, if you rolled to a lesser number, it would revert.

Importantly, vm.roll only sets the block number, while the block timestamp (block.timestamp) remains what it was. Foundry also provides vm.warp to set the timestamp (see next section). In combination, you often advance both number and time to simulate progression. These cheatcodes let you “push the block.timestamp and block.number into the future” easily. (A Foundry tutorial on a lottery contract, for instance, uses vm.warp(...) and vm.roll(...) back-to-back to simulate the passing of the lottery interval.)

Because block.number is normally monotonic, vm.roll can also be used to shrink (rewind) the block height by giving a smaller number, though care is needed since contracts may assume increasing blocks.

Advanced tip: Invariant or fuzz tests might internally use roll to explore block-time invariants. Also, Forge’s block manipulation works seamlessly across forks, if you fork mainnet, vm.roll still overrides the number for your test environment. Overall, vm.roll unlocks any scenario where a certain block number condition must be met, without waiting in real-time.

Cheatcode vm.warp, Controlling the Block Timestamp

Similar to vm.roll, vm.warp(uint256 newTime) sets the blockchain’s current block.timestamp to newTime. This is essential for testing time-based logic (deadlines, vesting, interest accrual, etc.). For example, consider a time-lock:

image

In tests you might write:

image

The call to vm.warp(...) leaps time to a point where block.timestamp >= releaseTime, so the withdraw logic passes. If you tried calling before warp, it would revert as expected. Combining vm.warp with vm.expectRevert is a common pattern to test both pre- and post-deadline behavior.

Foundry also offers handy shortcuts: skip(x) advances time by x seconds, and rewind(x) moves time backwards (if needed). But the core cheatcode is vm.warp. As with roll, timestamp changes are only effective for the next transaction; the test context continues with the new time.

image

In real contracts, you might need to test interest that accrues over time, auction expiration, or NFT reveal deadlines. vm.warp lets you simulate long waits instantly. Just be mindful: if your contract also depends on block number, you may need both vm.warp and vm.roll together (or use skip/rewind which affect time only). In any case, these time-travel cheats make it trivial to hit very old or future timestamps.

Simeon Cholakov
Simeon Cholakov

Security Researcher