sherlock-audit / 2024-06-velocimeter-judging

11 stars 7 forks source link

StraawHaat - A malicious user can create duplicated `veNFTs` within a checkpoint when the `VotingEscrow._moveTokenDelegates` function is called multiple times within the same block #651

Closed sherlock-admin4 closed 3 months ago

sherlock-admin4 commented 3 months ago

StraawHaat

High

A malicious user can create duplicated veNFTs within a checkpoint when the VotingEscrow._moveTokenDelegates function is called multiple times within the same block

Summary

A malicious user can create duplicated veNFTs within a checkpoint when the VotingEscrow._moveTokenDelegates function is called multiple times within the same block. This duplication can lead to inflated voting balances, which can be exploited for malicious purposes.

Note: the same vulnerability exists in VotingEscrow._moveAllDelegates.

Vulnerability Detail

Note: This vulnerability is inspired by this issue from Spearbit: https://solodit.xyz/issues/inflated-voting-balance-due-to-duplicated-venfts-within-a-checkpoint-spearbit-none-velodrome-finance-pdf

The _moveTokenDelegates() function handles the delegation of voting power associated with NFT. When tokens (veNFTs) are transferred from one account to another, the function updates the voting power of the involved accounts.

The function finds the correct checkpoint for the current block using _findWhatCheckpointToWrite():

uint32 nextSrcRepNum = _findWhatCheckpointToWrite(srcRep);
    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 problem is that the function can called multiple times within the same block. This allows users to create duplicated veNFTs (token IDs) within a checkpoint. This duplication can lead to inflated voting balances, which can be exploited for malicious purposes.

Consider the following situation:

Initial State:

First Transfer within Block:

Second Transfer within the Same Block:

Malicious users can manipulate their voting power by duplicating veNFTs within the same block. This can affect governance decisions and reward distributions.

Code Snippet

https://github.com/sherlock-audit/2024-06-velocimeter/blob/main/v4-contracts/contracts/VotingEscrow.sol#L1362-L1411 https://github.com/sherlock-audit/2024-06-velocimeter/blob/main/v4-contracts/contracts/VotingEscrow.sol#L1413-L1429 https://github.com/sherlock-audit/2024-06-velocimeter/blob/main/v4-contracts/contracts/VotingEscrow.sol#L1431-L1486

Tool used

Manual Review

Recommendation

The _moveTokenDelegates() and _moveAllDelegates() function need to ensure that checkpoints are managed correctly, and tokens are not duplicated within the same block.

Duplicate of #228