# Reliance on lifiData.receivingAssetId can cause loss of funds

GenericSwapFacet.swapTokensGeneric trusts lifiData.receivingAssetId (analytics-only) instead of SwapData's final asset, so mismatched input strands swapped tokens in the contract.

- 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/75
- Library page: https://bugbountylibrary.com/r/c4%3A2022-03-lifinance%3AH-01
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** GenericSwapFacet.swapTokensGeneric() in LI.FI contracts
**Root cause:** The post-swap balance delta and final transfer use _lifiData.receivingAssetId, a metadata field unused by the swap logic, rather than the receivingAssetId of the last SwapData entry that actually determines the swapped output asset.
**Steps:**
1. Call `swapTokensGeneric()` with `_lifiData.receivingAssetId = 0xUSDC`
2. Pass a single SwapData with `sendingAssetId = 0xWETH`, `receivingAssetId = 0xDAI`
3. Contract records pre-balance of USDC, executes swap producing DAI
4. postSwapBalance of USDC is 0, so `transferAsset(USDC, msg.sender, 0)` sends nothing
5. DAI remains stuck in the contract, lost to the user
**How found:** Code reading of swapTokensGeneric: noticed the final transfer and balance delta reference _lifiData.receivingAssetId, a field documented as analytics-only, while the actual swap uses SwapData.receivingAssetId — a mismatch between the accounting asset and the swapped asset.
**Exploitation:** A user or integrator submitting a swap where lifiData.receivingAssetId does not match the final SwapData.receivingAssetId causes the swapped output tokens to remain in the contract; the balance-delta check on the wrong asset yields zero and no transfer occurs.
**Insight:** In multi-step swap/aggregator contracts, verify that the asset used for balance-delta accounting and final payout is derived from the executed swap path, not from caller-supplied metadata; grep for metadata struct fields used in transfer/balance logic.
