# All swapping functions lack checks for returned tokens

LI.FI GenericSwapFacet swap functions never verify output tokens were received, so a non-swapping router call returns success while user funds are already taken.

- Payout: Audit contest
- Severity: high
- Program: LI.FI
- Found by: 0xDjango
- Disclosed: 2022-05-05
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2022-03-lifinance-findings/issues/76
- Library page: https://bugbountylibrary.com/r/c4%3A2022-03-lifinance%3AH-02
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:business-logic

**Target:** GenericSwapFacet / LibSwap swap functions (Solidity smart contract)
**Root cause:** LibSwap.swap() only requires the external call to succeed and swapTokensGeneric() performs no final balance check, so a calldata targeting a non-swapping function (e.g. getAmountsOut()) returns true without delivering tokens.
**Steps:**
1. User calls swapTokensGeneric() with fromTokens transferred to the contract
2. swapData targets a router function that returns success but performs no swap, e.g. `getAmountsOut()`
3. LibSwap.swap() sees success and does not revert
4. No output token balance check exists in swapTokensGeneric()
5. User's fromTokens remain in the contract with no received asset
**How found:** Code review of LibSwap.swap() and GenericSwapFacet.swapTokensGeneric() noting only call-success is checked and no output balance verification exists at inner or outer level.
**Exploitation:** Attacker crafts swapData calldata pointing at a benign view function on a whitelisted router that returns true; the swap succeeds without delivering output tokens while the user's input tokens are already in the contract.
**Insight:** In DEX-aggregator facets, whitelisting router addresses is insufficient; whitelist function selectors and assert post-swap balance deltas, since any view function on a whitelisted router returns success without swapping.
