# A temporary issue shows in the staking functionality which leads to the users receiving less minted tokens

SafEth staking prices rETH derivative using full contract balance, so when pool lacks space for that balance it uses inflated Uniswap pool price, causing depositors to receive fewer minted safETH shares.

- Payout: Contest reward
- Severity: high
- Program: Asymmetry Finance
- Credited: CodingNameKiki
- Disclosed: 2023-07-28
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2023-03-asymmetry-findings/issues/1004
- Library page: https://bugbountylibrary.com/r/c4%3A2023-03-asymmetry%3AH-02
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:oracle-manipulation

**Target:** SafEth staking contract / Reth derivative ethPerDerivative
**Root cause:** SafEth.stake() calls ethPerDerivative(derivatives[i].balance()) with the entire rETH balance; poolCanDeposit then returns false even when the user's actual deposit fits, so ethPerDerivative returns the higher Uniswap poolPrice instead of the RocketPool getEthValue price.
**Steps:**
1. Note ethPerDerivative(_amount): if poolCanDeposit(_amount) returns getEthValue(1e18), else returns poolPrice() (Uniswap slot0 sqrtPriceX96 derived).
2. poolCanDeposit checks rocketDepositPool.getBalance() + _amount <= maxDepositPoolSize && _amount >= minDeposit.
3. In stake(), underlyingValue uses ethPerDerivative(derivatives[i].balance()) — the whole derivative balance, not msg.value.
4. Set state: totalSupply=199999999999999999864, rETH balance=187976926409237403412, pool has ~100e18 free space.
5. User stakes 5e18: underlyingValue computed with poolPrice 1069517062752670179 → 201044530198482424206, preDepositPrice=1005222650992412121.
6. deposit() sees poolCanDeposit(5e18)==true, deposits to RocketPool, depositAmount=4698999533488942411.
7. derivativeReceivedEthValue uses normal price 1063960369075232250 → 4999549277935239332.
8. mintAmount = 4999549277935239332*1e18/1005222650992412121 = 4973574036557377784 vs correct 4999549277935239332 — user loses ~0.026 ETH on unstake.
**How found:** Code reading of stake()'s underlyingValue loop noticed ethPerDerivative is passed derivatives[i].balance() rather than the deposit amount, and that ethPerDerivative branches on poolCanDeposit of that argument — so a large derivative balance flips the branch to the swap-only poolPrice.
**Exploitation:** Any depositor during a period where the rETH derivative balance exceeds remaining RocketPool deposit capacity but the user's own deposit fits triggers the inflated preDepositPrice, systematically minting fewer safETH shares; loss realized on unstake (~0.025 ETH per 5 ETH in the example).
**Insight:** When a pricing helper branches on whether an amount fits a capacity limit, check what amount the caller actually passes: passing the contract's whole balance instead of the user's deposit can flip the branch and desync the price used for share issuance from the price used for the actual deposit.
