59564 sc high double calling updateperiodeffectivestake during the exit flow makes unstake revert trapping staked vet

Submitted on Nov 13th 2025 at 15:30:43 UTC by @niffylord for Audit Comp | Vechain | Stargate Hayabusaarrow-up-right

  • Report ID: #59564

  • 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

If a delegator signals exit while their validator is still active and the validator subsequently exits before the delegator calls unstake, Stargate.sol subtracts the delegator’s effective stake twice. The second subtraction underflows, so unstake reverts forever and the user’s VET remains locked.

Vulnerability Details

  • requestDelegationExit immediately reduces the validator checkpoint for the exiting token via _updatePeriodEffectiveStake(..., false) to ensure future rewards omit the delegator.【F:packages/contracts/contracts/Stargate.sol†L523-L569】

  • Later, unstake checks the validator status. If it sees VALIDATOR_STATUS_EXITED, it runs _updatePeriodEffectiveStake(..., false) again, assuming the reduction has not yet happened.【F:packages/contracts/contracts/Stargate.sol†L260-L283】

  • Because checkpoints store deltas, the second subtraction reuses the same (validator, tokenId, period) entries. When the validator had little remaining stake, the subtraction underflows and Solidity panics (0x11), reverting the entire transaction. No retry can succeed because the same branch keeps triggering.

Impact Details

triangle-exclamation

Suggested Mitigations

  • Track whether _updatePeriodEffectiveStake already ran for the exiting delegation (e.g., store a boolean flag per token or delegation) and skip the second decrease in unstake.

  • Alternatively, recompute the current checkpoint value in unstake and only subtract when the stored amount is greater than or equal to the token’s effective stake.

  • Refactor the flow so validator status transitions trigger the checkpoint adjustment once, removing duplicate calls from user entry points.

References

  • Stargate.sol requestDelegationExit: first checkpoint decrease.【F:packages/contracts/contracts/Stargate.sol†L523-L569】

  • Stargate.sol unstake: second checkpoint decrease on exited validators.【F:packages/contracts/contracts/Stargate.sol†L260-L283】

  • _updatePeriodEffectiveStake: raw subtraction without underflow guard.【F:packages/contracts/contracts/Stargate.sol†L993-L1012】

  • Unit test reproducer: Delegation.test.ts “should revert unstake after requesting exit if validator exits before unstake.”【F:packages/contracts/test/unit/Stargate/Delegation.test.ts†L205-L231】

Proof of Concept

1

Step

Stake an NFT and delegate it to a validator that is currently active.

2

Step

Advance periods so the delegation is active, then call requestDelegationExit.

3

Step

Advance one more period and mark the validator status as EXITED.

4

Step

Call unstake; it attempts the second subtraction and reverts with panic 0x11.

Relevant test from packages/contracts/test/unit/Stargate/Delegation.test.ts:

Testing

  • Regression covered by packages/contracts/test/unit/Stargate/Delegation.test.ts.

  • To reproduce: yarn contracts:test:unit (fails before fix with panic 0x11, passes once mitigation is applied).

Was this helpful?