57399 sc critical erc4626 staking lockbook breaks share fungibility partial transfers can dos withdrawals

Submitted on Oct 25th 2025 at 20:36:18 UTC by @TECHFUND_inc for Audit Comp | Belongarrow-up-right

  • Report ID: #57399

  • Report Type: Smart Contract

  • Report severity: Critical

  • Target: https://github.com/immunefi-team/audit-comp-belong/blob/main/contracts/v2/periphery/Staking.sol

  • Impacts: Permanent freezing of funds

triangle-exclamation

Description

Brief/Intro The Staking vault enforces time-locks via per-deposit “Stake” entries that are only created on deposit. Withdraw/redeem consume only unlocked Stake entries. Since share transfers do not create Stake entries for recipients, transferred-in sLONG are invisible to the unlock logic. This breaks ERC4626 share fungibility and causes stuck balances or forces users into penalized emergency flows.

Vulnerability Details

  • Root cause

    • On deposit: _deposit pushes Stake({shares, timestamp}) to stakes[to].

    • On withdraw/redeem: _withdraw calls _consumeUnlockedSharesOrRevert(owner, shares), which iterates stakes[owner] and must fully cover the requested shares from unlocked entries.

    • On transfer: no Stake entries are created/updated; the receiver’s transferred shares have no corresponding Stake entries, so they can never be “consumed” by the unlock logic.

1

BUG — transferred-in sLONG are non-redeemable

Scenario:

  • Alice holds 100 sLONG and transfers 50 to Bob.

  • Bob deposits 100 LONG (mints 100 sLONG). Even after unlocking, Bob’s stakes sum to 100 (his deposit only).

  • redeem(150) reverts with MinStakePeriodNotMet; the extra 50 sLONG (received by transfer) are irredeemable via withdraw/redeem and only convertible via emergency flow with penalty.

Impact:

  • Recipients of transferred sLONG can end up with permanent or penalized-only access to those shares.

2

BUG — partial transfer can DoS standard withdrawals before unlock

Scenario:

  • Alice deposits 100 sLONG (single stake entry).

  • Alice transfers 50 away.

  • Alice then attempts to withdraw/redeem 50 (the remaining balance). The call reverts with MinStakePeriodNotMet because the single Stake entry (100) remains locked and the algorithm cannot source unlocked shares from any other entry.

Impact:

  • Legitimate positions become partially inaccessible via standard withdraw/redeem paths until the original stake entry unlocks.

  • Demonstrates brittle coupling between balance and per-deposit stake entries.

Recommendations

  • Ensure transferred shares are represented in the lockbook (e.g., create/merge Stake entries on incoming transfers), or track a single per-user lock bucket that follows the shares on transfer.

  • Alternatively, disallow transferring locked shares (enforce transfer restrictions) to prevent creation of irredeemable balances.

Impact Details

  • Funds stuck for recipients of sLONG; only recoverable via emergencyWithdraw/emergencyRedeem with penalty.

  • Legitimate positions can become partially inaccessible via standard withdraw/redeem.

References

https://github.com/immunefi-team/audit-comp-belong/blob/a17f775dcc4c125704ce85d4e18b744daece65af/contracts/v2/periphery/Staking.sol#L258-L290

Proof of Concept

Include the below code in staking.test.ts file:

Observed test output:


Was this helpful?