The extend
function does not consider user's current voting power (decayed in time):
The problem lies in how _votingState.calculateAndUpdatePower
is being used within the extend
function. It's using userLock.amount
as the locked amount, which is the original locked amount. It's not taking into account the fact that the user's voting power has already decayed over time.
Therefore, when the lock is extended, the power is being recalculated as if the user had just locked userLock.amount
for the entire new duration, leading to the power boost.
Bob also locks 10,000 RAACTokens for a year. He notices that there's no minimum extension duration check in the contract. Every 10 seconds, he calls the extend
function, extending his lock by just 10 seconds each time. Because the extend
function calculates voting power based on the total extended duration from the current moment (due to Bug 1), and there's no minimum extension duration, Bob effectively "resets" his voting power calculation every 10 seconds. His voting power never decreases, even though it's supposed to decay over time. He maintains a disproportionately high voting power indefinitely by repeatedly extending his lock by tiny increments, circumventing the intended power decay mechanism. He's essentially getting the benefit of a full lock duration repeatedly.
Here's the Bob scenario broken down into steps, incorporating code snippets where relevant:
Initial State:
Bob locks 10,000 RAACTokens for one year (365 days). Let's assume his initial voting power is calculated based on this lock.
Bob notices the extend
function lacks a minimum extension duration check:
Every 10 seconds, Bob calls extend(10)
.
Inside extend
, the _lockState.extendLock
function correctly updates the lock's end
time. However, the crucial problem is in the subsequent voting power calculation:
Because calculateAndUpdatePower
uses userLock.amount
(the original 10,000) instead of Bob's current, decayed voting power, it recalculates his power as if he had just locked 10,000 RAACTokens for the entire duration leading up to newUnlockTime
. This effectively resets his voting power to the initial high value.
Since there's no minimum extensionDuration
, Bob can repeat steps 3-5 indefinitely. Each 10-second extension triggers a power recalculation based on the original amount, constantly "refreshing" his voting power and preventing it from decaying.
Consequence:
Bob maintains a disproportionately high voting power, effectively circumventing the intended power decay mechanism. He gains an unfair advantage in the veRAACToken system.
By calling extend in short time periods, user can get max voting power, avoiding the power decay that's supposed to happen over time . This gives him an unfair advantage in the reward system.
vscode
The extend
function must be modified to calculate voting power based on the current, decayed voting power of the user, not the original locked amount.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.