sherlock-audit / 2024-06-velocimeter-judging

11 stars 7 forks source link

MohammedRizwan - `Checkpoint`'s `timestamp` variable is not initialized in `VotingEscrow.sol` #671

Closed sherlock-admin2 closed 3 months ago

sherlock-admin2 commented 4 months ago

MohammedRizwan

Medium

Checkpoint's timestamp variable is not initialized in VotingEscrow.sol

Summary

Checkpoint's timestamp variable is not initialized in VotingEscrow.sol

Vulnerability Detail

Checkpoint is implemented as:

    struct Checkpoint {
        uint timestamp;
        uint[] tokenIds;
    }

This contains a timestamp variable which stores the time stamp i.e block.timestamp of the checkpoint is created and tokenIds.

The issue is that the timestamp variable of a Checkpoint is not initialized in VotingEscrow.sol contract. Therefore, any function that relies on the timestamp of a Checkpoint will break.

There are two instances where this issue would affected in following functions.

1) getPastVotesIndex()

    function getPastVotesIndex(address account, uint timestamp) public view returns (uint32) {
        uint32 nCheckpoints = numCheckpoints[account];
        if (nCheckpoints == 0) {
            return 0;
        }
        // First check most recent balance
@>        if (checkpoints[account][nCheckpoints - 1].timestamp <= timestamp) {
            return (nCheckpoints - 1);
        }

        // Next check implicit zero balance
@>        if (checkpoints[account][0].timestamp > timestamp) {
            return 0;
        }

        . . . some code . . . 

The above function relies on the timestamp of the latest Checkpoint for optimization purposes. If the request timestamp is the most recently updated checkpoint, it will return the latest index immediately and skip the binary search. Since the timestamp variable is not populated, the optimization will not work.

2) _findWhatCheckpointToWrite()

    function _findWhatCheckpointToWrite(address account)
        internal
        view
        returns (uint32)
    {
        uint _timestamp = block.timestamp;
        uint32 _nCheckPoints = numCheckpoints[account];

        if (
            _nCheckPoints > 0 &&
@>          checkpoints[account][_nCheckPoints - 1].timestamp == _timestamp
        ) {
            return _nCheckPoints - 1;
        } else {
            return _nCheckPoints;
        }
    }

The above function verifies if the timestamp of the latest checkpoint of an account is equal to the current timestmp. If true, the function will return the index number of the last checkpoint.

Impact

Due to non-initialization of timestamp variable of the Checkpoint in the VotingEscrow.sol, the functions depending on timestamp variable would permanently break. This would also affect the functions used in another functions of VotingEscrow.sol

Code Snippet

https://github.com/sherlock-audit/2024-06-velocimeter/blob/main/v4-contracts/contracts/VotingEscrow.sol#L44-L47

https://github.com/sherlock-audit/2024-06-velocimeter/blob/main/v4-contracts/contracts/VotingEscrow.sol#L1312-L1317

Tool used

Manual Review

Recommendation

Consider Initializing the timestamp variable of the Checkpoint in the VotingEscrow.sol.

Duplicate of #288