A logic flaw in the stake accounting mechanism leads to a double-decrease of a user's effective stake under specific, non-user-controlled conditions. When a user requests a delegation exit and the associated validator subsequently exits the network or has its status changed to QUEUED, any attempt by the user to unstake or re-delegate will trigger an arithmetic underflow. This reverts the transaction, permanently locking the user's VET principal in the protocol with no available recovery mechanism for the user.
2. Vulnerability Details
1
Step
A user stakes VET and delegates to a validator. A checkpoint is created for their effectiveStake.
2
Step
The user calls requestDelegationExit(). This function correctly decreases the effectiveStake for future periods by writing a new checkpoint with a value of zero. This constitutes the first decrease.
3
Step
The validator's on-chain status changes. This can happen in two ways:
The validator exits the network (its status becomes VALIDATOR_STATUS_EXITED).
The validator's status is changed to QUEUED, which in turn changes the user's delegation.status to PENDING.
4
Step
The user then calls unstake() or delegate() to a new validator. Both functions contain a conditional check that is now satisfied due to the validator's status change:
This block is now executed, triggering the second decrease on a stake that has already been zeroed out in preparation for exit.
5
Step
Inside _updatePeriodEffectiveStake(), the Checkpoints.upperLookup function retrieves the most recent checkpoint value, which is 0 (from the first decrease). The code then attempts to calculate currentValue - effectiveStake (i.e., 0 - effectiveStake), which causes an arithmetic underflow and reverts the entire transaction.
3. Impact
Permanent Loss of Funds: Users who encounter this scenario will have their VET tokens permanently locked in the ProtocolStaker contract. The unstake and delegate functions become irrevocably unusable for their NFT, preventing them from ever retrieving their principal investment.
Protocol Insolvency for Affected Delegations: The protocol's liability (the vetAmountStaked recorded in the NFT) can no longer be settled. This breaks the core accounting invariant that all staked funds must be redeemable.
No User-Side Mitigation: Once funds are locked, there is no action a user can take to recover them. The only recovery path is through a privileged contract upgrade performed by the DEFAULT_ADMIN_ROLE.
Systemic Risk: While the trigger is conditional on validator status, validator churn is an expected and normal part of any proof-of-stake network's lifecycle. Therefore, any user who has requested to exit their delegation is at risk of permanent fund loss if their validator exits or is temporarily suspended for any reason. The vulnerability punishes users for following the intended protocol flow.
4. References
Proof of Concept: packages/contracts/test/unit/Stargate/Finding5_POC.test.ts
Flawed Logic in unstake(): packages/contracts/contracts/Stargate.sol#L266-L283
Flawed Logic in delegate(): packages/contracts/contracts/Stargate.sol#L398-L413
Underflow Location in _updatePeriodEffectiveStake(): packages/contracts/contracts/Stargate.sol#L994-L1012
(base) ➜ audit-comp-vechain-stargate-hayabusa git:(main) ✗ cd packages/contracts
VITE_APP_ENV=local yarn hardhat test test/unit/Stargate/Finding5_POC.test.ts --network hardhat
yarn run v1.22.22
$ /Users/test/Documents/web3-audit/2025/Imm/audit-comp-vechain-stargate-hayabusa/node_modules/.bin/hardhat test test/unit/Stargate/Finding5_POC.test.ts --network hardhat
WARNING: You are currently using Node.js v18.20.8, which is not supported by Hardhat. This can lead to unexpected behavior. See https://hardhat.org/nodejs-versions
****************** WARNING: You are operating on a non-vechain network ******************
- Ensure your hardhat config file has a network that:
- 1. Is a VeChain valid network (set url and optionally gasPayer parameter)
- 2. Has the name of the network containing "vechain" (e.g. "vechain_mainnet", "vechain_testnet", "vechain_solo", ...)
-
- This is required to use the VeChain provider and its functions.
- Note that this is only a warning and you can use hardhat without a VeChain network.
- BUT it's possible that some functionalities will not be available.
·------------------------|--------------------------------|--------------------------------·
| Solc version: 0.8.20 · Optimizer enabled: true · Runs: 1 │
·························|··························································
| Contract Name · Deployed size (KiB) (change) · Initcode size (KiB) (change) │
·······················|·························································
| Clock · 0.452 (0.000) · 0.509 (0.000) │
·······················|······················································
| DataTypes · 0.084 (0.000) · 0.138 (0.000) │
·······················|················································
| Errors · 0.084 (0.000) · 0.138 (0.000) │
·······················|·······································
| Levels · 7.115 (0.000) · 7.172 (0.000) │
·······················|········································
| MintingLogic · 6.549 (0.000) · 6.605 (0.000) │
·······················|······································
| Settings · 0.966 (0.000) · 1.022 (0.000) │
·······················|······································
| Stargate · 19.478 (0.000) · 19.731 (0.000) │
·······················|····································
| StargateNFT · 23.061 (0.000) · 23.314 (0.000) │
·······················|··································
| StargateProxy · 0.166 (0.000) · 1.016 (0.000) │
·······················|·································
| Token · 4.592 (0.000) · 4.648 (0.000) │
·······················|·································
| TokenManager · 4.828 (0.000) · 4.885 (0.000) │
·------------------------|--------------------------------|--------------------------------·
Finding 5: Double Decrease Effective Stake on Exit
Validator exits network after user requests exit
Locked stake: 1.0 VET
Protocol debt: 1.0 VET
✔ should revert unstake due to double effective stake decrease
Validator status changes to QUEUED after user requests exit
Delegation status: 1 (PENDING)
✔ should revert unstake when delegation becomes PENDING
User cannot re-delegate after exit request when validator exits
User cannot unstake OR re-delegate - funds permanently locked
✔ should revert re-delegation due to double effective stake decrease
3 passing (801ms)
✨ Done in 3.44s.