The lock
function in the veRAACToken
contract checks whether totalSupply() + amount
exceeds MAX_TOTAL_SUPPLY
. However, the actual veRAAC tokens minted are calculated as (amount * duration) / MAX_LOCK_DURATION
, which can result in a discrepancy. This allows the total veRAAC supply to exceed MAX_TOTAL_SUPPLY
if many short-term locks are created, undermining the intended supply cap.
The check for exceeding MAX_TOTAL_SUPPLY
uses the raw amount
of RAAC tokens being locked (totalSupply() + amount > MAX_TOTAL_SUPPLY
) instead of the actual veRAAC tokens that will be minted.
The actual veRAAC tokens minted are calculated as (amount * duration) / MAX_LOCK_DURATION
, which could be significantly smaller than the amount
for short-term locks.
This discrepancy means that multiple short-term locks could collectively exceed MAX_TOTAL_SUPPLY
without triggering the check
Supply Check: The lock function checks:
Here, amount is the number of RAAC tokens being locked, not the veRAAC tokens being minted.
Actual Minted Amount: The veRAAC minted is newPower, calculated as:
If duration == MAX_LOCK_DURATION
(4 years), newPower = amount
.
If duration < MAX_LOCK_DURATION
, newPower < amount
.
If duration = MIN_LOCK_DURATION
(1 year), newPower = amount * 365 / 1460 = amount / 4
.
Scenario Analysis:
Max Duration Lock: Lock 50M RAAC for 4 years:
Check: totalSupply() (0) + 50M < 100M
→ Passes
Mint: `(50M * 1460 days) / 1460 days = 50M` veRAAC
Total supply = 50M veRAAC (within limit)
Min Duration Locks: Lock 50M RAAC for 1 year, then another 50M for 1 year:
First lock:
Check: 0 + 50M < 100M
→ Passes
Mint: (50M * 365 days) / 1460 days = 12.5M
veRAAC
Total supply = 12.5M veRAAC
Second lock:
Check: 12.5M + 50M = 62.5M < 100M
→ Passes
Mint: (50M * 365 days) / 1460 days = 12.5M
veRAAC
Total supply = 25M veRAAC
Now, lock another 50M RAAC for 1 year:
Check: 25M + 50M = 75M < 100M
→ Passes
Mint: 12.5M veRAAC
Total supply = 37.5M veRAAC
Continue this pattern: You could lock 400M RAAC (4 * 100M) over multiple 1-year locks, minting 400M * 365 / 1460 = 100M
veRAAC, hitting the cap exactly, but the check only sees totalSupply() + amount
, allowing further locks.
The MAX_TOTAL_SUPPLY
(100M veRAAC) is intended as a cap on circulating veRAAC tokens, which represent voting power and boost capabilities. Exceeding this cap dilutes the system’s integrity, potentially inflating voting power or rewards beyond design limits.
Example: If users lock 400M RAAC in 1-year locks (minting 100M veRAAC), then later extend locks or create new 4-year locks, the total veRAAC could grow beyond 100M, as the initial check doesn’t account for the time-weighted minting formula.
Violating the total supply cap in a vote-escrow token system undermines governance and economic assumptions, potentially leading to over-allocation of voting power or rewards.
Manual Review
Modify the lock function to check the actual veRAAC tokens that will be minted ((amount * duration) / MAX_LOCK_DURATION)
against MAX_TOTAL_SUPPLY
instead of the raw 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.