---
# System prepended metadata

title: Understanding Uniswap V2 Code (3/n) - Flash Swap

---

# Understanding Uniswap V2 Code (3/n) - Flash Swap
> How to borrow tokens for free, use them, and pay them back all in one atomic transaction
## TLDR
- **Flash Swap**: Borrow tokens -> Use them -> Repay with 0.3% fee, all in ONE transaction
- **No Collateral Needed**: As long as you repay within the same transaction, no upfront capital required
- **Key Mechanism**: Optimistic transfer (send first, verify later) enables the callback pattern
- **If Pepayment Fails**: Entire transaction reverts, pool loses nothing
- **Real use case**: Arbitrage 100 DAI between DEXs → Profit USDC with $0 initial capital

## What is a Flash Swap?
### Simple definition: 
- Borrow tokens -> Use them -> Repay them
- All in ONE transaction
### Key characteristics:
- Don't need tokens upfront
- Borrow first, repay later (in the same transaction)
- If can't repay, the entire transaction reverts

Think of it like a **collateralless loan that must be repaid immediately**

### Flash Swap vs Normal Swap
| Feature | Normal Swap | Flash Swap |
|---------|-------------|------------|
| **Input needed** | Must send tokens first | No upfront tokens |
| **Callback** | No | Yes (`uniswapV2Call`) |
| **Use case** | Buy/sell tokens | Arbitrage, liquidation, refinancing |
| **Risk** | Price slippage | Must repay or revert |

**Mental Model:**
- Normal swap: "I give you A, you give me B"
- Flash swap: "Give me B first, I'll pay you A+fee after I use it"

## How Flash Swap Works
### Step 1: Optimistic Transfer
In previous [note](https://hackmd.io/@ChloeIsCoding/Syji7iXJbe), we have looked at the `swap()` function
- Pair send tokens to the user first, without checking if the user can pay

    ```solidity
    // UniswapV2Pair.sol
    function swap(
        uint amount0Out,        // amount of token0 to send out
        uint amount1Out,        // amount of token1 to send out
        address to,             // address to send the output token
        bytes calldata data     // for flash swap
    ) external lock {
        ...
    // Optimistically transfer output tokens to user
        uint balance0;
        uint balance1;
        // Validate recipient: can't send to token addresses themselves
        address _token0 = token0;
        address _token1 = token1;
        require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO');
        // Optimistically send output token first
        if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
        if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens

    // Call the user's contract back

    // Verify user paid back the tokens

    // Validate 0.3% fee and the constant product formula (k)
        ...
    }
    ```

### Step 2: Callback (where user's contract execute the code)
- Uniswap pair calls back to user's contract via `uniswapV2Call()`
- User's contract does its business logic (arbitrage, liquidation, etc.)
- User's contract must return the borrowed tokens + fee before `uniswapV2Call()` returns
    ```solidity
    // UniswapV2Pair.sol - swap()
    // Call the user's contract back
    if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(
        msg.sender, // who initiated the swap
        amount0Out, // amount of token0 user got
        amount1Out, // amount of token1 user got
        data);      // user custom data
    ```

### Step 3: Check Payment
- Ensure user pays back the token
    ```solidity
    // UniswapV2Pair.sol - swap()
    // Verify user paid back the tokens
    balance0 = IERC20(_token0).balanceOf(address(this));
    balance1 = IERC20(_token1).balanceOf(address(this));

    uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
    uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
    require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
    ```

### Step 4: Validate 0.3% fee and Invariant k
- Ensure user repay with 0.3% fee on top
- This ensures the pool doesn't lose money
    ```solidity
    // UniswapV2Pair.sol - swap()
    uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
    uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
    require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');
    ```

## Example: Arbitrage Flash Swap
Scenario
- Uniswap DAI-USDC
    - reserve0 (DAI) = 1000
    - reserve1 (USDC) = 1000
    - Price: 1 DAI = 1 USDC
- Sushiswap DAI-USDC
    - reserve0 (DAI) = 1000
    - reserve1 (USDC) = 2000
    - Price: 1 DAI = 2 USDC (price inefficiency!)
- Arbitrage opportunity
    - DAI on Sushiswap is overvalued vs Uniswap
- Strategy
    - Borrow 100 DAI from Uniswap (pay nothing upfront!)
    - Sell on Sushiswap for 181 USDC
    - Buy back 101 DAI with 113 USDC (to repay Uniswap with fee)
    - Profit: 68 USDC with 0 initial capital 🎉


Step 1: Initiate flash swap on Uniswap
```solidity
uniswapV2Pair.swap(
    100,             // amount0Out (borrow 100 DAI)
    0,              // amount1Out (don't need USDC)
    address(this),  // to (my contract)
    data            // pass sushiswap address
)
```
State after transfer
- Uniswap transfer 100 DAI to my contract immediately
- My contract balance +100 DAI

Step 2: Uniswap calls my callback
```solidity
function uniswapV2Call(
    address sender,
    uint amount0,           // 10 DAI
    uint amount1,           // 0 USDC
    bytes calldata data
) external {
    // Execute the arbitrage:
    // 1. Sell DAI on Sushiswap for USDC
    // 2. Calculate flash swap repayment
        // need to send DAI back
        // balance0 (DAI) = 900 + amount0In
        // balance1 (USDC) = 1000
        // calculate amont0In based on the invariant k equation = 100.3
    // 3. Profit calculation
}
```
1. Sell 100 DAI on SushiSwap for USDC
    - Calculate the amount of USDC received (assuming 0.3% fee on SusshiSwap)
        - Formula: `amountOut = (reserveOut x amountIn * 0.997) / (reserveIn + amountIn * 0.997)`
    - USDC amountOut = 2000 x 100 x 0.997 / (1000 + 100 x 0.997) = 181 USDC
2. Calculate flash swap repayment
    - Need to repay DAI back to Uniswap with fee
        - balance0 (DAI) = 900 + amount0In
        - balance1 (USDC) = 1000 (unchanged)
    - Calculate the amount0In (DAI) needed to repay on Uniswap
        - balance0Adjusted = (900 + amount0In) x 1000 - amount0In * 3 = 900,000 + 997 x amount0In
        - balance1Ajusted = 1000 x 1000 = 1000,000
        - Require: `balance0Adjusted x balance1Adjusted >= reserve0 x reserve1 x 10^6`
        - amount0In >= 100.3 = 101 DAI
    - Calculate the USDC amount needed to swap for DAI on Uniswap 
        - Formula: `amountIn = (reserveIn x amountOut x 1000) / (997 x (reserveOut - amountOut) + 1)`
        - USDC amountIn = 1000 x 101 x 1000 / (997 x (1000 - 101) + 1) = 113 USDC
3. Arbitrage profit calculation
    - Profit = 181 USDC - 113 USDC = 68 USDC

Step 3: Check input token received
- balance0 (DAI) = 900 + 101
- balance1 (USDC) = 1000
- amount0In = 1
- amount1In = 0

Step 4: Validate invariant k
- balance0Adjusted = 1001 * 1000 - 1 * 3 = 1,000,997
- balance1Adjusted = 1000 * 1000 - 0 * 3 = 1000,000
- reserve0 = 1000
- reserve1 = 1000
- balance0Adjusted x balance1Adjusted > reserve0 x reserve1
- ✅ Arbitrage succeeded

## Reference
- UniswapV2 doc: https://docs.uniswap.org/contracts/v2/overview
- UniswapV2 Flash Swap: https://docs.uniswap.org/contracts/v2/guides/smart-contract-integration/using-flash-swaps
- UniswapV2 core repo: https://github.com/Uniswap/v2-core
- UniswapV2 periphery repo: https://github.com/Uniswap/v2-periphery

## Discussion
Found an error? Have questions? 
- Twitter: [@chloe_zhuX](https://x.com/Chloe_zhuX)
- Telegram: [@Chloe_zhu](https://t.me/chloe_zhu)
- GitHub: [@Chloezhu010](https://github.com/Chloezhu010)

---

*Last updated: Nov 3rd, 2025*
*Part of my #LearnInPublic Solidity series*