60069 sc high incorrect claimable period calculation leading to attacker keep claiming even after exiting the delegation

Submitted on Nov 18th 2025 at 07:49:27 UTC by @Bizarro for Audit Comp | Vechain | Stargate Hayabusaarrow-up-right

  • Report ID: #60069

  • Report Type: Smart Contract

  • Report severity: High

  • Target: https://github.com/immunefi-team/audit-comp-vechain-stargate-hayabusa/tree/main/packages/contracts/contracts/Stargate.sol

  • Impacts:

    • Protocol insolvency

    • Theft of unclaimed yield

Description

Brief / Intro

The checks in _claimableDelegationPeriods() are not sufficient to prevent a delegator from claiming rewards after their delegation has ended. Off-by-one and missing boundary checks allow claiming periods beyond the delegation end.

1

Vulnerability detail — off-by-one when endPeriod equals nextClaimablePeriod

In _claimableDelegationPeriods() when a delegation ends exactly at the next claimable period, the function can incorrectly return periods beyond the delegation's end, allowing claims for periods after exit.

Problematic code (logic):

if (
    endPeriod != type(uint32).max &&
    endPeriod < currentValidatorPeriod &&
    endPeriod > nextClaimablePeriod  // ❌ WRONG: Should be >= 
) {
    return (nextClaimablePeriod, endPeriod);
}

// If above check fails, falls through to:
if (nextClaimablePeriod < currentValidatorPeriod) {
    return (nextClaimablePeriod, completedPeriods);  // ❌ Returns too many periods
}

Example path:

  • User delegated from period 5 to period 10.

  • User requested exit at period 9 → endPeriod = 10.

  • User claimed up to period 9 → lastClaimedPeriod = 9.

  • nextClaimablePeriod = lastClaimedPeriod + 1 = 10.

  • Validator: completedPeriods = 12, currentValidatorPeriod = 13.

Evaluation:

// Check 1: endPeriod > nextClaimablePeriod
if (10 > 10) // FALSE - skip

// Falls through to Check 2: nextClaimablePeriod < currentValidatorPeriod  
if (10 < 13) // TRUE
    return (10, 12) // ❌ WRONG: Returns periods 10, 11, 12

Correct behavior (fix):

// Check 1: endPeriod >= nextClaimablePeriod
if (10 >= 10) // TRUE
    return (10, 10) // ✅ Only period 10
2

Vulnerability detail — missing check when nextClaimablePeriod > endPeriod

When a delegation has ended and the last claimed period equals the end period, the function can still return non-zero ranges (allowing further claims) because there's no explicit guard ensuring nextClaimablePeriod <= endPeriod. The logic falls through and returns claimable periods beyond the delegation end.

Relevant code flow:

if (
    endPeriod != type(uint32).max && // -> true 
    endPeriod < currentValidatorPeriod && // -> true
    endPeriod > nextClaimablePeriod  // -> false
) {
    return (nextClaimablePeriod, endPeriod); 
}

if (nextClaimablePeriod < currentValidatorPeriod) {
    return (nextClaimablePeriod, completedPeriods);  // -> claim from nextClaimablePeriod to completedPeriods.
}

Example path:

  • User delegated from period 5 to period 10.

  • User requested exit at period 9 → endPeriod = 10.

  • User claimed up to period 9 → lastClaimedPeriod = 9.

  • nextClaimablePeriod = 10.

  • Validator: completedPeriods = 9, currentValidatorPeriod = 10.

  • User claims → updates lastClaimedPeriod = 10.

  • Later validator: completedPeriods = 15, currentValidatorPeriod = 16.

Evaluation:

// Check 1
if (10 != max32 && 10 < 16 && 10 > 11) // FALSE - skip

// Falls through to Check 2: nextClaimablePeriod < currentValidatorPeriod  
if (11 < 16) // TRUE
    return (11, 15) // ❌ WRONG: Returns periods 11..15

A missing check to ensure nextClaimablePeriod <= endPeriod (or returning (0,0) when nextClaimablePeriod > endPeriod) causes over-claiming.

Impact Details

  • Over-claiming rewards for periods after delegation exit.

  • Reduced funds available to honest delegators; potential protocol insolvency from excessive payouts.

References

  • Vulnerable file: https://github.com/immunefi-team/audit-comp-vechain-stargate-hayabusa/blob/e9c0bc9b0f24dc0c44de273181d9a99aaf2c31b0/packages/contracts/contracts/Stargate.sol#L879C5-L934C6

Proof of Concept

Below instructions and code show how to reproduce the issue by instrumenting the contract and running a unit test.

Add a console.log to track the new effective values (example location): https://github.com/immunefi-team/audit-comp-vechain-stargate-hayabusa/blob/e9c0bc9b0f24dc0c44de273181d9a99aaf2c31b0/packages/contracts/contracts/Stargate.sol#L742

Example console log insertion:

Add this test in test/unit/Stargate/Rewards.test.ts and run yarn contracts:test:unit:

Expected test output (demonstrating the incorrect returned ranges):

(Full test logs available in the original report.)

Suggested fix (summary)

  • Change the strict > comparison to >= when checking endPeriod against nextClaimablePeriod.

  • Add a guard to return (0,0) or otherwise prevent claims when nextClaimablePeriod > endPeriod (i.e., ensure you never return claimable ranges that start after the delegation end).

(Do not add other changes — the above describes only the minimal intended logic fixes based on the report.)

Was this helpful?