The extend
function in the veRAACToken
contract is designed to allow users to extend the duration of an existing lock, thereby increasing their voting power (ve tokens) by delaying the decay. The extension mechanism, however, contains a flaw in its duration constraint. The contract calculates the "total new duration" as the sum of the remaining lock time and the extension duration, and then rejects the extension if this total exceeds the predefined MAX_LOCK_DURATION
(1460 days).
The bug arises when a user's lock is near expiry (i.e., the remaining duration is very low). In such cases, a user can request an extension with an extension duration nearly equal to the maximum allowed duration. This effectively resets the lock to a nearly full duration, resulting in a significant recovery of voting power—even though the user’s original commitment was minimal at that moment. Malicious actors can exploit this behavior repeatedly to maintain or artificially boost their governance influence, undermining the intended time-decay mechanism of voting power.
Function Behavior:
The extend
function first calls _lockState.extendLock
, which calculates:
It then recalculates the user’s voting power using the new unlock time and adjusts the ve token balance accordingly.
Flawed Constraint:
The check if (totalNewDuration > state.maxLockDuration)
compares the sum of the remaining duration and the extension to MAX_LOCK_DURATION
.
Issue: When the lock is near expiry, remainingDuration
is very small. Therefore, a user can supply an extensionDuration
almost as large as MAX_LOCK_DURATION
without violating the check.
This loophole allows a user to extend their lock almost to the full allowed duration, thereby "resetting" the decay process and regaining nearly full voting power—even if most of the originally locked duration has already elapsed.
Exploitation Potential:
An attacker can repeatedly extend their lock right before expiry, each time extending it to near the maximum duration.
This repeated resetting effectively prevents the intended decay of voting power, enabling the attacker to maintain an undeservedly high influence in governance decisions indefinitely.
Initial Lock:
A user creates a lock with the minimum allowed duration (MIN_LOCK_DURATION = 365 days
).
The voting power (ve tokens) is initially calculated based on this duration.
Approaching Expiry:
After nearly 364 days, the lock has only a short period remaining before expiration.
At this point, the remaining duration (lock.end - block.timestamp
) is minimal.
Extension Exploit:
The user calls the extend
function with an extensionDuration
nearly equal to MAX_LOCK_DURATION
(1460 days).
Calculation in _lockState.extendLock
:
Remaining Duration: Very small (e.g., a few hours or minutes).
Total New Duration: ≈ (small remaining duration) + (nearly 1460 days) ≤ 1460 days.
The check passes, and the new unlock time is set to almost block.timestamp + 1460 days
.
Resetting Voting Power:
Voting power is recalculated using the new, nearly maximal duration.
The user receives a significant boost in ve tokens, effectively “resetting” the decay process.
This process can be repeated, allowing the attacker to perpetually maintain a high voting power.
To demonstrate this vulnerability, the following Proof of Concept (PoC) is provided. The PoC is written using the Foundry tool.
Step 1: Create a Foundry project and place all the contracts in the src
directory.
Step 2: Create a test
directory and a mocks
folder within the src
directory (or use an existing mocks folder).
Step 3: Create all necessary mock contracts, if required.
Step 4: Create a test file (with any name) in the test
directory.
Step 5: Add the following test PoC in the test file, after the setUp
function.
Step 6: To run the test, execute the following commands in your terminal
Step 7: Review the output.
As demonstrated, the test confirms that users can extend their existing duration indefinitely.
Governance Manipulation:
The ability to repeatedly reset the lock’s duration allows malicious actors to artificially inflate their voting power. This undermines the core design of time-weighted voting, which is intended to reward long-term commitment rather than opportunistic resets. As a result, governance decisions can be skewed, compromising the protocol’s fairness and stability.
Economic and Security Risks:
Inflated voting power may enable attackers to push through proposals that benefit them or harm the protocol. Such manipulation could lead to economic losses for stakeholders and erode trust in the governance mechanism.
System Integrity:
The flaw introduces an inconsistency between the locked amount and the actual voting power. This misalignment can have downstream effects on other protocol functions that rely on accurate ve token balances, potentially leading to broader systemic vulnerabilities.
Manual Review
Foundry
Below is a recommended diff to address the flaw in the duration extension logic. The core idea is to constrain the new unlock time relative to the original lock start time rather than merely the remaining duration. This may require storing the original lock start time (if not already stored).
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.