59776 sc high exited delegators can over claim vtho rewards for post exit periods due to off by one error in claimabledelegationperiods

Submitted on Nov 15th 2025 at 17:38:20 UTC by @jayx for Audit Comp | Vechain | Stargate Hayabusaarrow-up-right

  • Report ID: #59776

  • 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: Theft of unclaimed yield

Description

Brief / Intro

The Stargate contract contains an off‑by‑one error in _claimableDelegationPeriods that allows users who have exited a delegation to continue claiming VTHO rewards for validator periods that occurred after their delegation ended.

When a delegation's endPeriod equals the user's next claimable period, the "ended" branch condition (endPeriod > nextClaimablePeriod) evaluates to false and the function falls through to the "active" branch, incorrectly returning the validator's latest completed period as the last claimable period. This lets ex‑delegators drain rewards that should remain in the pool for currently active delegators, resulting in theft of unclaimed yield and unfair distribution of protocol rewards.

Vulnerability Details

The root cause lies in the _claimableDelegationPeriods function of Stargate.sol.

Current implementation (relevant excerpt):

if (
    endPeriod != type(uint32).max &&
    endPeriod < currentValidatorPeriod &&
    endPeriod > nextClaimablePeriod  // ❌ Bug: Excludes equality case
) {
    return (nextClaimablePeriod, endPeriod);  // ✅ Correct cap at endPeriod
}

// Falls through to "active" branch
if (nextClaimablePeriod < currentValidatorPeriod) {
    return (nextClaimablePeriod, completedPeriods);  // ❌ Inflated: Includes post-exit periods
}

The problem is the strict inequality endPeriod > nextClaimablePeriod. Consider this scenario:

1

Step 1 — Initial state and first claim

A user delegates to a validator and claims rewards once, which advances lastClaimedPeriod to, say, period 3.

2

Step 2 — User requests exit

The user requests an exit, setting endPeriod to 4 (the period during which the exit was requested).

3

Step 3 — Validator continues validating

The validator completes periods 4, 5, 6, etc.

4

Step 4 — Next claim after exit

When claimRewards is called again:

  • nextClaimablePeriod = lastClaimedPeriod + 1 = 4

  • endPeriod = 4

The condition endPeriod > nextClaimablePeriod evaluates to 4 > 4 = false, so the "ended" branch is skipped.

5

Step 5 — Active branch incorrectly used

The function falls through to the "active" branch and returns lastClaimablePeriod = completedPeriods (e.g., 6). As a result, claimableDelegationPeriods returns (4, 6), exposing periods 4, 5, and 6 as claimable even though the delegation ended at period 4.

The correct condition should be endPeriod >= nextClaimablePeriod to ensure that when endPeriod equals nextClaimablePeriod, the function caps the window at endPeriod and does not expose later periods.

This bug is amplified because claimRewards calls _claimableDelegationPeriods internally, and the calculated window is used directly to compute VTHO transfers:

Since the loop iterates inclusive of lastClaimablePeriod, the inflated lastClaimablePeriod directly results in excess VTHO being transferred to the ex‑delegator.

Impact Details

This vulnerability enables theft of unclaimed yield from the protocol's VTHO reward pool, with these consequences:

  • Direct financial loss: Ex‑delegators can claim VTHO rewards for periods during which they were no longer delegating, stealing yield that should either remain in the pool or be distributed to currently active delegators. Over multiple periods and users, the drain can be significant.

  • Unfair reward distribution: Active delegators receive proportionally less reward because a portion of each period's rewards is being siphoned by ex‑delegators who are no longer participating. This breaks the protocol's economic guarantees.

References

https://github.com/immunefi-team/audit-comp-vechain-stargate-hayabusa/blob/e9c0bc9b0f24dc0c44de273181d9a99aaf2c31b0/packages/contracts/contracts/Stargate.sol#L916C8-L930C10

Proof of Concept

The test below reproduces the current (buggy) behavior. It performs delegation, claims once, requests exit, advances validator completed periods, and then observes that an ex‑delegator can still claim for periods after their endPeriod.

Suggested fix: change the condition to endPeriod >= nextClaimablePeriod so the delegation's endPeriod properly caps the claimable window when equal to nextClaimablePeriod.

Was this helpful?