59709 sc high post exit rewards overpayment theft of unclaimed yield due to misclamped claim window in stargate

Submitted on Nov 15th 2025 at 02:22:41 UTC by @Queerantagonism for Audit Comp | Vechain | Stargate Hayabusaarrow-up-right

  • Report ID: #59709

  • 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

Exploitability: No special permissions required; any NFT owner following normal flows (delegate → request exit → wait → claim) can over-claim.

The private function _claimableDelegationPeriods determines the first and last claimable periods. For delegations that have ended, the intended logic is to clamp the last claimable period to endPeriod. However, the current condition enforces the clamp only when endPeriod > nextClaimablePeriod, thereby missing the equality case.

Excerpt (contracts/Stargate.sol):

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

When endPeriod == nextClaimablePeriod (a common case during exit in the first active period), the condition fails and the logic falls through to the “active” branch:

This incorrectly includes post-exit periods, allowing claimableRewards to pay for future periods that should be excluded.

Faulty Flow

1

User delegates NFT to a validator

User delegates their NFT to a validator as normal.

2

User requests exit during their first active period

This makes endPeriod = current period.

3

Several validator periods complete

_claimableDelegationPeriods mistakenly returns lastClaimable = completedPeriods which extends beyond endPeriod.

4

_claimableRewards aggregates over the incorrect range

This causes overpayment to the user.

Attack Scenario

1

Delegate an NFT to a validator.

2

Request exit during the first active period (very realistic scenario).

3

Wait a few more periods.

4

Call claimRewards or claimableRewards and receive post-exit rewards.

This process can be repeated across many tokens, effectively stealing unclaimed yield and risking protocol reserves (VTHO depletion).

Proof of Concept

The PoC leverages the repository’s existing mocks (ProtocolStakerMock, StargateNFTMock) and deploys Stargate via its proxy, as required. It demonstrates:

  • lastClaimable > endPeriod (over-extended claim range)

  • Overpaid rewards = 0.15 ETH (0.05 for valid period 2 + 0.10 for invalid period 3)

Place file at: packages/contracts/test/unit/Stargate/POC_ClaimAfterExit.test.ts

Run:

npx hardhat test --network hardhat test/unit/Stargate/POC_ClaimAfterExit.test.ts

Output (observed):

  • PoC test passes.

  • lastClaimable = 3 while endPeriod = 2 (range exceeds end).

  • claimableRewards(tokenIdA) = 0.15 ETH (overpayment confirmed).

Fix

Goal: Clamp claimable range to endPeriod for delegations that have ended.

Suggested patch in contracts/Stargate.sol for _claimableDelegationPeriods:

(Primary change: ensure the ended-delegation branch triggers whenever endPeriod < currentValidatorPeriod — and handle equality where nextClaimablePeriod == endPeriod by returning (nextClaimablePeriod, endPeriod) — avoiding falling through to the active branch.)

Why This Fix Is Sufficient

  • claimRewards and claimableRewards rely solely on the range returned by _claimableDelegationPeriods.

  • Clamping ensures post-exit periods are excluded, eliminating overpayment.

  • The equality case (endPeriod == nextClaimablePeriod) is now properly treated as ended, preventing invalid fallthrough.

  • The fix is minimal, interface-compatible, and aligns with reward semantics: no rewards after endPeriod.

Was this helpful?