59358 sc high off by one error in reward claim logic allows delegators to steal vtho for periods after delegation ended
Submitted on Nov 11th 2025 at 13:33:27 UTC by @Tomioka for Audit Comp | Vechain | Stargate Hayabusa
Report ID: #59358
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 VeChain Stargate protocol contains an off-by-one boundary check error in the reward claiming logic that allows users to claim VTHO rewards for periods after their delegation has ended. When a user has claimed rewards up to their final delegation period and then the delegation ends, a faulty inequality check (> instead of >=) causes the clamping logic to fail, allowing the user to claim rewards for all subsequent periods up to the validator's current completed period. This enables direct theft of VTHO rewards that should be distributed to active delegators.
Vulnerability Details
When users claim rewards, the protocol calculates which periods are claimable using the _claimableDelegationPeriods function. This function should ensure users can only claim rewards for periods during which they were actively delegated. For ended delegations, it should clamp the maximum claimable period to the delegation's endPeriod.
The vulnerable snippet:
function _claimableDelegationPeriods(
StargateStorage storage $,
uint256 _tokenId,
uint256 _delegationId
) internal view returns (uint32 firstClaimablePeriod, uint32 lastClaimablePeriod) {
// ... [code to calculate nextClaimablePeriod] ...
// Attempt to clamp to endPeriod
if (
endPeriod != type(uint32).max &&
endPeriod < currentValidatorPeriod &&
endPeriod > nextClaimablePeriod // BUG: Should be >=
) {
return (nextClaimablePeriod, endPeriod);
}
// Falls through when endPeriod == nextClaimablePeriod
if (nextClaimablePeriod < currentValidatorPeriod) {
return (nextClaimablePeriod, completedPeriods); // Returns beyond endPeriod!
}
// ...
}Location: Stargate.sol:917-921
The Bug Explained
When endPeriod == nextClaimablePeriod the condition endPeriod > nextClaimablePeriod is false, so the clamping branch is bypassed and the function falls through to the general case which returns completedPeriods (the validator's current period). This allows the caller to claim far beyond their delegation end.
This equality case commonly occurs when:
A user has claimed all rewards up to period N-1
Their delegation ends at period N (endPeriod = N)
The next claimable period is N (nextClaimablePeriod = N)
Why this is exploitable:
Users who claim regularly are more vulnerable because their
nextClaimablePeriodoften equalsendPeriod.After delegation end, a malicious final claim can request rewards up to the validator's current period, stealing VTHO intended for active delegators.
Impact Details
Direct theft of VTHO from the protocol's reward distribution pool.
Stolen amount = (user_stake / validator_total_stake) × reward_per_period × overclaimed_periods.
Theft compounds linearly with time elapsed after delegation end.
Realistic example (from report):
User stake: 500,000
Validator total stake: 10,000,000
Reward per period: 100,000 VTHO
User waits 10 periods after delegation end
Legitimate reward (period 10 only): (500,000 / 10,000,000) × 100,000 = 5,000 VTHO
Stolen reward (periods 10–19): (500,000 / 10,000,000) × 100,000 × 10 = 50,000 VTHO
Total overclaim: 45,000 VTHO stolen (9× legitimate amount)
Affected parties:
Protocol: loses VTHO from reward pool
Active delegators: receive reduced rewards
Honest users who claim promptly: paradoxically more vulnerable
Scale:
Any user can exploit this after exiting delegation
Incentivizes delaying final claim to maximize overclaim
References
Vulnerable function: [Stargate.sol:_claimableDelegationPeriods]
packages/contracts/contracts/Stargate.sol#L917-L921Claim rewards function: [Stargate.sol:claimRewards]
packages/contracts/contracts/Stargate.sol
Proof of Concept
Prerequisites
Normal user operations only:
User has an active delegation
User claims rewards regularly
User requests exit or delegation naturally ends
Validator continues operating (periods advance)
Running the Proof of Concept
From the packages/contracts directory run the Hardhat test with the environment variables used during testing.
Commands:
Or set environment variables first:
Full PoC (test file)
(The report contains the full TypeScript PoC test that demonstrates lastClaimable > delegation end after exit.)
Recommended Fix
Primary fix — change the strict > to >=:
Alternative defensive fix — always clamp when delegation has ended:
Why these fixes work:
>=handles the equality case so the final legitimate period is returned as the last claimable period.The defensive fix ensures that when a delegation has ended, the last claimable period never exceeds the delegation
endPeriod.
Notes
All links and file references in this page are preserved as in the original report.
Was this helpful?