56941 sc critical staking vault vulnerable to first depositor donation attack

Submitted on Oct 21st 2025 at 23:34:16 UTC by @fullcounterhuner for Audit Comp | Belongarrow-up-right

  • Report ID: #56941

  • 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

Description

Summary

The Staking vault initializes with zero total supply and relies on the vanilla ERC4626 share math from Solady. Without any seeded liquidity or virtual share offsets, the first depositor can donate tokens to the vault and dramatically raise the price per share before others join. Severity: Critical, as later stakers can lose nearly all deposited assets to the initial attacker.

Finding Description

contracts/v2/periphery/Staking.sol inherits Solady's ERC4626 implementation and never seeds the share supply during initialize(). When the first user calls deposit(), the vault mints shares 1:1 against assets because totalSupply() equals zero, establishing the initial exchange rate. The contract also treats the token balance in the vault as canonical totalAssets() and does not adjust conversions with any virtual shares or assets. As a result, an attacker who controls the initial shares can transfer additional underlying tokens directly to the vault (or wait for reward distribution) to inflate the apparent asset balance while totalSupply() remains near zero. Subsequent users calling previewDeposit() or deposit() then receive almost no shares for their full contribution, effectively donating their assets to the first minter. After the one-day lock expires, the attacker redeems the inflated share for nearly all pooled funds.

Impact Explanation

Successful exploitation allows the attacker to drain the majority of assets supplied by later participants, resulting in high user fund loss and rendering the staking pool economically unusable.

Likelihood Explanation

The attack is straightforward, requires only being the first depositor, and leverages standard ERC4626 behavior without needing special privileges, making the likelihood high.

Proof of Concept

1

Attack steps (high level)

  1. Attacker deposits a tiny amount (e.g., 1 wei) of LONG via deposit() to mint the first share(s) at a 1:1 exchange rate because totalSupply() == 0.

  2. Attacker transfers a large amount of LONG directly to the vault (a raw token transfer), inflating totalAssets() while totalSupply() remains near the initial tiny amount.

  3. Victim deposits a significant amount of LONG and receives almost zero shares due to the inflated exchange rate—effectively donating their deposit to the attacker.

  4. After the withdrawal lock period (or via an emergency mechanism), the attacker calls redeem() and withdraws nearly the entire vault balance, capturing the victim's funds.

PoC test (hardhat)

chevron-rightHardhat config changes (hardhat.config.ts)hashtag
chevron-rightTest file (test/v2/platform/staking.test.ts) — donation attack testhashtag
chevron-rightRun commandhashtag

Recommendation

Consider seeding the vault with permanently locked liquidity or introducing virtual share and asset offsets so that unsolicited donations cannot skew the exchange rate. One approach could be to mint a fixed MINIMUM_SHARES amount to an irrecoverable address during initialization or to adopt Solmate-style virtual balance modifiers within the convertToAssets() and convertToShares() helpers.


End of report.

Was this helpful?