59358 sc high off by one error in reward claim logic allows delegators to steal vtho for periods after delegation ended

Submitted on Nov 11th 2025 at 13:33:27 UTC by @Tomioka for Audit Comp | Vechain | Stargate Hayabusaarrow-up-right

  • Report ID: #59358

  • 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 VeChain Stargate protocol contains an off-by-one boundary check error in the reward claiming logic that allows users to claim VTHO rewards for periods after their delegation has ended. When a user has claimed rewards up to their final delegation period and then the delegation ends, a faulty inequality check (> instead of >=) causes the clamping logic to fail, allowing the user to claim rewards for all subsequent periods up to the validator's current completed period. This enables direct theft of VTHO rewards that should be distributed to active delegators.

Vulnerability Details

When users claim rewards, the protocol calculates which periods are claimable using the _claimableDelegationPeriods function. This function should ensure users can only claim rewards for periods during which they were actively delegated. For ended delegations, it should clamp the maximum claimable period to the delegation's endPeriod.

The vulnerable snippet:

function _claimableDelegationPeriods(
    StargateStorage storage $,
    uint256 _tokenId,
    uint256 _delegationId
) internal view returns (uint32 firstClaimablePeriod, uint32 lastClaimablePeriod) {
    // ... [code to calculate nextClaimablePeriod] ...

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

    // Falls through when endPeriod == nextClaimablePeriod
    if (nextClaimablePeriod < currentValidatorPeriod) {
        return (nextClaimablePeriod, completedPeriods); // Returns beyond endPeriod!
    }
    // ...
}

Location: Stargate.sol:917-921

1

The Bug Explained

When endPeriod == nextClaimablePeriod the condition endPeriod > nextClaimablePeriod is false, so the clamping branch is bypassed and the function falls through to the general case which returns completedPeriods (the validator's current period). This allows the caller to claim far beyond their delegation end.

This equality case commonly occurs when:

  • A user has claimed all rewards up to period N-1

  • Their delegation ends at period N (endPeriod = N)

  • The next claimable period is N (nextClaimablePeriod = N)

Why this is exploitable:

  • Users who claim regularly are more vulnerable because their nextClaimablePeriod often equals endPeriod.

  • After delegation end, a malicious final claim can request rewards up to the validator's current period, stealing VTHO intended for active delegators.

Impact Details

  • Direct theft of VTHO from the protocol's reward distribution pool.

  • Stolen amount = (user_stake / validator_total_stake) × reward_per_period × overclaimed_periods.

  • Theft compounds linearly with time elapsed after delegation end.

Realistic example (from report):

  • User stake: 500,000

  • Validator total stake: 10,000,000

  • Reward per period: 100,000 VTHO

  • User waits 10 periods after delegation end

Legitimate reward (period 10 only): (500,000 / 10,000,000) × 100,000 = 5,000 VTHO

Stolen reward (periods 10–19): (500,000 / 10,000,000) × 100,000 × 10 = 50,000 VTHO

Total overclaim: 45,000 VTHO stolen (9× legitimate amount)

Affected parties:

  • Protocol: loses VTHO from reward pool

  • Active delegators: receive reduced rewards

  • Honest users who claim promptly: paradoxically more vulnerable

Scale:

  • Any user can exploit this after exiting delegation

  • Incentivizes delaying final claim to maximize overclaim

References

  • Vulnerable function: [Stargate.sol:_claimableDelegationPeriods]packages/contracts/contracts/Stargate.sol#L917-L921

  • Claim rewards function: [Stargate.sol:claimRewards]packages/contracts/contracts/Stargate.sol

Proof of Concept

Prerequisites

  • Normal user operations only:

    • User has an active delegation

    • User claims rewards regularly

    • User requests exit or delegation naturally ends

    • Validator continues operating (periods advance)

Running the Proof of Concept

From the packages/contracts directory run the Hardhat test with the environment variables used during testing.

Commands:

Or set environment variables first:

Full PoC (test file)

(The report contains the full TypeScript PoC test that demonstrates lastClaimable > delegation end after exit.)

Primary fix — change the strict > to >=:

Alternative defensive fix — always clamp when delegation has ended:

Why these fixes work:

  1. >= handles the equality case so the final legitimate period is returned as the last claimable period.

  2. The defensive fix ensures that when a delegation has ended, the last claimable period never exceeds the delegation endPeriod.

Notes

  • All links and file references in this page are preserved as in the original report.

Was this helpful?