60210 sc high during a validator exit users will be unable to unstake due to underflow

Submitted on Nov 20th 2025 at 00:08:26 UTC by @oxrex for Audit Comp | Vechain | Stargate Hayabusaarrow-up-right

  • Report ID: #60210

  • 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: Permanent freezing of funds

Description

Brief / Intro

During a validator EXIT, users will be unable to unstake due to an underflow issue inside the unstake() function.

Vulnerability Details

The unstaking process in the current implementation of Vechain staking is implemented in two main user actions:

1

User requests delegation exit

  1. A user requests to exit delegation validator X. In requestDelegationExit the validator's effective stake for the next period is reduced:

function requestDelegationExit(
        uint256 _tokenId
    ) external whenNotPaused onlyTokenOwner(_tokenId) nonReentrant {
    ...

    // decrease the effective stake
    _updatePeriodEffectiveStake($, delegation.validator, _tokenId, completedPeriods + 2, false);

    emit DelegationExitRequested(_tokenId, delegation.validator, delegationId, exitBlock);
}
2

User calls unstake()

  1. The user then calls unstake() to get their staked VET tokens back and claim earned rewards.

If the validator has not been exited before the user calls unstake(), the user receives their VET back as expected.

However, a problematic sequence creates an underflow:

1

Problematic sequence — part 1

  • User requests exit from validator X (reducing the validator effective stake for the upcoming period).

2

Problematic sequence — part 2

  • Validator X is exited (validator becomes status EXITED).

3

Problematic sequence — part 3

  • User calls unstake(). Because the validator status is EXITED, unstake() attempts to call _updatePeriodEffectiveStake again — even though the effective stake was already reduced when the user requested exit.

This double deduction creates two failure modes:

  • If the user is the sole delegator to that validator, the second deduction causes an underflow, permanently locking the user's tokens.

  • If there are multiple delegators, the second deduction corrupts the validator's effective stake and will cause other delegators' future unstake calls to underflow as well, propagating the issue.

Relevant excerpt from unstake():

The PoC attached with this report demonstrates the bug. The fix is to avoid decreasing effective stake twice for the same delegation/validator (i.e., ensure _updatePeriodEffectiveStake isn't called redundantly when the exit was already processed in requestDelegationExit).

Impact Details

triangle-exclamation

References

https://github.com/immunefi-team/audit-comp-vechain-stargate-hayabusa/blob/main/packages/contracts/contracts/Stargate.sol#L276-L283

Proof of Concept

chevron-rightPoC test (paste into Rewards.test.ts and run)hashtag

Expected revert seen when underflow occurs:

Was this helpful?