60539 sc medium critical withdraw dos zero reward validators cause permanent user fund lock via broken reward claim logic

Submitted on Nov 23rd 2025 at 22:04:29 UTC by @uzemy for Audit Comp | Vechain | Stargate Hayabusaarrow-up-right

  • Report ID: #60539

  • Report Type: Smart Contract

  • Report severity: Medium

  • Target: https://github.com/immunefi-team/audit-comp-vechain-stargate-hayabusa/tree/main/packages/contracts/contracts/Stargate.sol

  • Impacts:

    • Permanent freezing of funds

Description

Brief/Intro

A flaw in the state-transition logic of the Stargate staking flow causes permanent fund lock when a validator reports zero rewards. Because claimRewards() fails to advance the lastClaimedPeriod under zero-reward conditions, users cannot clear accumulated period gaps beyond maxClaimablePeriods. Once this occurs, all attempts to unstake revert indefinitely, leaving user funds permanently inaccessible. The issue is triggerable under normal protocol conditions and requires no special permissions.

Vulnerability Details

The issue originates from the reward claiming logic failing to advance lastClaimedPeriod when the user accrues zero rewards. In _claimRewards(), the function exits early if claimableAmount == 0, preventing the normal state update:

uint256 claimableAmount = _claimableRewards($, _tokenId, 0);
if (claimableAmount == 0) {
    return; // lastClaimedPeriod not updated
}
$.lastClaimedPeriod[_tokenId] = lastClaimablePeriod;

Because the claim state never advances, the user’s unclaimed-period range remains unchanged. Subsequent calls to delegate() and unstake() enforce the maxClaimablePeriods constraint:

The _exceedsMaxClaimablePeriods computation depends on the span between the first and last claimable periods:

If a validator produces enough consecutive zero-reward periods to exceed maxClaimablePeriods, the user becomes permanently blocked:

  • _claimRewards() cannot reduce the gap (because it returns early),

  • _exceedsMaxClaimablePeriods() continues to revert, and

  • unstake() becomes permanently inaccessible.

No privileged access is required, normal delegation to a low-performance validator is sufficient to trigger the lock. The result is a permanent denial-of-service on user withdrawals.

Impact Details

This vulnerability enables a permanent and irreversible loss of user access to staked VET funds, triggered under normal protocol conditions without requiring any privileged role, malicious validator, or manipulation of external systems. If a validator produces zero rewards for more than maxClaimablePeriods, _claimRewards() fails to advance lastClaimedPeriod due to an early return, while unstake() continues to enforce the period gap invariant. Once this invariant is violated, all future attempts to unstake permanently revert with MaxClaimablePeriodsExceeded, even if rewards later resume.

This results in:

  • Permanent freezing of funds. (Permanent inability for affected users to withdraw their staked VET, effectively freezing their assets)

  • Permanent freezing of unclaimed yield (VTHO rewards)

Proof of Concept

Proof of Concept

#Instructions.

  • Place this snippet after the last it block in "packages/contracts/test/unit/Stargate/Delegation.test.ts"

  • run yarn contracts:test:unit

Was this helpful?