56872 sc critical freezing of funds

Submitted on Oct 21st 2025 at 12:37:02 UTC by @shadowHunter for Audit Comp | Belongarrow-up-right

  • Report ID: #56872

  • Report Type: Smart Contract

  • Report severity: Critical

  • Target: https://github.com/belongnet/checkin-contracts/blob/main/contracts/v2/periphery/Staking.sol

  • Impacts:

    • Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield

    • Permanent freezing of funds

Description

Brief/Intro

The Staking contract mints shares to represent users’ staked positions. These shares are freely transferable ERC20 tokens because of ERC4626 inheritance.

The vault tracks staked positions in the stakes[address] array to enforce:

  • Minimum stake periods (minStakePeriod)

  • Reward distribution (proportional to shares)

  • Emergency withdrawal penalties

Transferring shares to another address does not update the internal stakes[] array, resulting in a permanent desync between the internal staking record and the transferable ERC20 shares.

1

Vulnerability Details — sequence of actions

  • Assume Alice and Bob are two users.

  • Alice deposits 100 LONG tokens → receives 100 sLONG shares.

  • Alice transfers 100 sLONG shares to Bob.

  • Bob now holds the shares but has no corresponding entries in stakes[Bob].

  • Bob attempts a normal withdraw → fails due to missing unlocked stake.

  • Bob can only use emergencyWithdraw → receives 90% of assets, losing 10% as a penalty.

2

Impact Details

  • The new holder of transferred shares cannot withdraw normally — the vault sees no unlocked shares.

  • Emergency withdrawal is the only recovery path, but it always imposes the configured penalty (10% by default).

  • Users effectively lose access to their funds or pay a financial cost simply due to a transfer.

This can lead to permanent freezing of funds.

Recommendation

Prevent permanent freezing by making shares non-transferable. For example:

circle-info

Override ERC20 transfer and transferFrom to revert so shares cannot be moved without updating the internal bookkeeping.

Proof of Concept

chevron-rightClick to expand the full PoC test and observed outputhashtag

Observed output: As expected Redeem reverts (vm.expectRevert())

Was this helpful?