Lucene search

K
code423n4Code4renaCODE423N4:2022-10-PALADIN-FINDINGS-ISSUES-234
HistoryOct 30, 2022 - 12:00 a.m.

Reuse of previous voting difference in extendPledge() charges too much fees

2022-10-3000:00:00
Code4rena
github.com
6
warden pledge
extendpledge
voting difference
fees
reward amount
vulnerability

Lines of code

Vulnerability details

Description

In Warden Pledge, creators can extend the life span of an existing pledge using extendPledge.

Here’s the implementation:

uint256 addedDuration = newEndTimestamp - oldEndTimestamp;
if(addedDuration < minDelegationTime) revert Errors.DurationTooShort();
uint256 totalRewardAmount = (pledgeParams.rewardPerVote * pledgeParams.votesDifference * addedDuration) / UNIT;
uint256 feeAmount = (totalRewardAmount * protocalFeeRatio) / MAX_PCT ;
if(totalRewardAmount > maxTotalRewardAmount) revert Errors.IncorrectMaxTotalRewardAmount();
if(feeAmount > maxFeeAmount) revert Errors.IncorrectMaxFeeAmount();

The issue is that totalRewardAmount, and more importantly, the feeAmount, are generated using the previously calculated votesDifference. It was defined in createPledge as:
vars.votesDifference = targetVotes - votingEscrow.balanceOf(receiver);

votesDifference was used correctly in createPledge because during creation time, theoretically the reward amount required to pay a fulfillement until the end depends on amount of votes to transfer, which is targetVotes - votingEscrow.balanceOf(receiver);

When extending the pledge, it is entirely reasonable that balanceOf(receiver) is much higher than before, and therefore the maximum votes that could be transferred while still under targetVotes is much lower. Since fee % is taken linearly from totalRewardAmount, it is also unrealistically high.

Impact

Protocol demands much more reward amount and fees than correct amount.

Proof of Concept

  1. Bob creates a pledge, with target = 200, current veCRV balance = 100, rewardVotes = 10, remaining time = 1 week. voteDifference = 200 - 100 = 100.
  2. Bob locked additional CRV, and now has 180 veCRV.
  3. After 5 days, Bob wishes to extend pledge life span and calls extendPledge( + 1 week).
    Calculated reward is:
    uint256 totalRewardAmount = (pledgeParams.rewardPerVote * pledgeParams.votesDifference * addedDuration) / UNIT;
    Actual calculation:
    totalRewardAmount = 10 * 100 * (9 * 86400) / 1e18
    Correct calculation:
    totalRewardAmount = 10 * 20 * (9 * 86400) / 1e18
    The total is 5 X more than it should be. Pledgers cannot theoretically pledge to Bob 100 votes, because his 180 current balance will only lower very gradually.
    Fee amount will also be 5x higher than should be:
    uint256 feeAmount = (totalRewardAmount * protocalFeeRatio) / MAX_PCT ;

Tools Used

Manual audit

Recommended Mitigation Steps

voteDifference should be supplied as a parameter to the extendPledge / increasePledgeRewardPerVote APIs. Creator pays linearly to voteDifference, so it does not introduce any threates to give control to user of this parameter, which is effectively set in stone in createPledge().


The text was updated successfully, but these errors were encountered:

All reactions