# User can steal refunded underlying tokens from initRange operation inside RangeManager

initRange leaves Uniswap mint refunds stranded in RangeManager; anyone back-running with removeAssetsFromStep triggers cleanup() to sweep the dust to themselves.

- Payout: Contest reward
- Severity: medium
- Program: Good Entry
- Credited: said
- Disclosed: 2023-10-02
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2023-08-goodentry-findings/issues/254
- Library page: https://bugbountylibrary.com/r/c4%3A2023-08-goodentry%3AM-06
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:chained technique:source-review vuln:accounting-error vuln:business-logic

**Target:** RangeManager.initRange / TokenisableRange.init (Uniswap v3 mint refunds)
**Root cause:** initRange forwards only the minted TR token to the owner and never returns the underlying tokens refunded by the Uniswap v3 mint, leaving them in RangeManager. cleanup() deposits any residual ASSET_0/ASSET_1 balance to msg.sender's lending position.
**Steps:**
1. Owner calls `generateRange` then `initRange(tr, amount0, amount1)`; RangeManager pulls tokens and calls `TokenisableRange(tr).init`.
2. `init` mints a Uniswap v3 position with amount0Min/amount1Min = 95% of desired; POS_MGR refunds unused tokens back to RangeManager.
3. `initRange` transfers only `ERC20(tr).balanceOf(address(this))` to owner; refunded ASSET_0/ASSET_1 remain in RangeManager.
4. Attacker back-runs the initRange tx with `removeAssetsFromStep` (or any path invoking `cleanup()`).
5. `cleanup()` reads ASSET_0/ASSET_1 balances, approves LENDING_POOL, and deposits them with `msg.sender` = attacker, crediting the attacker's position.
**How found:** Code review of initRange vs TokenisableRange.init: noticed init refunds unused tokens to RangeManager (msg.sender of init) but initRange only forwards the TR token, then traced cleanup()'s balance sweep to msg.sender as the theft vector.
**Exploitation:** Attacker monitors mempool for the owner's initRange transaction, back-runs it with a call that triggers cleanup(); the residual refunded underlying tokens are deposited into the lending pool on the attacker's behalf, giving them claim on the funds.
**Insight:** When a wrapper contract calls an external mint/deposit that refunds excess input, check whether the wrapper forwards the refund; any function that sweeps `balanceOf(address(this))` to `msg.sender` turns stranded dust into a back-runnable theft primitive.
