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 Hayabusa
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).maxendPeriod < currentValidatorPeriodendPeriod > nextClaimablePeriod
After a full claim up to endPeriod:
lastClaimedPeriod = endPeriodnextClaimablePeriod = 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
lastClaimedPeriodClaims 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.
_calculateEffectiveStakestill 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)_claimRewardsand_claimableRewards_claimableRewardsForPeriod_updatePeriodEffectiveStake/_calculateEffectiveStake
packages/contracts/contracts/mocks/ProtocolStakerMock.sol
signalDelegationExitandgetDelegationPeriodDetailsgetDelegatorsRewardsmock 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, andProtocolStakerMock)
Proof of Concept
Summary steps (PoC):
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?