59386 sc high fund freeze from double stake subtraction when validator exits

Submitted on Nov 11th 2025 at 18:23:40 UTC by @humanitia for Audit Comp | Vechain | Stargate Hayabusaarrow-up-right

  • Report ID: #59386

  • 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

The delegator signals exit and later the validator moves to an EXITED state. Stargate tries to subtract that NFT’s stake twice: the first subtraction already zeroed the validator’s checkpoint, so the second subtraction underflows and reverts. Affected NFTs can never unstake or redelegate.

Vulnerability Details

Relevant code paths:

unstake path

if (
    currentValidatorStatus == VALIDATOR_STATUS_EXITED ||
    delegation.status == DelegationStatus.PENDING
) {
    _updatePeriodEffectiveStake(..., false);
}

delegate path

unchecked subtraction inside _updatePeriodEffectiveStake

Both unstake and _delegate call _updatePeriodEffectiveStake whenever the validator is EXITED or the delegation is still pending.

When a user already requested exit, the first call to _updatePeriodEffectiveStake during that flow can push the checkpoint to zero for the next period. Later, if the validator itself becomes EXITED, the same subtraction path runs again even though delegatorsEffectiveStake[_validator].upperLookup(_period) already returns 0. _updatePeriodEffectiveStake subtracts without a floor so the second subtraction underflows, reverting with panic 0x11.

Impact Details

Any NFT that requests exit is stuck once its validator exits, halting unstaking or redelegation and causing fund lock for all affected users.

triangle-exclamation

Proof of Concept

Add to packages/contracts/test/unit/Stargate/Stake.test.ts

New import

Test:

Reproduction steps:

1

Stake and delegate

Stake and delegate an NFT.

2

Request delegation exit

Call requestDelegationExit(tokenId).

3

Advance completed periods

Advance the validator’s completed periods so delegation becomes EXITED.

4

Exit validator

Flip validator to VALIDATOR_STATUS_EXITED.

5

Attempt unstake — revert

Call unstake(tokenId). The call reverts with panic code 0x11 (arithmetic under/overflow) because _updatePeriodEffectiveStake subtracts from zero.

Was this helpful?