ChainScore Labs
LABS
Guides

What Happens Internally When You Add or Remove Liquidity

Chainscore © 2025
core-concepts

Core Prerequisite Concepts

Understanding the mechanics of Automated Market Makers (AMMs) requires familiarity with these foundational DeFi concepts.

01

Constant Product Formula (x*y=k)

Constant Product Market Maker (CPMM) is the core algorithm for many DEXs like Uniswap V2. It dictates that the product of the reserves of two tokens in a pool must remain constant.

  • Price is determined by the ratio of the reserves: Price of Token A = Reserve of Token B / Reserve of Token A.
  • Adding liquidity increases both reserves proportionally, keeping k constant.
  • Removing liquidity decreases both reserves, also preserving k.
  • This formula creates price slippage as trade size increases relative to pool depth.
02

Liquidity Provider (LP) Tokens

LP Tokens are ERC-20 tokens minted upon deposit and burned upon withdrawal, representing a user's share of a liquidity pool.

  • They are proof of your proportional ownership of the pooled assets.
  • The quantity minted is based on your contribution relative to existing reserves.
  • Holding LP tokens entitles you to a share of the trading fees accrued by the pool.
  • To withdraw your underlying assets, you must burn your LP tokens, redeeming your share of the current reserves.
03

Reserve Balances & Price Impact

Reserve Balances are the real-time quantities of each token held in the pool's smart contract.

  • The ratio of these reserves determines the instantaneous spot price.
  • Adding liquidity shifts the ratio if deposits aren't perfectly proportional, causing an implicit swap and potential loss.
  • Removing liquidity changes the reserves and thus the price for subsequent traders.
  • Large single-sided deposits or withdrawals create significant price impact, moving the market and affecting LP returns.
04

Impermanent Loss (Divergence Loss)

Impermanent Loss is the opportunity cost LPs face when the price ratio of the pooled assets changes compared to simply holding them.

  • It occurs because the AMM algorithm automatically buys the depreciating asset and sells the appreciating one.
  • The loss is 'impermanent' until you withdraw; if prices revert, the loss disappears.
  • It is mathematically guaranteed when the price ratio diverges from the ratio at deposit.
  • Profits from trading fees must outweigh this loss for providing liquidity to be profitable.
05

Tick & Concentrated Liquidity

Concentrated Liquidity, introduced by Uniswap V3, allows LPs to provide capital within specific price ranges (ticks) instead of the full 0 to ∞ curve.

  • This increases capital efficiency, earning more fees on the same capital within the chosen range.
  • Adding liquidity requires specifying a lower and upper price tick boundary.
  • The liquidity is only active and earning fees when the market price is within your range.
  • Removing liquidity calculates your share of the reserves currently concentrated between your ticks.

Step-by-Step: Adding Liquidity

Process overview

1

Approve Token Spending

Authorize the pool contract to transfer your tokens.

Detailed Instructions

Before depositing tokens, you must grant the liquidity pool contract permission to withdraw them from your wallet. This is done by calling the approve function on each token's ERC-20 contract, specifying the pool's address and the amount. For a full deposit, you can approve the maximum uint256 value (2**256 - 1) to avoid repeated transactions.

  • Sub-step 1: Call approve(spender, amount) on the first token's contract (e.g., WETH).
  • Sub-step 2: Call approve(spender, amount) on the second token's contract (e.g., USDC).
  • Sub-step 3: Verify the approval by checking the allowance(yourAddress, poolAddress) for each token.
solidity
// Example: Approving a Uniswap V2 router IERC20(wethAddress).approve(uniswapRouterAddress, type(uint256).max);

Tip: Always verify the spender address is the correct, verified router or pool contract to prevent security risks.

2

Calculate Deposit Amounts

Determine the required token quantities based on the pool's current ratio.

Detailed Instructions

The amounts you deposit must maintain the pool's existing reserve ratio to avoid introducing price impact. For a constant product AMM like Uniswap V2, if you provide liquidity for an existing pool, you calculate one token amount based on the other using the formula: amountB = (amountA * reserveB) / reserveA. Most front-ends perform this calculation automatically when you input one side.

  • Sub-step 1: Query the pool's current reserves for Token A and Token B.
  • Sub-step 2: Decide the amount of Token A you wish to deposit.
  • Sub-step 3: Calculate the required proportional amount of Token B using the reserve ratio.
solidity
// Calculating the proportional deposit for tokenB uint256 amountBDesired = (amountADesired * reserveB) / reserveA;

Tip: The calculated amounts have a small slippage tolerance; transactions may revert if the pool's ratio changes before your transaction is mined.

3

Call the Add Liquidity Function

Execute the transaction to mint liquidity pool tokens.

Detailed Instructions

Invoke the router's addLiquidity function, passing the token addresses, your calculated amounts, minimum acceptable amounts (to protect against slippage), your recipient address, and a deadline. Internally, the router transfers your tokens to the pool and calls the pool's mint function, which issues LP tokens to your address representing your share of the pool.

  • Sub-step 1: Construct the addLiquidity call with all required parameters.
  • Sub-step 2: Sign and broadcast the transaction via your wallet.
  • Sub-step 3: Monitor the transaction hash for confirmation on-chain.
solidity
// Uniswap V2 Router function signature router.addLiquidity( address tokenA, address tokenB, uint amountADesired, uint amountBDesired, uint amountAMin, uint amountBMin, address to, uint deadline );

Tip: The to address receives the minted LP tokens. Setting a reasonable deadline (e.g., block.timestamp + 1200) prevents stale transactions from executing in unfavorable conditions.

4

Verify Minted LP Tokens and Pool State

Confirm the transaction outcome and your new position.

Detailed Instructions

After the transaction is confirmed, verify that you received the expected amount of LP tokens (e.g., Uniswap V2 LP tokens are ERC-20 tokens). Check the pool's updated total supply and reserves to confirm your deposit was included. Your share of the pool is calculated as yourLPBalance / totalLPSupply. This share entitles you to a proportional claim on the pool's reserves and accumulated fees.

  • Sub-step 1: Check your wallet balance for the LP token at the pool's contract address.
  • Sub-step 2: Query the pool's totalSupply() and getReserves() to see the new state.
  • Sub-step 3: Calculate your pool ownership percentage using the formula above.
solidity
// Querying LP token balance and pool data uint256 myLPTokens = IERC20(pairAddress).balanceOf(msg.sender); (uint112 reserve0, uint112 reserve1, ) = IUniswapV2Pair(pairAddress).getReserves(); uint256 totalSupply = IUniswapV2Pair(pairAddress).totalSupply();

Tip: Bookmark the pool address and your LP token balance; you will need them to remove liquidity later.

Step-by-Step: Removing Liquidity

Process for withdrawing your assets from a liquidity pool and burning your LP tokens.

1

Initiate the Removal Transaction

Call the pool's smart contract to request liquidity removal.

Detailed Instructions

You must call the removeLiquidity function on the AMM's router contract (e.g., Uniswap V2's Router02). This function requires specifying the exact amount of LP tokens you wish to burn and the minimum amounts of each underlying token you are willing to accept, known as slippage tolerance. The function signature typically includes the two token addresses, the liquidity amount, and the minimum amounts for tokenA and tokenB.

  • Sub-step 1: Connect your wallet to the DApp interface and navigate to the pool management section.
  • Sub-step 2: Input the amount of LP tokens to remove or select a percentage (e.g., 25%, 50%, 100%).
  • Sub-step 3: Review the estimated amounts of Token A and Token B you will receive, which are calculated based on the current pool reserves.
solidity
// Example function call structure router.removeLiquidity( address(tokenA), address(tokenB), liquidityAmount, // e.g., 1000000000000000000 (1.0 LP token with 18 decimals) amountAMin, // Minimum Token A to accept amountBMin, // Minimum Token B to accept msg.sender, block.timestamp + 300 // Deadline: transaction reverts after 5 minutes );

Tip: Setting appropriate minimum amounts (amountAMin, amountBMin) is critical to protect against front-running and significant price movements between transaction submission and confirmation.

2

Internal LP Token Burn and Reserve Update

The contract burns your LP tokens and calculates your share of the pool.

Detailed Instructions

Upon receiving your transaction, the router contract first transfers your LP tokens from your wallet to itself. It then calls the pool's core contract (e.g., the Pair contract). The core contract performs a burn operation on the received LP tokens, permanently destroying them. It calculates your proportional share of the pool's total reserves using the formula: share = liquidity / totalSupply. Your entitled amounts of Token A and Token B are amountA = share * reserveA and amountB = share * reserveB.

  • Sub-step 1: The router's removeLiquidity function calls the pair contract's burn function, passing your address.
  • Sub-step 2: The pair contract burns the LP tokens, reducing the totalSupply.
  • Sub-step 3: It calculates the amounts of both tokens owed to you based on the current reserves and your burned liquidity share.
solidity
// Simplified internal logic in the Pair contract function burn(address to) external lock returns (uint amount0, uint amount1) { (uint112 _reserve0, uint112 _reserve1,) = getReserves(); uint liquidity = balanceOf[address(this)]; // LP tokens sent to contract uint _totalSupply = totalSupply; amount0 = liquidity * _reserve0 / _totalSupply; // Using full precision amount1 = liquidity * _reserve1 / _totalSupply; _burn(address(this), liquidity); _update(_reserve0 - amount0, _reserve1 - amount1); // Updates state reserves _safeTransfer(_token0, to, amount0); _safeTransfer(_token1, to, amount1); }

Tip: The _update call reduces the pool's recorded reserves, which updates the k = x * y constant product. This is the inverse of the minting process.

3

Token Transfer and Slippage Validation

The contract sends you the underlying tokens and enforces your minimums.

Detailed Instructions

After calculating the owed amounts, the core contract transfers Token A and Token B to your specified address. Crucially, the router contract then validates that the amounts received are greater than or equal to the amountAMin and amountBMin parameters you provided. This is the slippage check. If the actual amounts are lower (due to a price change executed by another transaction in the same block), the entire transaction will revert, protecting you from an unfavorable trade. The tokens are typically sent directly to the msg.sender (your wallet) from the pair contract's reserves.

  • Sub-step 1: The pair contract executes _safeTransfer for both tokens to your address.
  • Sub-step 2: Control returns to the router contract, which checks the balances received.
  • Sub-step 3: The router compares the actual received amounts against your specified minimums (require(amountA >= amountAMin && amountB >= amountBMin)).
solidity
// Router's safety check after receiving tokens from the pair (uint amountA, uint amountB) = removeLiquidity(...); require(amountA >= amountAMin, 'Router: INSUFFICIENT_A_AMOUNT'); require(amountB >= amountBMin, 'Router: INSUFFICIENT_B_AMOUNT');

Tip: The deadline parameter (block.timestamp + 300) provides a second layer of protection, causing a revert if network congestion delays your transaction beyond the specified time, preventing execution at a future, potentially worse, price.

4

Post-Removal State and Impact

Understand the final state of the pool and your position.

Detailed Instructions

After a successful removal, your LP token balance is reduced by the burned amount, and you hold the raw Token A and Token B assets. The pool's state has permanently changed: its total liquidity (totalSupply of LP tokens) has decreased, and its reserves for both tokens are lower. This changes the pool's marginal price (the exchange rate between Token A and Token B), typically causing a small price impact. The constant product invariant k is now lower (k_new = (reserveA - amountA) * (reserveB - amountB)), but the ratio k_new / totalSupply_new remains consistent with the value represented by each remaining LP token.

  • Sub-step 1: Verify your wallet balance: LP tokens decreased, underlying tokens increased.
  • Sub-step 2: Observe the pool's new metrics on a block explorer: reduced reserves and liquidity.
  • Sub-step 3: Note that the removal likely caused a minor price shift; the asset you received more of has slightly depreciated against the other in the pool.
solidity
// Viewing final state - The new reserves are stored in the pair contract // Pseudocode for a view function call (uint112 newReserve0, uint112 newReserve1, ) = pairContract.getReserves(); uint newTotalSupply = pairContract.totalSupply(); // Price of token0 in terms of token1: newReserve1 / newReserve0

Tip: Removing liquidity does not incur a trading fee. The fee accrual mechanism only applies to swap transactions. Your withdrawal is based purely on your ownership share of the current reserves.

Pool State and User Balance Changes

Comparison of key internal state changes during liquidity operations.

State VariableAdding LiquidityRemoving LiquidityNet Effect on Pool

Total Liquidity (L)

Increases by ΔL

Decreases by ΔL

L' = L ± ΔL

Reserve A (R_A)

Increases by ΔR_A

Decreases by ΔR_A

R_A' = R_A ± ΔR_A

Reserve B (R_B)

Increases by ΔR_B

Decreases by ΔR_B

R_B' = R_B ± ΔR_B

Constant Product K

Increases to (R_A + ΔR_A)*(R_B + ΔR_B)

Decreases to (R_A - ΔR_A)*(R_B - ΔR_B)

K' = R_A' * R_B'

User's LP Token Balance

Increases by minted amount

Decreases by burned amount

LP' = LP ± minted/burned

Pool Share %

Increases (ΔL / (L + ΔL))

Decreases (ΔL / L)

Share' = User LP / Total LP

Price Impact (Slippage)

Typically < 0.5% for balanced adds

Typically < 0.3% for balanced removes

Function of ΔR relative to R

Protocol Fee Accrual (0.03%)

Accrues on ΔR_A & ΔR_B

Not applicable on removal

Accumulates in pool reserves

Economic Effects and Considerations

Adding liquidity introduces impermanent loss (divergence loss) risk, which is the opportunity cost between holding assets versus providing them in a pool. This loss occurs when the price ratio of the paired assets changes after deposit. It is not a realized loss until withdrawal but permanently reduces your portfolio value relative to simple holding.

  • Price divergence impact: The greater the price change, the higher the potential loss. A 2x price move can result in ~5.7% IL, while a 5x move can cause ~25% IL.
  • Fee income trade-off: Earned trading fees are meant to offset this risk. High-volume pools (e.g., ETH/USDC) may generate 10-20% APY in fees, potentially covering moderate IL.
  • Correlation benefit: Pairs with highly correlated assets (e.g., stablecoins) experience minimal IL, making them lower-risk yield positions.

Protocol-Specific Implementations

Concentrated Liquidity Mechanics

When you add liquidity to a Uniswap V3 pool, you are not depositing into a uniform price curve from 0 to infinity. Instead, you select a price range (tickLower, tickUpper) where your capital will be active. The protocol mints a non-fungible position NFT representing your specific liquidity parameters. Internally, the contract stores your liquidity as liquidity L, a virtual quantity used to calculate the real token amounts x and y based on the current price P and your chosen range. Removing liquidity burns the NFT and calculates your claimable token amounts based on the accrued fees and the remaining liquidity in the position.

Key Internal State Changes

  • Tick Mapping: Liquidity is added to a global tick bitmap and liquidity netting system, updating the liquidityGross and liquidityNet for the boundary ticks.
  • Fee Accounting: Fees are tracked separately as tokensOwed0 and tokensOwed1 per position, accrued proportionally to your in-range liquidity.
  • Virtual Reserves: The pool's overall liquidity L is summed from all active positions, but the actual token reserves are a derived function of L and the current sqrtPrice.

Example Calculation

If you deposit 1 ETH and 3000 USDC into the ETH/USDC 0.3% pool within the $2500-$3500 range, and the price is $3000, your initial liquidity L is calculated. If the price moves to $3200, some of your ETH is sold for USDC, and your position consists of more USDC and less ETH until the price exits your upper tick.