Section 7 of 16

Build
+15 Lynx

Pair: The Swap

What You Are Building

The swap() function is the heart of the protocol. Every trade on Uniswap V2 goes through this function. It also enables flash loans. You will also implement _mintFee(), which handles protocol fee collection. Together, these two functions complete the UniswapV2Pair contract.

How swap() Works

The swap function takes output amounts as parameters, not input amounts. The caller specifies how much of token0 and/or token1 they want to receive. The function transfers those amounts out optimistically, then verifies that enough input tokens were sent to maintain the constant product invariant (accounting for the 0.3% fee).

This design is unintuitive at first. Why specify outputs instead of inputs? Because it enables flash loans. A caller can receive tokens, do something with them (arbitrage, liquidation, anything), and then return tokens. The function does not care what happens between the optimistic transfer and the invariant check.

The Swap Flow

  1. Validate outputs: At least one output must be greater than zero. You cannot call swap with both outputs as zero.
  2. Check reserves: Both outputs must be strictly less than the current reserves. You cannot withdraw more than the pool has.
  3. Optimistic transfer: Transfer the requested output amounts to the recipient. The tokens leave the contract before any input is verified.
  4. Flash loan callback: If the caller passed non-empty data, call uniswapV2Call() on the recipient. This is the flash loan hook. The recipient can use the tokens and return them (plus fee) in the same transaction.
  5. Read new balances: After the transfers and callback, read the actual token balances.
  6. Calculate input amounts: amountIn = balance > reserve - amountOut ? balance - (reserve - amountOut) : 0. At least one input must be greater than zero.
  7. Fee-adjusted K check: The core invariant. balance0Adjusted * balance1Adjusted >= reserve0 * reserve1 * 1000000, where balanceAdjusted = balance * 1000 - amountIn * 3. The factor of 3/1000 is the 0.3% fee.
  8. Update reserves: Call _update() with the new balances.
  9. Emit Swap.

The K Invariant with Fees

The constant product formula x * y = k is the textbook version. In practice, Uniswap V2 enforces:

(balance0 * 1000 - amount0In * 3) * (balance1 * 1000 - amount1In * 3) >= reserve0 * reserve1 * 1000000

This means 0.3% of every input is excluded from the product calculation. After the swap, the effective K is slightly larger than before (the fee stays in the pool). Over time, this grows K and increases the value of LP tokens.

The _mintFee() Function

Uniswap V2 has an optional protocol fee. When enabled, 1/6th of the 0.3% swap fee (0.05%) goes to a protocol-designated address (feeTo). Instead of skimming tokens on every swap, the protocol fee is collected lazily by minting LP tokens.

The math works like this:

  1. Read kLast (the K value at the last mint/burn).
  2. Compute rootK = sqrt(reserve0 * reserve1) and rootKLast = sqrt(kLast).
  3. If rootK > rootKLast (K grew from swap fees since the last collection):
    • numerator = totalSupply * (rootK - rootKLast)
    • denominator = rootK * 5 + rootKLast
    • liquidity = numerator / denominator
    • Mint liquidity LP tokens to feeTo.

Why * 5? Because the protocol takes 1/6th. The denominator is constructed so that the minted LP tokens dilute existing LPs by exactly the right amount.

If feeTo is address(0), the protocol fee is disabled. In that case, _mintFee() sets kLast to zero (to save gas on future calls) and returns false.

The IUniswapV2Callee Interface

For flash loans, the pair calls uniswapV2Call(msg.sender, amount0Out, amount1Out, data) on the recipient. Any contract that wants to use flash loans must implement this interface.

Your Task

This is the biggest section. You need to implement two functions:

  1. swap(): The complete swap logic with all validations, optimistic transfers, optional callback, input calculation, fee-adjusted K check, and reserve update.
  2. _mintFee(): Protocol fee calculation and LP token minting.

Take it step by step. The swap has many moving parts, but each step is straightforward on its own.

Your Code

Solution.sol
Solidity
Loading editor...

Requirements

swap requires at least one output > 0
swap checks outputs < reserves
swap validates recipient is not a token address
swap performs optimistic transfer of token0
swap performs optimistic transfer of token1
swap calls flash loan callback when data is non-empty
swap requires at least one input
swap applies 0.3% fee in K check
swap enforces K invariant
swap emits Swap event
_mintFee reads feeTo from factory
_mintFee uses sqrt for rootK
_mintFee uses denominator with factor 5
_mintFee mints LP tokens to feeTo
_mintFee clears kLast when fee is off