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 Hayabusa
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
Attack Scenario
Delegate an NFT to a validator.
Request exit during the first active period (very realistic scenario).
Wait a few more periods.
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 = 3whileendPeriod = 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
claimRewardsandclaimableRewardsrely 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?