60241 sc medium permanent freezing of staked funds caused by accumulation with zero rewards

Submitted on Nov 20th 2025 at 10:17:47 UTC by @Paludo0x for Audit Comp | Vechain | Stargate Hayabusaarrow-up-right

  • Report ID: #60241

  • 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 delegator's NFT in the Stargate protocol can become permanently locked under these conditions:

  • It accumulates more claimable periods than maxClaimablePeriods.

  • It receives zero rewards across those periods.

Because lastClaimedPeriod only advances when the claimable reward is greater than zero, users with consistently zero rewards are unable to reduce the claimable period window. Once the period span exceeds the maximum allowed, all calls to unstake() and delegate() revert with MaxClaimablePeriodsExceeded, resulting in a permanent freeze of the user's staked VET.

Vulnerability details (summary)

The root cause is the interaction between:

  • computation of claimable period window (_claimableDelegationPeriods),

  • the maxClaimablePeriods guard (_exceedsMaxClaimablePeriods),

  • and how lastClaimedPeriod is updated in _claimRewards.

key points:

  • _claimableDelegationPeriods computes a window from lastClaimedPeriod + 1 up to either the delegation endPeriod or the validator's completedPeriods.

  • As the validator completes more periods, lastClaimablePeriod (the window upper bound) can grow indefinitely for active delegations.

  • The guard _exceedsMaxClaimablePeriods checks the window length and blocks operations when the window length >= maxClaimablePeriods.

  • _claimRewards updates lastClaimedPeriod only if the computed claimableAmount is > 0. If claimableAmount == 0, the function returns early without updating lastClaimedPeriod.

  • Integer division when computing per-period rewards can make a delegator's reward round to 0:

  • If rewards round to zero for many consecutive periods, lastClaimedPeriod stays unchanged, the window grows, then becomes and remains larger than maxClaimablePeriods. Because unstake() and redelegation check this guard before calling _claimRewards, the delegator cannot advance lastClaimedPeriod and becomes permanently locked.

Example of relevant code snippets

  • Window computation (excerpt):

  • Max periods guard:

  • Claim rewards (excerpt):

Impact

  • Affected delegators:

    • Cannot claim non-zero rewards (there are none).

    • Cannot shrink the claimable period window.

    • Cannot unstake or redelegate because those operations revert before allowing _claimRewards to advance the cursor.

  • This results in permanently frozen VET for affected NFTs.

Severity stated: Critical in text (but reported severity: Medium).

Proof of Concept (PoC)

chevron-rightPoC scenario and logs (expand)hashtag

The PoC demonstrates the vulnerability with this scenario:

  • Total stake: 498,750 VET (95 whales × 1,500 VET each)

  • Dust user stake: 150 VET (0.03% share)

  • Rewards per period: 1000 wei (from mock)

  • Dust user reward calculation: (150 × 1000) / 498,750 = 0 (integer division)

PoC log excerpt:

PoC code

The full PoC test provided in the report (TypeScript / Hardhat test) is included below:

Required changes (provided in report)

The report includes three required edits used to run the PoC. These are reproduced here verbatim.

1

1. Mock Configuration

File: contracts/mocks/ProtocolStakerMock.sol Location: Line 292 in function getDelegatorsRewards()

Change:

Reason: Simulate low VTHO generation conditions where dust delegators' rewards round to zero.

2

2. Hardhat Configuration

File: hardhat.config.ts Location: Lines 76-81 (hardhat network) and 82-90 (vechain_solo network).

Changes:

And:

Reason: The test requires 96+ accounts (95 mega whales + 1 dust user + deployer).

3

3. Deploy Helper

File: test/helpers/deploy.ts Location: Lines 57-64

Change:

Reason: Prevents array out-of-bounds errors when funding test accounts.

References

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

(Report includes PoC test and mock/hardhat changes above.)


If you want, I can:

  • produce a minimal patch suggestion (contract-side) to fix the issue (e.g., ensure lastClaimedPeriod is advanced even when rewards are zero, but only up to a safe clamped bound), or

  • convert the PoC test into a smaller reproducer or a transaction sequence for on-chain verification.

Which would you prefer?

Was this helpful?