sherlock-audit / 2024-06-velocimeter-judging

11 stars 7 forks source link

Ch_301 - `killGaugeTotally()` disrupts `_reset()` for newEpoch votes #545

Closed sherlock-admin3 closed 3 months ago

sherlock-admin3 commented 3 months ago

Ch_301

High

killGaugeTotally() disrupts _reset() for newEpoch votes

Summary

Vulnerability Detail

The killGaugeTotally() function is used to completely remove a gauge from the contract by the emergency council. However, once a gauge has been killed, it causes DOS for every tokenID that has used that pool in the previous epochs. This is due to an omitted check during the _reset() function:

File: Voter.sol

    function _vote(uint _tokenId, address[] memory _poolVote, uint256[] memory _weights) internal {
        _reset(_tokenId);

In the reset function, the bribe contract is called directly without checks, causing the reset call to revert because gauges[_pool] == address(0) :

File: Voter.sol

      IBribe(external_bribes[gauges[_pool]])._withdraw(uint256(_votes), _tokenId);

Impact

Code Snippet

Here is a coded POC to demonstrate the issue, Kindly paste the function in the KillGauges.t.sol:


  function testKillEffects() public {
    address gaugeAddress = address(gauge);
    vm.warp(block.timestamp + 1 weeks);

    address[] memory pools = new address[](1);
    pools[0] = address(pair);
    uint256[] memory weights = new uint256[](1);
    weights[0] = 100;

    voter.vote(1, pools, weights);

    voter.killGaugeTotally(gaugeAddress);
    vm.warp(block.timestamp + 2 weeks);

    voter.vote(1, pools, weights);
  }

Tool used

Manual Review

Recommendation

An appropriate fix would be to check if the gauges[_pool] is set in the _reset() function:


        for (uint i = 0; i < _poolVoteCnt; i ++) {
            address _pool = _poolVote[i];
            uint256 _votes = votes[_tokenId][_pool];

-            if (_votes != 0) {
+           if (_votes != 0 && external_bribes[gauges[_pool]] != address(0) ) {
                _updateFor(gauges[_pool]);
                weights[_pool] -= _votes;
                votes[_tokenId][_pool] -= _votes;
                if (_votes > 0) {
                    IBribe(external_bribes[gauges[_pool]])._withdraw(uint256(_votes), _tokenId);
                    _totalWeight += _votes;
                } else {
                    _totalWeight -= _votes;
                }
                emit Abstained(_tokenId, _votes);
            }
        }

Duplicate of #10