59809 sc high user balances are permanently frozen in specific delegation scenarios

Submitted on Nov 16th 2025 at 02:59:50 UTC by @hrmneffdii for Audit Comp | Vechain | Stargate Hayabusaarrow-up-right

  • Report ID: #59809

  • 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

This report details a critical vulnerability discovered in the Stargate contract's delegation logic. A flaw in the _delegate function can cause a validator's delegatorsEffectiveStake to be improperly reduced twice for the same user.

This "double-decrease" bug desynchronizes the validator's internal stake accounting, leading to an arithmetic underflow when another user attempts to interact with their own stake. As a result, other delegators on the same validator will have their funds permanently frozen, as all management functions (e.g., delegate, requestDelegationExit, unstake) will fail.

Vulnerability details

1

Path: requestDelegationExit()

When a user (e.g., "Bob" from the PoC) calls requestDelegationExit (Step 5 in PoC), the function correctly calls _updatePeriodEffectiveStake(..., isIncrease: false). This reduces the validator's EffectiveStake to reflect the user's exit. This is the expected behavior.

2

Path: _delegate() (during re-delegation)

Later, the validator exits. If the user then calls _delegate to move their stake to a new validator, the logic in _delegate can also reduce the original validator's EffectiveStake again:

  • _getDelegationStatus returns DelegationStatus.EXITED for the token.

  • This triggers the conditional if (status == DelegationStatus.EXITED || status == DelegationStatus.PENDING) which withdraws the user's VET via withdrawDelegation.

  • Later, a second condition if (currentValidatorStatus == VALIDATOR_STATUS_EXITED || status == DelegationStatus.PENDING) is true and the code calls _updatePeriodEffectiveStake(..., isIncrease: false) a second time.

The code does not account for the stake already having been subtracted during requestDelegationExit, causing a double-decrease of the same user's stake from the validator's EffectiveStake.

Destructive scenario (PoC summary)

1

A validator has EffectiveStake = 3e18 (Alice 1.5e18 + Bob 1.5e18).

2

Bob calls requestDelegationExit. Validator EffectiveStake becomes 1.5e18 (3e18 - 1.5e18). (Correct)

3

Validator exits. Bob calls delegate to a new validator. Due to the bug, the code reduces the validator's EffectiveStake again, resulting in EffectiveStake = 0 (1.5e18 - 1.5e18). (Incorrect)

4

Alice (still delegated to the exited validator with 1.5e18) attempts to call delegate to move her funds.

5

The _delegate logic for Alice attempts _updatePeriodEffectiveStake(..., isIncrease: false):

  • currentValue = 0 (incorrect)

  • effectiveStake (Alice) = 1.5e18

  • updatedValue = currentValue - effectiveStake -> 0 - 1.5e18 This causes an arithmetic underflow (Solidity 0.8.x+), and Alice's transaction reverts.

6

The same underflow occurs for Alice if she calls requestDelegationExit or other management operations; her funds become effectively frozen with no in-contract fix available.

triangle-exclamation

Proof of Concept

How to run

Run the following test: yarn contracts:test:unit:verbose -- -- --grep "User balances are permanently frozen in specific delegation scenarios"

Test Case (excerpt)

chevron-rightLogshashtag

Was this helpful?