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