# PaprController pays swap fee in buyAndReduceDebt, not user

PaprController.buyAndReduceDebt charges the swap fee to the controller's own underlying balance via transfer instead of transferFrom, causing reverts or draining stray tokens.

- Payout: Contest reward
- Severity: medium
- Program: Backed Protocol
- Credited: Jeiwan
- Disclosed: 2023-01-31
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2022-12-backed-findings/issues/196
- Library page: https://bugbountylibrary.com/r/c4%3A2022-12-backed%3AM-06
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** PaprController.buyAndReduceDebt swap fee logic
**Root cause:** The fee branch calls underlying.transfer(params.swapFeeTo, fee) from the controller's own balance rather than transferFrom(msg.sender, ...), so the contract pays the fee instead of the caller.
**Steps:**
1. Set swapFeeBips nonzero in SwapParams for buyAndReduceDebt.
2. Call buyAndReduceDebt; controller executes underlying.transfer(swapFeeTo, amountIn*fee/BIPS_ONE) from its own balance.
3. Since controller holds no underlying, transfer reverts (Arithmetic over/underflow in test); any mistakenly-sent underlying can be siphoned to swapFeeTo.
**How found:** Code reading of the fee branch noticed transfer instead of transferFrom; confirmed by modifying the existing test to set fee=1e3, which reverted.
**Exploitation:** Caller sets swapFeeTo to an arbitrary address and nonzero swapFeeBips; if the controller holds stray underlying tokens, the fee transfer moves them to the attacker-chosen recipient. Otherwise the call reverts, breaking the fee feature.
**Insight:** When a contract collects a fee 'from the caller', verify the token movement direction: transfer vs transferFrom determines whose balance is debited, and a contract not designed to hold tokens will revert or leak stray balances.
