DeFiFoundry
50,000 USDC
View results
Submission Details
Severity: medium
Invalid

Missing Maximum Lock Amount Validation in veRAACToken's Lock Function

Summary

The lock function in veRAACToken checks individual lock amounts but fails to validate against the maximum total locked amount defined in _lockState.maxTotalLocked, potentially allowing locks beyond the intended protocol limits.

Vulnerability Details

In veRAACToken.sol:

https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/tokens/veRAACToken.sol#L212-L244

The vulnerability exists because:

  1. _initializeLockParameters() sets MAX_TOTAL_LOCKED_AMOUNT to 18 tokens:

ttps://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/tokens/veRAACToken.sol#L1-L7

  1. lock() function only validates:

  • Individual lock amount (MAX_LOCK_AMOUNT: 10M)

  • Total supply limit

  • Lock duration

  1. Missing validation against _lockState.maxTotalLocked

Impact

High severity because:

  1. Protocol's economic security depends on total locked amount

  2. Can exceed intended maximum locked tokens (18)

  3. Affects voting power calculations

  4. Disrupts protocol's governance mechanism

  5. Could lead to economic imbalances in the system

Tools Used

  • Manual Review

  • Static Analysis

  • Control Flow Analysis

Recommendations

Add total locked amount validation:

function lock(uint256 amount, uint256 duration) external nonReentrant whenNotPaused {
if (amount == 0) revert InvalidAmount();
if (amount > MAX_LOCK_AMOUNT) revert AmountExceedsLimit();
if (totalSupply() + amount > MAX_TOTAL_SUPPLY) revert TotalSupplyLimitExceeded();
if (duration < MIN_LOCK_DURATION || duration > MAX_LOCK_DURATION)
revert InvalidLockDuration();
// Add check for total locked amount
if (_lockState.totalLocked + amount > _lockState.maxTotalLocked)
revert TotalLockedExceedsLimit();
// Rest of the function...
}

Existing Mitigations Analysis

1. Individual Lock Amount Limit

https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374404082424234e0b6018cb7f/contracts/core/tokens/veRAACToken.sol#L214-L215
if (amount == 0) revert InvalidAmount();
if (amount > MAX_LOCK_AMOUNT) revert AmountExceedsLimit();

✅ Limits each lock to 10M tokens
❌ Doesn't prevent multiple locks

2. Total Supply Check

https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374404082424234e0b6018cb7f/contracts/core/tokens/veRAACToken.sol#L216
if (totalSupply() + amount > MAX_TOTAL_SUPPLY) revert TotalSupplyLimitExceeded();

✅ Prevents exceeding max total supply
❌ Different from total locked amount

3. Lock Duration Limits

https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374404082424234e0b6018cb7f/contracts/core/tokens/veRAACToken.sol#L217-L218
if (duration < MIN_LOCK_DURATION || duration > MAX_LOCK_DURATION)
revert InvalidLockDuration();

✅ Ensures minimum 1 year lock
✅ Maximum 4 year lock period

4. Safety Checks

https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374404082424234e0b6018cb7f/contracts/core/tokens/veRAACToken.sol#L212
function lock(uint256 amount, uint256 duration) external nonReentrant whenNotPaused {

✅ NonReentrant guard
✅ Pausable functionality
✅ SafeTransfer for tokens

Missing Critical Mitigations

  1. No total locked amount validation:

// Missing check
if (_lockState.totalLocked + amount > _lockState.maxTotalLocked)
revert TotalLockedExceedsLimit();
  1. No tracking of per-user total locks:

// Missing check
if (userTotalLocked[msg.sender] + amount > USER_MAX_TOTAL_LOCKED)
revert UserTotalLockedExceedsLimit();

Conclusion

The existing mitigations are NOT sufficient because:

  1. Individual transaction limits don't protect against cumulative issues

  2. Total supply check is separate from locked amount tracking

  3. No user-level aggregate limits

  4. Missing core validation against maxTotalLocked

Recommendation

Add comprehensive checks:

function lock(uint256 amount, uint256 duration) external nonReentrant whenNotPaused {
// Existing checks
if (amount == 0) revert InvalidAmount();
if (amount > MAX_LOCK_AMOUNT) revert AmountExceedsLimit();
if (totalSupply() + amount > MAX_TOTAL_SUPPLY) revert TotalSupplyLimitExceeded();
if (duration < MIN_LOCK_DURATION || duration > MAX_LOCK_DURATION)
revert InvalidLockDuration();
// Add these checks
if (_lockState.totalLocked + amount > _lockState.maxTotalLocked)
revert TotalLockedExceedsLimit();
if (userTotalLocked[msg.sender] + amount > USER_MAX_TOTAL_LOCKED)
revert UserTotalLockedExceedsLimit();
// Update tracking
_lockState.totalLocked += amount;
userTotalLocked[msg.sender] += amount;
// Rest of function...
}
Updates

Lead Judging Commences

n0kto Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Other
n0kto Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Other

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.