Search code examples
ethereumsolidity

How to Avoid making time-based decisions in contract business logic?


I wrote a Money-saving smart contract where users deposit ETH and define the amount of time they want to keep that ETH in the contract, EX: USER X deposits 2ETH for one year, they can only withdraw their ETH after that period. But solidity linter keeps telling me that I should not rely on block.timestamp to make decisions. This is the Saving struct I'm using to map every address to a balance and endTime:

struct Saving {
    uint256 balance;
    uint256 endTime;
}

Here is my function modifier where I require the withdrawal time to be greater than the endTime I stored at the deposit moment:

modifier onlyValidTimeWithdraw() {
    require(
        block.timestamp > balances[msg.sender].endTime,
        "You cannot withdraw yet"
    );
    _;
}

This is the message I get through the linter. enter image description here

After doing some research I found that I should not have time-dependent conditions in my contract since miners can manipulate timestamps, but I did not find any alternative to this.


Solution

  • Miners can manipulate block timestamp to an extent of approx. few seconds, which is enough to affect business logic depending on a second-level precision.

    function betLottery() external {
        // the block.timestamp can be affected by a miner
        // and they can submit their own winning transaction
        if (block.timestamp % 2 == 0) {
            win();
        }
    }
    

    But since your logic depends on much longer period, I'd simply ignore or suppress the warning.

    Or if it fits your usecase, you can validate against the block number, which most linters allow.

    struct Saving {
        uint256 balance;
        uint256 endBlock;
    }
    
    require(
        block.number > balances[msg.sender].endBlock,
        "You cannot withdraw yet"
    );