Fatskills
Practice. Master. Repeat.
Study Guide: Blockchain and Web3 Development: DeFi and Tokenomics Automated Market Makers Uniswap V2V3 Constant Product Formula
Source: https://www.fatskills.com/cryptocurrency-bitcoin-blockchain-and-more/chapter/blockchain-and-web3-development-blockchain-and-web3-development-defi-and-tokenomics-automated-market-makers-uniswap-v2v3-constant-product-formula

Blockchain and Web3 Development: DeFi and Tokenomics Automated Market Makers Uniswap V2V3 Constant Product Formula

By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.

⏱️ ~5 min read

Study Guide – Automated Market Makers (Uniswap V2/V3, Constant Product Formula)


What This Is

An Automated Market Maker (AMM) is a smart‑contract‑based liquidity pool that replaces order‑books with a simple math rule—most famously the constant‑product formula x * y = k. Anyone can deposit two tokens, and traders can swap against the pool instantly, without needing a counter‑party. The first real‑world proof‑of‑concept is Uniswap, where a user swaps 1 ETH for ~2,500 USDC in a single transaction, and the price automatically adjusts according to the pool’s balances.


Key Terms & Code Snippets

  • AMM (x·y = k) – The core invariant: the product of the two token reserves (x and y) stays constant (k). When you add one token, the other must be removed to keep k unchanged.
  • Liquidity Provider (LP) – An address that deposits equal‑value amounts of two ERC‑20 tokens into a pool and receives LP tokens representing its share.
  • LP Token (ERC‑20) – A receipt minted by the pool contract; holders can later redeem it for the underlying assets plus accrued fees.
  • Swap Fee (e.g., 0.30 %) – A small percentage taken from each trade and credited to LPs; in Uniswap V3 the fee tier is selectable per pool.
  • Tick & Price Range (V3) – V3 lets LPs concentrate liquidity in a custom price interval (tickLower / tickUpper), dramatically improving capital efficiency.
  • getAmountOut (Solidity) – Helper that applies the constant‑product formula and fee to compute how many output tokens a swap will return.

solidity function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint) {
uint amountInWithFee = amountIn * 997; // 0.30% fee → 1000‑3
uint numerator = amountInWithFee * reserveOut;
uint denominator = reserveIn * 1000 + amountInWithFee;
return numerator / denominator; }


  • swap (V2 Router) – The external function a dApp calls to trade tokens; it pulls the input token, runs the invariant check, and sends the output token.

js const tx = await router.swapExactTokensForTokens(
ethers.utils.parseUnits('1.0', 18), // amountIn
0, // amountOutMin (slippage)
[tokenA.address, tokenB.address], // path
wallet.address, // to
Math.floor(Date.now() / 1000) + 60 // deadline );


  • sqrtPriceX96 (V3) – Fixed‑point Q64.96 representation of the current price; used for precise math without floating‑point errors.
  • feeGrowthGlobalX128 – Tracks cumulative fees per unit of liquidity; LPs claim fees by comparing the current value with the snapshot taken when they minted LP tokens.
  • delegatecall vs call – V3 pools use delegatecall to execute user‑provided hooks (e.g., flash swaps) while preserving the pool’s storage context.


Step‑by‑Step / Process Flow

  1. Write the Pool Contract – Clone the Uniswap V2 core repo or use the V3 periphery library. In Remix, add UniswapV2Pair.sol (or UniswapV3Pool.sol) and import OpenZeppelin’s ERC‑20.
  2. Compile with Hardhat – Run npx hardhat compile using Solidity 0.8.19 (the version Uniswap V3 ships with).
  3. Deploy to a Testnet

bash
npx hardhat run scripts/deploy.js --network goerli

The script should:

- Deploy the factory (UniswapV2Factory or UniswapV3Factory).

- Call createPair(tokenA, tokenB) (V2) or createPool(tokenA, tokenB, fee) (V3).
4. Add Liquidity – Using Ethers.js, approve both tokens, then call addLiquidity (V2) or mint (V3) with the desired price range.
5. Swap via Router – In your front‑end, instantiate UniswapV2Router02 (or V3 router) and invoke swapExactTokensForTokens. Verify the output amount matches the getAmountOut calculation.


Common Mistakes

  • Mistake: Supplying unequal token values when creating a V2 pair.
    Correction: Always deposit tokens with the same USD value; otherwise the pool starts with a skewed price and LPs lose capital.

  • Mistake: Forgetting to approve the router contract before calling swap.
    Correction: Call token.approve(router.address, amount) first; otherwise the transaction reverts with “ERC20: transfer amount exceeds allowance”.

  • Mistake: Using tx.origin for access control in a flash‑swap callback.
    Correction: Use msg.sender (or a role‑based onlyOwner) because tx.origin can be spoofed through a malicious contract, opening a re‑entrancy vector.

  • Mistake: Ignoring the price‑range parameter in V3 and depositing liquidity across the entire curve.
    Correction: Choose a tight tick range around the current price to earn higher fees per capital; otherwise you earn the same as V2 but pay higher gas.

  • Mistake: Relying on block.timestamp for deadline checks in the router.
    Correction: Use a reasonable buffer (e.g., now + 300) and enforce a maximum slippage; miners can manipulate timestamps by up to ~15 seconds.


Blockchain Developer Interview / Practical Insights

  1. “Explain why an AMM can’t guarantee a specific price.” – Interviewers expect you to mention the invariant (x·y = k) and that price is derived from pool balances, which shift with each trade.
  2. “Differentiate Uniswap V2’s constant‑product from V3’s concentrated liquidity.” – Highlight that V2 uses a single global k, while V3 splits liquidity into ticks allowing LPs to allocate capital where they expect most trades.
  3. “What are the gas‑implications of using delegatecall in a V3 pool?” – Show you know that delegatecall preserves storage but adds ~10 k gas overhead; it’s acceptable for flash‑swap hooks but should be minimized.
  4. “How would you audit the fee‑distribution logic?” – Mention checking feeGrowthGlobalX128 updates, ensuring LPs can’t claim more than their share, and verifying that rounding errors are handled with uint128 arithmetic.

Quick Check Questions

  1. Scenario: A contract calls router.swapExactETHForTokens but sets amountOutMin = 0.
    Answer: This opens a front‑running risk; the attacker can push the price just before the swap, causing the user to receive far fewer tokens. Always set a realistic amountOutMin based on slippage tolerance.

  2. Scenario: An LP adds liquidity to a V3 pool with tickLower = -887272 and tickUpper = 887272.
    Answer: That’s the full‑range (same as V2) and defeats V3’s capital efficiency; the LP will earn lower fees per capital compared to a concentrated range.

  3. Scenario: A developer uses require(reserve0 > 0 && reserve1 > 0) inside the swap function.
    Answer: The check is unnecessary because the invariant already prevents division‑by‑zero; it adds extra gas and can block legitimate first‑swap initialization.


Last‑Minute Cram Sheet (10 one‑liners)

  1. ⚠️ Never trust tx.origin for auth – it can be hijacked via a malicious contract.
  2. k = reserve0 * reserve1 – the constant‑product invariant; any deviation reverts the swap.
  3. V2 fee = 0.30 % → multiplier 997/1000 – used in getAmountOut.
  4. V3 fee tiers: 0.05 %, 0.30 %, 1 % (encoded as 500, 3000, 10000).
  5. sqrtPriceX96 = price * 2^96; use FullMath.mulDiv for precise arithmetic.
  6. Gas tip: Store reserves in a single uint112 slot (reserve0, reserve1) to stay under the 32‑byte slot limit.
  7. Compiler version: Uniswap V3 contracts compile cleanly with pragma solidity ^0.8.19;.
  8. LP token decimals = 18 – matches ERC‑20 standard; use balanceOf to compute share.
  9. Flash swap: Call swap with amount0Out or amount1Out > 0 and implement uniswapV2Call (V2) or uniswapV3SwapCallback (V3).
  10. Common attack vector: price manipulation via large single swaps; mitigate with maxSwap limits or time‑weighted average price (TWAP) oracles.


ADVERTISEMENT