60154 sc high exited delegations can continue claiming vtho rewards for future periods

Submitted on Nov 19th 2025 at 09:53:12 UTC by @incogknito for Audit Comp | Vechain | Stargate Hayabusaarrow-up-right

  • Report ID: #60154

  • 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.sol contract allows a delegation that has already exited and fully claimed all legitimate rewards up to its endPeriod to continue claiming VTHO rewards for later staking periods, where it should no longer be entitled to anything. Because the per-period reward share is computed using a token’s full effective stake but the total delegators’ effective stake no longer includes that token, an attacker can indefinitely siphon extra VTHO yield, causing systematic overpayment of rewards and direct loss of unclaimed yield from the protocol and/or honest delegators.

Vulnerability Details

When a delegation exits, the protocol sets a finite endPeriod for that delegation. After that period, the NFT should no longer earn rewards. However, Stargate.sol's period-selection logic allows an exited NFT to keep accruing rewards for periods strictly after endPeriod.

Looking at the _claimableDelegationPeriods function:

(uint32 startPeriod, uint32 endPeriod) =
    $.protocolStakerContract.getDelegationPeriodDetails(delegationId);
(, , , uint32 completedPeriods) =
    $.protocolStakerContract.getValidationPeriodDetails(validator);

uint32 currentValidatorPeriod = completedPeriods + 1;
uint32 nextClaimablePeriod = $.lastClaimedPeriod[_tokenId] + 1;
if (nextClaimablePeriod < startPeriod) {
    nextClaimablePeriod = startPeriod;
}

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

// fallback (“active”) branch
if (nextClaimablePeriod < currentValidatorPeriod) {
    return (nextClaimablePeriod, completedPeriods);
}

Suppose the delegation has already claimed all rewards up to its last valid period endPeriod, then lastClaimedPeriod[_tokenId] = endPeriod; so, nextClaimablePeriod = endPeriod + 1. And as the validator continues operating, completedPeriods increases so that completedPeriods > endPeriod and currentValidatorPeriod = completedPeriods + 1.

In this state, the 'ended delegation' branch requires endPeriod > nextClaimablePeriod, which is false, so that branch is skipped. The fallback 'active' branch sees nextClaimablePeriod < currentValidatorPeriod as true, and returns firstClaimablePeriod = endPeriod + 1 and lastClaimablePeriod = completedPeriods.

So, after all legitimate periods up to endPeriod have been claimed, _claimableDelegationPeriods starts returning new claimable periods strictly greater than endPeriod, even though the delegation has ended.

Impact Details

Any regular user who has delegated once can delegate an NFT to a validator and earn rewards normally, call requestDelegationExit while the delegation is active, setting a finite endPeriod, claim all legitimate rewards up to endPeriod and as the validator continues to earn delegator rewards for subsequent periods, repeatedly call claimRewards to receive additional VTHO for periods > endPeriod, where they should not participate.

This occurs because, in the _claimableRewardsForPeriod function, the numerator uses the exited NFT’s full effective stake, while the denominator excludes that stake from the validator’s total delegators’ effective stake for those periods.

References

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

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

Proof of Concept

The following PoC is a Hardhat unit test that reproduces the issue.

1

Overview — sequence of actions reproduced by the test

    1. Attacker stakes an NFT and delegates it to a validator; an honest delegator does the same to the same validator.

    1. Fast-forward periods so both delegations become ACTIVE and rewards accrue.

    1. Attacker requests exit while ACTIVE, setting a finite endPeriod. After one more completed period, the delegation becomes EXITED.

    1. Attacker legitimately claims all rewards up to endPeriod.

    1. Advance many more periods; the exited attacker delegation remains in status EXITED.

    1. BUG: the exited delegator gets NEW claimable periods (strictly after endPeriod) and can claim VTHO for them, receiving tokens that they should not be eligible for.

Full test file (place at packages/contracts/test/unit/Stargate/ExitedDelegationRewards.PoC.test.ts):

To run the test:

  • Place the file at packages/contracts/test/unit/Stargate/ExitedDelegationRewards.PoC.test.ts

  • cd packages/contracts

  • npx hardhat test test/unit/Stargate/ExitedDelegationRewards.PoC.test.ts

Was this helpful?