Testing Passage Of Time In Smart Contracts

Many ERC20 tokens allow for staking of other assets for a reward yield based on the amount of time staked. In smart contracts you can track the passage of time using block.timestamp which is the time since epoch measured in seconds. Measuring this kind of time to pay out yield is being used by contracts in De-fi as well as many NFT that are providing utility tokens for staking the NFT themselves!

We can create our own staking logic in smart contracts and test the rewards over passage of time easily by using OpenZeppelin Test Helpers. This is a package of helpers for hardhat tests that let you easily test things like reverting transactions, event emissions, as well as passage of time.

Here is a quick example of installing the package and creating a test that can fast forward time while staking assets!

1. Install packages

You will need to install the test helpers as well as the hardhat web3 integration package.

yarn add @openzeppelin/test-helpers

and

yarn add @nomiclabs/hardhat-web3 web3

2. Import helpers

Put this at the beginning of your hardhat test file.

const {
  BN,           // Big Number support
  constants,    // Common constants, like the zero address and largest integers
  expectEvent,  // Assertions for emitted events
  expectRevert, // Assertions for transactions that should fail
  time
} = require('@openzeppelin/test-helpers');

3. Add fast forwarding to your test

Here is a snippet of smart contract that uses a logged timestamp to determine the amount of time an asset has been staked and reward the staker with a rate based on that time. This is from the Anonymice $CHEETH Contract, that lets you stake NFT for $CHEETH tokens.

totalRewards =
            totalRewards +
                ((block.timestamp - tokenIdToTimeStamp[tokenId]) *
                    EMISSIONS_RATE);

Here is a small snippet of a staking test that can fast forward the block time to increase and test the yield from the contract.

await cheethContract.stakeByIds([0,1])
//fast forward time by 50000 seconds
await time.increase(50000);

//unstake anonymice
await cheethContract.unstakeAll()

//check balance
var balance = await cheethContract.balanceOf(currentAddress.address)

//make assertion on balance based on time passed 

4. Running on Hardhat

This part was a little tricky to figure out. The helpers won't be able to run on the integrated hardhat network without a few steps.

Run the hardhat integrated node:

 npx hardhat node

Run your hardhat tests pointed at localhost:

npx hardhat test --network localhost

This will let the test helpers interact with the test node.