# User can trade away an asset using Balancer batchSwap without removing it from account

BalancerController only records the first token as tokensOut, so a user can set an intermediate batchSwap step amount above the prior step's output to drain an unaccounted asset.

- Payout: Contest reward
- Severity: medium
- Program: Sentiment
- Credited: obront
- Disclosed: 2022-10-31
- Write-up quality: 7/10
- Original: https://github.com/sherlock-audit/2022-11-sentiment-judging/issues/7
- Library page: https://bugbountylibrary.com/r/sherlock%3A2022-11-sentiment%3AM-3
- Tags: domain:web3 impact:integrity kind:contest platform:sherlock sev:medium tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** BalancerController.sol batchSwap accounting
**Root cause:** BalancerController derives tokensOut only from the first token in the swap chain and trusts user-supplied step amounts, ignoring Balancer's returned assetDeltas; an intermediate step amount exceeding the previous step's output pulls extra user-approved tokens that never appear in tokensOut.
**Steps:**
1. User holds 400 BAL and 100 WETH, both approved to Balancer.
2. Call `batchSwap()` with path BAL => WETH => USDC.
3. Set step 1 `amount = 400` (all BAL).
4. Set step 2 `amount = [step 1 output] + 100` (pulls extra 100 WETH).
5. Observe WETH balance drops but WETH never appears in `tokensOut`; `assets` array and `hasAsset[WETH]` remain true.
**How found:** Reading BalancerController's tokensOut derivation and noticing batchSwap step amounts can exceed the previous step's output if the user owns and approved those tokens, then testing with Foundry.
**Exploitation:** User submits a batchSwap whose intermediate step amount exceeds the prior step's returned amount, spending additional approved tokens; Sentiment's controller records only the first token as tokensOut, so the extra token is removed from the account without a corresponding removeAsset call, desyncing accounting.
**Insight:** When wrapping an AMM router, don't infer token flows from the user-supplied path/amounts; use the router's returned assetDeltas (positive = out, negative = in). Intermediate step amounts can exceed prior outputs and silently pull extra approved tokens.
