Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: high
Invalid

Precision Loss in `vestedAmount` Calculation with Large `VESTING_DURATION`

Summary

The calculation of vestedAmount in the _calculateReleasableAmount function uses integer division, which can lead to precision loss, especially with the provided constants:

  • VESTING_DURATION = 700 days

  • MIN_RELEASE_INTERVAL = 1 day

This results in:

  1. Incorrect vesting amounts: Small amounts of tokens may not be vested correctly, especially during the early stages of the vesting schedule.

  2. Loss of tokens: Beneficiaries may receive fewer tokens than they are entitled to due to rounding down in the division.

  3. Cumulative discrepancies: Over time, the cumulative effect of precision loss could lead to significant discrepancies between the expected and actual vested amounts.

Vulnerability Details

The _calculateReleasableAmount function calculates the vestedAmount as:

uint256 vestedAmount = (schedule.totalAmount * timeFromStart) / schedule.duration;

Due to integer division, the result is rounded down, leading to precision loss. For example:

  • If schedule.totalAmount = 1000, timeFromStart = 1 day, and VESTING_DURATION = 700 days, the calculation is:

vestedAmount = (1000 * 1) / 700 = 1.428... tokens

Due to integer division, this rounds down to 1 token, resulting in a loss of 0.428 tokens.

Steps to reproduce:

  1. Deploy the contract and create a vesting schedule with the following parameters:

    • beneficiary: A valid address.

    • category: A valid category with sufficient allocation.

    • amount: 1000 tokens.

    • startTime: Current block timestamp.

    • duration: 700 days (VESTING_DURATION).

  2. Wait for 1 day (timeFromStart = 1 day).

  3. Call the release function.

  4. Observe that the releasableAmount is 1 token, even though the beneficiary should receive 1.428 tokens.

Proof of Concept (PoC) :

// Test case for precision loss with provided constants
function test_precisionLossWithConstants() public {
address beneficiary = address(0x123);
bytes32 category = keccak256("EMPLOYEE");
uint256 amount = 1000; // 1000 tokens
uint256 startTime = block.timestamp;
// Create a vesting schedule
vm.prank(orchestrator);
vestingContract.createVestingSchedule(beneficiary, category, amount, startTime);
// Fast-forward 1 day (MIN_RELEASE_INTERVAL)
vm.warp(block.timestamp + 1 days);
// Attempt to release tokens
vm.prank(beneficiary);
vestingContract.release();
// Verify that the releasable amount is 1 token due to precision loss
VestingSchedule memory schedule = vestingContract.getVestingSchedule(beneficiary);
assertEq(schedule.releasedAmount, 1); // Expected: 1 token (due to rounding down)
}

Impact

  • Precision loss is highly likely to occur during the early stages of the vesting schedule (e.g., within the first few days or weeks).

  • The impact depends on the specific values of schedule.totalAmount, timeFromStart, and VESTING_DURATION.

Tools Used

Manual Review

Recommendations

To mitigate precision loss, consider the following solutions:

Scale Up Values:

  • Multiply schedule.totalAmount and timeFromStart by a scaling factor (e.g., 1e18) before performing the division, then scale down the result.

  • Example:

uint256 vestedAmount = (schedule.totalAmount * timeFromStart * SCALING_FACTOR) / schedule.duration;
vestedAmount = vestedAmount / SCALING_FACTOR;
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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