# Balancer using safeApprove may lead to revert.

Tapioca's Balancer._routerSwap leaves residual ERC20 allowance after Stargate rounds down amount by convertRate, causing later safeApprove calls to revert.

- Payout: Contest reward
- Severity: medium
- Program: Tapioca
- Credited: bin2chen
- Disclosed: 2024-02-14
- Write-up quality: 6/10
- Original: https://github.com/sherlock-audit/2024-02-tapioca-judging/issues/94
- Library page: https://bugbountylibrary.com/r/sherlock%3A2024-02-tapioca%3AM-21
- Tags: domain:web3 impact:dos kind:contest platform:sherlock sev:medium tech:evm tech:solidity technique:source-review vuln:business-logic

**Target:** Balancer.sol _routerSwap / Stargate router allowance
**Root cause:** safeApprove sets allowance to _amount but Stargate's router rounds _amountLD down via div(convertRate).mul(convertRate), so the full allowance isn't consumed; OZ safeApprove reverts when changing a nonzero allowance to another nonzero value.
**Steps:**
1. Call Balancer._routerSwap with _amount not divisible by pool.convertRate (e.g. DAI convertRate=1e12)
2. safeApprove(router, _amount) sets allowance
3. router.swap rounds _amountLD down, consuming less than _amount, leaving residual allowance
4. Next _routerSwap calls safeApprove(router, newAmount) which reverts on nonzero-to-nonzero approval
**How found:** Code review of _routerSwap's safeApprove combined with reading Stargate router.swap source, noticing convertRate rounding leaves allowance unused.
**Insight:** When integrating with routers/pools that round amounts (convertRate, dust truncation), never assume safeApprove allowance is fully consumed; reset to zero or use forceApprove.
