31540 - [SC - Insight] Expired Token Locks Impacting Vote Weight Calcu...

Submitted on May 21st 2024 at 04:17:10 UTC by @cheatcode for Boost | Alchemix

Report ID: #31540

Report type: Smart Contract

Report severity: Insight

Target: https://github.com/alchemix-finance/alchemix-v2-dao/blob/main/src/VotingEscrow.sol

Impacts:

  • Manipulation of governance voting result deviating from voted outcome and resulting in a direct change from intended effect of original results

Description

Description

The _balanceOfTokenAt function in the Voting Escrow contract calculates the voting power of tokens without verifying if the locks have expired. This results in expired token locks still contributing to the voting power, which can lead to inaccurate representation of active voter support in governance decisions.

Impact

Including expired locks in the vote weight calculation can distort the actual voting outcomes, enabling outdated stakes to influence current governance decisions. This misalignment can lead to decisions that do not reflect the present intentions of active stakeholders, undermining the protocol's governance integrity and effectiveness.

Vulnerable Code

function _balanceOfTokenAt(uint256 _tokenId, uint256 _time) internal view returns (uint256) {
    uint256 _epoch = userPointEpoch[_tokenId];
    if (_epoch == 0 || _time < pointHistory[userFirstEpoch[_tokenId]].ts) {
        return 0;
    } else {
        uint256 _min = 0;
        uint256 _max = userPointEpoch[_tokenId];
        for (uint256 i = 0; i < 128; ++i) {
            if (_min >= _max) {
                break;
            }
            uint256 _mid = (_min + _max + 1) / 2;
            if (userPointHistory[_tokenId][_mid].ts <= _time) {
                _min = _mid;
            } else {
                _max = _mid - 1;
            }
        }
        Point memory lastPoint = userPointHistory[_tokenId][_min];
        int256 biasCalculation = locked[_tokenId].maxLockEnabled
            ? int256(0)
            : lastPoint.slope * (int256(_time) - int256(lastPoint.ts));
        lastPoint.bias -= biasCalculation;
        if (lastPoint.bias < 0) {
            lastPoint.bias = 0;
        }
        return uint256(lastPoint.bias);
    }
}

Mitigation

Incorporating a check at the beginning of the _balanceOfTokenAt function to verify that the token's lock has not expired ensures that the function returns a vote weight of zero for expired tokens. This modification prevents expired locks from affecting vote weight calculations, ensuring that the voting power reflects only the active and legitimate stakes, thereby enhancing the accuracy and fairness of governance processes.

Proof of Concept

Expected Output

  1. Before Token Expiration:

    • With the issue: Votes before expiration (Issue): 100

    • With the fix: Votes before expiration (Fixed): 100

  2. After Token Expiration:

    • With the issue: Votes after expiration (Issue): 0 (This output might mistakenly show a value other than zero if not properly handled in the real contract)

    • With the fix: Votes after expiration (Fixed): 0

This script simulates the problem of token locks influencing voting weights after expiration and the effectiveness of the proposed solution in ensuring only active tokens contribute to voting power.

Last updated

Was this helpful?