# Missing fromToken != toToken check

MarginRouter.crossSwapExactTokensForTokens with a fake pair and identical tokens lets attacker withdraw fund tokens while balance check passes, bypassing cooling-off period.

- Payout: Audit contest
- Severity: high
- Program: Marginswap
- Disclosed: 2021-05-03
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2021-04-marginswap-findings/issues/20
- Library page: https://bugbountylibrary.com/r/c4%3A2021-04-marginswap%3AH-02
- Tags: area:web3 domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:access-control vuln:business-logic

**Target:** MarginRouter.crossSwapExactTokensForTokens / Fund withdraw
**Root cause:** No check that fromToken != toToken, and _swapExactT4T withdraws from Fund before _swap records startingBalance, so a fake pair returning 0 output makes endingBalance == startingBalance and the require passes.
**Steps:**
1. Call `crossSwapExactTokensForTokens(1000 WETH, 0, [ATTACKER_CONTRACT], [WETH, WETH])` with attacker-controlled fake pair and token[0]==token[1].
2. `UniswapStyleLib.getAmountsOut(amountIn - fees, pairs, tokens)` queries attacker contract, which returns fake reserves yielding 0 output.
3. `_swapExactT4T` calls `Fund(fund()).withdraw(tokens[0], pairs[0], amounts[0])`, sending funds to the fake pair.
4. `_swap` reads `startingBalance` after the withdraw; attacker contract does nothing, so `endingBalance == startingBalance + 0` and the require passes.
**How found:** Code review of the swap flow noticed the missing fromToken != toToken check and that startingBalance is captured after the Fund withdraw, making the balance invariant trivially satisfiable with a fake pair.
**Exploitation:** Attacker supplies a malicious pair contract and identical input/output token; the router withdraws real fund tokens to the attacker's contract, which returns zero output, satisfying the balance check and effectively withdrawing funds without waiting the coolingOffPeriod.
**Insight:** When a router trusts caller-supplied pair addresses, check that the balance snapshot is taken before any token movement; otherwise a no-op fake pair can satisfy the post-swap invariant and turn a swap into a free withdrawal.

