The veRAACToken::increase
function is intended to allow users to add more tokens to their lock, thereby increasing their voting power (ve tokens). However, because ve token power decays over time using a time-weighted formula, a situation may arise where a user’s new calculated voting power is lower than their current balance especially when the increase occurs near the end of the lock duration. The function currently only mints additional tokens based on the difference between the new and current power:
This implementation lacks any mechanism to reduce (burn) the user's balance if the new calculated voting power is lower than the existing one. As a result, users could either experience unexpected transaction failures (if the subtraction underflows) or maintain an erroneously high ve token balance relative to their actual locked amount. This flaw poses a significant risk to the integrity of the governance mechanism.
Voting Power Calculation:
The voting power is determined by the formula:
A user locking 4 tokens for one year receives 1 ve token:
Time Decay and Its Implications:
The voting power decays linearly over time. Therefore, if a user calls increase
near the lock expiration, the decay will have significantly reduced the current effective voting power.
For example, a user initially locks 100,000e18
tokens. Near the end of the lock (e.g., on the second last day), the decay means their current effective voting power is close to zero.
Flawed Increase Function:
The function recalculates the voting power (newPower
) after an increase in the locked amount.
It then attempts to adjust the user's ve token balance by minting:
Problem: If newPower
is less than balanceOf(msg.sender)
, which is likely when the lock is nearing expiration, the subtraction yields a negative result. In Solidity (version 0.8+), this underflow causes a revert. Even if using an earlier version without automatic underflow checks, wrapping could result in an incorrect, excessive minting.
Lack of a Burn Mechanism:
The absence of logic to reduce the user’s ve token balance (i.e., a burn function) means that the contract does not account for situations where the decayed voting power should be lower than the previously minted amount.
A proper implementation would compare the new calculated power with the current balance and, if necessary, burn the excess tokens:
Initial Lock:
User locks 100,000e18
tokens for 365 days, receiving a corresponding ve token balance calculated via the decay formula.
Near Lock Expiry:
On the second last day of the lock period, the user decides to increase their lock by adding a huge amount, e.g., 9,900,000e18
tokens, making the total locked amount near the maximum allowable (10,000,000e18
).
Calculation Outcome:
Despite the large additional amount, due to the heavy decay (since almost 365 days have elapsed), the newly calculated voting power (newPower
) ends up being lower than the user's current ve token balance.
Faulty Adjustment:
The function attempts:
Because newPower < balanceOf(msg.sender)
, the subtraction underflows. In Solidity 0.8+, this triggers a revert, blocking the increase; in earlier versions, it could wrap around, leading to a massive erroneous mint.
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 there exist an Arithmetic Over/Underflow (Actually Underflow) Error in the veRAACToken::increase
.
Governance Manipulation:
Users could maintain a ve token balance higher than their actual voting power based on the decayed locked amount. This discrepancy undermines the integrity of the governance mechanism by granting disproportionate influence.
Denial of Service (DoS):
Legitimate users attempting to increase their lock near expiry may find their transactions reverted, effectively preventing them from updating their positions and potentially locking them out of governance participation.
Inconsistent State:
The contract’s internal state may become inconsistent, with ve token balances not accurately reflecting the underlying locked amounts, risking further logic errors in any system relying on these values.
Manual Review
Foundry
Implement Proper Adjustment Logic:
Modify the increase
function to compare the newly calculated voting power (newPower
) with the current balance. Depending on the outcome:
If newPower
> current balance: Mint the difference.
If newPower
< current balance: Burn the excess tokens to align the balance with the decayed power.
Example adjustment:
Consider Upgrading to SafeMath or Solidity 0.8+:
Utilize Solidity 0.8 or later, which has built-in overflow and underflow checks, to prevent arithmetic errors that could lead to vulnerabilities.
If using an earlier version, incorporate SafeMath libraries to mitigate risks.
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.