sherlock-audit / 2024-06-velocimeter-judging

11 stars 7 forks source link

Atomic Tortilla Falcon - loss of precision when `_weights[i]` is small compared to `_totalVoteWeight` #714

Closed sherlock-admin4 closed 3 months ago

sherlock-admin4 commented 3 months ago

Atomic Tortilla Falcon

Low/Info

loss of precision when _weights[i] is small compared to _totalVoteWeight

Vulnerability Detail

function _vote(uint _tokenId, address[] memory _poolVote, uint256[] memory _weights) internal {
    _reset(_tokenId);
    uint _poolCnt = _poolVote.length;
    uint256 _weight = IVotingEscrow(_ve).balanceOfNFT(_tokenId);
    uint256 _totalVoteWeight = 0;
    uint256 _totalWeight = 0;
    uint256 _usedWeight = 0;

    for (uint i = 0; i < _poolCnt; i++) {
        _totalVoteWeight += _weights[i];
    }

    for (uint i = 0; i < _poolCnt; i++) {
        address _pool = _poolVote[i];
        address _gauge = gauges[_pool];

        if (isGauge[_gauge]) {
            require(isAlive[_gauge], "gauge already dead");
            uint256 _poolWeight = _weights[i] * _weight / _totalVoteWeight;
            require(votes[_tokenId][_pool] == 0);
            require(_poolWeight != 0);
            _updateFor(_gauge);

            poolVote[_tokenId].push(_pool);

            weights[_pool] += _poolWeight;
            votes[_tokenId][_pool] += _poolWeight;
            IBribe(external_bribes[_gauge])._deposit(uint256(_poolWeight), _tokenId);
            _usedWeight += _poolWeight;
            _totalWeight += _poolWeight;
            emit Voted(msg.sender, _tokenId, _poolWeight);
        }
    }
    if (_usedWeight > 0) IVotingEscrow(_ve).voting(_tokenId);
    totalWeight += uint256(_totalWeight);
    usedWeights[_tokenId] = uint256(_usedWeight);
}

The bug is in the calculation of _poolWeight:

uint256 _poolWeight = _weights[i] * _weight / _totalVoteWeight;

This calculation can lead to a loss of precision due to integer division. If _weights[i] is small compared to _totalVoteWeight, the result of the multiplication might be less than _totalVoteWeight, causing the division to result in zero. This can lead to votes being lost or skewed.

To fix this, we should rearrange the calculation to minimize the loss of precision:

uint256 _poolWeight = (_weight * _weights[i]) / _totalVoteWeight;

Code Snippet

https://github.com/sherlock-audit/2024-06-velocimeter/blob/main/v4-contracts/contracts/Voter.sol#L267

Tool used

Manual Review