# Unused allowances in SwapFarm can be used maliciously by FARM_SWAP_CALLER or a compro- mised router

SwapFarm leaves residual router allowances after wrap/unwrap swaps, letting a malicious router or compromised FARM_SWAP_CALLER spend leftover approvals to drain funds.

- Payout: Contest reward
- Severity: medium
- Program: infiniFi
- Credited: msgdot
- Disclosed: 2025-05-23
- Write-up quality: 6/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_infinifi_april2025.pdf#3.2.5
- Library page: https://bugbountylibrary.com/r/cantina%3Ainfinifi_april2025%3A3.2.5
- Tags: domain:web3 impact:funds-loss kind:contest platform:cantina sev:medium tech:evm tech:solidity technique:chained technique:source-review vuln:access-control vuln:business-logic

**Target:** SwapFarm.sol wrapAssets/unwrapAssets router approvals
**Root cause:** wrapAssets/unwrapAssets use forceApprove on the router but only reset the opposite token's approval, leaving unused allowances when amountInMaximum exceeds amount spent.
**Steps:**
1. Call `wrapAssets(1000e6, router, swapAmountInMaximum)` where router spends 0 of approved USDC, leaving residual allowance
2. Later call `unwrapAssets(500e18, router, swapWithAttack)`; router uses stale USDC allowance to `transferFrom(farm, attacker, amount)`
3. Repeat each cooldown cycle to drain ~3% of funds per day
**How found:** Noticed forceApprove before arbitrary calldata plus asymmetric approval resets (wrapAssets resets assetToken, unwrapAssets resets wrapToken) creates cross-call residual allowances exploitable via amountInMaximum or malicious swap paths.
**Exploitation:** Malicious/compromised router reuses leftover assetToken or wrapToken allowance from a prior swap to transferFrom farm funds to an attacker address during a subsequent swap call.
**Escalation:** Repeated swaps every 4h cooldown drain ~3% of funds daily; malicious router can take up to 100% of previous swap.
**Insight:** When a contract forceApproves a router then executes caller-supplied calldata, check whether approvals are zeroed after the call and whether reset logic covers both tokens across paired functions.
