# AnyswapFacet can be exploited to approve arbitrary tokens.

AnyswapFacet calls underlying() twice on attacker-controlled token, letting a malicious token return different addresses to approve/drain LiFi contract funds.

- Payout: Audit contest
- Severity: medium
- Program: LI.FI
- Found by: kirk-baird
- Disclosed: 2022-05-05
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2022-03-lifinance-findings/issues/117
- Library page: https://bugbountylibrary.com/r/c4%3A2022-03-lifinance%3AM-01
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:chained technique:source-review vuln:access-control vuln:business-logic

**Target:** AnyswapFacet bridge facet (startBridgeTokensViaAnyswap / swapAndStartBridgeTokensViaAnyswap)
**Root cause:** The facet calls IAnyswapToken(_anyswapData.token).underlying() twice on an unvalidated, attacker-supplied token address; a malicious token returns a benign token on the first call (to pass balance checks) and a valuable token the contract holds on the second call (used for approval/transfer).
**Steps:**
1. Deploy malicious ERC20 whose underlying() returns attacker token on first call and a valuable token (native or ERC20 held by LiFi) on second call.
2. Call startBridgeTokensViaAnyswap() with _anyswapData.token = malicious token, _anyswapData.router = attacker address, _anyswapData.amount = target amount.
3. First underlying() returns attacker token; transferFromERC20 of attacker token passes the ERR_INVALID_AMOUNT balance check.
4. _startBridge() calls underlying() again, now returning the valuable token held by LiFi.
5. Contract approves/transfers that valuable token to attacker-controlled router (or sends native via anySwapOutNative).
**How found:** Code review of AnyswapFacet: noticed underlying() is invoked twice on an unvalidated user-supplied token address, enabling a stateful/malicious token to return different values across calls.
**Exploitation:** Attacker supplies a malicious token contract and attacker-controlled router; the double underlying() call lets them pass balance checks with worthless tokens while the contract approves or transfers real tokens/native held by LiFi to the attacker's router.
**Insight:** When a contract calls a method on a user-supplied token address more than once, a malicious token can return different values per call; look for repeated external calls on unvalidated addresses to bypass balance checks and redirect approvals.
