# 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*