57298 sc critical state sync omission in staking transfers forces transferred slong holders into penalized emergency exits
Submitted on: Oct 25th 2025 at 04:23:25 UTC by @InquisitorScythe for Audit Comp | Belong
Report ID: #57298
Report Type: Smart Contract
Severity: Critical
Target: https://github.com/immunefi-team/audit-comp-belong/blob/main/contracts/v2/periphery/Staking.sol
Impact category: Griefing (no profit motive required; harms users/protocol)
Description
Brief / Intro
The staking vault mints and allows free transfer of sLONG (ERC4626-style shares), but it does not synchronize the per-account stake ledger on transfers. A recipient who obtains sLONG via ERC20 transfer receives shares (ERC20 balance) but has no corresponding unlocked stake entries in the contract's per-account ledger. Such recipients cannot pass the normal withdrawal checks and are forced to use the emergency withdrawal path, which burns shares and applies a permanent penalty routed to the treasury.
Root cause
The per-account stake ledger:
Is declared as
mapping(address staker => Stake[] times) public stakes;Is appended to only on deposits (via
_deposit)Is never reconciled or updated on ERC20 transfers (no transfer hooks or overrides)
Thus, transferred-in sLONG holders have token balances but zero stake entries, so they cannot consume "unlocked" stakes via the normal withdrawal flow and are blocked into emergency withdrawals with penalties.
Vulnerability Details (selected snippets)
File:
contracts/v2/periphery/Staking.solstakesdeclaration:
_deposit— locks freshly minted shares on deposit:
_withdraw— requires consumption of unlocked stake entries:
_consumeUnlockedSharesOrRevert— iterates per-account stake entries and reverts if not enough unlocked shares:
_emergencyWithdraw— burns shares regardless of lock status and applies penalty:
Note: line ranges cited in the report correspond to the repository's current main branch at the time of reporting.
Impact (concise)
Transfers only move ERC20 balances and do not update
stakes[...]. Recipients have shares but no stake entries.Attempting a standard
withdraw/redeemcalls_consumeUnlockedSharesOrRevertand reverts withMinStakePeriodNotMetbecause the recipient has no unlocked stake entries.Recipients are forced to use
emergencyWithdraw/emergencyRedeem, which burns shares, applies the configured penalty (default 10%), and sends the penalty totreasury.Monetary impact: transferred-in holders permanently lose a fraction of their assets proportional to
penaltyPercentage. This can be arbitrarily large depending on transferred amount.Attack surface: any integration or flow that transfers sLONG balances (e.g., auto-stake, rewards routing, third-party transfers) can inadvertently subject recipients to penalized exits.
References
Vulnerable file in related repo: https://github.com/belongnet/checkin-contracts/blob/main/contracts/v2/periphery/Staking.sol
Proof of Concept
Below is the Hardhat test PoC used to reproduce the issue: depositor stakes LONG, transfers the minted sLONG to a recipient, and the recipient cannot perform normal redeem — only emergency redeem succeeds (with penalty).
File: test/v2/platform/staking-transfer-poc.test.ts
Example run:
(Full console output and test artifacts omitted for brevity.)
If you want, I can:
Suggest minimal code fixes or design approaches to keep per-account stake consistency on transfers (e.g., hooks updating
stakeson transfer, or migrating stakes on transfer/delegation).Draft a patch/PR against the referenced repository demonstrating a fix.
Was this helpful?