59863 sc high over claim of delegation rewards after exit

Submitted on Nov 16th 2025 at 14:05:48 UTC by @jo13 for Audit Comp | Vechain | Stargate Hayabusaarrow-up-right

  • Report ID: #59863

  • 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:

  • Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield

  • Protocol insolvency

  • Theft of unclaimed yield

Description

Brief / Intro

A delegated NFT can continue to claim delegation rewards for periods after its delegation has ended, even after the user has already claimed all rewards up to the recorded endPeriod. This allows an attacker to over‑claim VTHO rewards beyond their legitimate share, effectively stealing yield from other delegators and breaking the accounting assumptions for the rewards distribution.

Vulnerability Details

The Stargate contract tracks claimable delegation periods with _claimableDelegationPeriods and uses lastClaimedPeriod as a cursor. When a delegation exits (finite endPeriod), claims are supposed to be bounded to [startPeriod, endPeriod]. However, once the user has claimed all rewards up to endPeriod, the next call computes a new range that starts after endPeriod and extends up to the validator’s latest completed period. Combined with the effective stake snapshot logic, this lets an exited delegation keep earning rewards.

Core logic: _claimableDelegationPeriods

Key conditions:

  • For an ended delegation:

    • endPeriod != type(uint32).max

    • endPeriod < currentValidatorPeriod

    • endPeriod > nextClaimablePeriod

After a full claim up to endPeriod:

  • lastClaimedPeriod = endPeriod

  • nextClaimablePeriod = endPeriod + 1

Now endPeriod > nextClaimablePeriod is false, so the “ended delegation” branch is skipped. The function then falls into the “active” branch:

Thus, for an exited delegation that has already claimed everything up to endPeriod, _claimableDelegationPeriods returns:

Claiming and lastClaimedPeriod

Claims are executed by _claimRewards:

_claimableRewards simply sums per‑period rewards over [firstClaimablePeriod, lastClaimablePeriod], and lastClaimedPeriod is updated to lastClaimablePeriod at the end, enabling repeated over‑claims for subsequent periods.

Reward share calculation

Per-period share:

Snapshots for delegatorsEffectiveStake are updated via _updatePeriodEffectiveStake on delegation and on exit, but _calculateEffectiveStake always recomputes the attacker's stake from current NFT data. After exit:

  • The attacker’s stake has been removed from the snapshot (denominator) for future periods.

  • _calculateEffectiveStake still returns a positive value for the attacker token.

  • For periods after endPeriod, the attacker’s numerator includes their full effective stake, but the denominator corresponds to only remaining delegators.

This combination makes the attacker’s share for post‑exit periods artificially large.

Impact Details

The vulnerability allows an exited delegator to continue claiming rewards for periods that occur after their delegation has ended, enabling them to repeatedly over-claim VTHO rewards they are not entitled to. Because their effective stake is still used in the numerator while their stake is removed from the denominator for post-exit periods, the attacker receives an artificially inflated share of delegation rewards. This directly steals yield from honest delegators, creates accounting inconsistencies between expected and distributed rewards, and undermines the economic integrity, fairness, and trust of the entire reward distribution mechanism.

References

Contracts:

  • packages/contracts/contracts/Stargate.sol

    • _claimableDelegationPeriods (claimable period window logic)

    • _claimRewards and _claimableRewards

    • _claimableRewardsForPeriod

    • _updatePeriodEffectiveStake / _calculateEffectiveStake

  • packages/contracts/contracts/mocks/ProtocolStakerMock.sol

    • signalDelegationExit and getDelegationPeriodDetails

    • getDelegatorsRewards mock behavior

Test (PoC):

  • packages/contracts/test/unit/Stargate/Rewards.test.ts

    • it("allows claiming rewards again after exit and full claim (over-claim bug)", ...)

Config / Setup:

  • packages/contracts/hardhat.config.ts (networks and reward calculations)

  • packages/contracts/test/helpers/deploy.ts (deployment and wiring of Stargate, StargateNFT, and ProtocolStakerMock)

Proof of Concept

Summary steps (PoC):

1

1. Stake and delegate attacker token

  • Stake NFT for attacker and delegate to a validator.

2

2. Stake and delegate victim token

  • Stake NFT for another user (victim) and delegate to the same validator so total effective stake remains positive after attacker exit.

3

3. Advance periods and request exit

  • Advance validator completed periods, request delegation exit for attacker so the delegation has a finite endPeriod, then advance one more period so the delegation becomes exited.

4

4. First claim after exit

  • Claim rewards after exit; this should claim all periods up to endPeriod.

5

5. Advance more periods and over-claim

  • Advance additional periods after exit. The attacker can still claim rewards for periods after endPeriod (over-claim), and receive funds again.

PoC test to reproduce (add this function to packages/contracts/test/unit/Stargate/Rewards.test.ts and run VITE_APP_ENV=local yarn test:hardhat --grep "over-claim bug"):


Was this helpful?