Vyper Vested Claims

First Flight #34
Beginner FriendlyDeFi
100 EXP
View results
Submission Details
Severity: medium
Valid

Precision Loss in Vesting Calculations

Summary

The token vesting contract contains a vulnerability related to precision loss in the vesting calculations. The _calculate_vested_amount function performs integer division operations that can result in rounding errors. This issue may lead to tokens becoming permanently locked in the contract, particularly when dealing with small token amounts or when allocations don't divide evenly by 100.

Vulnerability Details

In the current implementation:

@view
def _calculate_vested_amount(total_amount: uint256) -> uint256:
"""
@notice This function is used to calculate the vested amount
@param total_amount: uint256, the total amount of tokens
@return vested: uint256, the vested amount
"""
current_time: uint256 = block.timestamp
start_time: uint256 = self.vesting_start_time
end_time: uint256 = self.vesting_end_time
vested: uint256 = 0
if current_time >= end_time:
return total_amount
vesting_duration: uint256 = end_time - start_time
elapsed: uint256 = current_time - start_time
instant_release: uint256 = (total_amount * 31) // 100
linear_vesting: uint256 = (total_amount * 69) // 100
vested = instant_release + (linear_vesting * elapsed) // vesting_duration
return vested

The function performs integer division in three locations:

  1. instant_release: uint256 = (total_amount * 31) // 100

  2. linear_vesting: uint256 = (total_amount * 69) // 100

  3. vested = instant_release + (linear_vesting * elapsed) // vesting_duration

Proof of Concept

Consider a user with an allocation of 100 tokens:

  • instant_release = (100 * 31) // 100 = 31 tokens

  • linear_vesting = (100 * 69) // 100 = 69 tokens

  • Total calculated tokens = 31 + 69 = 100 tokens (no precision loss)

Now consider a user with an allocation of 17 tokens:

  • instant_release = (17 * 31) // 100 = 5 tokens (real value: 5.27)

  • linear_vesting = (17 * 69) // 100 = 11 tokens (real value: 11.73)

  • Total calculated tokens = 5 + 11 = 16 tokens (1 token permanently lost)

Impact

This precision loss can result in several issues:

  1. Token Lockup: Due to integer division and rounding down, some tokens may become permanently trapped in the contract and never claimable by users. For example, if a user has 10 tokens, instant_release will be (10 * 31) // 100 = 3 and linear_vesting will be (10 * 69) // 100 = 6, totaling only 9 tokens instead of 10.

  2. Inconsistent Vesting: The rounding errors accumulate over time, potentially resulting in users with the same allocation receiving different amounts based on when they claim.

  3. Proportional Variance: The impact is more significant for smaller allocations. For a user with 100 tokens, the error might be 1%, but for a user with 10 tokens, it could be 10%.

  4. Fund Recovery Complications: If tokens are trapped, the owner would need to use the rescue_tokens function to recover them, adding complexity to the management process.

Tools Used

Manual Review

Recommendations

To address this precision loss issue, consider implementing one of these solutions:

  1. Use Higher Precision Calculations:

    # Use basis points (10000) instead of percentage (100) for higher precision
    instant_release: uint256 = (total_amount * 3100) // 10000
    linear_vesting: uint256 = (total_amount * 6900) // 10000
  2. Ensure Total Calculation Consistency:

    instant_release: uint256 = (total_amount * 31) // 100
    # Calculate linear_vesting as the remainder to ensure no rounding loss
    linear_vesting: uint256 = total_amount - instant_release
  3. Include Rounding Correction:

    vested = instant_release + (linear_vesting * elapsed + vesting_duration // 2) // vesting_duration
  4. Use Fixed-Point Arithmetic:
    Implement fixed-point arithmetic with higher precision to minimize rounding errors throughout the calculations.

Updates

Appeal created

bube Lead Judge 6 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Rounding issue in vesting calculation

Support

FAQs

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